1
0
Files
otyaSMILEBASIC/SMILEBASIC/parser.d
2015-06-03 21:52:09 +09:00

351 lines
9.6 KiB
D

module otya.smilebasic.parser;
import otya.smilebasic.token;
import otya.smilebasic.type;
import otya.smilebasic.node;
import std.ascii;
import std.stdio;
class Lexical
{
TokenType[] table;
wstring code;
int index;
this(wstring input)
{
this.code = input;
table = new TokenType[256];
for(int i = 0;i<256;i++)
{
table[i] = TokenType.Unknown;
}
table['+'] = TokenType.Plus;
table['-'] = TokenType.Minus;
table['*'] = TokenType.Mul;
table['/'] = TokenType.Div;
table['('] = TokenType.LParen;
table[')'] = TokenType.RParen;
table[','] = TokenType.Comma;
}
bool empty()
{
return index >= code.length;
}
Token token;
void popFront()
{
if(empty())
{
token = Token(TokenType.Unknown);
return;
}
int i = index;
for(;i < code.length;i++)
{
wchar c = code[i];
if(c == ' ') continue;
if(c.isDigit())
{
int num;
for(;i < code.length;i++)
{
c = code[i];
if(!c.isDigit())
{
break;
}
num = num * 10 + (c - '0');
}
token = Token(TokenType.Integer, Value(num));
break;
}
if(c.isAlpha())
{
wstring iden;
for(;i < code.length;i++)
{
c = code[i];
if(!c.isAlpha())
{
break;
}
iden ~= c;
}
token = Token(TokenType.Iden, Value(iden));
break;
}
if(table[cast(char)c] == TokenType.Unknown)
{
//error
}
token = Token(table[cast(char)c]);
i++;
break;
}
index = i;
}
Token front()
{
return token;
}
}
unittest
{
{
auto lex = new Lexical("1");
assert(lex.empty() == false);
lex.popFront();
auto token = lex.front();
assert(token.type == TokenType.Integer);
assert(token.value.integerValue == 1);
assert(lex.empty() == true);
}
{
auto lex = new Lexical("12345");
assert(lex.empty() == false);
lex.popFront();
auto token = lex.front();
assert(token.type == TokenType.Integer);
assert(token.value.integerValue == 12345);
assert(lex.empty() == true);
}
{
auto lex = new Lexical("12345 67890");
assert(lex.empty() == false);
lex.popFront();
auto token = lex.front();
assert(token.type == TokenType.Integer);
assert(token.value.integerValue == 12345);
assert(lex.empty() == false);
lex.popFront();
token = lex.front();
assert(token.type == TokenType.Integer);
assert(token.value.integerValue == 67890);
assert(lex.empty() == true);
}
}
class Parser
{
wstring code;
Lexical lex;
this(wstring input)
{
this.code = input;
lex = new Lexical(input);
}
int getOPRank(TokenType type)
{
switch(type)
{
// return 8;//&&,||
case TokenType.And:
case TokenType.Or:
case TokenType.Xor:
return 7;//AND,OR,XOR
// return 6;//==,!=,<,<=,>,>=
// return 5;//<<,>>
case TokenType.Plus:
case TokenType.Minus:
return 4;//+,-(bin)
case TokenType.Mul:
case TokenType.Div:
return 3;//*,/,DIV,MOD
// return 2;//-,NOT,!
// return 1;//()
default:
return 0;//TODO:エラーにすべきかは実装次第
}
}
int calc()
{
lex.popFront();
auto exp = expression();
version(none)writeln();
return calc(exp);
}
int calc(Expression exp)
{
switch(exp.type)
{
case NodeType.Constant:
return (cast(Constant)exp).value.integerValue;
case NodeType.BinaryOperator:
{
auto binop = cast(BinaryOperator)exp;
auto i1 = calc(binop.item1);
auto i2 = calc(binop.item2);
switch(binop.operator)
{
case TokenType.Plus:
return i1 + i2;
case TokenType.Minus:
return i1 - i2;
case TokenType.Mul:
return i1 * i2;
case TokenType.Div:
return i1 / i2;
default:
return - - -1;
}
}
break;
case NodeType.Variable:
return 100;
case NodeType.CallFunction:
{
auto func = cast(CallFunction)exp;
int result = 100;
if(func.name == "ADD")
{
result = 0;
foreach(Expression i ; func.args)
{
result += calc(i);
}
}
return result;
}
default:
return - - -1;
}
}
Expression expression()
{
Expression node = null;
return term(8, node);
}
Expression term(int order, Expression node)
{
if(order == 1)
{
return factor();
}
Expression exp = term(order - 1, node);
auto token = lex.front();
if(order == getOPRank(token.type))
{
BinaryOperator op = new BinaryOperator(exp);
while(order == getOPRank(token.type))
{
auto tt = token.type;
op.operator = token.type;
lex.popFront();
op.item2 = term(order - 1, node);
token = lex.front();
version(none)
write(tt, " ");
if(order == getOPRank(token.type))
{
BinaryOperator op2 = new BinaryOperator(op);
op.item1 = op2;
}
version(none)stdout.flush();
}
return op;
}
return exp;
}
Expression factor()
{
auto token = lex.front();
Expression node = null;
switch(token.type)
{
case TokenType.Integer:
version(none)write(token.value.integerValue, ' ');
version(none)stdout.flush();
node = new Constant(Value(token.value.integerValue));
break;
case TokenType.Iden:
if(!lex.empty())
lex.popFront();
if(lex.front().type == TokenType.LParen)
{
auto func = new CallFunction(token.value.stringValue);
node = func;
//関数呼び出しだった
while(true)
{
lex.popFront();
token = lex.front();
if(token.type == TokenType.Comma)
{
func.addArg(new VoidExpression());
lex.popFront();
token = lex.front();
}
else
func.addArg(expression());
if(lex.front().type == TokenType.RParen) break;
}
}
else
{
return new Variable(token.value.stringValue);
}
break;
case TokenType.LParen:
lex.popFront();
node = expression();
token = lex.front();
if(token.type != TokenType.RParen)
//error
{}
break;
default:
return node;
}
if(!lex.empty())
lex.popFront();
return node;
}
}
private void test(wstring exp, int result)
{
auto parser = new Parser(exp);
assert(parser.calc() == result);
}
//D言語の式の評価結果と同じか検証
private void Test(const char[] V)()
{
mixin("writeln(\""~V~"\",\" = \" ,"~V~");test(\""~V~"\", "~V~");");
}
unittest
{
struct Eval
{
wstring val;
this(wstring val)
{
this.val = val;
}
bool opAssign(int result)
{
auto parser = new Parser(val);
try
{
assert(parser.calc() == result);
}
catch(Exception e)
{
assert(false, e.toString());
}
return false;
}
}
test("1+1", 2);//1+1は2
test("2*3+2", 8);//2*3+2は8
test("2*3+2+1", 9);//2*3+2+1は9
Eval("2+3*4+2") = 2+3*4+2;
Eval("2+3*4*5+6*7+8") = 2+3*4*5+6*7+8;
Eval("2+3*4*5") = 2+3*4*5;
Eval("(2+3)*4*5") = (2+3)*4*5;
Eval("(1)+(2)") = (1)+(2);
Test!"2+(3+4)*5"();
Test!"2+20/5*3"();
//Eval("test(2+5)") = 2+5;
//Eval("test(2+5, test(2, 3+5))") = 3+5;
Test!"2-3*4-5"();
}