2015-06-04 20:51:03 +09:00
|
|
|
module otya.smilebasic.compiler;
|
|
|
|
|
import otya.smilebasic.node;
|
2015-06-04 22:53:15 +09:00
|
|
|
import otya.smilebasic.token;
|
2015-06-04 20:51:03 +09:00
|
|
|
import otya.smilebasic.vm;
|
2015-06-04 22:53:15 +09:00
|
|
|
import otya.smilebasic.type;
|
2015-06-04 20:51:03 +09:00
|
|
|
import std.stdio;
|
|
|
|
|
class Compiler
|
|
|
|
|
{
|
|
|
|
|
Statements statements;
|
|
|
|
|
this(Statements statements)
|
|
|
|
|
{
|
|
|
|
|
this.statements = statements;
|
2015-06-04 22:53:15 +09:00
|
|
|
code = new Code[0];
|
2015-06-04 20:51:03 +09:00
|
|
|
}
|
|
|
|
|
|
2015-06-04 22:53:15 +09:00
|
|
|
Code[] code;
|
2015-06-05 19:57:35 +09:00
|
|
|
int[wstring] global;
|
|
|
|
|
int globalIndex = 0;
|
2015-06-04 22:53:15 +09:00
|
|
|
void genCodeImm(Value value)
|
|
|
|
|
{
|
|
|
|
|
code ~= new Push(value);
|
|
|
|
|
}
|
2015-06-05 19:57:35 +09:00
|
|
|
void genCodePushGlobal(int ind)
|
|
|
|
|
{
|
|
|
|
|
code ~= new PushG(ind);
|
|
|
|
|
}
|
2015-06-05 21:41:40 +09:00
|
|
|
void genCodePopGlobal(int ind)
|
|
|
|
|
{
|
|
|
|
|
code ~= new PopG(ind);
|
|
|
|
|
}
|
2015-06-04 22:53:15 +09:00
|
|
|
void genCodeOP(TokenType op)
|
|
|
|
|
{
|
|
|
|
|
code ~= new Operate(op);
|
|
|
|
|
}
|
2015-06-05 21:41:40 +09:00
|
|
|
int defineGlobalVarIndex(wstring name)
|
|
|
|
|
{
|
|
|
|
|
int global = this.global.get(name, 0);
|
|
|
|
|
if(global == 0)
|
|
|
|
|
{
|
|
|
|
|
this.global[name] = ++globalIndex = global;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//error:二重定義
|
|
|
|
|
}
|
|
|
|
|
return global;
|
|
|
|
|
}
|
|
|
|
|
int getGlobalVarIndex(wstring name)
|
|
|
|
|
{
|
|
|
|
|
int global = this.global.get(name, 0);
|
|
|
|
|
if(global == 0)
|
|
|
|
|
{
|
|
|
|
|
//local変数をあたる
|
|
|
|
|
//それでもだめならOPTION STRICTならエラー
|
|
|
|
|
this.global[name] = ++globalIndex = global;
|
|
|
|
|
}
|
|
|
|
|
return global;
|
|
|
|
|
}
|
2015-06-04 22:53:15 +09:00
|
|
|
void compileExpression(Expression exp)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
switch(exp.type)
|
|
|
|
|
{
|
|
|
|
|
case NodeType.Constant:
|
|
|
|
|
genCodeImm((cast(Constant)exp).value);
|
|
|
|
|
break;
|
|
|
|
|
case NodeType.BinaryOperator:
|
|
|
|
|
{
|
|
|
|
|
auto binop = cast(BinaryOperator)exp;
|
|
|
|
|
compileExpression(binop.item1);
|
|
|
|
|
compileExpression(binop.item2);
|
|
|
|
|
genCodeOP(binop.operator);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case NodeType.Variable:
|
2015-06-05 19:57:35 +09:00
|
|
|
auto var = cast(Variable)exp;
|
2015-06-05 21:41:40 +09:00
|
|
|
genCodePushGlobal(getGlobalVarIndex(var.name));
|
2015-06-04 22:53:15 +09:00
|
|
|
break;
|
|
|
|
|
case NodeType.CallFunction:
|
|
|
|
|
{
|
|
|
|
|
auto func = cast(CallFunction)exp;
|
|
|
|
|
if(func.name == "ADD")
|
|
|
|
|
{
|
|
|
|
|
foreach_reverse(Expression i ; func.args)
|
|
|
|
|
{
|
|
|
|
|
compileExpression(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2015-06-05 22:29:37 +09:00
|
|
|
stderr.writeln("Compile:NotImpl ", exp.type);
|
2015-06-04 22:53:15 +09:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VM compile()
|
2015-06-04 20:51:03 +09:00
|
|
|
{
|
|
|
|
|
foreach(Statement i ; statements.statements)
|
|
|
|
|
{
|
|
|
|
|
switch(i.type)
|
|
|
|
|
{
|
2015-06-04 22:53:15 +09:00
|
|
|
case NodeType.Print:
|
|
|
|
|
{
|
|
|
|
|
auto print = cast(Print)i;
|
|
|
|
|
foreach_reverse(PrintArgument j ; print.args)
|
|
|
|
|
{
|
|
|
|
|
switch(j.type)
|
|
|
|
|
{
|
|
|
|
|
case PrintArgumentType.Expression:
|
|
|
|
|
compileExpression(j.expression);
|
|
|
|
|
break;
|
|
|
|
|
case PrintArgumentType.Line:
|
|
|
|
|
genCodeImm(Value("\n"));
|
|
|
|
|
break;
|
|
|
|
|
case PrintArgumentType.Tab:
|
|
|
|
|
genCodeImm(Value("\t"));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
code ~= new PrintCode(print.args.length);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2015-06-05 21:41:40 +09:00
|
|
|
case NodeType.Assign:
|
|
|
|
|
{
|
|
|
|
|
auto assign = cast(Assign)i;
|
|
|
|
|
compileExpression(assign.expression);
|
|
|
|
|
genCodePopGlobal(getGlobalVarIndex(assign.name));
|
|
|
|
|
}
|
|
|
|
|
break;
|
2015-06-04 20:51:03 +09:00
|
|
|
default:
|
|
|
|
|
stderr.writeln("Compile:NotImpl ", i.type);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-06-05 22:29:37 +09:00
|
|
|
return new VM(code, globalIndex + 1, global);
|
2015-06-04 20:51:03 +09:00
|
|
|
}
|
|
|
|
|
}
|
2015-06-05 22:29:37 +09:00
|
|
|
|
|
|
|
|
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.testGetGlobaVariable("A").integerValue);
|
|
|
|
|
assert(vm.testGetGlobaVariable("A").integerValue == 1+2+3+4*5/4-5+(6+6)*7);
|
|
|
|
|
}
|