better error message
This commit is contained in:
parent
56215cbac1
commit
00ed5e867d
@ -1,9 +1,21 @@
|
|||||||
|
import ast
|
||||||
|
|
||||||
class CustomError(Exception):
|
class CustomError(Exception):
|
||||||
def __init__(self, msg):
|
def __init__(self, msg, node = None):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
if node is not None:
|
||||||
|
self.at(node)
|
||||||
|
|
||||||
|
def at(self, node):
|
||||||
|
self.msg = f'Error at {node.lineno}:{node.col_offset+1}-' \
|
||||||
|
f'{node.end_lineno}:{node.end_col_offset+1} ' \
|
||||||
|
f'"{ast.unparse(node)}"\n{self.msg}'
|
||||||
|
return self
|
||||||
|
|
||||||
def stringify_subst(subst):
|
def stringify_subst(subst):
|
||||||
if isinstance(subst, str):
|
if isinstance(subst, str):
|
||||||
return subst
|
return subst
|
||||||
elements = [f"{key}: {str(value)}" for key, value in subst.items()]
|
elements = [f"{key}: {str(value)}" for key, value in subst.items()]
|
||||||
return "{" + ', '.join(elements) + "}"
|
return "{" + ', '.join(elements) + "}"
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from type_def import *
|
|||||||
from inference import *
|
from inference import *
|
||||||
|
|
||||||
# we assume having the following types:
|
# we assume having the following types:
|
||||||
# bool, int32 with associated operations
|
# bool, int32 and float with associated operations
|
||||||
|
|
||||||
# not handled now: named expression, type guard
|
# not handled now: named expression, type guard
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ def parse_expr(ctx: Context,
|
|||||||
return parse_if_expr(ctx, sym_table, body)
|
return parse_if_expr(ctx, sym_table, body)
|
||||||
if isinstance(body, ast.ListComp):
|
if isinstance(body, ast.ListComp):
|
||||||
return parse_list_comprehension(ctx, sym_table, body)
|
return parse_list_comprehension(ctx, sym_table, body)
|
||||||
raise CustomError(f'{body} is not yet supported')
|
raise CustomError(f'{body} is not yet supported', body)
|
||||||
|
|
||||||
|
|
||||||
def get_unary_op(op):
|
def get_unary_op(op):
|
||||||
@ -73,8 +73,10 @@ def parse_constant(ctx: Context,
|
|||||||
return ctx.types['bool']
|
return ctx.types['bool']
|
||||||
elif isinstance(v, int):
|
elif isinstance(v, int):
|
||||||
return ctx.types['int32']
|
return ctx.types['int32']
|
||||||
|
elif isinstance(v, float):
|
||||||
|
return ctx.types['float']
|
||||||
else:
|
else:
|
||||||
raise CustomError(f'unknown constant {v}')
|
raise CustomError(f'unknown constant {v}', node)
|
||||||
|
|
||||||
def parse_name(ctx: Context,
|
def parse_name(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
@ -82,7 +84,7 @@ def parse_name(ctx: Context,
|
|||||||
if node.id in sym_table:
|
if node.id in sym_table:
|
||||||
return sym_table[node.id]
|
return sym_table[node.id]
|
||||||
else:
|
else:
|
||||||
raise CustomError(f'unbounded variable {node.id}')
|
raise CustomError(f'unbounded variable {node.id}', node)
|
||||||
|
|
||||||
def parse_list(ctx: Context,
|
def parse_list(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
@ -92,7 +94,7 @@ def parse_list(ctx: Context,
|
|||||||
return ListType(BotType())
|
return ListType(BotType())
|
||||||
for t in types[1:]:
|
for t in types[1:]:
|
||||||
if t != types[0]:
|
if t != types[0]:
|
||||||
raise CustomError(f'inhomogeneous list is not allowed')
|
raise CustomError(f'inhomogeneous list is not allowed', node)
|
||||||
return ListType(types[0])
|
return ListType(types[0])
|
||||||
|
|
||||||
def parse_tuple(ctx: Context,
|
def parse_tuple(ctx: Context,
|
||||||
@ -107,7 +109,7 @@ def parse_attribute(ctx: Context,
|
|||||||
obj = parse_expr(node.value)
|
obj = parse_expr(node.value)
|
||||||
if node.attr in obj.fields:
|
if node.attr in obj.fields:
|
||||||
return obj.fields[node.attr]
|
return obj.fields[node.attr]
|
||||||
raise CustomError(f'unknown field {node.attr} in {obj}')
|
raise CustomError(f'unknown field {node.attr} in {obj}', node)
|
||||||
|
|
||||||
def parse_bool_ops(ctx: Context,
|
def parse_bool_ops(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
@ -117,7 +119,7 @@ def parse_bool_ops(ctx: Context,
|
|||||||
right = parse_expr(ctx, sym_table, node.values[1])
|
right = parse_expr(ctx, sym_table, node.values[1])
|
||||||
b = ctx.types['bool']
|
b = ctx.types['bool']
|
||||||
if left != b or right != b:
|
if left != b or right != b:
|
||||||
raise CustomError('operands of bool ops must be booleans')
|
raise CustomError('operands of bool ops must be booleans', node)
|
||||||
return b
|
return b
|
||||||
|
|
||||||
def parse_bin_ops(ctx: Context,
|
def parse_bin_ops(ctx: Context,
|
||||||
@ -126,7 +128,10 @@ def parse_bin_ops(ctx: Context,
|
|||||||
left = parse_expr(ctx, sym_table, node.left)
|
left = parse_expr(ctx, sym_table, node.left)
|
||||||
right = parse_expr(ctx, sym_table, node.right)
|
right = parse_expr(ctx, sym_table, node.right)
|
||||||
op = get_bin_ops(node.op)
|
op = get_bin_ops(node.op)
|
||||||
return resolve_call(left, op, [right], {}, ctx)
|
try:
|
||||||
|
return resolve_call(left, op, [right], {}, ctx)
|
||||||
|
except CustomError as e:
|
||||||
|
raise e.at(node)
|
||||||
|
|
||||||
def parse_unary_ops(ctx: Context,
|
def parse_unary_ops(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
@ -135,9 +140,12 @@ def parse_unary_ops(ctx: Context,
|
|||||||
if isinstance(node.op, ast.Not):
|
if isinstance(node.op, ast.Not):
|
||||||
b = ctx.types['bool']
|
b = ctx.types['bool']
|
||||||
if t != b:
|
if t != b:
|
||||||
raise CustomError('operands of bool ops must be booleans')
|
raise CustomError('operands of bool ops must be booleans', node)
|
||||||
return b
|
return b
|
||||||
return resolve_call(t, get_unary_op(node.op), [], {}, ctx)
|
try:
|
||||||
|
return resolve_call(t, get_unary_op(node.op), [], {}, ctx)
|
||||||
|
except CustomError as e:
|
||||||
|
raise e.at(node)
|
||||||
|
|
||||||
def parse_compare(ctx: Context,
|
def parse_compare(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
@ -147,17 +155,21 @@ def parse_compare(ctx: Context,
|
|||||||
boolean = ctx.types['bool']
|
boolean = ctx.types['bool']
|
||||||
ops = [get_bin_ops(v) for v in node.ops]
|
ops = [get_bin_ops(v) for v in node.ops]
|
||||||
for a, b, op in zip(items[:-1], items[1:], ops):
|
for a, b, op in zip(items[:-1], items[1:], ops):
|
||||||
result = resolve_call(a, op, [b], {}, ctx)
|
try:
|
||||||
if result != boolean:
|
result = resolve_call(a, op, [b], {}, ctx)
|
||||||
raise CustomError(
|
if result != boolean:
|
||||||
f'result of comparison must be bool instead of {result}')
|
raise CustomError(
|
||||||
|
f'result of comparison must be bool instead of {result}')
|
||||||
|
except CustomError as e:
|
||||||
|
raise e.at(node)
|
||||||
|
|
||||||
return boolean
|
return boolean
|
||||||
|
|
||||||
def parse_call(ctx: Context,
|
def parse_call(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
node):
|
node):
|
||||||
if len(node.keywords) > 0:
|
if len(node.keywords) > 0:
|
||||||
raise CustomError('keyword arguments are not supported')
|
raise CustomError('keyword arguments are not supported', node)
|
||||||
args = [parse_expr(ctx, sym_table, v) for v in node.args]
|
args = [parse_expr(ctx, sym_table, v) for v in node.args]
|
||||||
obj = None
|
obj = None
|
||||||
f = None
|
f = None
|
||||||
@ -166,32 +178,35 @@ def parse_call(ctx: Context,
|
|||||||
f = node.func.attr
|
f = node.func.attr
|
||||||
elif isinstance(node.func, ast.Name):
|
elif isinstance(node.func, ast.Name):
|
||||||
f = node.func.id
|
f = node.func.id
|
||||||
return resolve_call(obj, f, args, {}, ctx)
|
try:
|
||||||
|
return resolve_call(obj, f, args, {}, ctx)
|
||||||
|
except CustomError as e:
|
||||||
|
raise e.at(node)
|
||||||
|
|
||||||
def parse_subscript(ctx: Context,
|
def parse_subscript(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
node):
|
node):
|
||||||
value = parse_expr(ctx, sym_table, node.value)
|
value = parse_expr(ctx, sym_table, node.value)
|
||||||
if not isinstance(value, ListType):
|
if not isinstance(value, ListType):
|
||||||
raise CustomError(f'cannot take index of {value}')
|
raise CustomError(f'cannot take index of {value}', node)
|
||||||
i32 = ctx.types['int32']
|
i32 = ctx.types['int32']
|
||||||
if isinstance(node.slice, ast.Slice):
|
if isinstance(node.slice, ast.Slice):
|
||||||
if node.slice.lower is not None:
|
if node.slice.lower is not None:
|
||||||
if parse_expr(ctx, sym_table, node.slice.lower) != i32:
|
if parse_expr(ctx, sym_table, node.slice.lower) != i32:
|
||||||
raise CustomError(f'slice index must be int32')
|
raise CustomError(f'slice index must be int32', node.slice.lower)
|
||||||
if node.slice.upper is not None:
|
if node.slice.upper is not None:
|
||||||
if parse_expr(ctx, sym_table, node.slice.upper) != i32:
|
if parse_expr(ctx, sym_table, node.slice.upper) != i32:
|
||||||
raise CustomError(f'slice index must be int32')
|
raise CustomError(f'slice index must be int32', node.slice.upper)
|
||||||
if node.slice.step is not None:
|
if node.slice.step is not None:
|
||||||
if parse_expr(ctx, sym_table, node.slice.step) != i32:
|
if parse_expr(ctx, sym_table, node.slice.step) != i32:
|
||||||
raise CustomError(f'slice index must be int32')
|
raise CustomError(f'slice index must be int32', node.slice.step)
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
s = parse_expr(ctx, sym_table, node.slice)
|
s = parse_expr(ctx, sym_table, node.slice)
|
||||||
if s == i32:
|
if s == i32:
|
||||||
return value.params[0]
|
return value.params[0]
|
||||||
else:
|
else:
|
||||||
raise CustomError(f'index of type {s} is not supported')
|
raise CustomError(f'index of type {s} is not supported', node)
|
||||||
|
|
||||||
def parse_if_expr(ctx: Context,
|
def parse_if_expr(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
@ -199,11 +214,11 @@ def parse_if_expr(ctx: Context,
|
|||||||
b = ctx.types['bool']
|
b = ctx.types['bool']
|
||||||
t = parse_expr(ctx, sym_table, node.test)
|
t = parse_expr(ctx, sym_table, node.test)
|
||||||
if t != b:
|
if t != b:
|
||||||
raise CustomError(f'type of conditional must be bool instead of {t}')
|
raise CustomError(f'type of conditional must be bool instead of {t}', node)
|
||||||
ty1 = parse_expr(ctx, sym_table, node.body)
|
ty1 = parse_expr(ctx, sym_table, node.body)
|
||||||
ty2 = parse_expr(ctx, sym_table, node.orelse)
|
ty2 = parse_expr(ctx, sym_table, node.orelse)
|
||||||
if ty1 != ty2:
|
if ty1 != ty2:
|
||||||
raise CustomError(f'divergent type for if expression: {ty1} != {ty2}')
|
raise CustomError(f'divergent type for if expression: {ty1} != {ty2}', node)
|
||||||
return ty1
|
return ty1
|
||||||
|
|
||||||
def parse_simple_binding(name, ty):
|
def parse_simple_binding(name, ty):
|
||||||
@ -222,7 +237,7 @@ def parse_simple_binding(name, ty):
|
|||||||
expected = len(result) + len(binding)
|
expected = len(result) + len(binding)
|
||||||
result |= parse_simple_binding(x, y)
|
result |= parse_simple_binding(x, y)
|
||||||
if len(result) != expected:
|
if len(result) != expected:
|
||||||
raise CustomError('variable name clash')
|
raise CustomError('variable name clash', x)
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
raise CustomError(f'binding to {name} is not supported')
|
raise CustomError(f'binding to {name} is not supported')
|
||||||
@ -231,16 +246,20 @@ def parse_list_comprehension(ctx: Context,
|
|||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
node):
|
node):
|
||||||
if len(node.generators) != 1:
|
if len(node.generators) != 1:
|
||||||
raise CustomError('list comprehension with more than 1 for loop is not supported')
|
raise CustomError(
|
||||||
|
'list comprehension with more than 1 for loop is not supported', node)
|
||||||
if node.generators[0].is_async:
|
if node.generators[0].is_async:
|
||||||
raise CustomError('async list comprehension is not supported')
|
raise CustomError('async list comprehension is not supported', node)
|
||||||
ty = parse_expr(ctx, sym_table, node.generators[0].iter)
|
ty = parse_expr(ctx, sym_table, node.generators[0].iter)
|
||||||
if not isinstance(ty, ListType):
|
if not isinstance(ty, ListType):
|
||||||
raise CustomError(f'unable to iterate over {ty}')
|
raise CustomError(f'unable to iterate over {ty}', node)
|
||||||
sym_table2 = sym_table | parse_simple_binding(node.generators[0].target, ty.params[0])
|
try:
|
||||||
|
sym_table2 = sym_table | parse_simple_binding(node.generators[0].target, ty.params[0])
|
||||||
|
except CustomError as e:
|
||||||
|
raise e.at(node)
|
||||||
b = ctx.types['bool']
|
b = ctx.types['bool']
|
||||||
for c in node.generators[0].ifs:
|
for c in node.generators[0].ifs:
|
||||||
if parse_expr(ctx, sym_table2, c) != b:
|
if parse_expr(ctx, sym_table2, c) != b:
|
||||||
raise CustomError(f'condition should be of boolean type')
|
raise CustomError(f'condition should be of boolean type', c)
|
||||||
return ListType(parse_expr(ctx, sym_table2, node.elt))
|
return ListType(parse_expr(ctx, sym_table2, node.elt))
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ def parse_stmts(ctx: Context,
|
|||||||
elif isinstance(node, ast.Break) or isinstance(node, ast.Continue):
|
elif isinstance(node, ast.Break) or isinstance(node, ast.Continue):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
raise CustomError(f'{node} is not supported yet')
|
raise CustomError(f'{node} is not supported yet', node)
|
||||||
sym_table2 |= a
|
sym_table2 |= a
|
||||||
used_sym_table2 |= b
|
used_sym_table2 |= b
|
||||||
if returned:
|
if returned:
|
||||||
@ -40,24 +40,24 @@ def get_target_type(ctx: Context,
|
|||||||
if isinstance(target, ast.Subscript):
|
if isinstance(target, ast.Subscript):
|
||||||
t = get_target_type(ctx, sym_table, used_sym_table, target.value)
|
t = get_target_type(ctx, sym_table, used_sym_table, target.value)
|
||||||
if not isinstance(t, ListType):
|
if not isinstance(t, ListType):
|
||||||
raise CustomError(f'cannot index through type {t}')
|
raise CustomError(f'cannot index through type {t}', target)
|
||||||
if isinstance(target.slice, ast.Slice):
|
if isinstance(target.slice, ast.Slice):
|
||||||
raise CustomError(f'assignment to slice is not supported')
|
raise CustomError(f'assignment to slice is not supported', target)
|
||||||
i = parse_expr(ctx, sym_table, target.slice)
|
i = parse_expr(ctx, sym_table, target.slice)
|
||||||
if i != ctx.types['int32']:
|
if i != ctx.types['int32']:
|
||||||
raise CustomError(f'index must be int32')
|
raise CustomError(f'index must be int32', target.slice)
|
||||||
return t.params[0]
|
return t.params[0]
|
||||||
elif isinstance(target, ast.Attribute):
|
elif isinstance(target, ast.Attribute):
|
||||||
t = get_target_type(ctx, sym_table, used_sym_table, target.value)
|
t = get_target_type(ctx, sym_table, used_sym_table, target.value)
|
||||||
if target.attr not in t.fields:
|
if target.attr not in t.fields:
|
||||||
raise CustomError(f'{t} has no field {target.attr}')
|
raise CustomError(f'{t} has no field {target.attr}', target)
|
||||||
return t.fields[target.attr]
|
return t.fields[target.attr]
|
||||||
elif isinstance(target, ast.Name):
|
elif isinstance(target, ast.Name):
|
||||||
if target.id not in sym_table:
|
if target.id not in sym_table:
|
||||||
raise CustomError(f'unbounded {target.id}')
|
raise CustomError(f'unbounded {target.id}', target)
|
||||||
return sym_table[target.id]
|
return sym_table[target.id]
|
||||||
else:
|
else:
|
||||||
raise CustomError(f'assignment to {target} is not supported')
|
raise CustomError(f'assignment to {target} is not supported', target)
|
||||||
|
|
||||||
def parse_stmt_binding(ctx: Context,
|
def parse_stmt_binding(ctx: Context,
|
||||||
sym_table: dict[str, Type],
|
sym_table: dict[str, Type],
|
||||||
@ -67,15 +67,17 @@ def parse_stmt_binding(ctx: Context,
|
|||||||
if isinstance(target, ast.Name):
|
if isinstance(target, ast.Name):
|
||||||
if target.id in used_sym_table:
|
if target.id in used_sym_table:
|
||||||
if used_sym_table[target.id] != ty:
|
if used_sym_table[target.id] != ty:
|
||||||
raise CustomError('inconsistent type')
|
raise CustomError(f'inconsistent type, {target.id} was ' \
|
||||||
|
f'defined to be {used_sym_table[target.id]}, ' \
|
||||||
|
f'but is now {ty}', target)
|
||||||
if target.id == '_':
|
if target.id == '_':
|
||||||
return {}
|
return {}
|
||||||
return {target.id: ty}
|
return {target.id: ty}
|
||||||
elif isinstance(target, ast.Tuple):
|
elif isinstance(target, ast.Tuple):
|
||||||
if not isinstance(ty, TupleType):
|
if not isinstance(ty, TupleType):
|
||||||
raise CustomError(f'cannot pattern match over {ty}')
|
raise CustomError(f'cannot pattern match over {ty}', target)
|
||||||
if len(target.elts) != len(ty.params):
|
if len(target.elts) != len(ty.params):
|
||||||
raise CustomError(f'pattern matching length mismatch')
|
raise CustomError(f'pattern matching length mismatch', target)
|
||||||
result = {}
|
result = {}
|
||||||
for x, y in zip(target.elts, ty.params):
|
for x, y in zip(target.elts, ty.params):
|
||||||
new = parse_stmt_binding(ctx, sym_table, used_sym_table, x, y)
|
new = parse_stmt_binding(ctx, sym_table, used_sym_table, x, y)
|
||||||
@ -83,12 +85,12 @@ def parse_stmt_binding(ctx: Context,
|
|||||||
result |= new
|
result |= new
|
||||||
used_sym_table |= new
|
used_sym_table |= new
|
||||||
if len(result) != old_len + len(new):
|
if len(result) != old_len + len(new):
|
||||||
raise CustomError(f'variable name clash')
|
raise CustomError(f'variable name clash', target)
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
t = get_target_type(ctx, sym_table, used_sym_table, target)
|
t = get_target_type(ctx, sym_table, used_sym_table, target)
|
||||||
if ty != t:
|
if ty != t:
|
||||||
raise CustomError(f'inconsistent type')
|
raise CustomError(f'type mismatch', target)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def parse_assign(ctx: Context,
|
def parse_assign(ctx: Context,
|
||||||
@ -113,7 +115,7 @@ def parse_if_stmt(ctx: Context,
|
|||||||
node):
|
node):
|
||||||
test = parse_expr(ctx, sym_table, node.test)
|
test = parse_expr(ctx, sym_table, node.test)
|
||||||
if test != ctx.types['bool']:
|
if test != ctx.types['bool']:
|
||||||
raise CustomError(f'condition must be bool instead of {test}')
|
raise CustomError(f'condition must be bool instead of {test}', node.test)
|
||||||
a, b, r = parse_stmts(ctx, sym_table, used_sym_table, return_ty, node.body)
|
a, b, r = parse_stmts(ctx, sym_table, used_sym_table, return_ty, node.body)
|
||||||
used_sym_table |= b
|
used_sym_table |= b
|
||||||
a1, b1, r1 = parse_stmts(ctx, sym_table, used_sym_table, return_ty, node.orelse)
|
a1, b1, r1 = parse_stmts(ctx, sym_table, used_sym_table, return_ty, node.orelse)
|
||||||
@ -127,12 +129,12 @@ def parse_for_stmt(ctx: Context,
|
|||||||
node):
|
node):
|
||||||
ty = parse_expr(ctx, sym_table, node.iter)
|
ty = parse_expr(ctx, sym_table, node.iter)
|
||||||
if not isinstance(ty, ListType):
|
if not isinstance(ty, ListType):
|
||||||
raise CustomError('only iteration over list is supported')
|
raise CustomError('only iteration over list is supported', node.iter)
|
||||||
binding = parse_simple_binding(node.target, ty.params[0])
|
binding = parse_simple_binding(node.target, ty.params[0])
|
||||||
for key, value in binding.items():
|
for key, value in binding.items():
|
||||||
if key in used_sym_table:
|
if key in used_sym_table:
|
||||||
if value != used_sym_table[key]:
|
if value != used_sym_table[key]:
|
||||||
raise CustomError('inconsistent type')
|
raise CustomError('inconsistent type', node)
|
||||||
# more sophisticated return analysis is needed...
|
# more sophisticated return analysis is needed...
|
||||||
a, b, _ = parse_stmts(ctx, sym_table | binding, used_sym_table | binding,
|
a, b, _ = parse_stmts(ctx, sym_table | binding, used_sym_table | binding,
|
||||||
return_ty, node.body)
|
return_ty, node.body)
|
||||||
@ -148,7 +150,7 @@ def parse_while_stmt(ctx: Context,
|
|||||||
node):
|
node):
|
||||||
ty = parse_expr(ctx, sym_table, node.test)
|
ty = parse_expr(ctx, sym_table, node.test)
|
||||||
if ty != ctx.types['bool']:
|
if ty != ctx.types['bool']:
|
||||||
raise CustomError('condition must be bool')
|
raise CustomError('condition must be bool', node.test)
|
||||||
# more sophisticated return analysis is needed...
|
# more sophisticated return analysis is needed...
|
||||||
a, b, _ = parse_stmts(ctx, sym_table, used_sym_table, return_ty, node.body)
|
a, b, _ = parse_stmts(ctx, sym_table, used_sym_table, return_ty, node.body)
|
||||||
a1, b1, _ = parse_stmts(ctx, sym_table, used_sym_table | b,
|
a1, b1, _ = parse_stmts(ctx, sym_table, used_sym_table | b,
|
||||||
@ -163,10 +165,10 @@ def parse_return_stmt(ctx: Context,
|
|||||||
node):
|
node):
|
||||||
if return_ty is None:
|
if return_ty is None:
|
||||||
if node.value is not None:
|
if node.value is not None:
|
||||||
raise CustomError('no return value is allowed')
|
raise CustomError('no return value is allowed', node)
|
||||||
return {}, {}, True
|
return {}, {}, True
|
||||||
ty = parse_expr(ctx, sym_table, node.value)
|
ty = parse_expr(ctx, sym_table, node.value)
|
||||||
if ty != return_ty:
|
if ty != return_ty:
|
||||||
raise CustomError(f'expected returning {return_ty} but got {ty}')
|
raise CustomError(f'expected returning {return_ty} but got {ty}', node)
|
||||||
return {}, {}, True
|
return {}, {}, True
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user