Returning from calls 10.5.1

This commit is contained in:
nazrin 2025-06-02 00:06:31 +00:00
parent 10cc5e6e89
commit 7f4946f1e9
10 changed files with 196 additions and 23 deletions

View file

@ -2,11 +2,11 @@ module jlox.interpreter;
import std.conv;
import std.stdio;
import std.algorithm;
import std.array;
import std.format : format;
import std.functional : ctEval;
import taggedalgebraic;
import jlox.expr;
import jlox.stmt;
import jlox.token;
@ -14,6 +14,7 @@ import jlox.tokentype;
import jlox.token : TValue;
import jlox.main;
import jlox.environment;
import jlox.loxfunction;
class RuntimeError : Exception{
const Token token;
@ -22,9 +23,38 @@ class RuntimeError : Exception{
this.token = token;
}
}
class Return : Exception{
const TValue value;
this(TValue value){
super(null);
this.value = value;
}
}
class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
private Environment environment = new Environment();
Environment globals = new Environment();
private Environment environment;
this(){
environment = globals;
import std.datetime.stopwatch;
auto sw = StopWatch(AutoStart.yes);
globals.define("clock", TValue.cal(new class LoxCallable{
int arity() => 0;
TValue call(Interpreter interpreter, TValue[] arguments) => TValue.dbl(sw.peek.total!"usecs" / (1000.0 * 1000.0));
}));
version(LoxExtraNativeFuncs){
globals.define("sleep", TValue.cal(new class LoxCallable{
int arity() => 1;
import core.thread.osthread;
TValue call(Interpreter interpreter, TValue[] arguments){
Thread.sleep(dur!"usecs"(cast(long)(arguments[0].dblValue * 1000 * 1000)));
return TValue.nil(tvalueNil);
}
}));
}
}
void interpret(Stmt[] statements){
try {
foreach(statement; statements)
@ -33,10 +63,10 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
Lox.runtimeError(error);
}
}
private void execute(Stmt stmt){
package void execute(Stmt stmt){
stmt.accept(this);
}
private void executeBlock(Stmt[] statements, Environment environment){
package void executeBlock(Stmt[] statements, Environment environment){
Environment previous = this.environment;
try {
this.environment = environment;
@ -75,6 +105,10 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
void visit(Stmt.Expression stmt){
evaluate(stmt.expression);
}
void visit(Stmt.Function stmt){
LoxFunction func = new LoxFunction(stmt);
environment.define(stmt.name.lexeme, TValue.cal(func));
}
void visit(Stmt.If stmt){
if(isTruthy(evaluate(stmt.condition)))
execute(stmt.thenBranch);
@ -85,8 +119,12 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
TValue value = evaluate(stmt.expression);
writeln(tvalueToString(value));
}
void visit(Stmt.Return stmt){
TValue value = stmt.value !is null ? evaluate(stmt.value) : TValue.nil(tvalueNil);
throw new Return(value);
}
void visit(Stmt.Var stmt){
environment.define(stmt.name.lexeme, stmt.initialiser is null ? TValue.nil(0) : evaluate(stmt.initialiser));
environment.define(stmt.name.lexeme, stmt.initialiser is null ? TValue.nil(tvalueNil) : evaluate(stmt.initialiser));
}
void visit(Stmt.While stmt){
while(isTruthy(evaluate(stmt.condition)))
@ -126,28 +164,30 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
TValue left = evaluate(expr.left);
TValue right = evaluate(expr.right);
static string m(TokenType t, string op, string v, string vv){
return q{case %s: return TValue.%s( left.%s %s right.%s );}.format(t, v, vv, op, vv);
}
with(TokenType) switch(expr.operator.type){
static foreach(t, op; [ MINUS: "-", SLASH: "/", STAR: "*" ]){
return q{case %s:
checkNumberOperand(expr.operator, left);
checkNumberOperand(expr.operator, right);
return TValue.%s( left.%s %s right.%s );
}.format(t, v, vv, op, vv);
}
with(TokenType) switch(expr.operator.type){
static foreach(t, op; [ MINUS: "-", SLASH: "/", STAR: "*" ])
mixin(ctEval!(m(t, op, "dbl", "dblValue")));
}
case PLUS:
if(left.isDbl && right.isDbl)
return TValue.dbl(left.dblValue + right.dblValue);
else if(left.isStr && right.isStr)
return TValue.str(left.strValue ~ right.strValue);
version(LoxConcatNonStrings){
if(left.isStr || right.isStr)
return TValue.str(tvalueToString(left) ~ tvalueToString(right));
}
checkNumberOperand(expr.operator, left);
checkNumberOperand(expr.operator, right);
assert(0);
static foreach(t, op; [ GREATER: ">", GREATER_EQUAL: ">=", LESS: "<", LESS_EQUAL: "<=" ]){
checkNumberOperand(expr.operator, left);
checkNumberOperand(expr.operator, right);
static foreach(t, op; [ GREATER: ">", GREATER_EQUAL: ">=", LESS: "<", LESS_EQUAL: "<=" ])
mixin(ctEval!(m(t, op, "bln", "dblValue")));
}
case BANG_EQUAL:
return TValue.bln(!isEqual(left, right));
@ -157,6 +197,16 @@ class Interpreter : Stmt.Visitor!void, Expr.Visitor!TValue {
assert(0);
}
}
TValue visit(Expr.Call expr){
TValue callee = evaluate(expr.callee);
if(!callee.isCal)
throw new RuntimeError(expr.paren, "Can only call functions and classes.");
auto arguments = expr.arguments.map!(a => evaluate(a));
LoxCallable func = callee.calValue;
if(arguments.length != func.arity())
throw new RuntimeError(expr.paren, "Expected " ~ func.arity().to!string ~ " arguments but got " ~ arguments.length.to!string ~ ".");
return func.call(this, arguments.array);
}
TValue visit(Expr.Variable expr){
return environment.get(expr.name);
}