Scanning on Demand 16

This commit is contained in:
nazrin 2025-06-03 22:57:02 +00:00
parent aba643a88e
commit 8fb449825d
6 changed files with 209 additions and 32 deletions

9
src/clox/compiler.d Normal file
View file

@ -0,0 +1,9 @@
module clox.compiler;
import std.stdio;
import clox.scanner;
void compile(string source) @nogc nothrow {
}

View file

@ -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
View 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 ~ "'.");
}
}

View file

@ -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];

View file

@ -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 == '_';

View file

@ -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;