2020-12-17 14:50:51 +08:00
|
|
|
import ast
|
|
|
|
from type_def import *
|
2020-12-18 13:03:36 +08:00
|
|
|
from helper import *
|
2020-12-17 14:50:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
def parse_type(ctx: Context, ty):
|
|
|
|
if ty is None:
|
2020-12-17 15:00:09 +08:00
|
|
|
return None, set()
|
2020-12-17 14:50:51 +08:00
|
|
|
elif isinstance(ty, ast.Name):
|
2020-12-17 17:00:43 +08:00
|
|
|
# we should support string either, but no need for toy implementaiton
|
2020-12-17 14:50:51 +08:00
|
|
|
if ty.id in ctx.types:
|
|
|
|
return ctx.types[ty.id], set()
|
|
|
|
elif ty.id in ctx.variables:
|
|
|
|
return ctx.variables[ty.id], {ty.id}
|
|
|
|
else:
|
|
|
|
raise CustomError(f"Unbounded Type {ty.id}")
|
|
|
|
elif isinstance(ty, ast.Subscript):
|
|
|
|
if isinstance(ty.value, ast.Name):
|
|
|
|
generic = ty.value.id
|
|
|
|
else:
|
|
|
|
raise CustomError(f"Unknown Generic Type {ty.value}")
|
|
|
|
if not isinstance(ty.slice, ast.Name) and not isinstance(ty.slice, ast.Tuple) \
|
|
|
|
and not isinstance(ty.slice, ast.Subscript):
|
|
|
|
raise CustomError(f"Generic Type of the form {ty.slice} is not supported")
|
|
|
|
if generic == 'tuple':
|
|
|
|
if not isinstance(ty.slice, ast.Tuple):
|
|
|
|
raise CustomError(f"Generic Type of the form {ty} is not supported")
|
|
|
|
param = []
|
|
|
|
var = set()
|
|
|
|
for t in ty.slice.elts:
|
|
|
|
p, v = parse_type(ctx, t)
|
|
|
|
param.append(p)
|
|
|
|
var |= v
|
|
|
|
return TupleType(param), var
|
|
|
|
elif generic == 'list':
|
|
|
|
param, var = parse_type(ctx, ty.slice)
|
|
|
|
return ListType(param), var
|
|
|
|
elif generic == 'virtual':
|
|
|
|
param, var = parse_type(ctx, ty.slice)
|
|
|
|
if not isinstance(param, ClassType):
|
|
|
|
raise CustomError(f"Parameter of virtual must be a class instead of {param}")
|
|
|
|
return VirtualClassType(param), var
|
|
|
|
else:
|
|
|
|
raise CustomError(f"Unknown Generic Type {ty.value}")
|
2020-12-17 15:00:09 +08:00
|
|
|
else:
|
|
|
|
raise CustomError(f"Unrecognized Type {ty}")
|
2020-12-17 14:50:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
def parse_function(ctx: Context, base, fn: ast.FunctionDef):
|
|
|
|
args = []
|
|
|
|
var = set()
|
|
|
|
for arg in fn.args.args:
|
|
|
|
name = arg.arg
|
|
|
|
ty, v = parse_type(ctx, arg.annotation)
|
|
|
|
var |= v
|
|
|
|
if name == 'self' and ty is None and base is not None:
|
2020-12-18 13:03:36 +08:00
|
|
|
ty = SelfType()
|
2020-12-17 14:50:51 +08:00
|
|
|
args.append(ty)
|
2020-12-18 13:03:36 +08:00
|
|
|
if isinstance(fn.returns, ast.Name) and fn.returns.id == 'self'\
|
|
|
|
and base is not None:
|
|
|
|
result, v = SelfType(), set()
|
|
|
|
else:
|
|
|
|
result, v = parse_type(ctx, fn.returns)
|
2020-12-17 14:50:51 +08:00
|
|
|
if len(v - var) > 0:
|
|
|
|
raise CustomError(f"Unbounded variable in return type of {fn.name}")
|
|
|
|
return args, result, var
|
|
|
|
|
|
|
|
|
|
|
|
def parse_class(ctx, c: ast.ClassDef):
|
|
|
|
node = ctx.types[c.name]
|
|
|
|
functions = []
|
|
|
|
|
|
|
|
for base in c.bases:
|
|
|
|
if not isinstance(base, ast.Name):
|
|
|
|
raise CustomError(f"Base class of the form {base} is not supported")
|
|
|
|
name = base.id
|
|
|
|
if name not in ctx.types:
|
|
|
|
raise CustomError(f"Unbounded base class name {base}")
|
|
|
|
if not isinstance(ctx.types[name], ClassType):
|
|
|
|
raise CustomError(f"Base class must be a class instead of {base}")
|
|
|
|
node.parents.append(ctx.types[name])
|
|
|
|
|
|
|
|
for stmt in c.body:
|
|
|
|
if isinstance(stmt, ast.AnnAssign):
|
|
|
|
if not isinstance(stmt.target, ast.Name):
|
|
|
|
raise CustomError(f"Assignment of the form {stmt.target} is not supported")
|
|
|
|
field = stmt.target.id
|
|
|
|
if field in node.fields:
|
|
|
|
raise CustomError(f"Duplicated fields {field} in {c.name}")
|
|
|
|
ty, var = parse_type(ctx, stmt.annotation)
|
|
|
|
if len(var) > 0:
|
|
|
|
raise CustomError(f"Type variable is not allowed in class fields")
|
|
|
|
if ty == None:
|
|
|
|
raise CustomError(f"{field} of {c.name} is not annotated")
|
|
|
|
node.fields[field] = ty
|
|
|
|
elif isinstance(stmt, ast.FunctionDef):
|
|
|
|
name = stmt.name
|
|
|
|
if name in node.methods:
|
|
|
|
raise CustomError(f"Duplicated method {name} in {c.name}")
|
|
|
|
args, result, var = parse_function(ctx, node, stmt)
|
|
|
|
node.methods[name] = (args, result, var)
|
|
|
|
functions.append((c.name, name, stmt))
|
|
|
|
else:
|
|
|
|
raise CustomError(f"{stmt} is not supported")
|
|
|
|
return functions
|
|
|
|
|
|
|
|
|
2020-12-23 10:34:56 +08:00
|
|
|
def parse_type_var(ctx: Context, node):
|
|
|
|
if isinstance(node, ast.Assign):
|
|
|
|
if not isinstance(node.value, ast.Call) or\
|
|
|
|
not isinstance(node.value.func, ast.Name) or\
|
|
|
|
node.value.func.id != 'TypeVar':
|
|
|
|
return
|
|
|
|
if not len(node.targets) == 1 or\
|
|
|
|
not isinstance(node.targets[0], ast.Name):
|
|
|
|
raise CustomError(
|
|
|
|
'complicated variable assignment is not allowed',
|
|
|
|
node)
|
|
|
|
if len(node.value.args) == 0 or\
|
|
|
|
not isinstance(node.value.args[0], ast.Constant) or\
|
|
|
|
not isinstance(node.value.args[0].value, str):
|
|
|
|
raise CustomError('call to TypeVar must at least have a name',
|
|
|
|
node.value)
|
|
|
|
name = node.value.args[0]
|
|
|
|
if name in ctx.variables:
|
|
|
|
raise CustomError('redefining type variable is not allowed', node)
|
|
|
|
constraints = []
|
|
|
|
for v in node.value.args[1:]:
|
|
|
|
if isinstance(v, ast.Constant):
|
|
|
|
value = v.value
|
|
|
|
elif isinstance(v, ast.Name):
|
|
|
|
value = v.id
|
|
|
|
else:
|
|
|
|
raise CustomError(
|
|
|
|
'TypeVar constraints must be either string or type name',
|
|
|
|
node)
|
|
|
|
if value not in ctx.types:
|
|
|
|
raise CustomError(f'unbounded type {value}', node)
|
|
|
|
constraints.append(ctx.types[value])
|
|
|
|
ctx.variables[node.targets[0].id] = TypeVariable(name, constraints)
|
|
|
|
|
|
|
|
|
2020-12-17 14:50:51 +08:00
|
|
|
def parse_top_level(ctx: Context, module: ast.Module):
|
|
|
|
to_be_processed = []
|
2020-12-23 10:34:56 +08:00
|
|
|
assignments = []
|
2020-12-17 14:50:51 +08:00
|
|
|
# first pass, obtain all type names
|
|
|
|
for element in module.body:
|
|
|
|
if isinstance(element, ast.ClassDef):
|
|
|
|
name = element.name
|
|
|
|
if name in ctx.types or name in ctx.variables:
|
|
|
|
raise CustomError(f"Duplicated class name: {name}")
|
|
|
|
ctx.types[name] = ClassType(name)
|
|
|
|
to_be_processed.append(element)
|
|
|
|
elif isinstance(element, ast.FunctionDef):
|
|
|
|
to_be_processed.append(element)
|
2020-12-23 10:34:56 +08:00
|
|
|
elif isinstance(element, ast.Assign):
|
|
|
|
assignments.append(element)
|
|
|
|
|
|
|
|
# second pass, obtain all type variables
|
|
|
|
for node in assignments:
|
|
|
|
parse_type_var(ctx, node)
|
2020-12-17 14:50:51 +08:00
|
|
|
|
2020-12-23 10:34:56 +08:00
|
|
|
# third pass, obtain all function types
|
2020-12-17 14:50:51 +08:00
|
|
|
function_stmts = []
|
|
|
|
for element in to_be_processed:
|
|
|
|
if isinstance(element, ast.ClassDef):
|
|
|
|
function_stmts += parse_class(ctx, element)
|
|
|
|
elif isinstance(element, ast.FunctionDef):
|
|
|
|
name = element.name
|
2020-12-21 09:38:37 +08:00
|
|
|
if name in ctx.functions:
|
2020-12-17 14:50:51 +08:00
|
|
|
raise CustomError(f"Duplicated function name {name}")
|
2020-12-18 13:03:36 +08:00
|
|
|
if name in ctx.types:
|
|
|
|
raise CustomError(f"Function name {name} clashed with type name")
|
2020-12-17 14:50:51 +08:00
|
|
|
args, result, var = parse_function(ctx, None, element)
|
2020-12-18 13:03:36 +08:00
|
|
|
ctx.functions[name] = (args, result, var)
|
2020-12-23 10:34:56 +08:00
|
|
|
function_stmts.append((None, name, element))
|
2020-12-17 14:50:51 +08:00
|
|
|
|
2020-12-18 13:03:36 +08:00
|
|
|
return ctx, function_stmts
|
2020-12-17 14:50:51 +08:00
|
|
|
|
|
|
|
|