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; static import std.uni; 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["LINPUT"] = TokenType.Linput; reserved["STOP"] = TokenType.Stop; 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, TokenValue(cast(int)num)); } else { token = Token(TokenType.Integer, TokenValue(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, TokenValue(1)); break; } if(r == TokenType.False) { token = Token(TokenType.Integer, TokenValue(0)); break; } if(r != TokenType.Unknown) { token = Token(r, TokenValue(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, TokenValue(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, TokenValue(str)); break; } if(c == '@' || c == '#') { //ラベル(もしくは文字列) wstring iden; bool isLabel = c == '@'; if (isLabel) iden ~= c; i++; for(;i < code.length;i++) { c = code[i]; if(!c.isAlpha() && !c.isDigit() && c != '_') { break; } iden ~= c; } if (isLabel) { token = Token(TokenType.Label, TokenValue(std.uni.toUpper(iden))); } else { token = Token(TokenType.Constant, TokenValue(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, TokenValue(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, TokenValue(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); } } ignoreVarNext(); return statements; } void ignoreVarNext() { 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(); token = lex.front; } } } 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(); if (token.type == TokenType.Call) { if (lex.front.type == TokenType.Iden) { auto arg = std.uni.toUpper(lex.front.value.castString); if (arg == "SPRITE") { node = new CallSprite(lex.location); break; } if (arg == "BG") { node = new CallBG(lex.location); break; } } } //命令呼び出し auto func = new CallFunctionStatement(name, lex.location); node = func; bool oldcomma; while(true) { token = lex.front(); 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(); if (token.type == TokenType.Comma) { func.addOut(new VoidExpression(lex.location)); } else { Expression arg = expression(); if (!arg) { func.addOut(new VoidExpression(lex.location)); break; } 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); return node; } 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); return node; } 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.Next: node = new Continue(lex.location); ignoreVarNext(); return node; 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(); if (!expr) { syntaxError(); return null; } return new Use(expr, lex.location); } case TokenType.Exec: { lex.popFront(); auto expr = expression(); if (!expr) { syntaxError(); return null; } return new Exec(expr, lex.location); } case TokenType.Swap: return swapStatement(); case TokenType.Linput: return linputStatement(); case TokenType.Stop: node = new StopStatement(lex.location); break; default: syntaxError(); break; } lex.popFront(); return node; } Linput linputStatement() { auto location = lex.location; lex.popFront(); Expression message = expression(); if(!message) { syntaxError(); return null; } auto token = lex.front(); if (token.type == TokenType.Semicolon) { lex.popFront(); Expression var = expression(); if (!isLValue(var)) { syntaxError(); return null; } return new Linput(message, var, location); } else { if (!isLValue(message)) { syntaxError(); return null; } return new Linput(message, location); } } 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); while(token.type == TokenType.Comma) { lex.popFront(); auto expr = expression(); if(!isLValue(expr)) { syntaxError(); } input.addVariable(expr); token = lex.front(); } 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; } otya.smilebasic.node.Data dataStatement() { otya.smilebasic.node.Data data = new otya.smilebasic.node.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(TokenValue(1), lex.location); } if(dec) { expr = new BinaryOperator(new Constant(TokenValue(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) { 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(); //VAR A,B,=>error出ない if(lex.front.type != TokenType.Iden) { break; } } 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(TokenValue(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(); if (multiline || token.type != TokenType.NewLine) { 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(); //3.3.2現在だとエラーじゃない multiline = true; } if(multiline) { else_ = multilineIfStatements(); } else { else_ = ifStatements(); } if (multiline || lex.front.type != TokenType.NewLine) { 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 || token.type == TokenType.Else || token.type == TokenType.Endif || token.type == TokenType.Elseif || token.type == TokenType.Print) { if(addline) print.addLine(); break; } addline = true; auto exp = expression(); if(exp is null) { syntaxError(); lex.popFront(); token = lex.front(); 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(TokenValue left, TokenValue 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(TokenValue(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; lex.popFront(); if(lex.front().type != TokenType.RParen) { //関数呼び出しだった while(true) { token = lex.front(); func.addArg(expression()); if(lex.front().type == TokenType.RParen) break; if(lex.front().type != TokenType.Comma) { syntaxError(); break; } lex.popFront(); } } } 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(TokenValue(0), (cast(Constant)a).value, TokenType.Minus, result)) { node = new Constant(TokenValue(result), lex.location); return node; } else { node = new BinaryOperator(new Constant(TokenValue(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(TokenValue(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"; }