Skip to content

Commit 827f652

Browse files
committed
type checking and function symbols
1 parent 922b28d commit 827f652

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

analyzer.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ def build_symtable(ast):
55
if not isinstance(ast, Function) or ast.name != 'main' or ast.deco['type'] != Type.VOID or len(ast.args)>0:
66
raise Exception('Cannot find a valid entry point')
77
symtable = SymbolTable()
8+
symtable.add_fun(ast.name, [], ast.deco)
89
process_scope(ast, symtable)
910

1011
def process_scope(fun, symtable):
@@ -14,54 +15,76 @@ def process_scope(fun, symtable):
1415
symtable.add_var(*v)
1516
for v in fun.var: # process local variables
1617
symtable.add_var(*v)
18+
for f in fun.fun: # process nested functions: first add function symbols to the table
19+
symtable.add_fun(f.name, [d['type'] for v,d in f.args], f.deco)
1720
for f in fun.fun: # then process nested function bodies
1821
process_scope(f, symtable)
1922
for s in fun.body: # process the list of statements
2023
process_stat(s, symtable)
2124
symtable.pop_scope()
2225

2326
def process_stat(n, symtable): # process "statement" syntax tree nodes
24-
if isinstance(n, Print):
27+
if isinstance(n, Print): # no type checking is necessary
2528
process_expr(n.expr, symtable)
2629
elif isinstance(n, Return):
2730
if n.expr is None: return
2831
process_expr(n.expr, symtable)
32+
if symtable.ret_stack[-1]['type'] != n.expr.deco['type']:
33+
raise Exception('Incompatible types in return statement, line %s', n.deco['lineno'])
2934
elif isinstance(n, Assign):
3035
process_expr(n.expr, symtable)
3136
deco = symtable.find_var(n.name)
37+
n.deco['type'] = deco['type']
38+
if n.deco['type'] != n.expr.deco['type']:
39+
raise Exception('Incompatible types in assignment statement, line %s', n.deco['lineno'])
3240
update_nonlocals(n.name, symtable) # used in "readable" python transpilation only
3341
elif isinstance(n, FunCall): # no type checking is necessary
3442
process_expr(n, symtable)
3543
elif isinstance(n, While):
3644
process_expr(n.expr, symtable)
45+
if n.expr.deco['type'] != Type.BOOL:
46+
raise Exception('Non-boolean expression in while statement, line %s', n.deco['lineno'])
3747
for s in n.body:
3848
process_stat(s, symtable)
3949
elif isinstance(n, IfThenElse):
4050
process_expr(n.expr, symtable)
51+
if n.expr.deco['type'] != Type.BOOL:
52+
raise Exception('Non-boolean expression in if statement, line %s', n.deco['lineno'])
4153
for s in n.ibody + n.ebody:
4254
process_stat(s, symtable)
4355
else:
4456
raise Exception('Unknown statement type')
4557

4658
def process_expr(n, symtable): # process "expression" syntax tree nodes
4759
if isinstance(n, ArithOp):
60+
n.deco['type'] = Type.INT
4861
process_expr(n.left, symtable)
4962
process_expr(n.right, symtable)
63+
if n.left.deco['type'] != Type.INT or n.right.deco['type'] != Type.INT:
64+
raise Exception('Arithmetic operation over non-integer type in line %s', n.deco['lineno'])
5065
elif isinstance(n, LogicOp):
66+
n.deco['type'] = Type.BOOL
5167
process_expr(n.left, symtable)
5268
process_expr(n.right, symtable)
53-
elif isinstance(n, Integer):
69+
if (n.left.deco['type'] != n.right.deco['type']) or \
70+
(n.op in ['<=', '<', '>=', '>'] and n.left.deco['type'] != Type.INT) or \
71+
(n.op in ['&&', '||'] and n.left.deco['type'] != Type.BOOL):
72+
raise Exception('Boolean operation over incompatible types in line %s', n.deco['lineno'])
73+
elif isinstance(n, Integer): # no type checking is necessary
5474
n.deco['type'] = Type.INT
55-
elif isinstance(n, Boolean):
75+
elif isinstance(n, Boolean): # no type checking is necessary
5676
n.deco['type'] = Type.BOOL
57-
elif isinstance(n, Var):
77+
elif isinstance(n, Var): # no type checking is necessary
5878
deco = symtable.find_var(n.name)
79+
n.deco['type'] = deco['type']
5980
update_nonlocals(n.name, symtable) # used in "readable" python transpilation only
6081
elif isinstance(n, FunCall):
6182
for s in n.args:
6283
process_expr(s, symtable)
63-
elif isinstance(n, String):
64-
pass
84+
deco = symtable.find_fun(n.name, [a.deco['type'] for a in n.args])
85+
n.deco['type'] = deco['type']
86+
elif isinstance(n, String): # no type checking is necessary
87+
n.deco['type'] = Type.STRING
6588
else:
6689
raise Exception('Unknown expression type', n)
6790

symtable.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
class SymbolTable():
22
def __init__(self):
33
self.variables = [{}] # stack of variable symbol tables
4+
self.functions = [{}] # stack of function symbol tables
45
self.ret_stack = [ None ] # stack of enclosing function symbols, useful for return statements
6+
def add_fun(self, name, argtypes, deco): # a function can be identified by its name and a list of argument types, e.g.
7+
signature = (name, *argtypes) # fun foo(x:bool, y:int) : int {...} has ('foo',Type.BOOL,Type.INT) signature
8+
if signature in self.functions[-1]:
9+
raise Exception('Double declaration of the function %s %s' % (signature[0], signature[1:]))
10+
self.functions[-1][signature] = deco
511

612
def add_var(self, name, deco):
713
if name in self.variables[-1]:
@@ -10,14 +16,23 @@ def add_var(self, name, deco):
1016

1117
def push_scope(self, deco):
1218
self.variables.append({})
19+
self.functions.append({})
1320
self.ret_stack.append(deco)
1421

1522
def pop_scope(self):
1623
self.variables.pop()
24+
self.functions.pop()
1725
self.ret_stack.pop()
1826

1927
def find_var(self, name):
2028
for i in reversed(range(len(self.variables))):
2129
if name in self.variables[i]:
2230
return self.variables[i][name]
2331
raise Exception('No declaration for the variable %s' % name)
32+
33+
def find_fun(self, name, argtypes):
34+
signature = (name, *argtypes)
35+
for i in reversed(range(len(self.functions))):
36+
if signature in self.functions[i]:
37+
return self.functions[i][signature]
38+
raise Exception('No declaration for the function %s' % signature[0], signature[1:])

0 commit comments

Comments
 (0)