Radicalc  Artifact [90a6fd4606]

Artifact 90a6fd4606b73ecdcf09c3b97b46b86031f1c41e3c685592352d760b7b6e08c3:

  • File src/AnnotateStep.cs — part of check-in [2d65274902] at 2017-08-28 20:07:00 on branch trunk — annotation (user: athaudia size: 9151)

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<Variable> variables = new List<Variable>();
	}

	public class Top {
		public List<Function> functions = new List<Function>();
		public List<Type> types = new List<Type>();
	}

	public class Function {
		public string name;
		public Type type;
		public List<Type> parameters = new List<Type>();
		public Block block;
	}

	public class Block {
		public Scope scope;
		public List<Statement> statements = new List<Statement>();
	}

	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<ConditionalBlock> elsif_blocks =
			new List<ConditionalBlock>();
		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<Expression> parameters = new List<Expression>();
	}

	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<Type> 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<ParseStep.Function, Function>();
		//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<Type>();
			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");
		}
	}
}