INPUTを実装.
This commit is contained in:
@@ -89,7 +89,7 @@
|
||||
<cv2pdbOptions />
|
||||
<objfiles />
|
||||
<linkswitches />
|
||||
<libfiles>DerelictSDL2.lib;DerelictUtil.lib;DerelictGL3.lib</libfiles>
|
||||
<libfiles>DerelictSDL2.lib;DerelictUtil.lib;DerelictGL3.lib;kernel32.lib</libfiles>
|
||||
<libpaths>$(DMDInstallDir)windows\lib\</libpaths>
|
||||
<deffile />
|
||||
<resfile />
|
||||
|
||||
@@ -79,6 +79,8 @@ DEF ONGOSUBTEST
|
||||
RETURN
|
||||
@4
|
||||
END
|
||||
INPUT "A";INPUT$
|
||||
?INPUT$
|
||||
@DATA_1
|
||||
DATA "A",1,2,3,4
|
||||
LOCATE 10,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -62,6 +62,7 @@ enum TokenType
|
||||
Read,
|
||||
Restore,
|
||||
On,
|
||||
Input,
|
||||
}
|
||||
|
||||
struct Token
|
||||
|
||||
Reference in New Issue
Block a user