1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
|
"""
Creating an AST from the parse tree
===================================
This example demonstrates how to transform a parse-tree into an AST using `lark.ast_utils`.
create_transformer() collects every subclass of `Ast` subclass from the module,
and creates a Lark transformer that builds the AST with no extra code.
This example only works with Python 3.
"""
import sys
from typing import List
from dataclasses import dataclass
from lark import Lark, ast_utils, Transformer, v_args
from lark.tree import Meta
this_module = sys.modules[__name__]
#
# Define AST
#
class _Ast(ast_utils.Ast):
# This will be skipped by create_transformer(), because it starts with an underscore
pass
class _Statement(_Ast):
# This will be skipped by create_transformer(), because it starts with an underscore
pass
@dataclass
class Value(_Ast, ast_utils.WithMeta):
"Uses WithMeta to include line-number metadata in the meta attribute"
meta: Meta
value: object
@dataclass
class Name(_Ast):
name: str
@dataclass
class CodeBlock(_Ast, ast_utils.AsList):
# Corresponds to code_block in the grammar
statements: List[_Statement]
@dataclass
class If(_Statement):
cond: Value
then: CodeBlock
@dataclass
class SetVar(_Statement):
# Corresponds to set_var in the grammar
name: str
value: Value
@dataclass
class Print(_Statement):
value: Value
class ToAst(Transformer):
# Define extra transformation functions, for rules that don't correspond to an AST class.
def STRING(self, s):
# Remove quotation marks
return s[1:-1]
def DEC_NUMBER(self, n):
return int(n)
@v_args(inline=True)
def start(self, x):
return x
#
# Define Parser
#
parser = Lark("""
start: code_block
code_block: statement+
?statement: if | set_var | print
if: "if" value "{" code_block "}"
set_var: NAME "=" value ";"
print: "print" value ";"
value: name | STRING | DEC_NUMBER
name: NAME
%import python (NAME, STRING, DEC_NUMBER)
%import common.WS
%ignore WS
""",
parser="lalr",
)
transformer = ast_utils.create_transformer(this_module, ToAst())
def parse(text):
tree = parser.parse(text)
return transformer.transform(tree)
#
# Test
#
if __name__ == '__main__':
print(parse("""
a = 1;
if a {
print "a is 1";
a = 2;
}
"""))
|