diff --git a/SMILEBASIC/TEST.txt b/SMILEBASIC/TEST.txt index 3999c53..caff4cb 100644 --- a/SMILEBASIC/TEST.txt +++ b/SMILEBASIC/TEST.txt @@ -34,6 +34,51 @@ DEF GOTOTEST ASSERT__ 0, "LOCAL LABEL" @LOCAL END + +ON 1 GOTO @0,@1,@2,@3 +@0 + ASSERT__ 0, "ON GOTO" +@2 + ASSERT__ 0, "ON GOTO" +@3 + ASSERT__ 0, "ON GOTO" +@1 +ONGOTOTEST +DEF ONGOTOTEST + ON 1 GOTO @0,@1,@2,@3 + @0 + ASSERT__ 0, "LOCAL ON GOTO" + @2 + ASSERT__ 0, "LOCAL ON GOTO" + @3 + ASSERT__ 0, "LOCAL ON GOTO" + @1 +END +ON 1 GOSUB @ONGOSUB0,@ONGOSUB1,@ONGOSUB2,@ONGOSUB3 +GOTO@ONGOSUB4 +@ONGOSUB0 + ASSERT__ 0, "ON GOSUB" +@ONGOSUB2 + ASSERT__ 0, "ON GOSUB" +@ONGOSUB3 + ASSERT__ 0, "ON GOSUB" +@ONGOSUB1 +RETURN +@ONGOSUB4 +ONGOSUBTEST +DEF ONGOSUBTEST + ON 1 GOTO @0,@1,@2,@3 + GOTO@4 + @0 + ASSERT__ 0, "ON ONGOSUB" + @2 + ASSERT__ 0, "ON ONGOSUB" + @3 + ASSERT__ 0, "ON ONGOSUB" + @1 + RETURN + @4 +END @DATA_1 DATA "A",1,2,3,4 LOCATE 10, diff --git a/SMILEBASIC/VM.d b/SMILEBASIC/VM.d index 2d74fd2..9e43fa2 100644 --- a/SMILEBASIC/VM.d +++ b/SMILEBASIC/VM.d @@ -120,6 +120,7 @@ enum CodeType GotoTrue, GosubS, ReturnSubroutine, + OnS, } abstract class Code { @@ -968,3 +969,70 @@ class IncCodeL : Code } } } +class OnBase : Code +{ + int[] labels; + this(int[] labels) + { + this.labels = labels; + } + int on(VM vm) + { + Value value; + vm.pop(value); + if(!value.isNumber()) + { + throw new TypeMismatch(); + } + int index = value.castInteger(); + if(index < 0 || index >= labels.length) + { + return -1; + } + return labels[index]; + } +} +class OnS : Code +{ + wstring[] labels; + bool isGosub; + Scope sc; + this(wstring[] labels, bool isGosub, Scope sc) + { + this.labels = labels; + this.isGosub = isGosub; + this.sc = sc; + this.type = CodeType.OnS; + } + override void execute(VM vm) + { + stderr.writeln("can't execute (compiler bug?)"); + } +} +class OnGoto : OnBase +{ + this(int[] labels) + { + super(labels); + } + override void execute(VM vm) + { + int index = on(vm); + if(index < 0) return; + vm.pc = index - 1; + } +} +class OnGosub : OnBase +{ + this(int[] labels) + { + super(labels); + } + override void execute(VM vm) + { + int index = on(vm); + if(index < 0) return; + vm.push(Value(vm.pc)); + vm.pc = index - 1; + } +} diff --git a/SMILEBASIC/compiler.d b/SMILEBASIC/compiler.d index 696ba24..85c92be 100644 --- a/SMILEBASIC/compiler.d +++ b/SMILEBASIC/compiler.d @@ -534,6 +534,11 @@ class Compiler skip.address = this.code.length; this.functions[func.name] = func; } + void compileOn(On on, Scope sc) + { + compileExpression(on.condition, sc); + genCode(new OnS(on.labels, on.isGosub, sc)); + } void compileStatement(Statement i, Scope s) { switch(i.type) @@ -697,6 +702,9 @@ class Compiler s.data.addData(j); } break; + case NodeType.On: + compileOn(cast(On)i, s); + break; default: stderr.writeln("Compile:NotImpl ", i.type); } @@ -739,6 +747,30 @@ class Compiler code[i] = new GosubAddr(globalLabel[label.label]); } } + if(c.type == CodeType.OnS) + { + auto label = cast(OnS)c; + int[] addresses = new int[label.labels.length]; + foreach(int index, wstring l; label.labels) + { + if(label.sc.func) + { + addresses[index] =label.sc.func.label[l]; + } + else + { + addresses[index] = globalLabel[l]; + } + } + if(label.isGosub) + { + code[i] = new OnGosub(addresses); + } + else + { + code[i] = new OnGoto(addresses); + } + } } return new VM(code, globalIndex + 1, global, functions); } diff --git a/SMILEBASIC/node.d b/SMILEBASIC/node.d index d825a8c..afbcdd0 100644 --- a/SMILEBASIC/node.d +++ b/SMILEBASIC/node.d @@ -451,12 +451,15 @@ class Data : Statement } class On : Statement { + Expression condition; bool isGosub; wstring[] labels; - this(bool isgosub) + this(Expression expr, bool isgosub) { + this.condition = expr; this.isGosub = isgosub; this.labels = new wstring[0]; + this.type = NodeType.On; } void addLabel(wstring label) { diff --git a/SMILEBASIC/parser.d b/SMILEBASIC/parser.d index 3848e5e..a1f92f4 100644 --- a/SMILEBASIC/parser.d +++ b/SMILEBASIC/parser.d @@ -67,6 +67,7 @@ class Lexical reserved["DATA"] = TokenType.Data; reserved["READ"] = TokenType.Read; reserved["RESTORE"] = TokenType.Restore; + reserved["ON"] = TokenType.On; reserved.rehash(); line = 1; } @@ -785,6 +786,9 @@ class Parser return incStatement(); case TokenType.Data: return dataStatement(); + case TokenType.On: + node = onStatement(); + break; default: syntaxError(); break; @@ -792,6 +796,42 @@ class Parser lex.popFront(); return node; } + 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);//gosub + } + else if(token.type == TokenType.Goto) + { + on = new On(cond, false);//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(); diff --git a/SMILEBASIC/petitcomputer.d b/SMILEBASIC/petitcomputer.d index 134c94b..7f34066 100644 --- a/SMILEBASIC/petitcomputer.d +++ b/SMILEBASIC/petitcomputer.d @@ -424,7 +424,7 @@ class PetitComputer renderConsoleGL(); SDL_GL_SwapWindow(window); auto renderticks = (SDL_GetTicks() - profile); - if(!renderprofile) writeln(renderticks); + if(renderprofile) writeln(renderticks); while (SDL_PollEvent(&event)) { switch (event.type)