1
0
Files
otyaSMILEBASIC/SMILEBASIC/parser.d
2016-12-10 17:24:23 +09:00

2096 lines
63 KiB
D

module otya.smilebasic.parser;
import otya.smilebasic.token;
import otya.smilebasic.type;
import otya.smilebasic.node;
import otya.smilebasic.compiler;
import std.ascii;
import std.stdio;
import std.conv;
import std.range;
class Lexical
{
TokenType[] table;
TokenType[wstring] reserved;
wstring code;
int index;
int line;
void initReservedWordsTable()
{
table = new TokenType[65536];
for(int i = 0;i<65536;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;
table[':'] = TokenType.Colon;
table[';'] = TokenType.Semicolon;
table['\r'] = TokenType.NewLine;
table['\n'] = TokenType.NewLine;
table['?'] = TokenType.Print;
table['='] = TokenType.Assign;
table['<'] = TokenType.Less;
table['>'] = TokenType.Greater;
table['!'] = TokenType.LogicalNot;
table['['] = TokenType.LBracket;
table[']'] = TokenType.RBracket;
reserved["OR"] = TokenType.Or;
reserved["AND"] = TokenType.And;
reserved["XOR"] = TokenType.Xor;
reserved["NOT"] = TokenType.Not;
reserved["PRINT"] = TokenType.Print;
reserved["GOTO"] = TokenType.Goto;
reserved["IF"] = TokenType.If;
reserved["THEN"] = TokenType.Then;
reserved["ELSE"] = TokenType.Else;
reserved["ENDIF"] = TokenType.Endif;
reserved["FOR"] = TokenType.For;
reserved["NEXT"] = TokenType.Next;
reserved["MOD"] = TokenType.Mod;
reserved["DIV"] = TokenType.IntDiv;
reserved["GOSUB"] = TokenType.Gosub;
reserved["RETURN"] = TokenType.Return;
reserved["END"] = TokenType.End;
reserved["BREAK"] = TokenType.Break;
reserved["CONTINUE"] = TokenType.Continue;
reserved["VAR"] = TokenType.Var;
reserved["DIM"] = TokenType.Dim;
reserved["DEF"] = TokenType.Def;
reserved["OUT"] = TokenType.Out;
reserved["out"] = TokenType.Out;//ekkitou
reserved["WHILE"] = TokenType.While;
reserved["WEND"] = TokenType.WEnd;
reserved["INC"] = TokenType.Inc;
reserved["DEC"] = TokenType.Dec;
reserved["DATA"] = TokenType.Data;
reserved["READ"] = TokenType.Read;
reserved["RESTORE"] = TokenType.Restore;
reserved["ON"] = TokenType.On;
reserved["INPUT"] = TokenType.Input;
reserved["TRUE"] = TokenType.True;
reserved["FALSE"] = TokenType.False;
reserved["CALL"] = TokenType.Call;
reserved["COMMON"] = TokenType.Common;
reserved["USE"] = TokenType.Use;
reserved["EXEC"] = TokenType.Exec;
reserved["ELSEIF"] = TokenType.Elseif;
reserved["REPEAT"] = TokenType.Repeat;
reserved["UNTIL"] = TokenType.Until;
reserved["SWAP"] = TokenType.Swap;
reserved.rehash();
}
this(wstring input)
{
initReservedWordsTable();
this.code = input;
line = 1;
}
this(Lexical lex)
{
this.code = lex.code;
this.reserved = lex.reserved;
this.table = lex.table;
this.pos = lex.pos;
this.token = lex.token;
this.isEmpty = lex.isEmpty;
this.index = lex.index;
this.line = lex.line;
}
Lexical save()
{
return new Lexical(this);
}
bool empty()
{
return index >= code.length;
}
bool isSmileBasicSuffix(wchar c)
{
return c == '$' || c == '%' || c == '#';
}
SourceLocation location;
Token token;
private int pos;
bool isEmpty;
void popFront()
{
if(index >= code.length)
{/*
if(!isEmpty)
{
//token = Token(TokenType.NewLine);
isEmpty = true;
//return;
}*/
token = Token(TokenType.NewLine);
return;
}
int i = index;
for(;i < code.length;i++)
{
wchar c = code[i];
if(c == ' ') continue;
if(c == '\'')
{
for(;i < code.length;i++)
{
c = code[i];
if(table[c] == TokenType.NewLine) break;
}
token = Token(TokenType.NewLine);
if (i >= code.length)
break;
}
if(c.isDigit() || c == '.')
{
bool dot;
int start = i;
double num;
for(;i < code.length;i++)
{
c = code[i];
if(c == '.')
{
if(dot) break;
dot = true;
}
if(!c.isDigit() && c != '.')
{
break;
}
}
wstring numstr = code[start..i];
num = numstr.to!double;
if(num <= int.max && num >= int.min && !dot)
{
token = Token(TokenType.Integer, Value(cast(int)num));
}
else
{
token = Token(TokenType.Integer, Value(num));
}
break;
}
if(c.isAlpha() || c == '_')
{
wstring iden;
for(;i < code.length;i++)
{
c = cast(wchar)code[i].toUpper;
if(!c.isAlpha() && !c.isDigit() && c != '_')
{
break;
}
iden ~= c;
}
if(isSmileBasicSuffix(c))
{
iden ~= c;
i++;
}
else//変数が予約語であることはありえない
{
auto r = reserved.get(iden, TokenType.Unknown);
//TRUE/FALSEは3.1でもDATAに使える定数
if(r == TokenType.True)
{
token = Token(TokenType.Integer, Value(1));
break;
}
if(r == TokenType.False)
{
token = Token(TokenType.Integer, Value(0));
break;
}
if(r != TokenType.Unknown)
{
token = Token(r, Value(iden));
break;
}
}
if (iden == "REM")
{
for(;i < code.length;i++)
{
c = code[i];
if(table[c] == TokenType.NewLine) break;
}
token = Token(TokenType.NewLine);
if (i >= code.length)
break;
}
else
{
token = Token(TokenType.Iden, Value(iden));
break;
}
}
if(c == '"')
{
i++;
wstring str;
for(;i < code.length;i++)
{
c = code[i];
if(c == '"' || c == '\r' || c == '\n')//"を閉じない文も許容
{
i++;
if (c == '\r' || c == '\n') i--;
break;
}
str ~= c;
}
token = Token(TokenType.String, Value(str));
break;
}
if(c == '@' || c == '#')
{
//ラベル(もしくは文字列)
wstring iden;
if (c == '@')
iden ~= c;
i++;
for(;i < code.length;i++)
{
c = code[i];
if(!c.isAlpha() && !c.isDigit() && c != '_')
{
break;
}
iden ~= c;
}
if (c == '@')
{
token = Token(TokenType.Label, Value(iden));
}
else
{
token = Token(TokenType.Constant, Value(std.uni.toUpper(iden)));
}
break;
}
if(c == '=' && i + 1 < code.length && code[i + 1] == '=')
{
token = Token(TokenType.Equal);
i += 2;
break;
}
if(c == '!' && i + 1 < code.length && code[i + 1] == '=')
{
token = Token(TokenType.NotEqual);
i += 2;
break;
}
if(c == '<' && i + 1 < code.length && code[i + 1] == '=')
{
token = Token(TokenType.LessEqual);
i += 2;
break;
}
if(c == '>' && i + 1 < code.length && code[i + 1] == '=')
{
token = Token(TokenType.GreaterEqual);
i += 2;
break;
}
if(c == '<' && i + 1 < code.length && code[i + 1] == '<')
{
token = Token(TokenType.LeftShift);
i += 2;
break;
}
if(c == '>' && i + 1 < code.length && code[i + 1] == '>')
{
token = Token(TokenType.RightShift);
i += 2;
break;
}
if(c == '&' && i + 1 < code.length)
{
if(code[i + 1].toUpper == 'H')
{
i += 2;
int start = i;
for(;i < code.length;i++)
{
c = cast(wchar)(code[i].toUpper);
if(!c.isDigit() && (c < 'A' || c > 'F'))
{
break;
}
}
wstring numstr = code[start..i];
int num = numstr.to!uint(16);
token = Token(TokenType.Integer, Value(num));
break;
}
if(code[i + 1].toUpper == 'B')
{
i += 2;
int start = i;
for(;i < code.length;i++)
{
c = code[i];
if(c != '1' && c != '0')
{
break;
}
}
wstring numstr = code[start..i];
int num = numstr.to!uint(2);
token = Token(TokenType.Integer, Value(num));
break;
}
if(code[i + 1] == '&')
{
token = Token(TokenType.LogicalAnd);
i += 2;
break;
}
}
if(c == '|' && i + 1 < code.length && code[i + 1] == '|')
{
token = Token(TokenType.LogicalOr);
i += 2;
break;
}
if(table[c] == TokenType.Unknown)
{
//error
}
token = Token(table[c]);
i++;
//CRLFだった
if(token.type == TokenType.NewLine)
{
line++;
if(c == '\r')
{
//CRLF
if(i < code.length && code[i] == '\n')
{
i++;
}
}
pos = i;
}
break;
}
this.location.pos = i - this.pos;
this.location.line = line;
this.location.pos2 = index - 1;
index = i;
}
Token front()
{
return token;
}
int getLine()
{
return line;
}
}
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 getLine(SourceLocation loc)
{
import std.string;
auto mae = code[0 .. loc.pos2].lastIndexOf('\n');
mae++;
auto ushiro = code[mae..$].indexOf('\n');
if(ushiro == -1) ushiro = code.length;
return code[mae .. mae + ushiro];
}
wstring code;
Lexical lex;
this(wstring input)
{
this.code = input;
lex = new Lexical(input);
initConstant();
}
const static int opMax = 11;
int getOPRank(TokenType type)
{
switch(type)
{
version(none)
{
case TokenType.LogicalAnd:
case TokenType.LogicalOr:
return 8;//&&,||
case TokenType.And:
case TokenType.Or:
case TokenType.Xor:
return 7;//AND,OR,XOR
}
case TokenType.LogicalAnd:
return 10;//&&,||
case TokenType.LogicalOr:
return 11;//&&,||
case TokenType.And:
return 7;//AND,OR,XOR
case TokenType.Or:
return 9;//AND,OR,XOR
case TokenType.Xor:
return 9;//AND,OR,XOR
case TokenType.Equal:
case TokenType.NotEqual:
case TokenType.Less:
case TokenType.LessEqual:
case TokenType.Greater:
case TokenType.GreaterEqual:
return 6;//==,!=,<,<=,>,>=
case TokenType.LeftShift:
case TokenType.RightShift:
return 5;//<<,>>
case TokenType.Plus:
case TokenType.Minus:
return 4;//+,-(bin)
case TokenType.Mul:
case TokenType.Div:
case TokenType.IntDiv:
case TokenType.Mod:
return 3;//*,/,DIV,MOD
case TokenType.LBracket:
return 2;//合ってるか不明
// 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.castInteger;
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;
}
}
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;
}
}
auto compiler()
{
auto compiler = new Compiler(parseProgram());
return compiler;
}
auto compile()
{
auto compiler = new Compiler(parseProgram());
return compiler.compile();
}
Statements parseProgram()
{
lex.popFront();
auto statements = new Statements(lex.location);
do
{
auto token = lex.front();
Statement statement;
if(token.type == TokenType.Common)
{
lex.popFront();
token = lex.front();
if (token.type != TokenType.Def)
{
syntaxError();
}
else
{
statement = defineFunction(true);
}
}
else if(token.type == TokenType.Def)
{
statement = defineFunction(false);
}
else
{
statement = this.statement();
}
if(statement != Statement.NOP)
{
statements.addStatement(statement);
}
} while(!lex.empty());
return statements;
}
bool isFuncReturnExpr = false;
wstring getFunctionArgument()
{
auto token = lex.front();
if(token.type != TokenType.Iden)
{
syntaxError();
return "";
}
lex.popFront();
if(lex.front().type == TokenType.LBracket)
{
lex.popFront();
if(lex.front().type != TokenType.RBracket)
{
syntaxError();
return "";
}
lex.popFront();
}
return token.value.stringValue;
}
DefineFunction defineFunction(bool isCommon)
{
lex.popFront();
auto token = lex.front();
if(token.type != TokenType.Iden)
{
syntaxError();
return null;
}
DefineFunction node = new DefineFunction(token.value.stringValue, isCommon, lex.location);
lex.popFront();
token = lex.front();
if(token.type == TokenType.LParen)
{
node.returnExpr = true;
lex.popFront();
//MEMO:引数に[]を付けようが扱いは同一
while(true)
{
if(lex.front().type == TokenType.RParen)
{
lex.popFront();
break;
}
wstring arg = getFunctionArgument();
if(arg.length == 0)
{
return null;
}
node.addArgument(arg);
token = lex.front();
if(token.type == TokenType.Comma)
{
lex.popFront();
continue;
}
//MEMO:引数に[]を付けようが扱いは同一
if(token.type == TokenType.RParen)
{
lex.popFront();
break;
}
syntaxError();
return null;
}
}
else
{
//MEMO:引数に[]を付けようが扱いは同一
token = lex.front();
if(token.type == TokenType.Iden)
{
while(true)
{
wstring arg = getFunctionArgument();
if(arg.length == 0)
{
return null;
}
node.addArgument(arg);
token = lex.front();
if(token.type == TokenType.Comma)
{
lex.popFront();
continue;
}
break;
}
}
if(token.type == TokenType.Out)
{
lex.popFront();
while(true)
{
wstring arg = getFunctionArgument();
if(arg.length == 0)
{
return null;
}
node.addOutArgument(arg);
token = lex.front();
if(token.type == TokenType.Comma)
{
lex.popFront();
continue;
}
break;
}
}
node.returnExpr = false;
//void関数にRETURN核とsyntaxerror
}
isFuncReturnExpr = node.returnExpr;//面倒くさい
node.functionBody = functionStatements();
lex.popFront();
isFuncReturnExpr = false;
return node;
}
Statements functionStatements()
{
auto statements = new Statements(lex.location);
while(true)
{
auto type = lex.front().type;
if(type == TokenType.End) break;
if(lex.empty())
{
//TODO:DEF without edn
syntaxError();
break;
}
auto statement = statement();
if(statement != Statement.NOP)
{
statements.addStatement(statement);
}
}
return statements;
}
Statements ifStatements()
{
auto statements = new Statements(lex.location);
bool flag = true;
while(!lex.empty())
{
auto type = lex.front().type;
flag = false;
if(type == TokenType.NewLine) break;
if(type == TokenType.Else) break;
if(type == TokenType.Elseif) break;
if(type == TokenType.Endif) break;
if(type == TokenType.Label)
{
statements.addStatement(new Goto(lex.front.value.stringValue, lex.location));
lex.popFront;
continue;
}
auto statement = statement();
if(statement != Statement.NOP)
{
statements.addStatement(statement);
}
}
return statements;
}
Statements multilineIfStatements()
{
auto statements = new Statements(lex.location);
while(!lex.empty())
{
auto type = lex.front().type;
if(type == TokenType.Else) break;
if(type == TokenType.Elseif) break;
if(type == TokenType.Endif) break;
auto statement = statement();
if(statement != Statement.NOP)
{
statements.addStatement(statement);
}
}
return statements;
}
Statements forStatements()
{
auto statements = new Statements(lex.location);
while(!lex.empty())
{
auto type = lex.front().type;
if(type == TokenType.Next) break;
auto statement = statement();
if(statement != Statement.NOP)
{
statements.addStatement(statement);
}
}
lex.popFront();
auto token = lex.front;
if(token.type == TokenType.Iden)
{
//NEXT I[,J[,K...]]プチコン3号だと無視
lex.popFront();
token = lex.front;
while(token.type == TokenType.Comma)
{
lex.popFront();
token = lex.front;
if(token.type != TokenType.Iden) break;
lex.popFront();
}
}
return statements;
}
Statements whileStatements()
{
auto statements = new Statements(lex.location);
while(!lex.empty())
{
auto type = lex.front().type;
if(type == TokenType.WEnd) break;
auto statement = statement();
if(statement != Statement.NOP)
{
statements.addStatement(statement);
}
}
lex.popFront();
return statements;
}
Statements repeatStatements()
{
auto statements = new Statements(lex.location);
while(!lex.empty())
{
auto type = lex.front().type;
if(type == TokenType.Until) break;
auto statement = statement();
if(statement != Statement.NOP)
{
statements.addStatement(statement);
}
}
return statements;
}
void syntaxError()
{
stderr.writeln("Syntax error (", lex.getLine(), ')', " Mysterious ", lex.front().type);
stderr.writeln(this.getLine(lex.location));
try
{
throw new Exception("Stacktrace");
}
catch(Exception e)
{
writeln(e);
}
}
//statement
Statement statement()
{
auto token = lex.front();
Statement node = null;
switch(token.type)
{
case TokenType.Print:
node = print();
return node;
case TokenType.Call:
case TokenType.Iden:
{
wstring name = token.value.stringValue;
auto uname = std.uni.toUpper(name);
if (uname == "OPTION")
{
lex.popFront;
token = lex.front;
if (token.type != TokenType.Iden)
{
syntaxError();
return null;
}
auto arg = std.uni.toUpper(token.value.stringValue);
if (arg != "STRICT" && arg != "DEFINT" && arg != "TOOL")
{
syntaxError();
return null;
}
lex.popFront();
return new Option(arg, lex.location);
}
if (uname == "XON" || uname == "XOFF")
{
lex.popFront;
token = lex.front;
if (token.type != TokenType.Iden)
{
syntaxError();
return null;
}
auto arg = std.uni.toUpper(token.value.stringValue);
if (arg != "MOTION" && arg != "EXPAD" && arg != "MIC" && (uname == "XOFF" || (arg != "WIIU" && arg != "3DS" && arg != "COMPAT")))
{
syntaxError();
return null;
}
lex.popFront();
return uname == "XON" ? new XOn(arg, lex.location) : new XOff(arg, lex.location);
}
if (token.type != TokenType.Call)
{
auto old = lex.save;
auto expr = expression;
token = lex.front;
if (token.type == TokenType.Assign)
{
if (expr.type == NodeType.Variable)
{
auto variable = cast(Variable)expr;
node = assign(variable.name);
return node;
}
node = assign(expr);
return node;
}
else
{
lex = old;
}
}
lex.popFront();
//命令呼び出し
auto func = new CallFunctionStatement(name, lex.location);
node = func;
bool oldcomma;
while(true)
{
token = lex.front();
if(token.type == TokenType.Comma)
{
func.addArg(new VoidExpression(lex.location));
lex.popFront();
token = lex.front();
}
else
{
auto expr = expression();
if(expr || oldcomma || lex.front.type == TokenType.Comma)//if(expr)
func.addArg(expr);
}
if(lex.front().type == TokenType.Out) break;
if(lex.front().type != TokenType.Comma) break;
oldcomma = true;
lex.popFront();
}
if(lex.front().type == TokenType.Out)
{
lex.popFront();
token = lex.front();
while(true)
{
token = lex.front();
Expression arg = expression();
if(!isLValue(arg))
{
//to support func args... OUT (no argument)
if (!func.outVariable.length)
{
break;
}
syntaxError();
return null;
}
func.addOut(arg);
token = lex.front();
if(token.type == TokenType.Comma)
{
lex.popFront();
continue;
}
break;
}
}
return node;
}
//break;
case TokenType.Colon:
case TokenType.NewLine:
break;
case TokenType.Label:
node = new Label(token.value.stringValue, lex.location);
break;
case TokenType.Goto:
lex.popFront();
token = lex.front();
if(token.type == TokenType.Label)
node = new Goto(token.value.stringValue, lex.location);
else
{
auto e = expression();
if(!e)
syntaxError();
node = new Goto(e, lex.location);
}
break;
case TokenType.Gosub:
lex.popFront();
token = lex.front();
if(token.type == TokenType.Label)
node = new Gosub(token.value.stringValue, lex.location);
else
{
auto e = expression();
if(!e)
syntaxError();
node = new Gosub(e, lex.location);
}
break;
case TokenType.If:
node = if_();
return node;
case TokenType.For:
return forStatement();
case TokenType.Return:
lex.popFront();
if(isFuncReturnExpr)
{
node = new Return(expression(), lex.location);
}
else
{
node = new Return(null, lex.location);
}
return node;
case TokenType.End:
node = new End(lex.location);
break;
case TokenType.Break:
node = new Break(lex.location);
break;
case TokenType.Continue:
node = new Continue(lex.location);
break;
case TokenType.Var:
{
auto old = lex.save;
lex.popFront();
token = lex.front;
if (token.type == TokenType.LParen)
{
lex = old;
token = lex.front;
goto case TokenType.Iden;
}
node = var();
return node;
}
case TokenType.Dim:
lex.popFront();
node = var();
return node;
case TokenType.While:
node = whileStatement();
break;
case TokenType.Repeat:
node = repeatStatement();
break;
case TokenType.Inc:
case TokenType.Dec:
return incStatement();
case TokenType.Data:
return dataStatement();
case TokenType.Read:
return readStatement();
case TokenType.Restore:
return restoreStatement();
case TokenType.On:
node = onStatement();
break;
case TokenType.Input:
return inputStatement();
case TokenType.Use:
{
lex.popFront();
auto expr = expression();
writeln("NOTIMPL:USE");
}
break;
case TokenType.Exec:
{
lex.popFront();
auto expr = expression();
writeln("NOTIMPL:EXEC");
}
break;
case TokenType.Swap:
return swapStatement();
default:
syntaxError();
break;
}
lex.popFront();
return node;
}
Swap swapStatement()
{
lex.popFront();
auto expr1 = expression();
if (lex.front.type == TokenType.Comma)
{
lex.popFront();
}
else
{
syntaxError();
}
auto expr2 = expression();
if (!expr1 || !expr2)
{
syntaxError();
}
if (!isLValue(expr1) || !isLValue(expr2))
{
syntaxError();
}
return new Swap(expr1, expr2, lex.location);
}
//左辺値か
bool isLValue(Expression expr)
{
if (!expr)
{
return false;
}
if(expr.type == NodeType.Variable)
{
return true;
}
if(expr.type == NodeType.VarRef)
{
return true;
}
if(expr.type == NodeType.BinaryOperator)
{
auto op = cast(BinaryOperator)expr;
if(!isLValue(op.item1)) return false;
return op.operator == TokenType.LBracket;
}
return false;
}
Input inputStatement()
{
lex.popFront();
Expression message = expression();
if(!message)
{
syntaxError();
return null;
}
auto token = lex.front();
if(token.type == TokenType.Semicolon || (token.type == TokenType.Comma && !isLValue(message)))
{
Input input = new Input(message, token.type == TokenType.Semicolon, lex.location);
do
{
lex.popFront();
auto expr = expression();
if(!isLValue(expr))
{
syntaxError();
}
input.addVariable(expr);
token = lex.front();
} while(token.type == TokenType.Comma);
return input;
}
else
{
if(!isLValue(message))
{
syntaxError();
return null;
}
Input input = new Input(null, true, lex.location);
input.addVariable(message);
do
{
lex.popFront();
auto expr = expression();
if(!isLValue(expr))
{
syntaxError();
}
input.addVariable(expr);
token = lex.front();
} while(token.type == TokenType.Comma);
return input;
}
}
On onStatement()
{
lex.popFront();
auto cond = expression();
if(!cond) return null;
auto token = lex.front();
On on;
if(token.type == TokenType.Gosub)
{
on = new On(cond, true, lex.location);//gosub
}
else if(token.type == TokenType.Goto)
{
on = new On(cond, false, lex.location);//gosub
}
else
{
//GOTO/GOSUBいる
syntaxError();
return null;
}
do
{
lex.popFront();
token = lex.front();
if(token.type != TokenType.Label)
{
syntaxError();
return null;
}
on.addLabel(token.value.stringValue);
lex.popFront();
token = lex.front();
} while(token.type == TokenType.Comma);
return on;
}
Data dataStatement()
{
Data data = new Data(lex.location);
lex.popFront();
auto token = lex.front();
while(true)
{
bool minusflag;
auto expr = cast(Constant)expression();
if (expr)
{
data.addData(expr.value);
}
else
{
syntaxError();
return null;
}
token = lex.front();
if(token.type == TokenType.Comma)
{
lex.popFront();
token = lex.front();
continue;
}
break;
}
return data;
}
Read readStatement()
{
Read read = new Read(lex.location);
Token token;
do
{
lex.popFront();
auto expr = expression();
if(!expr || !isLValue(expr))
{
syntaxError();
}
read.addVariable(expr);
token = lex.front();
} while(token.type == TokenType.Comma);
return read;
}
Restore restoreStatement()
{
lex.popFront();
auto label = expression();
if(!label)
{
syntaxError();
return null;
}
return new Restore(label, lex.location);
}
Inc incStatement()
{
auto token = lex.front();
bool dec = token.type == TokenType.Dec;
lex.popFront();
token = lex.front();
Expression var = expression();
if(!isLValue(var))
{
syntaxError();
return null;
}
token = lex.front();
Expression expr;
if(token.type == TokenType.Comma)
{
lex.popFront();
expr = expression();
if(!expr)
{
syntaxError();
return null;
}
}
else
{
expr = new Constant(Value(1), lex.location);
}
if(dec)
{
expr = new BinaryOperator(new Constant(Value(0), lex.location), TokenType.Minus, expr, lex.location);
}
return new Inc(var, expr, lex.location);
}
IndexExpressions indexExpressions()
{
IndexExpressions ie = new IndexExpressions(lex.location);
int count = 0;
while(true)
{
ie.addExpression(expression());
count++;
auto token = lex.front();
if(token.type == TokenType.Comma)
{
lex.popFront();
if(count >= 4)
{
syntaxError();
}
continue;
}
if(token.type == TokenType.RBracket)
{
break;
}
syntaxError();
break;
}
return ie;
}
ArrayAssign arrayAssign(wstring name)
{
lex.popFront();
auto ie = indexExpressions();
auto token = lex.front();
if(token.type != TokenType.RBracket)
{
return null;
}
lex.popFront();
token = lex.front();
if(token.type != TokenType.Assign)
{
//=が欲しい
syntaxError();
return null;
}
lex.popFront();
auto expr = expression();
token = lex.front();
auto node = new ArrayAssign(name, ie, expr, lex.location);
return node;
}
Statement var()
{
Var var = new Var(lex.location);
Token token;
while(true)
{
token = lex.front();
if(token.type != TokenType.Iden)
{
//TODO:VAR("A")の実装
syntaxError();
return null;
}
auto v = defineVar();
if(v.type == NodeType.DefineVariable)
{
var.addDefineVar(cast(DefineVariable)v);
}
if(v.type == NodeType.DefineArray)
{
var.addDefineArray(cast(DefineArray)v);
}
token = lex.front();
//VARの評価順は順番通り
//VAR iden[=expr],
if(token.type == TokenType.Comma)
{
lex.popFront();
}
else
{
break;
}
}
return var;
}
Statement defineVar()
{
Token token = lex.front();
wstring name = token.value.stringValue;
lex.popFront();
token = lex.front();
Expression expr;
//VAR iden[
if(token.type == TokenType.LBracket)
{
lex.popFront();
auto dim = indexExpressions();
token = lex.front();
if(token.type != TokenType.RBracket)
return null;
lex.popFront();
DefineArray ary = new DefineArray(name, dim, lex.location);
return ary;
}
else
{
//VAR iden=expr
if(token.type == TokenType.Assign)
{
lex.popFront();
expr = expression();
token = lex.front();
}
}
DefineVariable node = new DefineVariable(name, expr, lex.location);
return node;
}
For forStatement()
{
For node;
lex.popFront();
Statement initStatement = statement();
if(initStatement.type != NodeType.Assign)
{
syntaxError();
return null;
}
Assign init = cast(Assign)initStatement;
auto token = lex.front();
//TO,STEPは予約語ではない
if(token.type != TokenType.Iden || token.value.stringValue != "TO")
{
syntaxError();
return null;
}
lex.popFront();
Expression to = expression();
token = lex.front();
Expression step;
//TO,STEPは予約語ではない
if(token.type == TokenType.Iden && token.value.stringValue == "STEP")
{
lex.popFront();
step = expression();
}
else
{
//とりあえず
step = new Constant(Value(1), lex.location);
}
token = lex.front();
Statements statements = forStatements();
node = new For(init, to, step, statements, lex.location);
return node;
}
While whileStatement()
{
lex.popFront();
auto expr = expression();
if(expr is null)
{
syntaxError();
return null;
}
Statements statements = whileStatements();
auto node = new While(expr, statements, lex.location);
return node;
}
RepeatUntil repeatStatement()
{
lex.popFront();
Statements statements = repeatStatements();
auto token = lex.front;
if (token.type != TokenType.Until)
{
//TODO:REPEAT without UNTIL
syntaxError();
return null;
}
lex.popFront();
auto expr = expression();
if(expr is null)
{
syntaxError();
return null;
}
auto node = new RepeatUntil(expr, statements, lex.location);
return node;
}
If if_()
{
lex.popFront();
auto token = lex.front();
auto expr = expression();
if(expr is null)
{
syntaxError();
return null;
}
token = lex.front();
if(token.type != TokenType.Then)
{
if(token.type != TokenType.Goto)
{
//IF expr GOSUBは不可
syntaxError();
return null;
}
}
if(token.type != TokenType.Goto)
{
lex.popFront();
token = lex.front();
}
Statements then;
bool multiline = false;
if(token.type == TokenType.NewLine)
{
multiline = true;
lex.popFront();
then = multilineIfStatements();
}
else
{
then = ifStatements();
}
token = lex.front();
lex.popFront();
import std.typecons;
Tuple!(Statements, Expression)[] elseif = new Tuple!(Statements, Expression)[0];
while(token.type == TokenType.Elseif)
{
token = lex.front();
auto elseifcond = expression();
if(elseifcond is null)
{
syntaxError();
return null;
}
token = lex.front();
if(token.type != TokenType.Then)
{
if(token.type != TokenType.Goto)
{
//IF expr GOSUBは不可
syntaxError();
return null;
}
}
if(token.type != TokenType.Goto)
{
lex.popFront();
token = lex.front();
}
if(!multiline && lex.front().type == TokenType.NewLine)
{
syntaxError();
}
if(multiline)
{
elseif ~= tuple(multilineIfStatements(), elseifcond);
}
else
{
elseif ~= tuple(ifStatements(), elseifcond);
}
token = lex.front();
lex.popFront();
}
Statements else_;
if(token.type == TokenType.Else)
{
if(!multiline && lex.front().type == TokenType.NewLine)
{
//3.1現在だとエラー
syntaxError();
}
if(multiline)
{
else_ = multilineIfStatements();
}
else
{
else_ = ifStatements();
}
lex.popFront();
}
auto if_ = new If(expr, then, else_, elseif, lex.location);
return if_;
}
Assign assign(wstring name)
{
lex.popFront();
auto token = lex.front();//=の次
Expression expr = expression();
if(expr is null) return null;
auto a = new Assign(name, expr, lex.location);
return a;
}
AssignRef assign(Expression left)
{
lex.popFront();
auto token = lex.front();//=の次
Expression expr = expression();
if(expr is null) return null;
if (!isLValue(left))
{
syntaxError();
return null;
}
auto a = new AssignRef(left, expr, lex.location);
return a;
}
Print print()
{
auto print = new Print(lex.location);
bool addline = true;
lex.popFront();
auto token = lex.front();
while(true)
{
//3号から厳密になって必ずいる
if(token.type == TokenType.Colon || token.type == TokenType.NewLine)
{
if(addline) print.addLine();
break;
}
addline = true;
auto exp = expression();
if(exp is null)
{
lex.popFront();
syntaxError();
continue;
}
print.addArgument(exp);
token = lex.front();
if(lex.empty())
{
print.addLine();
break;
}
if(token.type != TokenType.Colon && token.type != TokenType.NewLine &&
token.type != TokenType.Semicolon && token.type != TokenType.Comma &&
token.type != TokenType.Else && token.type != TokenType.Endif &&
token.type != TokenType.Elseif && token.type != TokenType.Print)
{
syntaxError();
}
else
{
if(token.type == TokenType.Colon || token.type == TokenType.NewLine ||
token.type == TokenType.Else || token.type == TokenType.Endif ||
token.type == TokenType.Elseif || token.type == TokenType.Print)
{
print.addLine();
break;
}
lex.popFront();
if(token.type == TokenType.Semicolon)
{
addline = false;
token = lex.front();
continue;
}
if(token.type == TokenType.Comma)
{
print.addTab();
addline = false;
token = lex.front();
continue;
}
token = lex.front();
}
}
return print;
}
//compilerでやるべきな気がする
bool constcalc(Value left, Value right, TokenType type, out double result)
{
switch (type)
{
case TokenType.Plus:
result = (left.castDouble + right.castDouble);
return true;
case TokenType.Minus:
result = (left.castDouble - right.castDouble);
return true;
case TokenType.Mul:
result = (left.castDouble * right.castDouble);
return true;
case TokenType.Div:
result = (left.castDouble / right.castDouble);
return true;
case TokenType.IntDiv:
//TODO:範囲外だとOverflow
result = (left.castInteger / right.castInteger);
return true;
case TokenType.Mod:
result = (left.castDouble % right.castDouble);
return true;
case TokenType.And:
result = (left.castInteger & right.castInteger);
return true;
case TokenType.Or:
result = (left.castInteger | right.castInteger);
return true;
case TokenType.Xor:
result = (left.castInteger ^ right.castInteger);
return true;
case TokenType.LeftShift:
result = (left.castInteger << right.castInteger);
return true;
case TokenType.RightShift:
result = (left.castInteger >> right.castInteger);
return true;
default:
result = double.init;
return !true;
}
}
Expression expression()
{
Expression node = null;
return term(opMax, 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, lex.location);
while(order == getOPRank(token.type))
{
auto tt = token.type;
op.operator = token.type;
lex.popFront();
if(tt == TokenType.LBracket)
{
op.item2 = indexExpressions();
//[演算子
if(lex.front().type != TokenType.RBracket) return null;
lex.popFront();
}
else
{
op.item2 = term(order - 1, node);
}
token = lex.front();
bool constexpr;
if (op.item1.type == NodeType.Constant && op.item2.type == NodeType.Constant)
{
double result;
Constant leftconst = (cast(Constant)op.item1), rightconst = (cast(Constant)op.item2);
if (rightconst.value.isNumber && leftconst.value.isNumber)
{
if (constcalc(leftconst.value, rightconst.value, op.operator, result))
{
auto l = new Constant(Value(result), lex.location);
constexpr = true;
if(order != getOPRank(token.type))
return l;
op = new BinaryOperator(l, lex.location);
continue;
}
}
}
version(none)
write(tt, " ");
if(order == getOPRank(token.type))
{
BinaryOperator op2 = new BinaryOperator(op, lex.location);
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.String:
case TokenType.Integer:
version(none)write(token.value.integerValue, ' ');
version(none)stdout.flush();
node = new Constant(token.value, lex.location);
break;
case TokenType.Var:
if(!lex.empty())
lex.popFront();
if(lex.front().type == TokenType.LParen)
{
lex.popFront();
auto expr = expression;
node = new VarRef(expr, lex.location);
if(lex.front().type != TokenType.RParen)
syntaxError();
}
else
{
syntaxError();
return null;
}
break;
case TokenType.Call:
case TokenType.Iden:
if(!lex.empty())
lex.popFront();
if(lex.front().type == TokenType.LParen)
{
auto func = new CallFunction(token.value.stringValue, lex.location);
node = func;
//関数呼び出しだった
while(true)
{
lex.popFront();
token = lex.front();
if(token.type == TokenType.RParen) break;
if(token.type == TokenType.Comma)
{
func.addArg(new VoidExpression(lex.location));
lex.popFront();
token = lex.front();
}
else
func.addArg(expression());
if(lex.front().type == TokenType.RParen) break;
}
}
else
{
return new Variable(token.value.stringValue, lex.location);
}
break;
case TokenType.LParen:
lex.popFront();
node = expression();
token = lex.front();
if(token.type != TokenType.RParen)
//error
{}
break;
case TokenType.Label://3.1
//文字列リテラル
node = new Constant(token.value, lex.location);
break;
case TokenType.Minus:
//TODO:UnaryOperatorの実装
//とりあえず0-exprを作成
{
lex.popFront();
auto a = term(2/*array*/, null);
double result;
if (a.type == NodeType.Constant && constcalc(Value(0), (cast(Constant)a).value, TokenType.Minus, result))
{
node = new Constant(Value(result), lex.location);
return node;
}
else
{
node = new BinaryOperator(new Constant(Value(0), lex.location), TokenType.Minus, a, lex.location);
return node;
}
}
case TokenType.LogicalNot:
case TokenType.Not:
lex.popFront();
node = new UnaryOperator(token.type, term(2/*array*/, null), lex.location);
return node;
case TokenType.Constant:
{
if (token.value.castString in constant)
{
node = new Constant(Value(constant[token.value.castString]), lex.location);
}
else
{
syntaxError();
}
}
break;
default:
return node;
}
if(!lex.empty())
lex.popFront();
return node;
}
int[wstring] constant;
void initConstant()
{
constant = [
"ON" : 1,
"OFF" : 0,
"YES" : 1,
"NO" : 0,
"TRUE" : 1,
"FALSE" : 0,
"AQUA" : 0xFF00F8F8,
"BLACK" : 0xFF000000,
"BLUE" : 0xFF0000FF,
"CYAN" : 0xFF0000F8,
"FUCHSIA" : 0xFFF800F8,
"GRAY" : 0xFF808080,
"GREEN" : 0xFF008000,
"LIME" : 0xFF00F800,
"MAGENTA" : 0xFFF800F8,
"MAROON" : 0xFF800000,
"NAVY" : 0xFF000080,
"OLIVE" : 0xFF808000,
"PURPLE" : 0xFF800080,
"RED" : 0xFFF80000,
"SILVER" : 0xFFC0C0C0,
"TEAL" : 0xFF008080,
"WHITE" : 0xFFF8F8F8,
"YELLOW" : 0xFFF8F800,
"TBLACK" : 1,
"TMAROON" : 2,
"TRED" : 3,
"TGREEN" : 4,
"TLIME" : 5,
"TOLIVE" : 6,
"TYELLOW" : 7,
"TNAVY" : 8,
"TBLUE" : 9,
"TPURPLE" : 10,
"TMAGENTA" : 11,
"TTEAL" : 12,
"TCYAN" : 13,
"TGRAY" : 14,
"TWHITE" : 15,
"UP" : 0x0001,
"DOWN" : 0x0002,
"LEFT" : 0x0004,
"RIGHT" : 0x0008,
"A" : 0x0010,
"B" : 0x0020,
"X" : 0x0040,
"Y" : 0x0080,
"L" : 0x0100,
"R" : 0x0200,
"ZL" : 0x0800,
"ZR" : 0x1000,
"TROT0" : 0x00,
"TROT90" : 0x01,
"TROT180" : 0x02,
"TROT270" : 0x03,
"TREVH" : 0x04,
"TREVV" : 0x08,
"SPSHOW" : 0x01,
"SPROT0" : 0x00,
"SPROT90" : 0x02,
"SPROT180" : 0x04,
"SPROT270" : 0x06,
"SPREVH" : 0x08,
"SPREVV" : 0x10,
"SPADD" : 0x20,
"BGROT0" : 0x0000,
"BGROT90" : 0x1000,
"BGROT180" : 0x2000,
"BGROT270" : 0x3000,
"BGREVH" : 0x4000,
"BGREVV" : 0x8000,
"CHKXY" : 0x01,
"CHKZ" : 0x02,
"CHKUV" : 0x04,
"CHKI" : 0x08,
"CHKR" : 0x10,
"CHKS" : 0x20,
"CHKC" : 0x40,
"CHKV" : 0x80,
"BQAPF" : 0,
"BQLPF" : 1,
"BQHPF" : 2,
"BQBPF" : 3,
"BQBSF" : 4,
"BQLSF" : 5,
"BQHSF" : 6,
"BQPEQ" : 7,
"WFRECT" : 0,
"WFHAMM" : 1,
"WFHANN" : 2,
"WFBLKM" : 3,
"AOPADD" : 0,
"AOPSUB" : 1,
"AOPMUL" : 2,
"AOPDIV" : 3,
"AOPMAD" : 4,
"AOPLIP" : 5,
"AOPCLP" : 6,
];
}
}
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"();
Test!"1+2+3+4*5/4-5+(6+6)*7";
}