Scanning on Demand 16
This commit is contained in:
parent
aba643a88e
commit
8fb449825d
6 changed files with 209 additions and 32 deletions
9
src/clox/compiler.d
Normal file
9
src/clox/compiler.d
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
module clox.compiler;
|
||||
|
||||
import std.stdio;
|
||||
|
||||
import clox.scanner;
|
||||
|
||||
void compile(string source) @nogc nothrow {
|
||||
}
|
||||
|
||||
|
|
@ -1,38 +1,46 @@
|
|||
module clox.main;
|
||||
|
||||
import std.stdio;
|
||||
import std.file;
|
||||
|
||||
import clox.chunk;
|
||||
import clox.dbg;
|
||||
import clox.vm;
|
||||
|
||||
int main(string[] argv){
|
||||
Chunk chunk;
|
||||
extern(C) int isatty(int);
|
||||
|
||||
ubyte constant = chunk.addConstant(1.2);
|
||||
chunk.write(OpCode.Constant);
|
||||
chunk.write(constant);
|
||||
|
||||
constant = chunk.addConstant(3.4);
|
||||
chunk.write(OpCode.Constant);
|
||||
chunk.write(constant);
|
||||
|
||||
chunk.write(OpCode.Add);
|
||||
|
||||
constant = chunk.addConstant(5.6);
|
||||
chunk.write(OpCode.Constant);
|
||||
chunk.write(constant);
|
||||
|
||||
chunk.write(OpCode.Divide);
|
||||
|
||||
chunk.write(OpCode.Negate);
|
||||
|
||||
chunk.write(OpCode.Return);
|
||||
|
||||
VM vm = VM(0);
|
||||
vm.interpret(&chunk);
|
||||
|
||||
|
||||
return 0;
|
||||
struct Lox{
|
||||
VM vm;
|
||||
this(int _){
|
||||
vm = VM(0);
|
||||
}
|
||||
int runFile(string path){
|
||||
string source = path.readText();
|
||||
VM.InterpretResult result = vm.interpret(source);
|
||||
final switch(result){
|
||||
case VM.InterpretResult.CompileError: return 65;
|
||||
case VM.InterpretResult.RuntimeError: return 70;
|
||||
case VM.InterpretResult.Ok: return 0;
|
||||
}
|
||||
}
|
||||
int runPrompt(){
|
||||
while(true){
|
||||
write("> ");
|
||||
string line = stdin.readln();
|
||||
if(!line){
|
||||
writeln();
|
||||
return 0;
|
||||
}
|
||||
vm.interpret(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(string[] argv){
|
||||
Lox lox = Lox(0);
|
||||
if(isatty(stdin.fileno))
|
||||
return lox.runPrompt();
|
||||
else
|
||||
return lox.runFile("/dev/stdin");
|
||||
}
|
||||
|
||||
|
|
|
|||
153
src/clox/scanner.d
Normal file
153
src/clox/scanner.d
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
module clox.scanner;
|
||||
|
||||
import std.stdio;
|
||||
import std.ascii;
|
||||
|
||||
import common.util;
|
||||
|
||||
struct Token{
|
||||
enum Type : ubyte {
|
||||
LeftParen, RightParen, // Single-character tokens.
|
||||
LeftBrace, RightBrace,
|
||||
Comma, Dot, Minus, Plus,
|
||||
Semicolon, Slash, Star,
|
||||
Bang, BangEqual, // One or two character tokens.
|
||||
Equal, EqualEqual,
|
||||
Greater, GreaterEqual,
|
||||
Less, LessEqual,
|
||||
Identifier, String, Number, // Literals.
|
||||
And, Class, Else, False, // Keywords.
|
||||
For, Fun, If, Nil, Or,
|
||||
Print, Return, Super, This,
|
||||
True, Var, While,
|
||||
Error, EOF // Special
|
||||
}
|
||||
Type type;
|
||||
int line;
|
||||
string lexeme;
|
||||
static Token error(string msg) => Token(Token.Type.Error, 0, msg);
|
||||
}
|
||||
|
||||
struct Scanner{
|
||||
string start;
|
||||
string current;
|
||||
int line = 1;
|
||||
this(string source){
|
||||
start = current = source;
|
||||
}
|
||||
bool isAtEnd() const => current.length == 0;
|
||||
private char peek() const => current[0];
|
||||
private char peekNext() const => current.length >= 2 ? current[1] : '\0';
|
||||
private Token makeToken(Token.Type type) const{
|
||||
Token token;
|
||||
token.type = type;
|
||||
token.lexeme = start[0 .. current.ptr - start.ptr];
|
||||
return token;
|
||||
}
|
||||
private char advance(){
|
||||
char c = current[0];
|
||||
current = current[1 .. $];
|
||||
return c;
|
||||
}
|
||||
private bool match(char needle){
|
||||
if(isAtEnd || current[0] != needle)
|
||||
return false;
|
||||
current = current[1 .. $];
|
||||
return true;
|
||||
}
|
||||
private void skipWhitespace(){
|
||||
while(!isAtEnd){
|
||||
char c = peek();
|
||||
if(!c)
|
||||
return;
|
||||
if(c == '/' && peekNext() == '/'){
|
||||
while(!isAtEnd && peek() != '\n')
|
||||
advance();
|
||||
continue;
|
||||
}
|
||||
if(!c.isWhite)
|
||||
return;
|
||||
if(c == '\n')
|
||||
line++;
|
||||
current = current[1 .. $];
|
||||
}
|
||||
}
|
||||
private Token parseString(){
|
||||
while(peek() != '"' && !isAtEnd){
|
||||
if(peek() == '\n')
|
||||
line++;
|
||||
advance();
|
||||
}
|
||||
if(isAtEnd)
|
||||
return Token.error("Unterminated string.");
|
||||
advance();
|
||||
return makeToken(Token.Type.String);
|
||||
}
|
||||
private Token parseNumber(){
|
||||
while(peek().isDigit)
|
||||
advance();
|
||||
if(peek() == '.' && peekNext().isDigit){
|
||||
advance();
|
||||
while(peek().isDigit)
|
||||
advance();
|
||||
}
|
||||
return makeToken(Token.Type.Number);
|
||||
}
|
||||
private Token parseIdentifier(){
|
||||
while(peek().isAlphaNum_)
|
||||
advance();
|
||||
Token token = makeToken(Token.Type.Identifier);
|
||||
switch(token.lexeme){
|
||||
case "and": token.type = Token.Type.And; break;
|
||||
case "class": token.type = Token.Type.Class; break;
|
||||
case "else": token.type = Token.Type.Else; break;
|
||||
case "if": token.type = Token.Type.If; break;
|
||||
case "nil": token.type = Token.Type.Nil; break;
|
||||
case "or": token.type = Token.Type.Or; break;
|
||||
case "print": token.type = Token.Type.Print; break;
|
||||
case "return": token.type = Token.Type.Return; break;
|
||||
case "super": token.type = Token.Type.Super; break;
|
||||
case "var": token.type = Token.Type.Var; break;
|
||||
case "while": token.type = Token.Type.While; break;
|
||||
case "false": token.type = Token.Type.False; break;
|
||||
case "for": token.type = Token.Type.For; break;
|
||||
case "fun": token.type = Token.Type.Fun; break;
|
||||
case "this": token.type = Token.Type.This; break;
|
||||
case "true": token.type = Token.Type.True; break;
|
||||
default: break;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
Token scan(){
|
||||
skipWhitespace();
|
||||
start = current;
|
||||
if(isAtEnd)
|
||||
return Token(Token.Type.EOF);
|
||||
char c = advance();
|
||||
if(c.isAlpha_)
|
||||
return parseIdentifier();
|
||||
switch(c){
|
||||
case '(': return makeToken(Token.Type.LeftParen);
|
||||
case ')': return makeToken(Token.Type.RightParen);
|
||||
case '{': return makeToken(Token.Type.LeftBrace);
|
||||
case '}': return makeToken(Token.Type.RightBrace);
|
||||
case ';': return makeToken(Token.Type.Semicolon);
|
||||
case ',': return makeToken(Token.Type.Comma);
|
||||
case '.': return makeToken(Token.Type.Dot);
|
||||
case '-': return makeToken(Token.Type.Minus);
|
||||
case '+': return makeToken(Token.Type.Plus);
|
||||
case '/': return makeToken(Token.Type.Slash);
|
||||
case '*': return makeToken(Token.Type.Star);
|
||||
case '!': return makeToken(match('=') ? Token.Type.BangEqual : Token.Type.Bang);
|
||||
case '=': return makeToken(match('=') ? Token.Type.EqualEqual : Token.Type.Equal);
|
||||
case '<': return makeToken(match('=') ? Token.Type.LessEqual : Token.Type.Less);
|
||||
case '>': return makeToken(match('=') ? Token.Type.GreaterEqual : Token.Type.Greater);
|
||||
case '"': return parseString();
|
||||
default: break;
|
||||
}
|
||||
if(c.isDigit)
|
||||
return parseNumber();
|
||||
return Token.error("Unexpected character '" ~ c ~ "'.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6,6 +6,7 @@ import clox.chunk;
|
|||
import clox.value;
|
||||
import clox.dbg;
|
||||
import clox.util;
|
||||
import clox.compiler;
|
||||
|
||||
enum stackMax = 256;
|
||||
|
||||
|
|
@ -13,10 +14,14 @@ struct VM{
|
|||
const(ubyte)* ip;
|
||||
Stack!(Value, stackMax) stack;
|
||||
Chunk* chunk;
|
||||
enum InterpretResult{ Ok, CompileError, RunetimeError }
|
||||
enum InterpretResult{ Ok, CompileError, RuntimeError }
|
||||
this(int _) @nogc nothrow {
|
||||
stack = typeof(stack)(0);
|
||||
}
|
||||
InterpretResult interpret(string source) @nogc nothrow {
|
||||
compile(source);
|
||||
return InterpretResult.Ok;
|
||||
}
|
||||
InterpretResult interpret(Chunk* chunk) @nogc nothrow {
|
||||
this.chunk = chunk;
|
||||
ip = &chunk.code[0];
|
||||
|
|
|
|||
|
|
@ -7,3 +7,7 @@ template defaultCtor(){
|
|||
}
|
||||
}
|
||||
|
||||
import std.ascii : isAlpha, isAlphaNum;
|
||||
bool isAlpha_(dchar c) => c.isAlpha || c == '_';
|
||||
bool isAlphaNum_(dchar c) => c.isAlphaNum || c == '_';
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,9 @@ import std.conv;
|
|||
import jlox.token;
|
||||
import jlox.tokentype;
|
||||
import jlox.main;
|
||||
import common.util;
|
||||
|
||||
private bool isAlpha_(dchar c) => c.isAlpha || c == '_';
|
||||
private bool isAlphaNum_(dchar c) => c.isAlphaNum || c == '_';
|
||||
|
||||
class Scanner {
|
||||
class Scanner{
|
||||
private string source;
|
||||
private Token[] tokens;
|
||||
private uint start = 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue