とりあえず関数呼び出し
This commit is contained in:
@@ -2,6 +2,7 @@ module otya.smilebasic.vm;
|
|||||||
import otya.smilebasic.type;
|
import otya.smilebasic.type;
|
||||||
import otya.smilebasic.token;
|
import otya.smilebasic.token;
|
||||||
import otya.smilebasic.error;
|
import otya.smilebasic.error;
|
||||||
|
import otya.smilebasic.compiler;
|
||||||
import std.uni;
|
import std.uni;
|
||||||
import std.utf;
|
import std.utf;
|
||||||
import std.conv;
|
import std.conv;
|
||||||
@@ -14,6 +15,7 @@ class VM
|
|||||||
Value[] stack;
|
Value[] stack;
|
||||||
Value[] global;
|
Value[] global;
|
||||||
int[wstring] globalTable;
|
int[wstring] globalTable;
|
||||||
|
Function[wstring] functions;
|
||||||
int bp;
|
int bp;
|
||||||
ValueType getType(wstring name)
|
ValueType getType(wstring name)
|
||||||
{
|
{
|
||||||
@@ -30,7 +32,7 @@ class VM
|
|||||||
return ValueType.Double;//DEFINT時
|
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.code = code;
|
||||||
this.stack = new Value[16384];
|
this.stack = new Value[16384];
|
||||||
@@ -40,6 +42,7 @@ class VM
|
|||||||
{
|
{
|
||||||
this.global[v] = Value(getType(k));
|
this.global[v] = Value(getType(k));
|
||||||
}
|
}
|
||||||
|
this.functions = functions;
|
||||||
}
|
}
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
@@ -684,3 +687,49 @@ class PopGArray : Code
|
|||||||
throw new TypeMismatch();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class Scope
|
|||||||
{
|
{
|
||||||
GotoAddr breakAddr;
|
GotoAddr breakAddr;
|
||||||
GotoAddr continueAddr;
|
GotoAddr continueAddr;
|
||||||
|
Function func;
|
||||||
this()
|
this()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -17,19 +18,28 @@ class Scope
|
|||||||
this.breakAddr = breakAddr;
|
this.breakAddr = breakAddr;
|
||||||
this.continueAddr = continueAddr;
|
this.continueAddr = continueAddr;
|
||||||
}
|
}
|
||||||
|
this(Function func)
|
||||||
|
{
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class Function
|
class Function
|
||||||
{
|
{
|
||||||
int address;
|
int address;
|
||||||
wstring name;
|
wstring name;
|
||||||
|
int argCount;
|
||||||
int argumentIndex;
|
int argumentIndex;
|
||||||
int variableIndex;
|
int variableIndex;
|
||||||
int[wstring] variable;
|
int[wstring] variable;
|
||||||
int[wstring] label;
|
int[wstring] label;
|
||||||
this(int address, wstring name)
|
bool returnExpr;
|
||||||
|
this(int address, wstring name, bool returnExpr, int argCount)
|
||||||
{
|
{
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.returnExpr = returnExpr;
|
||||||
|
this.argCount = argCount;
|
||||||
|
this.variableIndex = 1;//0,bp,1,pc
|
||||||
}
|
}
|
||||||
int getLocalVarIndex(wstring name)
|
int getLocalVarIndex(wstring name)
|
||||||
{
|
{
|
||||||
@@ -56,6 +66,20 @@ class Function
|
|||||||
}
|
}
|
||||||
return var;
|
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
|
class Compiler
|
||||||
{
|
{
|
||||||
@@ -165,7 +189,11 @@ class Compiler
|
|||||||
}
|
}
|
||||||
void compileExpression(Expression exp)
|
void compileExpression(Expression exp)
|
||||||
{
|
{
|
||||||
|
if(!exp)
|
||||||
|
{
|
||||||
|
genCodeImm(Value(ValueType.Void));
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch(exp.type)
|
switch(exp.type)
|
||||||
{
|
{
|
||||||
case NodeType.Constant:
|
case NodeType.Constant:
|
||||||
@@ -206,13 +234,11 @@ class Compiler
|
|||||||
case NodeType.CallFunction:
|
case NodeType.CallFunction:
|
||||||
{
|
{
|
||||||
auto func = cast(CallFunction)exp;
|
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;
|
break;
|
||||||
case NodeType.IndexExpressions://[expr,expr,expr,expr]用
|
case NodeType.IndexExpressions://[expr,expr,expr,expr]用
|
||||||
@@ -333,6 +359,19 @@ class Compiler
|
|||||||
compileStatement(s, sc);
|
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)
|
void compileStatement(Statement i, Scope s)
|
||||||
{
|
{
|
||||||
switch(i.type)
|
switch(i.type)
|
||||||
@@ -393,11 +432,26 @@ class Compiler
|
|||||||
case NodeType.Return:
|
case NodeType.Return:
|
||||||
{
|
{
|
||||||
auto ret = cast(Return)i;
|
auto ret = cast(Return)i;
|
||||||
if(ret.expression is null)
|
if(s.func is null)
|
||||||
genCode(new ReturnSubroutine());
|
{
|
||||||
|
if(ret.expression is null)
|
||||||
|
genCode(new ReturnSubroutine());
|
||||||
|
else
|
||||||
|
throw new SyntaxError();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//関数未実装:error
|
//値を返せる関数
|
||||||
|
if(s.func.returnExpr)
|
||||||
|
{
|
||||||
|
compileExpression(ret.expression);
|
||||||
|
genCode(new ReturnFunction(s.func));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//TODO:実装中
|
||||||
|
throw new SyntaxError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -440,6 +494,11 @@ class Compiler
|
|||||||
Scope s = new Scope();
|
Scope s = new Scope();
|
||||||
foreach(Statement i ; statements.statements)
|
foreach(Statement i ; statements.statements)
|
||||||
{
|
{
|
||||||
|
if(i.type == NodeType.DefineFunction)
|
||||||
|
{
|
||||||
|
compileDefineFunction(cast(DefineFunction)i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
compileStatement(i, s);
|
compileStatement(i, s);
|
||||||
}
|
}
|
||||||
foreach(int i, Code c; code)
|
foreach(int i, Code c; code)
|
||||||
@@ -453,7 +512,7 @@ class Compiler
|
|||||||
code[i] = new GosubAddr(globalLabel[(cast(GosubS)c).label]);
|
code[i] = new GosubAddr(globalLabel[(cast(GosubS)c).label]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new VM(code, globalIndex + 1, global);
|
return new VM(code, globalIndex + 1, global, functions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,14 @@ class SyntaxError : SmileBasicError
|
|||||||
super("Syntax error");
|
super("Syntax error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class IllegalFunctionCall : SmileBasicError
|
||||||
|
{
|
||||||
|
this()
|
||||||
|
{
|
||||||
|
this.errnum = 4;
|
||||||
|
super("Illegal function call");
|
||||||
|
}
|
||||||
|
}
|
||||||
class TypeMismatch : SmileBasicError
|
class TypeMismatch : SmileBasicError
|
||||||
{
|
{
|
||||||
this()
|
this()
|
||||||
|
|||||||
@@ -59,15 +59,18 @@ V[0,1]=12
|
|||||||
?\"ABC\"[1][0]
|
?\"ABC\"[1][0]
|
||||||
?1/2
|
?1/2
|
||||||
GOSUB @A
|
GOSUB @A
|
||||||
|
?A()
|
||||||
END
|
END
|
||||||
@A
|
@A
|
||||||
?\"SUBROUTINE TEST\"
|
?\"SUBROUTINE TEST\"
|
||||||
RETURN
|
RETURN
|
||||||
DEF A()
|
DEF A()
|
||||||
?\"FUNCTION A\"
|
?\"FUNCTION A\"
|
||||||
|
RETURN 1
|
||||||
END
|
END
|
||||||
DEF B(A,B[],C)
|
DEF B(A,B[],C)
|
||||||
?\"FUNCTION B\"
|
?\"FUNCTION B\"
|
||||||
|
RETURN 0
|
||||||
END
|
END
|
||||||
");
|
");
|
||||||
version(none) auto parser = new Parser(readText("FIZZBUZZ.TXT").to!wstring);
|
version(none) auto parser = new Parser(readText("FIZZBUZZ.TXT").to!wstring);
|
||||||
@@ -80,6 +83,10 @@ END
|
|||||||
{
|
{
|
||||||
writeln(sbe);
|
writeln(sbe);
|
||||||
}
|
}
|
||||||
|
catch(Throwable t)
|
||||||
|
{
|
||||||
|
writeln(t);
|
||||||
|
}
|
||||||
readln();
|
readln();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -366,10 +366,18 @@ class DefineFunction : Statement
|
|||||||
wstring[] outArguments;
|
wstring[] outArguments;
|
||||||
wstring name;
|
wstring name;
|
||||||
Statements functionBody;
|
Statements functionBody;
|
||||||
|
bool returnExpr;
|
||||||
|
this(wstring name, bool returnExpr)
|
||||||
|
{
|
||||||
|
this.type = NodeType.DefineFunction;
|
||||||
|
this.name = name;
|
||||||
|
this.returnExpr = returnExpr;
|
||||||
|
}
|
||||||
this(wstring name)
|
this(wstring name)
|
||||||
{
|
{
|
||||||
this.type = NodeType.DefineFunction;
|
this.type = NodeType.DefineFunction;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.returnExpr = false;
|
||||||
}
|
}
|
||||||
void addArgument(wstring name)
|
void addArgument(wstring name)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -420,6 +420,7 @@ class Parser
|
|||||||
}
|
}
|
||||||
return statements;
|
return statements;
|
||||||
}
|
}
|
||||||
|
bool isFunc = false;
|
||||||
wstring getFunctionArgument()
|
wstring getFunctionArgument()
|
||||||
{
|
{
|
||||||
auto token = lex.front();
|
auto token = lex.front();
|
||||||
@@ -455,6 +456,7 @@ class Parser
|
|||||||
token = lex.front();
|
token = lex.front();
|
||||||
if(token.type == TokenType.LParen)
|
if(token.type == TokenType.LParen)
|
||||||
{
|
{
|
||||||
|
node.returnExpr = true;
|
||||||
lex.popFront();
|
lex.popFront();
|
||||||
//MEMO:引数に[]を付けようが扱いは同一
|
//MEMO:引数に[]を付けようが扱いは同一
|
||||||
while(true)
|
while(true)
|
||||||
@@ -488,12 +490,15 @@ class Parser
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
node.returnExpr = false;
|
||||||
//void関数にRETURN核とsyntaxerror
|
//void関数にRETURN核とsyntaxerror
|
||||||
writeln("NOTIMPL:DEF VOID");
|
writeln("NOTIMPL:DEF VOID");
|
||||||
syntaxError();
|
syntaxError();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
isFunc = true;//面倒くさい
|
||||||
node.functionBody = functionStatements();
|
node.functionBody = functionStatements();
|
||||||
|
isFunc = false;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
Statements functionStatements()
|
Statements functionStatements()
|
||||||
@@ -634,7 +639,15 @@ class Parser
|
|||||||
break;
|
break;
|
||||||
case TokenType.Return:
|
case TokenType.Return:
|
||||||
lex.popFront();
|
lex.popFront();
|
||||||
node = new Return(expression());
|
if(isFunc)
|
||||||
|
{
|
||||||
|
node = new Return(expression());
|
||||||
|
lex.popFront();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node = new Return(null);
|
||||||
|
}
|
||||||
return node;
|
return node;
|
||||||
case TokenType.End:
|
case TokenType.End:
|
||||||
node = new End();
|
node = new End();
|
||||||
@@ -1013,7 +1026,8 @@ class Parser
|
|||||||
{
|
{
|
||||||
lex.popFront();
|
lex.popFront();
|
||||||
token = lex.front();
|
token = lex.front();
|
||||||
|
|
||||||
|
if(token.type == TokenType.RParen) break;
|
||||||
if(token.type == TokenType.Comma)
|
if(token.type == TokenType.Comma)
|
||||||
{
|
{
|
||||||
func.addArg(new VoidExpression());
|
func.addArg(new VoidExpression());
|
||||||
@@ -1021,7 +1035,7 @@ class Parser
|
|||||||
token = lex.front();
|
token = lex.front();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
func.addArg(expression());
|
func.addArg(expression());
|
||||||
if(lex.front().type == TokenType.RParen) break;
|
if(lex.front().type == TokenType.RParen) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user