| @@ -27,6 +27,7 @@ For example, the following will parse all the Python files in the standard libra | |||||
| - [error\_reporting\_lalr.py](error_reporting_lalr.py) - A demonstration of example-driven error reporting with the LALR parser | - [error\_reporting\_lalr.py](error_reporting_lalr.py) - A demonstration of example-driven error reporting with the LALR parser | ||||
| - [python\_parser.py](python_parser.py) - A fully-working Python 2 & 3 parser (but not production ready yet!) | - [python\_parser.py](python_parser.py) - A fully-working Python 2 & 3 parser (but not production ready yet!) | ||||
| - [python\_bytecode.py](python_bytecode.py) - A toy example showing how to compile Python directly to bytecode | |||||
| - [conf\_lalr.py](conf_lalr.py) - Demonstrates the power of LALR's contextual lexer on a toy configuration language | - [conf\_lalr.py](conf_lalr.py) - Demonstrates the power of LALR's contextual lexer on a toy configuration language | ||||
| - [conf\_earley.py](conf_earley.py) - Demonstrates the power of Earley's dynamic lexer on a toy configuration language | - [conf\_earley.py](conf_earley.py) - Demonstrates the power of Earley's dynamic lexer on a toy configuration language | ||||
| - [custom\_lexer.py](custom_lexer.py) - Demonstrates using a custom lexer to parse a non-textual stream of data | - [custom\_lexer.py](custom_lexer.py) - Demonstrates using a custom lexer to parse a non-textual stream of data | ||||
| @@ -81,7 +81,7 @@ with_item: test ["as" expr] | |||||
| except_clause: "except" [test ["as" NAME]] | except_clause: "except" [test ["as" NAME]] | ||||
| suite: simple_stmt | _NEWLINE _INDENT stmt+ _DEDENT | suite: simple_stmt | _NEWLINE _INDENT stmt+ _DEDENT | ||||
| ?test: or_test ["if" or_test "else" test] | lambdef | |||||
| ?test: or_test ("if" or_test "else" test)? | lambdef | |||||
| ?test_nocond: or_test | lambdef_nocond | ?test_nocond: or_test | lambdef_nocond | ||||
| lambdef: "lambda" [varargslist] ":" test | lambdef: "lambda" [varargslist] ":" test | ||||
| lambdef_nocond: "lambda" [varargslist] ":" test_nocond | lambdef_nocond: "lambda" [varargslist] ":" test_nocond | ||||
| @@ -107,7 +107,7 @@ star_expr: "*" expr | |||||
| // sake of a __future__ import described in PEP 401 (which really works :-) | // sake of a __future__ import described in PEP 401 (which really works :-) | ||||
| !_comp_op: "<"|">"|"=="|">="|"<="|"<>"|"!="|"in"|"not" "in"|"is"|"is" "not" | !_comp_op: "<"|">"|"=="|">="|"<="|"<>"|"!="|"in"|"not" "in"|"is"|"is" "not" | ||||
| ?power: await_expr ["**" factor] | |||||
| ?power: await_expr ("**" factor)? | |||||
| ?await_expr: AWAIT? atom_expr | ?await_expr: AWAIT? atom_expr | ||||
| AWAIT: "await" | AWAIT: "await" | ||||
| @@ -137,7 +137,7 @@ dictorsetmaker: ( ((test ":" test | "**" expr) (comp_for | ("," (test ":" test | | |||||
| classdef: "class" NAME ["(" [arguments] ")"] ":" suite | classdef: "class" NAME ["(" [arguments] ")"] ":" suite | ||||
| arguments: argvalue ("," argvalue)* ["," [ starargs | kwargs]] | |||||
| arguments: argvalue ("," argvalue)* ("," [ starargs | kwargs])? | |||||
| | starargs | | starargs | ||||
| | kwargs | | kwargs | ||||
| | test comp_for | | test comp_for | ||||
| @@ -145,7 +145,7 @@ arguments: argvalue ("," argvalue)* ["," [ starargs | kwargs]] | |||||
| starargs: "*" test ("," "*" test)* ("," argvalue)* ["," kwargs] | starargs: "*" test ("," "*" test)* ("," argvalue)* ["," kwargs] | ||||
| kwargs: "**" test | kwargs: "**" test | ||||
| ?argvalue: test ["=" test] | |||||
| ?argvalue: test ("=" test)? | |||||
| @@ -0,0 +1,77 @@ | |||||
| # | |||||
| # This is a toy example that compiles Python directly to bytecode, without generating an AST. | |||||
| # It currently only works for very very simple Python code. | |||||
| # | |||||
| # It requires the 'bytecode' library. You can get it using | |||||
| # | |||||
| # $ pip install bytecode | |||||
| # | |||||
| from lark import Lark, Transformer, v_args | |||||
| from lark.indenter import Indenter | |||||
| from bytecode import Instr, Bytecode | |||||
| class PythonIndenter(Indenter): | |||||
| NL_type = '_NEWLINE' | |||||
| OPEN_PAREN_types = ['LPAR', 'LSQB', 'LBRACE'] | |||||
| CLOSE_PAREN_types = ['RPAR', 'RSQB', 'RBRACE'] | |||||
| INDENT_type = '_INDENT' | |||||
| DEDENT_type = '_DEDENT' | |||||
| tab_len = 8 | |||||
| @v_args(inline=True) | |||||
| class Compile(Transformer): | |||||
| def number(self, n): | |||||
| return [Instr('LOAD_CONST', int(n))] | |||||
| def string(self, s): | |||||
| return [Instr('LOAD_CONST', s[1:-1])] | |||||
| def var(self, n): | |||||
| return [Instr('LOAD_NAME', n)] | |||||
| def arith_expr(self, a, op, b): | |||||
| # TODO support chain arithmetic | |||||
| assert op == '+' | |||||
| return a + b + [Instr('BINARY_ADD')] | |||||
| def arguments(self, args): | |||||
| return args | |||||
| def funccall(self, name, args): | |||||
| return name + args + [Instr('CALL_FUNCTION', 1)] | |||||
| @v_args(inline=False) | |||||
| def file_input(self, stmts): | |||||
| return sum(stmts, []) + [Instr("RETURN_VALUE")] | |||||
| def expr_stmt(self, lval, rval): | |||||
| # TODO more complicated than that | |||||
| name ,= lval | |||||
| assert name.name == 'LOAD_NAME' # XXX avoid with another layer of abstraction | |||||
| return rval + [Instr("STORE_NAME", name.arg)] | |||||
| def __default__(self, *args): | |||||
| assert False, args | |||||
| python_parser3 = Lark.open('python3.lark', rel_to=__file__, start='file_input', | |||||
| parser='lalr', postlex=PythonIndenter(), | |||||
| transformer=Compile(), propagate_positions=False) | |||||
| def compile_python(s): | |||||
| insts = python_parser3.parse(s+"\n") | |||||
| return Bytecode(insts).to_code() | |||||
| code = compile_python(""" | |||||
| a = 3 | |||||
| b = 5 | |||||
| print("Hello World!") | |||||
| print(a+(b+2)) | |||||
| print((a+b)+2) | |||||
| """) | |||||
| exec(code) | |||||
| # -- Output -- | |||||
| # Hello World! | |||||
| # 10 | |||||
| # 10 | |||||
| @@ -501,7 +501,7 @@ class Grammar: | |||||
| empty_indices = [x==_EMPTY for x in expansion] | empty_indices = [x==_EMPTY for x in expansion] | ||||
| if any(empty_indices): | if any(empty_indices): | ||||
| exp_options = copy(options) | |||||
| exp_options = copy(options) or RuleOptions() | |||||
| exp_options.empty_indices = empty_indices | exp_options.empty_indices = empty_indices | ||||
| expansion = [x for x in expansion if x!=_EMPTY] | expansion = [x for x in expansion if x!=_EMPTY] | ||||
| else: | else: | ||||