diff --git a/SMILEBASIC/SMILEBASIC.visualdproj b/SMILEBASIC/SMILEBASIC.visualdproj index 153f9c6..c6cfc95 100644 --- a/SMILEBASIC/SMILEBASIC.visualdproj +++ b/SMILEBASIC/SMILEBASIC.visualdproj @@ -89,7 +89,7 @@ - DerelictSDL2.lib;DerelictUtil.lib;DerelictGL3.lib + DerelictSDL2.lib;DerelictUtil.lib;DerelictGL3.lib;kernel32.lib $(DMDInstallDir)windows\lib\ diff --git a/SMILEBASIC/TEST.txt b/SMILEBASIC/TEST.txt index caff4cb..5b4c6d7 100644 --- a/SMILEBASIC/TEST.txt +++ b/SMILEBASIC/TEST.txt @@ -79,6 +79,8 @@ DEF ONGOSUBTEST RETURN @4 END +INPUT "A";INPUT$ +?INPUT$ @DATA_1 DATA "A",1,2,3,4 LOCATE 10, diff --git a/SMILEBASIC/VM.d b/SMILEBASIC/VM.d index 9e43fa2..4c01cc6 100644 --- a/SMILEBASIC/VM.d +++ b/SMILEBASIC/VM.d @@ -1036,3 +1036,67 @@ class OnGosub : OnBase vm.pc = index - 1; } } +import std.string; +class InputCode : Code +{ + int count; + ValueType[] type; + Value[] output; + this(int count) + { + this.count = count; + type = new ValueType[count]; + output = new Value[count]; + } + override void execute(VM vm) + { + for(int i = 0; i < count; i++) + { + Value v; + vm.pop(v); + type[i] = v.type; + } + bool error; + do + { + if(error) + { + vm.petitcomputer.printConsoleString("?Redo from start \n"); + } + wstring input = vm.petitcomputer.input("", false); + wstring[] split = input.split(","); + error = false; + if(split.length < count) + { + error = true; + continue; + } + foreach(i, s; split) + { + if(i >= count) + { + //指定数超えたら無視 + break; + } + //先頭のスペースは無視する + munch(s, " "); + if(type[i] == ValueType.Double || type[i] == ValueType.Integer) + { + try + { + vm.push(Value(to!double(s))); + } + catch + { + error = true; + break; + } + } + else + { + vm.push(Value(s)); + } + } + } while(error); + } +} diff --git a/SMILEBASIC/compiler.d b/SMILEBASIC/compiler.d index 85c92be..b6affb3 100644 --- a/SMILEBASIC/compiler.d +++ b/SMILEBASIC/compiler.d @@ -198,6 +198,23 @@ class Compiler } code ~= new PopG(getGlobalVarIndex(name)); } + void getVarIndex(wstring name, Scope sc, out int index, out bool isLocal) + { + auto global = hasGlobalVarIndex(name); + if(sc.func) + isLocal = sc.func.hasLocalVarIndex(name) != 0; + if(sc.func && isLocal) + { + index = sc.func.getLocalVarIndex(name, this); + return; + } + if(global) + { + index = global; + return; + } + index = getGlobalVarIndex(name); + } void genCodeOP(TokenType op) { code ~= new Operate(op); @@ -386,6 +403,47 @@ class Compiler break; } } + void compilePopVar(Expression expr, Scope sc) + { + switch(expr.type) + { + case NodeType.Variable: + { + auto var = cast(Variable)expr; + int index; + bool local; + getVarIndex(var.name, sc, index, local); + genCode(local ? new PopL(index) : new PopG(index)); + } + break; + case NodeType.BinaryOperator: + { + auto binop = cast(BinaryOperator)expr; + compileExpression(binop.item2, sc); + if(binop.operator == TokenType.LBracket) + { + IndexExpressions ie = cast(IndexExpressions)binop.item2; + auto var = cast(Variable)binop.item1; + int index; + bool local; + getVarIndex(var.name, sc, index, local); + if(ie) + { + genCode(new PopArray(index, ie.expressions.length, local)); + } + else + { + genCode(new PopArray(index, 1, local)); + } + break; + } + } + default: + stderr.writeln("ERROR"); + break; + + } + } void compileIf(If node, Scope sc) { compileExpression(node.condition, sc); @@ -539,6 +597,27 @@ class Compiler compileExpression(on.condition, sc); genCode(new OnS(on.labels, on.isGosub, sc)); } + void compileInput(Input input, Scope sc) + { + if(input.question) + { + genCodeImm(Value("?\n")); + } + if(input.message) + { + compileExpression(input.message, sc); + } + genCode(new PrintCode((input.question ? 1 : 0) + (input.message ? 1 : 0))); + foreach_reverse(i; input.variables) + { + compileExpression(i, sc); + } + genCode(new InputCode(input.variables.length)); + foreach_reverse(i; input.variables) + { + compilePopVar(i, sc); + } + } void compileStatement(Statement i, Scope s) { switch(i.type) @@ -705,6 +784,9 @@ class Compiler case NodeType.On: compileOn(cast(On)i, s); break; + case NodeType.Input: + compileInput(cast(Input)i, s); + break; default: stderr.writeln("Compile:NotImpl ", i.type); } diff --git a/SMILEBASIC/node.d b/SMILEBASIC/node.d index afbcdd0..b33df16 100644 --- a/SMILEBASIC/node.d +++ b/SMILEBASIC/node.d @@ -40,6 +40,7 @@ enum NodeType Read, Restore, On, + Input, } abstract class Node { @@ -466,3 +467,20 @@ class On : Statement this.labels ~= label; } } +class Input : Statement +{ + Expression message; + bool question; + Expression[] variables; + this(Expression message, bool question) + { + this.message = message; + this.question = question; + this.variables = new Expression[0]; + this.type = NodeType.Input; + } + void addVariable(Expression lvalue) + { + variables ~= lvalue; + } +} diff --git a/SMILEBASIC/parser.d b/SMILEBASIC/parser.d index a1f92f4..5bf61c9 100644 --- a/SMILEBASIC/parser.d +++ b/SMILEBASIC/parser.d @@ -68,6 +68,7 @@ class Lexical reserved["READ"] = TokenType.Read; reserved["RESTORE"] = TokenType.Restore; reserved["ON"] = TokenType.On; + reserved["INPUT"] = TokenType.Input; reserved.rehash(); line = 1; } @@ -789,6 +790,8 @@ class Parser case TokenType.On: node = onStatement(); break; + case TokenType.Input: + return inputStatement(); default: syntaxError(); break; @@ -796,6 +799,60 @@ class Parser lex.popFront(); return node; } + //左辺値か + bool isLValue(Expression expr) + { + if(expr.type == NodeType.Variable) + { + 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) + { + Input input = new Input(message, token.type == TokenType.Semicolon); + do + { + lex.popFront(); + auto expr = expression(); + if(!isLValue(expr)) + { + syntaxError(); + } + input.addVariable(expr); + lex.popFront(); + token = lex.front(); + } while(token.type == TokenType.Comma); + return input; + } + else + { + if(!isLValue(message)) + { + syntaxError(); + return null; + } + Input input = new Input(null, true); + input.addVariable(message); + return input; + } + } On onStatement() { lex.popFront(); diff --git a/SMILEBASIC/petitcomputer.d b/SMILEBASIC/petitcomputer.d index 7f34066..b781dc7 100644 --- a/SMILEBASIC/petitcomputer.d +++ b/SMILEBASIC/petitcomputer.d @@ -55,6 +55,14 @@ class GraphicPage texture_format, GL_UNSIGNED_BYTE, surface.pixels ); } } +version(Windows) +{ + extern (Windows) static void* LoadLibraryA(in char*); + extern (Windows) static bool FreeLibrary(void*); + extern (Windows) static void* GetProcAddress(void*, in char*); + alias extern (Windows) void* function(void*, void*) ImmAssociateContext; + alias extern (Windows) int function(int) ImmDisableIME; +} class PetitComputer { this() @@ -396,14 +404,30 @@ class PetitComputer vsyncCount = 0; vsyncFrame = f; } + Mutex keybuffermutex; int keybufferpos; + int keybufferlen; //解析した結果キー入力のバッファは127くらい - wchar[] keybuffer = new wchar[127]; + wchar[] keybuffer = new wchar[128]; + void sendKey(wchar key) + { + keybuffer[keybufferpos] = cast(wchar)key; + keybufferlen++; + if(keybufferlen > keybuffer.length) + keybufferlen = keybuffer.length; + keybufferpos = (keybufferpos + 1) % keybuffer.length; + } void render() { bool renderprofile; try { + version(Windows) + { + auto imm32 = LoadLibraryA("imm32.dll".toStringz); + ImmDisableIME ImmDisableIME = cast(ImmDisableIME)GetProcAddress(imm32, "ImmDisableIME".toStringz); + ImmDisableIME(0); + } SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); window = SDL_CreateWindow("SMILEBASIC", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 400, 240, @@ -417,6 +441,17 @@ class PetitComputer glViewport(0, 0, 400, 240); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glEnable(GL_DEPTH_TEST); + version(Windows) + { + SDL_SysWMinfo wm; + if(SDL_GetWindowWMInfo(window, &wm)) + { + ImmAssociateContext ImmAssociateContext = cast(ImmAssociateContext)GetProcAddress(imm32, "ImmAssociateContext".toStringz); + auto aa = wm.info.win.window; + auto c = ImmAssociateContext(wm.info.win.window, null); + } + } + while(true) { auto profile = SDL_GetTicks(); @@ -435,8 +470,27 @@ class PetitComputer return; case SDL_KEYDOWN: auto key = event.key.keysym.sym; - keybuffer[keybufferpos] = cast(wchar)key; - keybufferpos = (keybufferpos + 1) % keybuffer.length; + if(key == SDLK_BACKSPACE) + { + keybuffermutex.lock(); + sendKey('\u0008'); + keybuffermutex.unlock(); + } + if(key == SDLK_RETURN) + { + keybuffermutex.lock(); + sendKey('\u000D'); + keybuffermutex.unlock(); + } + break; + case SDL_TEXTINPUT: + auto text = event.text.text[0..event.text.text.indexOf('\0')].to!wstring; + keybuffermutex.lock(); + foreach(wchar key; text) + { + sendKey(key); + } + keybuffermutex.unlock(); break; default: break; @@ -511,6 +565,7 @@ GOTO @LOOP`*/ bool running = true; vm.init(this); consolem = new Mutex(); + keybuffermutex = new Mutex(); core.thread.Thread thread = new core.thread.Thread(&render); thread.start(); auto startTicks = SDL_GetTicks(); @@ -554,10 +609,50 @@ GOTO @LOOP`*/ SDL_DestroyWindow(window); SDL_Quit(); } + void clearKeyBuffer() + { + keybuffermutex.lock(); + scope(exit) keybuffermutex.unlock(); + keybufferpos = 0; + keybufferlen = 0; + } wstring input(wstring prompt, bool useClipBoard) { printConsole(prompt); - return ""; + clearKeyBuffer(); + wstring buffer; + while(true) + { + auto oldpos = keybufferpos; + while(oldpos == keybufferpos) + { + SDL_Delay(4);//適当 ストレスを感じないくらい + } + auto kbp = keybufferpos; + auto len = kbp - oldpos; + if(len < 0) + { + //arienai + kbp = oldpos; + } + wchar k; + foreach(key; keybuffer[oldpos..kbp]) + { + printConsole(key); + if(key == '\r') + { + k = key; + break; + } + buffer ~= key; + } + clearKeyBuffer(); + if(k == '\r') + { + break; + } + } + return buffer; } int CSRX; int CSRY; diff --git a/SMILEBASIC/token.d b/SMILEBASIC/token.d index c84a2bf..ae207a5 100644 --- a/SMILEBASIC/token.d +++ b/SMILEBASIC/token.d @@ -62,6 +62,7 @@ enum TokenType Read, Restore, On, + Input, } struct Token