rust.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. from sccd.action_lang.static.statement import *
  2. from collections import defaultdict
  3. class UnsupportedFeature(Exception):
  4. pass
  5. def ident_scope_type(scope):
  6. return "Scope_%s" % (scope.name)
  7. def ident_scope_constructor(scope):
  8. return "new_" + ident_scope_type(scope)
  9. def ident_local(name):
  10. if name[0] == '@':
  11. return "builtin_" + name[1:]
  12. else:
  13. return "local_" + name
  14. @dataclass(frozen=True)
  15. class ScopeCommit:
  16. type_name: str
  17. supertype_name: str
  18. start: int
  19. end: int
  20. @dataclass
  21. class ScopeStackEntry:
  22. scope: Scope
  23. committed: int = 0
  24. class ScopeHelper():
  25. def __init__(self):
  26. self.scope_stack = []
  27. self.scope_structs = defaultdict(dict)
  28. self.scope_names = {}
  29. def root(self):
  30. return self.scope_stack[0].scope
  31. def push(self, scope):
  32. self.scope_stack.append( ScopeStackEntry(scope) )
  33. def pop(self):
  34. self.scope_stack.pop()
  35. def current(self):
  36. return self.scope_stack[-1]
  37. def basename(self, scope):
  38. return self.scope_names.setdefault(scope, "Scope%d_%s" % (len(self.scope_names), scope.name))
  39. def type(self, scope, end):
  40. if end == 0:
  41. return "action_lang::Empty"
  42. else:
  43. return self.basename(scope) + "_l" + str(end)
  44. def commit(self, offset, writer):
  45. start = self.current().committed
  46. end = offset
  47. type_name = self.type(self.current().scope, end)
  48. if start != end and end > 0:
  49. if start == 0:
  50. supertype_name = "action_lang::Empty"
  51. else:
  52. supertype_name = self.scope_structs[self.current().scope][start].type_name
  53. commit = ScopeCommit(type_name, supertype_name, start, end)
  54. self.scope_structs[self.current().scope][end] = commit
  55. writer.writeln("let mut scope = %s {" % type_name)
  56. writer.writeln(" _base: scope,")
  57. for v in self.current().scope.variables[start:end]:
  58. writer.writeln(" %s," % ident_local(v.name))
  59. writer.writeln("};")
  60. self.current().committed = end
  61. return type_name
  62. def write_rvalue(self, name, offset, writer):
  63. if offset < 0:
  64. writer.write("parent%d." % self.current().scope.nested_levels(offset))
  65. writer.write(ident_local(name))
  66. elif offset < self.current().committed:
  67. writer.write("scope.")
  68. writer.write(ident_local(name))
  69. else:
  70. writer.write(ident_local(name))
  71. class ActionLangRustGenerator(Visitor):
  72. def __init__(self, w):
  73. self.w = w
  74. self.scope = ScopeHelper()
  75. self.functions_to_write = [] # Function and Rust identifier
  76. def default(self, what):
  77. # self.w.write("<%s>" % what)
  78. raise UnsupportedFeature(what)
  79. def debug_print_stack(self):
  80. # Print Python stack in Rust file as a comment
  81. import traceback
  82. for line in ''.join(traceback.format_stack()).split('\n'):
  83. self.w.writeln("// "+line)
  84. def write_parent_params(self, scope, with_identifiers=True):
  85. args = []
  86. ctr = 1
  87. while scope is not self.scope.root() and scope.deepest_lookup > 0:
  88. arg = ""
  89. if with_identifiers:
  90. arg += "parent%d: " % ctr
  91. arg += "&mut %s" % self.scope.type(scope.parent, scope.parent_offset)
  92. args.append(arg)
  93. ctr += 1
  94. scope = scope.parent
  95. self.w.write(", ".join(reversed(args)))
  96. def write_parent_call_params(self, scope, skip: int = 0):
  97. args = []
  98. ctr = 0
  99. while scope is not self.scope.root() and scope.deepest_lookup > 0:
  100. if ctr == skip:
  101. # args.append("&mut scope")
  102. args.append("&mut scope")
  103. elif ctr > skip:
  104. args.append("parent%d" % (ctr-skip))
  105. ctr += 1
  106. scope = scope.parent
  107. self.w.write(", ".join(reversed(args)))
  108. # This is not a visit method because Scopes may be encountered whenever there's a function call, but they are written as structs and constructor functions, which can only be written at the module level.
  109. # When compiling Rust code, the Visitable.accept method must be called on the root of the AST, to write code wherever desired (e.g. in a main-function) followed by 'write_scope' at the module level.
  110. def write_decls(self):
  111. # Write functions
  112. for function, identifier in self.functions_to_write:
  113. scope = function.scope
  114. self.w.write("fn %s(" % (identifier))
  115. for p in function.params_decl:
  116. p.accept(self)
  117. self.w.write(", ")
  118. self.write_parent_params(scope)
  119. self.w.write(") -> ")
  120. self.write_return_type(function)
  121. self.w.writeln(" {")
  122. self.w.indent()
  123. self.w.writeln("let scope = action_lang::Empty{};")
  124. self.scope.push(function.scope)
  125. # Parameters are part of function's scope
  126. self.scope.commit(len(function.params_decl), self.w)
  127. # Visit the body. This may cause new functions to be added to self.functions_to_write, which we are iterating over (which is allowed in Python), so those will also be dealt with in this loop.
  128. function.body.accept(self)
  129. self.scope.pop()
  130. self.w.dedent()
  131. self.w.writeln("}")
  132. self.w.writeln()
  133. # Write function scopes (as structs)
  134. for scope, structs in self.scope.scope_structs.items():
  135. for end, commit in structs.items():
  136. self.w.writeln("inherit_struct! {")
  137. self.w.indent()
  138. self.w.writeln("%s (%s) {" % (commit.type_name, commit.supertype_name))
  139. for v in scope.variables[commit.start: commit.end]:
  140. self.w.write(" %s: " % ident_local(v.name))
  141. v.type.accept(self)
  142. self.w.writeln(",")
  143. self.w.writeln("}")
  144. self.w.dedent()
  145. self.w.writeln("}")
  146. self.w.writeln()
  147. def visit_Block(self, stmt):
  148. for s in stmt.stmts:
  149. s.accept(self)
  150. def visit_Assignment(self, stmt):
  151. stmt.lhs.accept(self)
  152. self.w.write(" = ")
  153. stmt.rhs.accept(self)
  154. self.w.writeln(";")
  155. if DEBUG:
  156. self.w.writeln("eprintln!(\"%s\");" % termcolor.colored(stmt.render(),'blue'))
  157. def visit_IfStatement(self, stmt):
  158. self.w.write("if ")
  159. stmt.cond.accept(self)
  160. self.w.writeln(" {")
  161. self.w.indent()
  162. stmt.if_body.accept(self)
  163. self.w.dedent()
  164. self.w.writeln("}")
  165. if stmt.else_body is not None:
  166. self.w.writeln("else {")
  167. self.w.indent()
  168. stmt.else_body.accept(self)
  169. self.w.dedent()
  170. self.w.writeln("}")
  171. def visit_ReturnStatement(self, stmt):
  172. return_type = stmt.expr.get_type()
  173. returns_closure_obj = (
  174. isinstance(return_type, SCCDFunction) and
  175. return_type.function.scope.parent is stmt.scope
  176. )
  177. self.w.write("return ")
  178. if returns_closure_obj:
  179. self.w.write("(scope, ")
  180. stmt.expr.accept(self)
  181. if returns_closure_obj:
  182. self.w.write(")")
  183. self.w.writeln(";")
  184. def visit_ExpressionStatement(self, stmt):
  185. self.w.write('')
  186. stmt.expr.accept(self)
  187. self.w.writeln(";")
  188. def visit_BoolLiteral(self, expr):
  189. self.w.write("true" if expr.b else "false")
  190. def visit_IntLiteral(self, expr):
  191. self.w.write(str(expr.i))
  192. def visit_StringLiteral(self, expr):
  193. self.w.write('"'+expr.string+'"')
  194. def visit_Array(self, expr):
  195. self.w.write("[")
  196. for el in expr.elements:
  197. el.accept(self)
  198. self.w.write(", ")
  199. self.w.write("]")
  200. def visit_BinaryExpression(self, expr):
  201. if expr.operator == "**":
  202. raise UnsupportedFeature("exponent operator")
  203. else:
  204. # always put parentheses
  205. self.w.write("(")
  206. expr.lhs.accept(self)
  207. self.w.write(" %s " % expr.operator
  208. .replace('and', '&&')
  209. .replace('or', '||')
  210. .replace('//', '/')) # integer division
  211. expr.rhs.accept(self)
  212. self.w.write(")")
  213. def visit_UnaryExpression(self, expr):
  214. self.w.write(expr.operator
  215. .replace('not', '! '))
  216. expr.expr.accept(self)
  217. def visit_Group(self, expr):
  218. expr.subexpr.accept(self)
  219. def visit_ParamDecl(self, expr):
  220. self.w.write(ident_local(expr.name))
  221. self.w.write(": ")
  222. expr.formal_type.accept(self)
  223. def visit_FunctionDeclaration(self, expr):
  224. function_identifier = "f%d_%s" % (len(self.functions_to_write), expr.scope.name)
  225. self.functions_to_write.append( (expr, function_identifier) )
  226. self.w.write(function_identifier)
  227. def visit_FunctionCall(self, expr):
  228. if isinstance(expr.function.get_type(), SCCDClosureObject):
  229. self.w.write("call_closure!(")
  230. expr.function.accept(self)
  231. self.w.write(", ")
  232. else:
  233. self.w.write("(")
  234. expr.function.accept(self)
  235. self.w.write(")(")
  236. # Call parameters
  237. for p in expr.params:
  238. p.accept(self)
  239. self.w.write(", ")
  240. if isinstance(expr.function.get_type(), SCCDClosureObject):
  241. self.write_parent_call_params(expr.function_being_called.scope, skip=1)
  242. else:
  243. self.write_parent_call_params(expr.function_being_called.scope)
  244. self.w.write(")")
  245. def visit_Identifier(self, lval):
  246. if lval.is_lvalue:
  247. # self.debug_print_stack()
  248. if lval.offset in self.scope.current().scope.children:
  249. # a child scope exists at the current offset (typically because we encountered a function declaration) - so we must commit our scope
  250. self.scope.commit(lval.offset, self.w)
  251. self.w.write('') # indent
  252. if lval.is_init:
  253. self.w.write("let mut ")
  254. self.w.write(ident_local(lval.name))
  255. else:
  256. self.scope.write_rvalue(lval.name, lval.offset, self.w)
  257. def visit_SCCDClosureObject(self, type):
  258. self.w.write("(%s, " % self.scope.type(type.scope, type.scope.size()))
  259. type.function_type.accept(self)
  260. self.w.write(")")
  261. def write_return_type(self, function: FunctionDeclaration):
  262. if function.return_type is None:
  263. self.w.write("()")
  264. else:
  265. function.return_type.accept(self)
  266. def visit_SCCDFunction(self, type):
  267. scope = type.function.scope
  268. self.w.write("fn(")
  269. for p in type.param_types:
  270. p.accept(self)
  271. self.w.write(", ")
  272. self.write_parent_params(scope, with_identifiers=False)
  273. self.w.write(") -> ")
  274. self.write_return_type(type.function)
  275. def visit__SCCDSimpleType(self, type):
  276. self.w.write(type.name
  277. .replace("int", "i32")
  278. .replace("float", "f64")
  279. .replace("str", "&'static str")
  280. )