Working Lilith block header parser
This commit is contained in:
parent
43bbcda050
commit
014ce0b21d
6 changed files with 134 additions and 55 deletions
|
@ -1,7 +1,7 @@
|
||||||
py_project(
|
py_project(
|
||||||
name = "lilith",
|
name = "lilith",
|
||||||
lib_deps = [
|
lib_deps = [
|
||||||
|
py_requirement("lark"),
|
||||||
],
|
],
|
||||||
test_deps = [
|
test_deps = [
|
||||||
py_requirement("hypothesis"),
|
py_requirement("hypothesis"),
|
||||||
|
|
|
@ -40,8 +40,12 @@ FIXME: we need bare words, we need strings
|
||||||
|
|
||||||
FIXME: We need the object language
|
FIXME: We need the object language
|
||||||
|
|
||||||
|
!def[openapi]
|
||||||
|
!frag[lang: yaml]
|
||||||
|
|
||||||
!def[main]
|
!def[main]
|
||||||
!frag[lang: lil]
|
!frag[lang: lil]
|
||||||
; is importing a bang-operation?
|
; is importing a bang-operation?
|
||||||
|
|
||||||
import[tagle]
|
import[tagle]
|
||||||
print[tangle[pitch, syntax]]
|
print[str.join["", list[pitch, syntax]]]
|
||||||
|
|
21
projects/lilith/src/python/lilith/grammar.lark
Normal file
21
projects/lilith/src/python/lilith/grammar.lark
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
%import common.WORD
|
||||||
|
%import common.NUMBER
|
||||||
|
%import common.WS
|
||||||
|
%ignore WS
|
||||||
|
|
||||||
|
STRING: /""".*?"""/ | /".*?"/
|
||||||
|
|
||||||
|
atom: NUMBER | WORD | STRING
|
||||||
|
|
||||||
|
expr: atom
|
||||||
|
|
||||||
|
args: expr ("," args)?
|
||||||
|
|
||||||
|
kwargs: expr ":" expr ("," kwargs)?
|
||||||
|
|
||||||
|
_args_kwargs: args ";" kwargs
|
||||||
|
_args: args
|
||||||
|
_kwargs: kwargs
|
||||||
|
arguments: _args_kwargs | _args | _kwargs
|
||||||
|
|
||||||
|
header: "!" WORD "[" arguments? "]"
|
|
@ -4,13 +4,26 @@ Variously poor parsing for Lilith.
|
||||||
|
|
||||||
import typing as t
|
import typing as t
|
||||||
import re
|
import re
|
||||||
|
from importlib.resources import read_text
|
||||||
|
|
||||||
import lark
|
import lark
|
||||||
|
|
||||||
|
GRAMMAR = read_text('lilith', 'grammar.lark')
|
||||||
|
|
||||||
|
|
||||||
|
# !foo[bar]
|
||||||
|
# !def[name]
|
||||||
|
# !frag[lang: yaml]
|
||||||
|
# !end
|
||||||
|
# all this following tex
|
||||||
|
class Args(t.NamedTuple):
|
||||||
|
positionals: object = []
|
||||||
|
kwargs: object = {}
|
||||||
|
|
||||||
|
|
||||||
class Block(t.NamedTuple):
|
class Block(t.NamedTuple):
|
||||||
tag: str
|
tag: str
|
||||||
args: list
|
args: Args
|
||||||
kwargs: list
|
|
||||||
body_lines: list
|
body_lines: list
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -19,6 +32,18 @@ class Block(t.NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
class TreeToTuples(lark.Transformer):
|
class TreeToTuples(lark.Transformer):
|
||||||
|
def atom(self, args):
|
||||||
|
return args[0]
|
||||||
|
|
||||||
|
def expr(self, args):
|
||||||
|
return args[0]
|
||||||
|
|
||||||
|
def args(self, args):
|
||||||
|
_args = [args[0].value]
|
||||||
|
if len(args) == 2:
|
||||||
|
_args = _args + args[1]
|
||||||
|
return _args
|
||||||
|
|
||||||
def kwargs(self, args):
|
def kwargs(self, args):
|
||||||
d = {}
|
d = {}
|
||||||
key, val = args[0:2]
|
key, val = args[0:2]
|
||||||
|
@ -27,47 +52,34 @@ class TreeToTuples(lark.Transformer):
|
||||||
d[key.value] = val.value
|
d[key.value] = val.value
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def args(self, args):
|
def _args_kwargs(self, args):
|
||||||
_args = [args[0].value]
|
return lark.Tree('args', (args[0], args[1]))
|
||||||
if len(args) == 2:
|
|
||||||
_args = _args + args[1]
|
|
||||||
return _args
|
|
||||||
|
|
||||||
def header(self, parse_args):
|
def _args(self, args):
|
||||||
print("Header", parse_args)
|
return lark.Tree('args', (args[0], {}))
|
||||||
tag = None
|
|
||||||
args = None
|
def _kwargs(self, args):
|
||||||
kwargs = None
|
return lark.Tree('args', ([], args[0]))
|
||||||
|
|
||||||
|
def arguments(self, args):
|
||||||
|
return args
|
||||||
|
|
||||||
|
def header(self, args):
|
||||||
|
print("Header", args)
|
||||||
|
tag = args[0]
|
||||||
|
arguments = args[1] if len(args) > 1 else ([], {})
|
||||||
body = []
|
body = []
|
||||||
|
|
||||||
iargs = iter(parse_args[1])
|
return Block(tag, Args(*arguments), body)
|
||||||
tag = parse_args[0]
|
|
||||||
v = next(iargs, None)
|
|
||||||
if isinstance(v, list):
|
|
||||||
args = v
|
|
||||||
v = next(iargs, None)
|
|
||||||
if isinstance(v, dict):
|
|
||||||
kwargs = v
|
|
||||||
|
|
||||||
return Block(tag, args, kwargs, body)
|
|
||||||
|
|
||||||
|
|
||||||
block_grammar = lark.Lark("""
|
def parser_with_transformer(grammar, start="header"):
|
||||||
%import common.WORD
|
return lark.Lark(grammar,
|
||||||
%import common.WS
|
start=start,
|
||||||
%ignore WS
|
|
||||||
?start: header
|
|
||||||
|
|
||||||
args: WORD ("," args)?
|
|
||||||
kwargs: WORD ":" WORD ("," kwargs)?
|
|
||||||
arguments: args "," kwargs | args | kwargs
|
|
||||||
header: "!" WORD "[" arguments? "]"
|
|
||||||
""",
|
|
||||||
parser='lalr',
|
parser='lalr',
|
||||||
transformer=TreeToTuples())
|
transformer=TreeToTuples())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def shotgun_parse(buff: str) -> t.List[object]:
|
def shotgun_parse(buff: str) -> t.List[object]:
|
||||||
def _parse():
|
def _parse():
|
||||||
block = None
|
block = None
|
||||||
|
|
|
@ -1,3 +1,26 @@
|
||||||
"""
|
"""
|
||||||
Pytest fixtures.
|
Pytest fixtures.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from lilith.parser import Block, parser_with_transformer, GRAMMAR
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def args_grammar():
|
||||||
|
return parser_with_transformer(GRAMMAR, "args")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def kwargs_grammar():
|
||||||
|
return parser_with_transformer(GRAMMAR, "kwargs")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def arguments_grammar():
|
||||||
|
return parser_with_transformer(GRAMMAR, "arguments")
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def header_grammar():
|
||||||
|
return parser_with_transformer(GRAMMAR, "header")
|
||||||
|
|
|
@ -1,26 +1,45 @@
|
||||||
"""tests covering the Lilith parser."""
|
"""tests covering the Lilith parser."""
|
||||||
|
|
||||||
from lilith.parser import block_grammar, Block
|
from lilith.parser import Args, Block, parser_with_transformer, GRAMMAR
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('example, result', [
|
||||||
|
("1", ["1"]),
|
||||||
|
("1, 2", ["1", "2"]),
|
||||||
|
("1, 2, 3", ["1", "2", "3"]),
|
||||||
|
])
|
||||||
|
def test_parse_args(args_grammar, example, result):
|
||||||
|
assert args_grammar.parse(example) == result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('example, result', [
|
||||||
|
("foo: bar", {"foo": "bar"}),
|
||||||
|
("foo: bar, baz: qux", {"foo": "bar", "baz": "qux"}),
|
||||||
|
])
|
||||||
|
def test_parse_kwargs(kwargs_grammar, example, result):
|
||||||
|
assert kwargs_grammar.parse(example) == result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('example, result', [
|
||||||
|
("1", (["1"], {})),
|
||||||
|
("1, 2", (["1", "2"], {})),
|
||||||
|
("1, 2, 3", (["1", "2", "3"], {})),
|
||||||
|
("foo: bar", ([], {"foo": "bar"})),
|
||||||
|
("foo: bar, baz: qux", ([], {"foo": "bar", "baz": "qux"})),
|
||||||
|
("1; foo: bar, baz: qux", (["1"], {"foo": "bar", "baz": "qux"})),
|
||||||
|
])
|
||||||
|
def test_parse_arguments(arguments_grammar, example, result):
|
||||||
|
assert arguments_grammar.parse(example) == result
|
||||||
|
|
||||||
@pytest.mark.parametrize('example, result', [
|
@pytest.mark.parametrize('example, result', [
|
||||||
('!def[syntax]',
|
('!def[syntax]',
|
||||||
Block('def', ['syntax'], None, [])),
|
Block('def', Args(['syntax'], {}), [])),
|
||||||
('!frag[lang: md]',
|
('!frag[lang: md]',
|
||||||
Block('frag', None, {'lang': 'md'}, [])),
|
Block('frag', Args([], {'lang': 'md'}), [])),
|
||||||
('!frag[foo, lang: md]',
|
('!frag[foo; lang: md]',
|
||||||
Block('frag', ['foo'], {'lang': 'md'}, [])),
|
Block('frag', Args(['foo'], {'lang': 'md'}), [])),
|
||||||
])
|
])
|
||||||
def test_parse_header(example, result):
|
def test_parse_header(header_grammar, example, result):
|
||||||
assert block_grammar.parse(example) == result
|
assert header_grammar.parse(example) == result
|
||||||
|
|
||||||
|
|
||||||
(
|
|
||||||
"""!def[designdoc]
|
|
||||||
!frag[lang: md]
|
|
||||||
# Designdoc
|
|
||||||
|
|
||||||
A design document""",
|
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in a new issue