using System; using System.Collections.Generic; using System.Linq; #pragma warning disable 169, 649 class AnnotateStep { public class Type { public string name; public int size; //in bits public bool builtin; } static int var_id = 0; public class Variable { public string name; //maybe remove?, nah, lookup and debug public int id; public Type type; public bool is_parameter; public Variable(string name, Type type, bool is_parameter) { this.name = name; this.type = type; this.is_parameter = is_parameter; this.id = var_id++; } } public class Scope { public Scope parent; public List variables = new List(); } public class Top { public List functions = new List(); public List types = new List(); } public class Function { public string name; public Type type; public List parameters = new List(); public Block block; } public class Block { public Scope scope; public List statements = new List(); } public class ConditionalBlock { public Expression condition; public Block block; } public class Statement { } public class StatementAssignment : Statement { public Expression left; //only varuse or member access public Expression right; } public class StatementIf : Statement { public ConditionalBlock if_block; public List elsif_blocks = new List(); public Block else_block; } public class StatementWhile : Statement { public ConditionalBlock block; } public class StatementReturn : Statement { public Expression expression; } public class StatementExpression : Statement { public Expression expression; } public class Expression { public Type type; } public class ExpressionFunctionCall : Expression { public Function function; public List parameters = new List(); } public class ExpressionMemberAccess : Expression { //todo: hmms...? } public class ExpressionVariableUse : Expression { public Variable variable; } public class ExpressionIntLiteral : Expression { public string value; } //////////////////////////////////////////////////////////////////////////////// static void InitIntrinsics(Top top) { var t = top.types; t.Add(new Type{name = "i8", size = 8, builtin = true}); t.Add(new Type{name = "i16", size = 16, builtin = true}); t.Add(new Type{name = "i32", size = 32, builtin = true}); t.Add(new Type{name = "i64", size = 64, builtin = true}); t.Add(new Type{name = "u8", size = 8, builtin = true}); t.Add(new Type{name = "u16", size = 16, builtin = true}); t.Add(new Type{name = "u32", size = 32, builtin = true}); t.Add(new Type{name = "u64", size = 64, builtin = true}); t.Add(new Type{name = "string", size = -1, builtin = true}); t.Add(new Type{name = "bool", size = 1, builtin = true}); } static Type GetType(Top top, string name) { var t = top.types.First(item => item.name == name); if(t == null) throw new ArgumentOutOfRangeException("Unknown type: " + name); return t; } static Function GetFunction(Top top, string name, List pars) { foreach(var fun in top.functions) { if(fun.name == name && fun.parameters.Count == pars.Count) { for(var i = 0; i < pars.Count; ++i) { if(fun.parameters[i] != pars[i]) break; } return fun; } } return null; } static Variable GetVariable(Scope scope, string name) { var v = scope.variables.FirstOrDefault(t => t.name == name); if(v == null) { if(scope.parent == null) throw new ArgumentOutOfRangeException("Unknown var: " + name); else return GetVariable(scope.parent, name); } return v; } static bool HasVariable(Scope scope, string name) { //not checking parent scopes var v = scope.variables.FirstOrDefault(t => t.name == name); return v != null; } public static Top Annotate(ParseStep.Top ptop) { var top = new Top(); InitIntrinsics(top); var dict = new Dictionary(); //pre annotate functions(without their body) foreach(var pf in ptop.functions) { var scope = new Scope(); var f = new Function {name = pf.name}; f.type = GetType(top, pf.type.name); foreach(var pfp in pf.parameters) { var t = GetType(top, pfp.type.name); var v = new Variable(pfp.name, t, true); scope.variables.Add(v); f.parameters.Add(t); } f.block = new Block {scope = scope}; top.functions.Add(f); dict[pf] = f; } //annotate bodies of functions foreach(var pf in ptop.functions) { var f = dict[pf]; Annotate(pf.block, f.block, f, top); } return top; } public static void Annotate(ParseStep.Block pblock, Block block, Function fun, Top top) { var stmts = block.statements; foreach(var ps in pblock.statements) { switch(ps) { case ParseStep.StatementAssignment assign: var assign_l = Annotate(assign.left, top, block.scope); var assign_r = Annotate(assign.right, top, block.scope); if(assign_l.type != assign_r.type) { var msg = "can't assign "+assign_r.type.name+" to "+ assign_l.type.name; throw new InvalidOperationException(msg); } stmts.Add(new StatementAssignment { left=assign_l, right=assign_r }); break; case ParseStep.StatementIf psif: var sif = new StatementIf(); sif.if_block = Annotate(psif.if_block, block.scope, fun, top); foreach(var pei in psif.elsif_blocks) { var b = Annotate(pei, block.scope, fun, top); sif.elsif_blocks.Add(b); } if(psif.else_block != null) { var b = new Block{scope = new Scope{parent = block.scope}}; Annotate(psif.else_block, b, fun, top); sif.else_block = b; } stmts.Add(sif); break; case ParseStep.StatementWhile pwhile: var swhile = new StatementWhile(); swhile.block = Annotate(pwhile.block, block.scope, fun, top); stmts.Add(swhile); break; case ParseStep.StatementReturn preturn: var e = Annotate(preturn.expression, top, block.scope); if(e.type != fun.type) { var m = "trying to return " + e.type.name; m += "but function is of type " + fun.type.name; throw new InvalidOperationException(m); } stmts.Add(new StatementReturn {expression = e}); break; case ParseStep.StatementVardef pvdef: var t = GetType(top, pvdef.type.name); var v = new Variable(pvdef.variable, t, false); if(HasVariable(block.scope, pvdef.variable)) { var m = "variable "+pvdef.variable+" already exists"; throw new InvalidOperationException(m); } block.scope.variables.Add(v); break; case ParseStep.StatementVardefAssignment pvdefa: var exp = Annotate(pvdefa.expression, top, block.scope); var va = new Variable(pvdefa.variable, exp.type, false); if(HasVariable(block.scope, pvdefa.variable)) { var m = "variable "+pvdefa.variable+" already exists"; throw new InvalidOperationException(m); } block.scope.variables.Add(va); var st = new StatementAssignment(); st.left = new ExpressionVariableUse(){variable=va}; st.right = exp; stmts.Add(st); break; default: throw new InvalidOperationException("unknown statement type"); } } } public static ConditionalBlock Annotate(ParseStep.ConditionalBlock pblock, Scope parent_scope, Function fun, Top top) { var cond = Annotate(pblock.condition, top, parent_scope); if(cond.type != GetType(top, "bool")) throw new InvalidOperationException("condition must be bool"); var block = new Block{scope = new Scope{parent = parent_scope}}; Annotate(pblock.block, block, fun, top); return new ConditionalBlock{condition = cond, block = block}; } public static Expression Annotate(ParseStep.Expression pexp, Top top, Scope scope) { switch(pexp) { case ParseStep.ExpressionFunctionCall pfuncall: var fun = new ExpressionFunctionCall(); var types = new List(); foreach(var ppar in pfuncall.parameters) { var par = Annotate(ppar, top, scope); fun.parameters.Add(par); types.Add(par.type); } fun.function = GetFunction(top, pfuncall.function, types); if(fun.function == null) { var m = "can't find function "+pfuncall.function+"("; foreach(var t in types) m += t.name+", "; m += ")"; throw new InvalidOperationException(m); } return fun; case ParseStep.ExpressionMemberAccess pmember: var member = new ExpressionMemberAccess(); throw new InvalidOperationException("nope"); //todo: can't do yet, need to implement those types first :P case ParseStep.ExpressionVariableUse pvaruse: var varuse = new ExpressionVariableUse(); varuse.variable = GetVariable(scope, pvaruse.variable); varuse.type = varuse.variable.type; return varuse; case ParseStep.ExpressionIntLiteral pintliteral: var intliteral = new ExpressionIntLiteral(); intliteral.type = GetType(top, "i32"); intliteral.value = pintliteral.value; return intliteral; default: throw new InvalidOperationException("unknown expression type"); } } }