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