text.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import lark
  2. from sccd.action_lang.static.statement import *
  3. from collections import defaultdict
  4. # Lark transformer for generating a parse tree of our own types.
  5. class Transformer(lark.Transformer):
  6. def __init__(self):
  7. self.macros = defaultdict(list)
  8. def set_macro(self, macro_id, constructor):
  9. self.macros[macro_id].append(constructor)
  10. def unset_macro(self, macro_id):
  11. self.macros[macro_id].pop()
  12. array = Array
  13. block = Block
  14. def string_literal(self, node):
  15. return StringLiteral(node[0][1:-1])
  16. def int_literal(self, node):
  17. return IntLiteral(int(node[0].value))
  18. def float_literal(self, node):
  19. return FloatLiteral(float(node[0].value))
  20. def bool_literal(self, node):
  21. return BoolLiteral({
  22. "True": True,
  23. "False": False,
  24. }[node[0].value])
  25. def duration_literal(self, node):
  26. val = int(node[0])
  27. suffix = node[1]
  28. unit = {
  29. "d": None, # 'd' stands for "duration", the non-unit for all zero-durations.
  30. # need this to parse zero-duration as a duration instead of int.
  31. "fs": FemtoSecond,
  32. "ps": PicoSecond,
  33. "ns": Nanosecond,
  34. "us": Microsecond,
  35. "ms": Millisecond,
  36. "s": Second,
  37. "m": Minute,
  38. "h": Hour
  39. }[suffix]
  40. return DurationLiteral(duration(val, unit))
  41. def func_call(self, node):
  42. return FunctionCall(node[0], node[1].children)
  43. def macro_call(self, node):
  44. macro_id = node[0]
  45. params = node[1].children
  46. try:
  47. constructor = self.macros[macro_id][-1]
  48. except IndexError as e:
  49. raise Exception("Unknown macro: %s" % macro_id) from e
  50. return constructor(params)
  51. def array_indexed(self, node):
  52. return ArrayIndexed(node[0], node[1])
  53. def identifier(self, node):
  54. name = node[0].value
  55. return Identifier(name)
  56. def binary_expr(self, node):
  57. return BinaryExpression(node[0], node[1].value, node[2])
  58. def unary_expr(self, node):
  59. return UnaryExpression(node[0].value, node[1])
  60. def group(self, node):
  61. return Group(node[0])
  62. def assignment(self, node):
  63. operator = node[1].value
  64. if operator == "=":
  65. return Assignment(node[0], node[2])
  66. else:
  67. # Increment, decrement etc. operators are just syntactic sugar
  68. bin_operator = {"+=": "+", "-=": "-", "*=": "*", "/=": "/", "//=": "//"}[operator]
  69. return Assignment(node[0], BinaryExpression(node[0], bin_operator, node[2]))
  70. def expression_stmt(self, node):
  71. return ExpressionStatement(node[0])
  72. def return_stmt(self, node):
  73. return ReturnStatement(node[0])
  74. def if_stmt(self, node):
  75. if len(node) == 2:
  76. return IfStatement(cond=node[0], if_body=node[1])
  77. else:
  78. return IfStatement(cond=node[0], if_body=node[1], else_body=node[2])
  79. def import_stmt(self, node):
  80. return ImportStatement(module_name=node[0])
  81. params_decl = list
  82. def param_decl(self, node):
  83. return ParamDecl(name=node[0].value, formal_type=node[1])
  84. def type_annot(self, node):
  85. return {
  86. "int": SCCDInt,
  87. "str": SCCDString,
  88. "float": SCCDFloat,
  89. "dur": SCCDDuration,
  90. }[node[0]]
  91. def func_type(self, node):
  92. if len(node) > 1:
  93. return SCCDFunction(param_types=node[0], return_type=node[1])
  94. else:
  95. return SCCDFunction(param_types=node[0])
  96. param_types = list
  97. def func_decl(self, node):
  98. return FunctionDeclaration(params_decl=node[0], body=node[1])
  99. import os, tempfile, pathlib
  100. grammar_dir = os.path.dirname(__file__)
  101. grammar_path = os.path.join(grammar_dir,"action_lang.g")
  102. with open(grammar_path) as file:
  103. grammar = file.read()
  104. cache_file = tempfile.gettempdir()+'/lark_cache_'+str(pathlib.Path(grammar_path).stat().st_mtime_ns)
  105. class TextParser:
  106. def __init__(self, parser=None):
  107. if parser is None:
  108. self.parser = lark.Lark(grammar, parser="lalr", start=["expr", "stmt"], transformer=Transformer(), cache=cache_file)
  109. else:
  110. self.parser = parser
  111. def parse_expr(self, text: str) -> Expression:
  112. return self.parser.parse(text, start="expr")
  113. def parse_stmt(self, text: str) -> Statement:
  114. return self.parser.parse(text, start="block")