diff --git a/SMILEBASIC/VM.d b/SMILEBASIC/VM.d index 6295c25..4758465 100644 --- a/SMILEBASIC/VM.d +++ b/SMILEBASIC/VM.d @@ -2,6 +2,7 @@ module otya.smilebasic.vm; import otya.smilebasic.type; import otya.smilebasic.token; import otya.smilebasic.error; +import otya.smilebasic.compiler; import std.uni; import std.utf; import std.conv; @@ -14,6 +15,7 @@ class VM Value[] stack; Value[] global; int[wstring] globalTable; + Function[wstring] functions; int bp; ValueType getType(wstring name) { @@ -30,7 +32,7 @@ class VM return ValueType.Double;//DEFINT時 } } - this(Code[] code, int len, int[wstring] globalTable) + this(Code[] code, int len, int[wstring] globalTable, Function[wstring] functions) { this.code = code; this.stack = new Value[16384]; @@ -40,6 +42,7 @@ class VM { this.global[v] = Value(getType(k)); } + this.functions = functions; } void run() { @@ -684,3 +687,49 @@ class PopGArray : Code throw new TypeMismatch(); } } +class ReturnFunction : Code +{ + Function func; + this(Function func) + { + this.func = func; + } + override void execute(VM vm) + { + int oldstacki = vm.stacki; + vm.stacki = vm.bp + 2; + Value bp, pc; + vm.pop(pc); + vm.pop(bp); + vm.push(Value(235)); + vm.pc = pc.integerValue; + vm.bp = bp.integerValue; + } +} +class CallFunctionCode : Code +{ + wstring name; + int argCount; + this(wstring name, int argCount) + { + this.name = name; + this.argCount = argCount; + } + override void execute(VM vm) + { + Function func = vm.functions[name]; + if(!func) + { + throw new SyntaxError(); + } + if(func.argCount != this.argCount) + { + throw new IllegalFunctionCall(); + } + //TODO:args + vm.push(Value(vm.bp)); + vm.push(Value(vm.pc)); + vm.bp = vm.stacki - 2; + vm.pc = func.address - 1; + } +} diff --git a/SMILEBASIC/compiler.d b/SMILEBASIC/compiler.d index fc10181..7d029f7 100644 --- a/SMILEBASIC/compiler.d +++ b/SMILEBASIC/compiler.d @@ -9,6 +9,7 @@ class Scope { GotoAddr breakAddr; GotoAddr continueAddr; + Function func; this() { } @@ -17,19 +18,28 @@ class Scope this.breakAddr = breakAddr; this.continueAddr = continueAddr; } + this(Function func) + { + this.func = func; + } } class Function { int address; wstring name; + int argCount; int argumentIndex; int variableIndex; int[wstring] variable; int[wstring] label; - this(int address, wstring name) + bool returnExpr; + this(int address, wstring name, bool returnExpr, int argCount) { this.address = address; this.name = name; + this.returnExpr = returnExpr; + this.argCount = argCount; + this.variableIndex = 1;//0,bp,1,pc } int getLocalVarIndex(wstring name) { @@ -56,6 +66,20 @@ class Function } return var; } + int defineArgumentIndex(wstring name) + { + int var = this.variable.get(name, 0); + if(var == 0) + { + this.variable[name] = var = ++variableIndex; + } + else + { + //error:二重定義 + //TODO:3.1ではエラーにならない + } + return var; + } } class Compiler { @@ -165,7 +189,11 @@ class Compiler } void compileExpression(Expression exp) { - + if(!exp) + { + genCodeImm(Value(ValueType.Void)); + return; + } switch(exp.type) { case NodeType.Constant: @@ -206,13 +234,11 @@ class Compiler case NodeType.CallFunction: { auto func = cast(CallFunction)exp; - if(func.name == "ADD") + foreach_reverse(Expression i ; func.args) { - foreach_reverse(Expression i ; func.args) - { - compileExpression(i); - } + compileExpression(i); } + genCode(new CallFunctionCode(func.name, func.args.length)); } break; case NodeType.IndexExpressions://[expr,expr,expr,expr]用 @@ -333,6 +359,19 @@ class Compiler compileStatement(s, sc); } } + void compileDefineFunction(DefineFunction node) + { + auto skip = genCodeGoto(); + Function func = new Function(this.code.length, node.name, node.returnExpr, node.arguments.length); + Scope sc = new Scope(func); + foreach(wstring arg; node.arguments) + { + func.defineArgumentIndex(arg); + } + compileStatements(node.functionBody, sc); + skip.address = this.code.length; + this.functions[func.name] = func; + } void compileStatement(Statement i, Scope s) { switch(i.type) @@ -393,11 +432,26 @@ class Compiler case NodeType.Return: { auto ret = cast(Return)i; - if(ret.expression is null) - genCode(new ReturnSubroutine()); + if(s.func is null) + { + if(ret.expression is null) + genCode(new ReturnSubroutine()); + else + throw new SyntaxError(); + } else { - //関数未実装:error + //値を返せる関数 + if(s.func.returnExpr) + { + compileExpression(ret.expression); + genCode(new ReturnFunction(s.func)); + } + else + { + //TODO:実装中 + throw new SyntaxError(); + } } } break; @@ -440,6 +494,11 @@ class Compiler Scope s = 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) @@ -453,7 +512,7 @@ class Compiler code[i] = new GosubAddr(globalLabel[(cast(GosubS)c).label]); } } - return new VM(code, globalIndex + 1, global); + return new VM(code, globalIndex + 1, global, functions); } } diff --git a/SMILEBASIC/error.d b/SMILEBASIC/error.d index 84c92e1..212ce7f 100644 --- a/SMILEBASIC/error.d +++ b/SMILEBASIC/error.d @@ -27,6 +27,14 @@ class SyntaxError : SmileBasicError super("Syntax error"); } } +class IllegalFunctionCall : SmileBasicError +{ + this() + { + this.errnum = 4; + super("Illegal function call"); + } +} class TypeMismatch : SmileBasicError { this() diff --git a/SMILEBASIC/main.d b/SMILEBASIC/main.d index 0519575..a67ed1f 100644 --- a/SMILEBASIC/main.d +++ b/SMILEBASIC/main.d @@ -59,15 +59,18 @@ V[0,1]=12 ?\"ABC\"[1][0] ?1/2 GOSUB @A +?A() END @A ?\"SUBROUTINE TEST\" RETURN DEF A() ?\"FUNCTION A\" + RETURN 1 END DEF B(A,B[],C) ?\"FUNCTION B\" + RETURN 0 END "); version(none) auto parser = new Parser(readText("FIZZBUZZ.TXT").to!wstring); @@ -80,6 +83,10 @@ END { writeln(sbe); } + catch(Throwable t) + { + writeln(t); + } readln(); return 0; } diff --git a/SMILEBASIC/node.d b/SMILEBASIC/node.d index 6978f9d..7ad6c4a 100644 --- a/SMILEBASIC/node.d +++ b/SMILEBASIC/node.d @@ -366,10 +366,18 @@ class DefineFunction : Statement wstring[] outArguments; wstring name; Statements functionBody; + bool returnExpr; + this(wstring name, bool returnExpr) + { + this.type = NodeType.DefineFunction; + this.name = name; + this.returnExpr = returnExpr; + } this(wstring name) { this.type = NodeType.DefineFunction; this.name = name; + this.returnExpr = false; } void addArgument(wstring name) { diff --git a/SMILEBASIC/parser.d b/SMILEBASIC/parser.d index 839100d..d672ef8 100644 --- a/SMILEBASIC/parser.d +++ b/SMILEBASIC/parser.d @@ -420,6 +420,7 @@ class Parser } return statements; } + bool isFunc = false; wstring getFunctionArgument() { auto token = lex.front(); @@ -455,6 +456,7 @@ class Parser token = lex.front(); if(token.type == TokenType.LParen) { + node.returnExpr = true; lex.popFront(); //MEMO:引数に[]を付けようが扱いは同一 while(true) @@ -488,12 +490,15 @@ class Parser } else { + node.returnExpr = false; //void関数にRETURN核とsyntaxerror writeln("NOTIMPL:DEF VOID"); syntaxError(); return null; } + isFunc = true;//面倒くさい node.functionBody = functionStatements(); + isFunc = false; return node; } Statements functionStatements() @@ -634,7 +639,15 @@ class Parser break; case TokenType.Return: lex.popFront(); - node = new Return(expression()); + if(isFunc) + { + node = new Return(expression()); + lex.popFront(); + } + else + { + node = new Return(null); + } return node; case TokenType.End: node = new End(); @@ -1013,7 +1026,8 @@ class Parser { lex.popFront(); token = lex.front(); - + + if(token.type == TokenType.RParen) break; if(token.type == TokenType.Comma) { func.addArg(new VoidExpression()); @@ -1021,7 +1035,7 @@ class Parser token = lex.front(); } else - func.addArg(expression()); + func.addArg(expression()); if(lex.front().type == TokenType.RParen) break; } }