module otya.smilebasic.compiler; import otya.smilebasic.node; import otya.smilebasic.token; import otya.smilebasic.vm; import otya.smilebasic.type; import otya.smilebasic.error; import otya.smilebasic.systemvariable; import std.stdio; class DebugInfo { int old; std.container.Array!SourceLocation location; this() { } void addLocation(SourceLocation loc, Code[] code) { for(int i = 0; i < code.length - old; i++) { location ~= loc; } old = location.length; } SourceLocation getLocationByAddress(int addr) { return location[addr]; } } class Scope { GotoAddr breakAddr; GotoAddr continueAddr; Function func; DataTable data; this() { this.data = new DataTable(); } this(GotoAddr breakAddr, GotoAddr continueAddr, Scope parent) { this.breakAddr = breakAddr; this.continueAddr = continueAddr; this.func = parent.func; this.data = parent.data; } this(Function func) { this(); this.func = func; } } class DataTable { Value[] data; int[wstring] label; //dataIndexは関数ごとに別じゃないといけない int dataIndex; void addData(Value data) { this.data ~= data; } void addLabel(wstring label) { this.label[label] = cast(int)data.length; } void read(out Value value, VM vm) { if(dataIndex >= data.length) throw new OutOfDATA(); value = data[dataIndex]; dataIndex++; } } class Function { static const frameSize = 3; int address; wstring name; int argCount; int argumentIndex; int variableIndex; VMVariable[wstring] variable; int[wstring] label; bool returnExpr; int outArgCount; /** 0:bp 1:pc 2:data table(global) 3:data index 4:function pointer */ this(int address, wstring name, bool returnExpr, int argCount) { this.address = address; this.name = name; this.returnExpr = returnExpr; this.argCount = argCount; this.variableIndex = frameSize - 1;//1;//0,bp,1,pc if(returnExpr) { outArgCount = 1; } } int getLocalVarIndex(wstring name, Compiler c) { int var = this.variable.get(name, VMVariable()).index; if(var == 0) { //local変数をあたる //それでもだめならOPTION STRICTならエラー this.variable[name] = VMVariable(var = ++variableIndex, c.getType(name)); } return var; } int hasLocalVarIndex(wstring name) { int var = this.variable.get(name, VMVariable()).index; return var; } int defineLocalVarIndex(wstring name, Compiler c) { int var = this.variable.get(name, VMVariable()).index; if(var == 0) { this.variable[name] = VMVariable(var = ++variableIndex, c.getType(name)); } else { //error:二重定義 throw new DuplicateVariable(); } return var; } int defineLocalVarIndexVoid(wstring name, Compiler c) { int var = this.variable.get(name, VMVariable()).index; if(var == 0) { this.variable[name] = VMVariable(var = ++variableIndex, ValueType.Void); } else { //error:二重定義 throw new DuplicateVariable(); } return var; } int defineArgumentIndex(wstring name, Compiler c) { int var = this.variable.get(name, VMVariable()).index; if(var == 0) { this.variable[name] = VMVariable(var = --argumentIndex, c.getType(name)); } else { //error:二重定義 //TODO:3.1ではエラーにならない } return var; } } class Compiler { ValueType getType(wstring name) { wchar s = name[name.length - 1]; switch(s) { case '$': return ValueType.String; case '#': return ValueType.Double; case '%': return ValueType.Integer; default: return ValueType.Double;//非DEFINT時 //return ValueType.Integer;//DEFINT時 } } Statements statements; DebugInfo debugInfo; this(Statements statements) { this.debugInfo = new DebugInfo; this.statements = statements; code = new Code[0]; global["DATE$"] = VMVariable(-1); sysVariable["DATE$"] = new Date(); global["TIME$"] = VMVariable(-1); sysVariable["TIME$"] = new Time(); global["MAINCNT"] = VMVariable(-1); sysVariable["MAINCNT"] = new Maincnt(); global["CSRX"] = VMVariable(-1); sysVariable["CSRX"] = new CSRX(); global["CSRY"] = VMVariable(-1); sysVariable["CSRY"] = new CSRY(); global["CSRZ"] = VMVariable(-1); sysVariable["CSRZ"] = new CSRZ(); addSystemVariable("VERSION", new Version()); addSystemVariable("FREEMEM", new FreeMem()); addSystemVariable("TABSTEP", new TabStep()); } void addSystemVariable(wstring name, SystemVariable var) { global[name] = VMVariable(-1); sysVariable[name] = var; } void registerSystemVariable(VM vm) { foreach(k, ref v; sysVariable) { v.vm = vm; } } SystemVariable[wstring] sysVariable; Code[] code; VMVariable[wstring] global; int[wstring] globalLabel; Function[wstring] functions; int globalIndex = 0; void genCode(Code c) { code ~= c; } void genCodeImm(Value value) { code ~= new Push(value); } void genCodePushGlobal(int ind) { code ~= new PushG(ind); } void genCodePopGlobal(int ind) { code ~= new PopG(ind); } SystemVariable getSystemVariable(wstring name) { auto var = sysVariable.get(name, null); return var; } void genCodePushSysVar(wstring name) { code ~= new PushSystemVariable(getSystemVariable(name)); } void genCodePopSysVar(wstring name) { code ~= new PopSystemVariable(getSystemVariable(name)); } void genCodePushVar(wstring name, Scope sc) { if(sc.func) { auto local = sc.func.hasLocalVarIndex(name); if(local) { code ~= new PushL(local); return; } } auto global = hasGlobalVarIndex(name); if(global) { if(global < 0) { genCodePushSysVar(name); return; } code ~= new PushG(global); return; } if(sc.func) { code ~= new PushL(sc.func.getLocalVarIndex(name, this)); return; } code ~= new PushG(getGlobalVarIndex(name)); } void genCodePopVar(wstring name, Scope sc) { if(sc.func) { auto local = sc.func.hasLocalVarIndex(name); if(local) { code ~= new PopL(local); return; } } auto global = hasGlobalVarIndex(name); if(global) { if(global < 0) { genCodePopSysVar(name); return; } code ~= new PopG(global); return; } if(sc.func) { code ~= new PopL(sc.func.getLocalVarIndex(name, this)); return; } code ~= new PopG(getGlobalVarIndex(name)); } void getVarIndex(wstring name, Scope sc, out int index, out bool isLocal) { auto global = hasGlobalVarIndex(name); if(sc.func) isLocal = sc.func.hasLocalVarIndex(name) != 0; if(sc.func && isLocal) { index = sc.func.getLocalVarIndex(name, this); return; } if(global) { index = global; return; } index = getGlobalVarIndex(name); } void genCodeOP(TokenType op) { code ~= new Operate(op); } void genCodeGoto(wstring label, Scope sc) { code ~= new GotoS(label, sc); } void genCodeGosub(wstring label, Scope sc) { code ~= new GosubS(label, sc); } GotoAddr genCodeGoto() { auto c = new GotoAddr(-1); code ~= c; return c; } GotoAddr genCodeGoto(int addr) { auto c = new GotoAddr(addr); code ~= c; return c; } GotoTrue genCodeGotoTrue() { auto c = new GotoTrue(-1); code ~= c; return c; } GotoFalse genCodeGotoFalse() { auto c = new GotoFalse(-1); code ~= c; return c; } int defineGlobalVarIndex(wstring name) { int global = this.global.get(name, VMVariable()).index; if(global == 0) { this.global[name] = VMVariable(global = ++globalIndex, getType(name)); } else { //error:二重定義 throw new DuplicateVariable(); } return global; } int defineVarIndex(wstring name, Scope sc) { if(sc.func) { return sc.func.defineLocalVarIndex(name, this); } return defineGlobalVarIndex(name); } int defineVarIndexVoid(wstring name, Scope sc) { if(sc.func) { return sc.func.defineLocalVarIndexVoid(name, this); } int global = this.global.get(name, VMVariable()).index; if(global == 0) { this.global[name] = VMVariable(global = ++globalIndex, ValueType.Void); } else { //error:二重定義 throw new DuplicateVariable(); } return global; } int getGlobalVarIndex(wstring name) { int global = this.global.get(name, VMVariable()).index; if(global == 0) { //local変数をあたる //それでもだめならOPTION STRICTならエラー this.global[name] = VMVariable(global = ++globalIndex, getType(name)); } return global; } int hasGlobalVarIndex(wstring name) { int global = this.global.get(name, VMVariable()).index; return global; } int getLocalVarIndex(wstring name, Scope sc) { if(sc.func) { int local = sc.func.getLocalVarIndex(name, this); return local; } return 0; } void compileExpression(Expression exp, Scope sc) { if(!exp) { genCodeImm(Value(ValueType.Void)); return; } switch(exp.type) { case NodeType.Constant: genCodeImm((cast(Constant)exp).value); break; case NodeType.BinaryOperator: { auto binop = cast(BinaryOperator)exp; compileExpression(binop.item1, sc); compileExpression(binop.item2, sc); if(binop.operator == TokenType.LBracket) { IndexExpressions ie = cast(IndexExpressions)binop.item2; if(ie) { genCode(new PushArray(cast(int)ie.expressions.length)); } else { genCode(new PushArray(1)); } break; } genCodeOP(binop.operator); } break; case NodeType.UnaryOperator: { auto una = cast(UnaryOperator)exp; compileExpression(una.item, sc); genCodeOP(una.operator); } break; case NodeType.Variable: auto var = cast(Variable)exp; genCodePushVar(var.name, sc); break; case NodeType.CallFunction: { auto func = cast(CallFunction)exp; auto bfuns = otya.smilebasic.builtinfunctions.BuiltinFunction.builtinFunctions.get(func.name, null); otya.smilebasic.builtinfunctions.BuiltinFunction bfun; if(bfuns) { bfun = bfuns.overloadResolution(func.args.length, 1); if(bfun.argments.length >= func.args.length) { auto k = bfun.argments.length - func.args.length; foreach(l;0..k) { genCodeImm(Value(ValueType.Void)); } } } foreach_reverse(Expression i ; func.args) { compileExpression(i, sc); } if(bfun) { genCode(new CallBuiltinFunction(bfun, cast(int)func.args.length, 1)); } else { writeln(func.name); genCode(new CallFunctionCode(func.name, cast(int)func.args.length)); } } break; case NodeType.IndexExpressions://[expr,expr,expr,expr]用 { auto index = cast(IndexExpressions)exp; int count = 0; foreach_reverse(Expression i; index.expressions) { compileExpression(i, sc); count++; if(count >= 4) break;//念のため } } break; default: stderr.writeln("Compile:NotImpl ", exp.type); break; } } void compilePopVar(Expression expr, Scope sc) { switch(expr.type) { case NodeType.Variable: { auto var = cast(Variable)expr; genCodePopVar(var.name, sc); //int index; //bool local; //getVarIndex(var.name, sc, index, local); //genCode(local ? new PopL(index) : new PopG(index)); } break; case NodeType.BinaryOperator: { auto binop = cast(BinaryOperator)expr; compileExpression(binop.item2, sc); if(binop.operator == TokenType.LBracket) { IndexExpressions ie = cast(IndexExpressions)binop.item2; auto var = cast(Variable)binop.item1; int index; bool local; getVarIndex(var.name, sc, index, local); if(ie) { genCode(new PopArray(index, cast(int)ie.expressions.length, local)); } else { genCode(new PopArray(index, 1, local)); } break; } } goto default; default: stderr.writeln("ERROR"); break; } } void compileIf(If node, Scope sc) { compileExpression(node.condition, sc); //条件式がfalseならendif or elseに飛ぶ auto else_ = genCodeGotoFalse(); compileStatements(node.then, sc); //もしelseもあるのならば、endifに飛ぶ GotoAddr then; if(node.hasElse) { then = genCodeGoto(); else_.address = cast(int)code.length; compileStatements(node.else_, sc); then.address = cast(int)code.length; } else { //endifに飛ばす else_.address = cast(int)code.length; } } void compileFor(For node, Scope s) { compileStatement(node.initExpression, s); auto forstart = code.length; compileExpression(node.stepExpression, s); genCodeImm(Value(0)); //step>=0 genCodeOP(TokenType.GreaterEqual); auto positiveZero = genCodeGotoTrue();//正の値または0 //stepが負の値の時の処理 //toよりcounterが小さい場合はBREAK //to==counterの時はBREAKしない //t c //0>0 false //FOR I=0 TO -1 STEP -1 //-1>0 false //1>0 true compileExpression(node.toExpression, s); genCodePushVar(node.initExpression.name, s); genCodeOP(TokenType.Greater); auto breakAddr = genCodeGotoTrue(); auto forAddr = genCodeGoto(); positiveZero.address = cast(int)code.length; //stepが正の値の時の処理 //toよりcounterが大きい場合はBREAK //t c //0<0 false //1<0 false //0<1 true break compileExpression(node.toExpression, s); genCodePushVar(node.initExpression.name, s); genCodeOP(TokenType.Less); genCode(breakAddr); forAddr.address = cast(int)code.length; s = new Scope(new GotoAddr(-1), new GotoAddr(-1), s); compileStatements(node.statements, s); s.continueAddr.address = cast(int)code.length; //counterに加算する genCodePushVar(node.initExpression.name, s); compileExpression(node.stepExpression, s); genCodeOP(TokenType.Plus); genCodePopVar(node.initExpression.name, s); genCodeGoto(cast(int)forstart); breakAddr.address = cast(int)code.length; s.breakAddr.address = cast(int)code.length; } void compileWhile(While node, Scope s) { auto whilestart = code.length; s = new Scope(new GotoAddr(-1), new GotoAddr(cast(int)whilestart), s); compileExpression(node.condExpression, s); auto breakAddr = genCodeGotoFalse(); compileStatements(node.statements, s); genCode(s.continueAddr); s.breakAddr.address = cast(int)code.length; breakAddr.address = cast(int)code.length; } void compileVar(Var node, Scope sc) { foreach(Statement v ; node.define) { if(v.type == NodeType.DefineVariable) { DefineVariable var = cast(DefineVariable)v; defineVarIndex(var.name, sc); continue; } if(v.type == NodeType.DefineArray) { DefineArray var = cast(DefineArray)v; int siz = 0; foreach_reverse(Expression expr; var.dim.expressions) { compileExpression(expr, sc); siz++; if(siz == 4) break;//4次元まで(パーサーで除去するけど万が一に備えて } defineVarIndexVoid(var.name, sc); genCode(new NewArray(getType(var.name), cast(int)var.dim.expressions.length)); genCodePopVar(var.name, sc); continue; } } } void compileInc(Inc node, Scope sc) { compileExpression(node.expression, sc); int index = sc.func ? sc.func.hasLocalVarIndex(node.name) : 0; if(index) { genCode(new IncCodeL(index)); } else { genCode(new IncCodeG(this.getGlobalVarIndex(node.name))); } } void compileStatements(Statements statements, Scope sc) { foreach(Statement s ; statements.statements) { compileStatement(s, sc); } } void compileDefineFunction(DefineFunction node) { auto skip = genCodeGoto(); Function func = new Function(cast(int)this.code.length, node.name, node.returnExpr, cast(int)node.arguments.length); Scope sc = new Scope(func); foreach(wstring arg; node.arguments) { func.defineArgumentIndex(arg, this); } foreach(wstring arg; node.outArguments) { func.defineLocalVarIndexVoid(arg, this); } if(!func.returnExpr) func.outArgCount = cast(int)node.outArguments.length; compileStatements(node.functionBody, sc); if(func.returnExpr) genCodeImm(Value(ValueType.Void)); genCode(new ReturnFunction(func)); skip.address = cast(int)this.code.length; this.functions[func.name] = func; } void compileOn(On on, Scope sc) { compileExpression(on.condition, sc); genCode(new OnS(on.labels, on.isGosub, sc)); } void compileInput(Input input, Scope sc) { if(input.question) { genCodeImm(Value("?\n")); } if(input.message) { compileExpression(input.message, sc); } genCode(new PrintCode((input.question ? 1 : 0) + (input.message ? 1 : 0))); foreach_reverse(i; input.variables) { compileExpression(i, sc); } genCode(new InputCode(cast(int)input.variables.length)); foreach_reverse(i; input.variables) { compilePopVar(i, sc); } } void compileRead(Read read, Scope sc) { genCode(new ReadCode(cast(int)read.variables.length)); foreach_reverse(i; read.variables) { compilePopVar(i, sc); } } void compileStatement(Statement i, Scope s) { switch(i.type) { case NodeType.Print: { auto print = cast(Print)i; foreach_reverse(PrintArgument j ; print.args) { switch(j.type) { case PrintArgumentType.Expression: compileExpression(j.expression, s); break; case PrintArgumentType.Line: genCodeImm(Value("\n")); break; case PrintArgumentType.Tab: genCodeImm(Value("\t")); break; default: break; } } code ~= new PrintCode(cast(int)print.args.length); } break; case NodeType.Assign: { auto assign = cast(Assign)i; compileExpression(assign.expression, s); genCodePopVar(assign.name, s); } break; case NodeType.Label: { auto label = cast(Label)i; if(s.func) { s.func.label[label.label] = cast(int)code.length; } else { globalLabel[label.label] = cast(int)code.length; } s.data.addLabel(label.label); } break; case NodeType.Goto: { auto gotou = cast(Goto)i; if(!gotou.label) { compileExpression(gotou.labelexpr, s); genCode(new GotoExpr(s)); } else { genCodeGoto(gotou.label, s); } } break; case NodeType.If: compileIf(cast(If)i, s); break; case NodeType.For: compileFor(cast(For)i, s); break; case NodeType.Gosub: { auto gosub = cast(Gosub)i; if(gosub.label) { genCodeGosub(gosub.label, s); } else { compileExpression(gosub.labelexpr, s); genCode(new GosubExpr(s)); } } break; case NodeType.Return: { auto ret = cast(Return)i; if(s.func is null) { if(ret.expression is null) genCode(new ReturnSubroutine()); else throw new SyntaxError(); } else { //値を返せる関数 if(s.func.returnExpr) { compileExpression(ret.expression, s); genCode(new ReturnFunction(s.func)); } else { genCode(new ReturnFunction(s.func)); } } } break; case NodeType.End: genCode(new EndVM()); break; case NodeType.Break: if(s.breakAddr is null) { //syntax-error break; } genCode(s.breakAddr); break; case NodeType.Continue: if(s.continueAddr is null) { //syntax-error break; } genCode(s.continueAddr); break; case NodeType.Var: compileVar(cast(Var)i, s); break; case NodeType.ArrayAssign: { auto assign = cast(ArrayAssign)i; compileExpression(assign.assignExpression, s); compileExpression(assign.indexExpression, s); int index; bool local; getVarIndex(assign.name, s, index, local); genCode(new PopArray(index, cast(int)assign.indexExpression.expressions.length, local)); } break; case NodeType.CallFunctionStatement: { auto func = cast(CallFunctionStatement)i; auto bfuns = otya.smilebasic.builtinfunctions.BuiltinFunction.builtinFunctions.get(func.name, null); otya.smilebasic.builtinfunctions.BuiltinFunction bfun; if(bfuns) { bfun = bfuns.overloadResolution(func.args.length, func.outVariable.length); int k = cast(int)bfun.argments.length - cast(int)func.args.length; foreach(l;0..k) { genCodeImm(Value(ValueType.Void)); } } foreach_reverse(Expression j ; func.args) { compileExpression(j, s); } if(bfun) { genCode(new CallBuiltinFunction(bfun, cast(int)func.args.length, cast(int)bfun.results.length));//func.outVariable.length)); } else { writeln(func.name); genCode(new CallFunctionCode(func.name, cast(int)func.args.length, cast(int)func.outVariable.length)); } if(bfun) { int k = cast(int)bfun.results.length - cast(int)func.outVariable.length; foreach(l;0..k) { genCode(new DecSP); } } //TODO:OUT foreach_reverse(wstring var; func.outVariable) { genCodePopVar(var, s); } } break; case NodeType.While: compileWhile(cast(While)i, s); break; case NodeType.Inc: compileInc(cast(Inc)i, s); break; case NodeType.Data: { auto data = cast(Data)i; foreach(j; data.data) s.data.addData(j); } break; case NodeType.Read: compileRead(cast(Read)i, s); break; case NodeType.Restore: { Restore restore = cast(Restore)i; Expression label = restore.label; if(label.type == NodeType.Constant) { Constant cons = cast(Constant)label; if(cons.value.isString) { genCode(new RestoreCodeS(cons.value.stringValue)); break; //文字列じゃないならば定数でも実行時エラー } } compileExpression(label, s); genCode(new RestoreExprCode(s.data)); } break; case NodeType.On: compileOn(cast(On)i, s); break; case NodeType.Input: compileInput(cast(Input)i, s); break; default: stderr.writeln("Compile:NotImpl ", i.type); } this.debugInfo.addLocation(i.location, code); } Scope globalScope; Code[] compileProgram() { Scope s = globalScope = new Scope(); foreach(Statement i ; statements.statements) { if(i.type == NodeType.DefineFunction) { compileDefineFunction(cast(DefineFunction)i); continue; } compileStatement(i, s); } foreach(int i, Code c; code) { if(c.type == CodeType.GotoS) { auto label = cast(GotoS)c; if(label.sc.func) { code[i] = new GotoAddr(label.sc.func.label[label.label]); } else { code[i] = new GotoAddr(globalLabel[label.label]); } } if(c.type == CodeType.GosubS) { auto label = cast(GosubS)c; if(label.sc.func) { code[i] = new GosubAddr(label.sc.func.label[label.label]); } else { code[i] = new GosubAddr(globalLabel[label.label]); } } if(c.type == CodeType.OnS) { auto label = cast(OnS)c; int[] addresses = new int[label.labels.length]; foreach(int index, wstring l; label.labels) { try { if(label.sc.func) { addresses[index] =label.sc.func.label[l]; } else { addresses[index] = globalLabel[l]; } } catch(Error re) { //undefined labelは実行時に出る(TECDEMOが動かない) addresses[index] = int.min; } } if(label.isGosub) { code[i] = new OnGosub(addresses); } else { code[i] = new OnGoto(addresses); } } if(c.type == CodeType.RestoreCodeS) { auto restore = cast(RestoreCodeS)c; code[i] = new RestoreCode(s.data.label[restore.label], s.data); } } genCode(new EndVM()); return code; } VM compile() { compileProgram(); VM vm = new VM(); vm.loadSlot(0, code, globalIndex + 1, global, functions, globalScope.data, globalLabel, debugInfo); vm.setCurrentSlot(0); registerSystemVariable(vm); return vm; } } unittest { import otya.smilebasic.parser; auto parser = new Parser("A=1+2+3+4*5/4-5+(6+6)*7"); auto vm = parser.compile(); vm.run(); writeln(vm.testGetGlobalVariable("A").castDouble); assert(vm.testGetGlobalVariable("A").castDouble == 1+2+3+4*5/4-5+(6+6)*7); }