semantics_visitor.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. import hutnparser as hp
  2. import symbol_table as st
  3. import sys
  4. import types_mv
  5. from declare_functions_visitor import DeclareFunctionsVisitor
  6. from visitor import Visitor
  7. class SemanticsVisitor(Visitor):
  8. def __init__(self):
  9. self.symbol_table = st.SymbolTable()
  10. # there is only one input file, list is for sharing it among visitors
  11. self.inputfiles = []
  12. # inherited attribute, set in funcdecl and used in return,
  13. # to ensure that (returned type == declared type)
  14. self.current_funcdecl = None
  15. self.declare_functions_visitor =\
  16. DeclareFunctionsVisitor(self.symbol_table, self.inputfiles)
  17. @staticmethod
  18. def incompatible_types(l_type, r_type):
  19. if type(l_type) != type(r_type):
  20. if types_mv.Void in (type(l_type), type(r_type)):
  21. return True
  22. if types_mv.Element in (type(l_type), type(r_type)):
  23. return False
  24. if l_type.isNotNumber() or r_type.isNotNumber():
  25. return True
  26. return False
  27. def do_check_binary_ops_arithmetic(self, l, r):
  28. l_type, r_type = self.get_type(l), self.get_type(r)
  29. if SemanticsVisitor.incompatible_types(l_type, r_type):
  30. raise RuntimeError(
  31. "{}:{}:{}: error: invalid operands to binary operator "
  32. "(have {} and {})".format(self.inputfiles[0],
  33. l.startpos['line'],
  34. l.startpos['column'],
  35. str(l_type),
  36. str(r_type)))
  37. def check_binary_ops_arithmetic(self, tree):
  38. l, r = tree.get_tail()[0], tree.get_tail()[2]
  39. self.do_check_binary_ops_arithmetic(l, r)
  40. def generalize_binary_ops_arithmetic(self, tree):
  41. l, r = tree.get_tail()[0], tree.get_tail()[2]
  42. l_type, r_type = self.get_type(l), self.get_type(r)
  43. return types_mv.generalize_arithmetic(l_type, r_type)
  44. def check_unary_ops_arithmetic(self, tree, operator_name):
  45. l = tree.get_tail()[1]
  46. l_type = self.get_type(l)
  47. if l_type.isNotNumber():
  48. raise RuntimeError(
  49. "{}:{}:{}: error: wrong type argument to unary {} "
  50. "({})".format(self.inputfiles[0],
  51. l.startpos['line'],
  52. l.startpos['column'],
  53. operator_name,
  54. str(l_type)))
  55. def promote_unary_ops_arithmetic(self, tree):
  56. l = tree.get_tail()[1]
  57. l_type = self.get_type(l)
  58. try:
  59. return types_mv.promote_arithmetic(l_type)
  60. except RuntimeError:
  61. raise RuntimeError(
  62. "Pathological situation in promote_unary_ops_arithmetic: "
  63. "check_unary_ops_arithmetic has not been executed")
  64. # if r_type is provided, r is not used
  65. def do_check_assignment(self, l, r, r_type = None):
  66. if r_type is not None:
  67. l_type = self.get_type(l)
  68. else:
  69. l_type, r_type = self.get_type(l), self.get_type(r)
  70. if SemanticsVisitor.incompatible_types(l_type, r_type):
  71. raise RuntimeError("{}:{}:{}: error: cannot assign a value of "
  72. "type '{}' to a variable of type '{}'"
  73. .format(self.inputfiles[0],
  74. l.startpos['line'],
  75. l.startpos['column'],
  76. str(r_type),
  77. str(l_type)))
  78. def check_assignment(self, tree):
  79. l, r = tree.get_tail()[0], tree.get_tail()[2]
  80. self.do_check_assignment(l, r)
  81. def check_return(self, tree):
  82. l = self.current_funcdecl
  83. if len(tree.get_tail()) > 1:
  84. r = tree.get_tail()[1]
  85. r_type = None
  86. else:
  87. r = None
  88. r_type = types_mv.Void()
  89. if l:
  90. self.do_check_assignment(l, r, r_type)
  91. else:
  92. raise RuntimeError(
  93. "{}:{}:{}: error: 'return' is used outside of a function"
  94. .format(self.inputfiles[0],
  95. tree.startpos['line'],
  96. tree.startpos['column']))
  97. def check_predicate(self, tree):
  98. if isinstance(self.get_type(tree), types_mv.Element):
  99. return
  100. if self.get_type(tree).isNotNumber():
  101. raise RuntimeError(
  102. "{}:{}:{}: error: predicates of type '{}' are not allowed"
  103. .format(self.inputfiles[0],
  104. tree.startpos['line'],
  105. tree.startpos['column'],
  106. self.get_type(tree)))
  107. def replace_child_binary_op_with_call(self, tree, i=0):
  108. child = tree.get_tail()[i]
  109. if len(child.get_tail()) > 1:
  110. l, op, r = child.get_tail()
  111. l_type, r_type = self.get_type(l), self.get_type(r)
  112. if type(l_type) != type(r_type):
  113. raise RuntimeError(
  114. "error in visit_conjuction: children were not casted")
  115. call_name = SemanticsVisitor.call_name_binary(l_type, op)
  116. call_tree = SemanticsVisitor.func_call(call_name, [l, r])
  117. try:
  118. self.visit(call_tree)
  119. except RuntimeError:
  120. call_signature = "{0} function {1}({2}, {2})".format(
  121. str(types_mv.Boolean()), call_name, l_type)
  122. raise RuntimeError(
  123. "{}:{}:{}: error: cannot perform {}: function '{}' is "
  124. "not found".format(
  125. self.inputfiles[0],
  126. tree.startpos['line'],
  127. tree.startpos['column'],
  128. child.head,
  129. call_signature))
  130. tree.replace_child(child, call_tree)
  131. self.set_type(tree, self.get_type(tree.get_tail()[i]))
  132. def replace_child_unary_op_with_call(self, tree):
  133. child = tree.get_tail()[0]
  134. if child.head == "keep_sign":
  135. tree.replace_child(child, child.get_tail()[1])
  136. else:
  137. op, l = child.get_tail()
  138. l_type = self.get_type(l)
  139. call_name = SemanticsVisitor.call_name_unary(l_type, op)
  140. call_tree = SemanticsVisitor.func_call(call_name, [l])
  141. try:
  142. self.visit(call_tree)
  143. except RuntimeError:
  144. call_signature = "{0} function {1}({2})".format(
  145. str(types_mv.Boolean()), call_name, l_type)
  146. raise RuntimeError(
  147. "{}:{}:{}: error: cannot perform {}: function '{}' is "
  148. "not found".format(
  149. self.inputfiles[0],
  150. tree.startpos['line'],
  151. tree.startpos['column'],
  152. child.head,
  153. call_signature))
  154. tree.replace_child(child, call_tree)
  155. self.set_type(tree, self.get_type(tree.get_tail()[0]))
  156. def cast_binary_ops_arithmetic(self, tree):
  157. l, op, r = tree.get_tail()
  158. l_type, r_type = self.get_type(l), self.get_type(r)
  159. if type(l_type) != type(r_type): # if two different numeric types
  160. g_type = types_mv.generalize_arithmetic(l_type, r_type)
  161. self.perform_implicit_cast(tree, l, l_type, g_type)
  162. self.perform_implicit_cast(tree, r, r_type, g_type)
  163. def cast_binary_ops_logical(self, tree):
  164. l, op, r = tree.get_tail()
  165. l_type, r_type = self.get_type(l), self.get_type(r)
  166. self.perform_implicit_cast(tree, l, l_type, types_mv.Boolean())
  167. self.perform_implicit_cast(tree, r, r_type, types_mv.Boolean())
  168. def cast_unary_ops_arithmetic(self, tree):
  169. l = tree.get_tail()[1]
  170. l_type = self.get_type(l)
  171. p_type = self.promote_unary_ops_arithmetic(tree)
  172. self.perform_implicit_cast(tree, l, l_type, p_type)
  173. @staticmethod
  174. def func_call(name, params):
  175. zero = {'line': 0, 'column': 0}
  176. tree = hp.Tree(
  177. "func_call",
  178. [
  179. hp.Tree("rvalue",
  180. [
  181. hp.Tree("ID", [name], zero, zero)
  182. ],
  183. zero, zero),
  184. # Tokens have no impact on visit_func_call. So leave them out.
  185. # hp.Tree("LPAREN", ["("], zero, zero),
  186. # hp.Tree("COMMA", [","], zero, zero),
  187. # hp.Tree("RPAREN", [")"], zero, zero),
  188. ],
  189. zero, zero)
  190. params = [hp.Tree("expression", [p], zero, zero) for p in params]
  191. tree.tail.extend(params)
  192. return hp.Tree("expression", [tree], zero, zero)
  193. @staticmethod
  194. def cast_name(from_type, to_type):
  195. from_t = str(from_type)[0].lower()
  196. to_t = str(to_type)[0].lower()
  197. cast_name = "cast_{}2{}".format(from_t, to_t)
  198. return cast_name
  199. def raise_implicit_cast_error(self, from_type, to_type, tree):
  200. cast_name = SemanticsVisitor.cast_name(from_type, to_type)
  201. cast_signature = "{} function {}({})".format(
  202. str(to_type), cast_name, str(from_type))
  203. raise RuntimeError(
  204. "{}:{}:{}: error: cannot perform implicit cast from '{}'"
  205. " to '{}': function '{}' is not found".format(
  206. self.inputfiles[0],
  207. tree.startpos['line'],
  208. tree.startpos['column'],
  209. str(to_type), str(from_type),
  210. cast_signature))
  211. def perform_implicit_cast(self, tree, child, from_type, to_type):
  212. if types_mv.Element in (type(from_type), type(to_type)):
  213. return
  214. if type(from_type) == type(to_type):
  215. return
  216. cast_name = SemanticsVisitor.cast_name(from_type, to_type)
  217. cast_tree = \
  218. SemanticsVisitor.func_call(cast_name, [child])
  219. try:
  220. self.visit(cast_tree)
  221. except RuntimeError:
  222. self.raise_implicit_cast_error(from_type, to_type, child)
  223. tree.replace_child(child, cast_tree)
  224. types = {
  225. "Integer": "integer",
  226. "Float": "float",
  227. "Boolean": "bool",
  228. "String": "string",
  229. "Action": "action",
  230. "Type": "type"
  231. }
  232. binary_ops = {
  233. "OR": "or",
  234. "AND": "and",
  235. "EQ": "eq",
  236. "NEQ": "neq",
  237. "LT": "lt",
  238. "GT": "gt",
  239. "LE": "lte",
  240. "GE": "gte",
  241. "PLUS": "addition",
  242. "MINUS": "subtraction",
  243. "STAR": "multiplication",
  244. "SLASH": "division"
  245. }
  246. unary_ops = {
  247. "NOT": "not",
  248. "MINUS": "neg"
  249. }
  250. @staticmethod
  251. def call_name_binary(operand_type, operator):
  252. call_name = "{}_{}".format(SemanticsVisitor.types[str(operand_type)],
  253. SemanticsVisitor.binary_ops[operator.head])
  254. return call_name
  255. @staticmethod
  256. def call_name_unary(operand_type, operator):
  257. call_name = "{}_{}".format(SemanticsVisitor.types[str(operand_type)],
  258. SemanticsVisitor.unary_ops[operator.head])
  259. return call_name
  260. def dump(self):
  261. return self.tree.get_text(with_implicit=True)
  262. # return "No code generation here"
  263. # a visit_* method for each non-terminal in the grammar
  264. def visit_start(self, tree):
  265. self.symbol_table.open_scope()
  266. self.inputfiles.append(tree.inputfile)
  267. for child in tree.get_tail():
  268. self.inputfiles[0] = child.inputfile
  269. self.declare_functions_visitor.visit(child)
  270. for child in tree.get_tail():
  271. self.inputfiles[0] = child.inputfile
  272. self.visit(child)
  273. self.inputfiles.pop()
  274. self.symbol_table.close_scope()
  275. self.tree = tree
  276. def visit_statement(self, tree):
  277. self.visit_children(tree)
  278. def visit_definition(self, tree):
  279. self.visit_vardecl(tree)
  280. def visit_vardecl(self, tree):
  281. var_global = len(tree.get_tail()) == 3
  282. type_spec = tree.get_tail()[var_global+0]
  283. var_id = tree.get_tail()[var_global+1]
  284. var_type = types_mv.string_to_type(type_spec.get_text())
  285. var_name = var_id.get_text()
  286. symbol = st.Symbol(var_name, var_type,
  287. is_global=var_global or self.current_funcdecl is None)
  288. try:
  289. self.symbol_table.add(symbol)
  290. except Exception:
  291. raise RuntimeError(
  292. "{}:{}:{}: error: redeclaration of '{}'".format(
  293. self.inputfiles[0], tree.startpos['line'],
  294. tree.startpos['column'], var_name))
  295. self.set_symbol(tree, symbol)
  296. def visit_assignment(self, tree):
  297. self.visit_children(tree)
  298. self.check_assignment(tree)
  299. def visit_expression(self, tree):
  300. self.visit_children(tree)
  301. self.set_type(tree, self.get_type(tree.get_tail()[0]))
  302. def visit_binary_operation(self, tree):
  303. self.visit_children(tree)
  304. self.replace_child_binary_op_with_call(tree)
  305. def visit_disjunction(self, tree):
  306. self.visit_children(tree)
  307. if len(tree.get_tail()) == 1:
  308. self.replace_child_binary_op_with_call(tree)
  309. else:
  310. self.replace_child_binary_op_with_call(tree, 2)
  311. self.cast_binary_ops_logical(tree)
  312. self.set_type(tree, types_mv.Boolean())
  313. def visit_conjunction(self, tree):
  314. self.visit_children(tree)
  315. if len(tree.get_tail()) == 1:
  316. self.replace_child_binary_op_with_call(tree)
  317. else:
  318. self.replace_child_binary_op_with_call(tree, 2)
  319. self.cast_binary_ops_logical(tree)
  320. self.set_type(tree, types_mv.Boolean())
  321. def visit_comparison(self, tree):
  322. self.visit_children(tree)
  323. if len(tree.get_tail()) == 1:
  324. self.replace_child_binary_op_with_call(tree)
  325. else:
  326. self.replace_child_binary_op_with_call(tree, 2)
  327. self.check_binary_ops_arithmetic(tree)
  328. self.cast_binary_ops_arithmetic(tree)
  329. self.set_type(tree, types_mv.Boolean())
  330. def visit_relation(self, tree):
  331. self.visit_children(tree)
  332. if len(tree.get_tail()) == 1:
  333. self.replace_child_binary_op_with_call(tree)
  334. else:
  335. self.replace_child_binary_op_with_call(tree, 2)
  336. self.check_binary_ops_arithmetic(tree)
  337. self.cast_binary_ops_arithmetic(tree)
  338. self.set_type(tree, types_mv.Boolean())
  339. def visit_sum(self, tree):
  340. self.visit_children(tree)
  341. if len(tree.get_tail()) == 1:
  342. self.replace_child_binary_op_with_call(tree)
  343. else:
  344. self.replace_child_binary_op_with_call(tree, 2)
  345. self.check_binary_ops_arithmetic(tree)
  346. self.cast_binary_ops_arithmetic(tree)
  347. # after the cast both parameters have the same (generalized) type:
  348. self.set_type(tree, self.get_type(tree.get_tail()[0]))
  349. def visit_term(self, tree):
  350. self.visit_children(tree)
  351. if len(tree.get_tail()) == 1:
  352. self.set_type(tree, self.get_type(tree.get_tail()[0]))
  353. else:
  354. self.check_binary_ops_arithmetic(tree)
  355. self.cast_binary_ops_arithmetic(tree)
  356. # after the cast both parameters have the same (generalized) type:
  357. self.set_type(tree, self.get_type(tree.get_tail()[0]))
  358. def visit_factor(self, tree):
  359. self.visit_children(tree)
  360. if tree.get_child("primary") is not None:
  361. self.set_type(tree, self.get_type(tree.get_tail()[0]))
  362. else:
  363. self.replace_child_unary_op_with_call(tree)
  364. def visit_logical_not(self, tree):
  365. self.visit_children(tree)
  366. l = tree.get_tail()[1]
  367. l_type = self.get_type(l)
  368. self.perform_implicit_cast(tree, l, l_type, types_mv.Boolean())
  369. self.set_type(tree, self.get_type(tree.get_tail()[1]))
  370. def visit_invert_sign(self, tree):
  371. self.visit_children(tree)
  372. self.check_unary_ops_arithmetic(tree, "minus")
  373. self.cast_unary_ops_arithmetic(tree)
  374. self.set_type(tree, self.get_type(tree.get_tail()[1]))
  375. def visit_keep_sign(self, tree):
  376. self.visit_children(tree)
  377. self.check_unary_ops_arithmetic(tree, "plus")
  378. self.cast_unary_ops_arithmetic(tree)
  379. self.set_type(tree, self.get_type(tree.get_tail()[1]))
  380. def visit_primary(self, tree):
  381. self.visit_children(tree)
  382. self.set_type(tree, self.get_type(tree.get_tail()[0]))
  383. def visit_parenthesized(self, tree):
  384. self.visit_children(tree)
  385. self.set_type(tree, self.get_type(tree.get_tail()[1]))
  386. def visit_atomvalue(self, tree):
  387. self.visit_children(tree)
  388. self.set_type(tree, self.get_type(tree.get_tail()[0]))
  389. def visit_type_specifier(self, tree):
  390. self.set_type(tree, types_mv.Type())
  391. def visit_actionname(self, tree):
  392. self.set_type(tree, types_mv.Action())
  393. def visit_string(self, tree):
  394. self.set_type(tree, types_mv.String())
  395. def visit_integer(self, tree):
  396. self.set_type(tree, types_mv.Integer())
  397. def visit_float(self, tree):
  398. self.set_type(tree, types_mv.Float())
  399. # there is no such rule in the grammar, we just avoid code duplicates
  400. def visit_id(self, tree):
  401. name = tree.get_text()
  402. try:
  403. symbol = self.symbol_table.get(name)
  404. except KeyError:
  405. raise RuntimeError("{}:{}:{}: error: '{}' is not declared".format(
  406. self.inputfiles[0], tree.startpos['line'],
  407. tree.startpos['column'], name))
  408. self.set_type(tree, symbol.type)
  409. self.set_symbol(tree, symbol)
  410. def visit_rvalue(self, tree):
  411. self.visit_id(tree)
  412. def visit_lvalue(self, tree):
  413. self.visit_id(tree)
  414. def visit_func_call(self, tree):
  415. self.visit_children(tree)
  416. symbol = self.get_symbol(tree.get_tail()[0])
  417. self.set_type(tree, symbol.type)
  418. if not symbol.is_func():
  419. if isinstance(symbol.type, types_mv.Element):
  420. sys.stderr.write("{}:{}:{}: warning: calling a variable of type "
  421. "'Element'\n".format(self.inputfiles[0],
  422. tree.startpos['line'],
  423. tree.startpos['column'],
  424. symbol.name))
  425. return # allow the call without knowing the declaration
  426. raise RuntimeError(
  427. "{}:{}:{}: error: '{}' is a variable of type '{}', not a "
  428. "function".format(self.inputfiles[0],
  429. tree.startpos['line'],
  430. tree.startpos['column'],
  431. symbol.name,
  432. symbol.type))
  433. expressions = tree.get_children("expression")
  434. if len(expressions) != len(symbol.params):
  435. raise RuntimeError(
  436. "{}:{}:{}: error: wrong number of arguments to "
  437. "function '{}'".format(self.inputfiles[0],
  438. tree.startpos['line'],
  439. tree.startpos['column'],
  440. symbol.signature()))
  441. for i in range(len(expressions)):
  442. arg_type = self.get_type(expressions[i])
  443. param_type = symbol.params[i]
  444. if SemanticsVisitor.incompatible_types(arg_type, param_type):
  445. raise RuntimeError(
  446. "{}:{}:{}: error: argument {} has type '{}' instead of "
  447. "'{}', calling function '{}'".format(
  448. self.inputfiles[0],
  449. tree.startpos['line'],
  450. tree.startpos['column'],
  451. i + 1,
  452. str(arg_type),
  453. str(param_type),
  454. symbol.signature()))
  455. if type(arg_type) != type(param_type):
  456. self.perform_implicit_cast(tree, expressions[i], arg_type,
  457. param_type)
  458. if symbol.name in ["input", "output"]:
  459. tree.head = symbol.name
  460. def visit_input(self, tree):
  461. pass # no need to visit it again
  462. def visit_output(self, tree):
  463. pass # no need to visit it again
  464. def visit_dictionary(self, tree):
  465. self.set_type(tree, types_mv.Element)
  466. def visit_list(self, tree):
  467. self.set_type(tree, types_mv.Element)
  468. def visit_dict_item(self, tree):
  469. pass
  470. def visit_ifelse(self, tree):
  471. self.visit_children(tree)
  472. expressions = tree.get_children("expression")
  473. for expression in expressions:
  474. self.check_predicate(expression)
  475. def visit_while(self, tree):
  476. self.visit_children(tree)
  477. expression = tree.get_child("expression")
  478. self.check_predicate(expression)
  479. def visit_block(self, tree):
  480. self.symbol_table.open_scope()
  481. self.visit_children(tree)
  482. self.symbol_table.close_scope()
  483. def visit_func_body(self, tree):
  484. self.visit_children(tree)
  485. def visit_funcdecl(self, tree):
  486. # here we only visit the body cause the declaration is already done
  487. # by declare_functions_visitor
  488. if tree.get_child('func_body') is not None:
  489. self.current_funcdecl = tree
  490. self.symbol_table.open_scope()
  491. self.visit_children(tree)
  492. self.symbol_table.close_scope()
  493. self.current_funcdecl = None
  494. def visit_parameter(self, tree):
  495. param_id = tree.get_child("ID")
  496. type_spec = tree.get_child("type_specifier")
  497. param_type = types_mv.string_to_type(type_spec.get_text())
  498. param_name = param_id.get_text()
  499. symbol = st.Symbol(param_name, param_type, is_global=False)
  500. try:
  501. self.symbol_table.add(symbol)
  502. except Exception:
  503. raise RuntimeError(
  504. "{}:{}:{}: error: redeclaration of '{}'".format(
  505. self.inputfiles[0], tree.startpos['line'],
  506. tree.startpos['column'], param_name))
  507. self.set_symbol(tree, symbol)
  508. def visit_return(self, tree):
  509. self.visit_children(tree)
  510. self.check_return(tree)
  511. def visit_bool(self, tree):
  512. self.set_type(tree, types_mv.Boolean())