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");
}
}
}