Types of Values 18
This commit is contained in:
parent
41404633da
commit
10c44eab2e
7 changed files with 230 additions and 28 deletions
|
|
@ -8,12 +8,42 @@ import clox.value;
|
||||||
import clox.container.rle;
|
import clox.container.rle;
|
||||||
import clox.container.int24;
|
import clox.container.int24;
|
||||||
|
|
||||||
|
enum SimpleOp;
|
||||||
|
enum LogicOp;
|
||||||
|
enum ValueOp;
|
||||||
|
enum ArithOp;
|
||||||
|
enum CompOp;
|
||||||
enum OpCode : ubyte{
|
enum OpCode : ubyte{
|
||||||
Constant,
|
Constant,
|
||||||
Add, Subtract, Multiply, Divide,
|
@ValueOp Nil,
|
||||||
Negate,
|
@ValueOp True,
|
||||||
Return,
|
@ValueOp False,
|
||||||
|
|
||||||
|
@(SimpleOp, CompOp) Equal,
|
||||||
|
@(SimpleOp, CompOp) Greater,
|
||||||
|
@(SimpleOp, CompOp) Less,
|
||||||
|
@(SimpleOp, CompOp) NotEqual,
|
||||||
|
@(SimpleOp, CompOp) GreaterEqual,
|
||||||
|
@(SimpleOp, CompOp) LessEqual,
|
||||||
|
|
||||||
|
@(SimpleOp, ArithOp) Add,
|
||||||
|
@(SimpleOp, ArithOp) Subtract,
|
||||||
|
@(SimpleOp, ArithOp) Multiply,
|
||||||
|
@(SimpleOp, ArithOp) Divide,
|
||||||
|
|
||||||
|
@(SimpleOp, LogicOp) Not,
|
||||||
|
@(SimpleOp, LogicOp) Negate,
|
||||||
|
@(SimpleOp) Return,
|
||||||
}
|
}
|
||||||
|
import std.traits: hasUDA;
|
||||||
|
bool isSimpleOp(alias op)() => hasUDA!(op, SimpleOp);
|
||||||
|
bool isValueOp(alias op)() => hasUDA!(op, ValueOp);
|
||||||
|
bool isLogicOp(alias op)() => hasUDA!(op, LogicOp);
|
||||||
|
bool isCompOp(alias op)() => hasUDA!(op, CompOp);
|
||||||
|
bool isArithOp(alias op)() => hasUDA!(op, ArithOp);
|
||||||
|
bool isSize1Op(alias op)() => isSimpleOp!op || isValueOp!op;
|
||||||
|
static assert( isSimpleOp!(OpCode.Equal) );
|
||||||
|
static assert( !isSimpleOp!(OpCode.Constant) );
|
||||||
|
|
||||||
struct Chunk{
|
struct Chunk{
|
||||||
Array!ubyte code;
|
Array!ubyte code;
|
||||||
|
|
|
||||||
59
src/clox/container/vartype.d
Normal file
59
src/clox/container/vartype.d
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
module clox.container.vartype;
|
||||||
|
|
||||||
|
import std.stdio;
|
||||||
|
import std.algorithm;
|
||||||
|
import std.array;
|
||||||
|
import std.uni;
|
||||||
|
import std.conv;
|
||||||
|
|
||||||
|
struct VarType(S) if(is(S == union)){
|
||||||
|
private enum members = __traits(derivedMembers, S);
|
||||||
|
mixin("enum Type{ None, ", [members].map!asCapitalized.join(", "), "}");
|
||||||
|
private S value;
|
||||||
|
private Type _type;
|
||||||
|
Type type() const @safe @nogc nothrow => _type;
|
||||||
|
private void check(Type t) const @safe nothrow @nogc{
|
||||||
|
assert(this.type == t, "Tried to get wrong type");
|
||||||
|
}
|
||||||
|
private template funcs(string G, string T){
|
||||||
|
mixin("bool is", G, "() const nothrow @nogc @safe => this.type == this.Type.", G, ";");
|
||||||
|
mixin("auto get", G, "() const nothrow @nogc { check(this.Type.", G, "); return value.", T, "; }");
|
||||||
|
mixin("void set", G, "(typeof(S.", T, ") v = typeof(S.", T, ").init){ this._type = this.Type.", G, "; this.value.", T, " = v; }");
|
||||||
|
mixin("static auto ", T, "(typeof(S.", T, ") v){ typeof(this) vt; vt.set", G, "(v); return vt; }");
|
||||||
|
mixin("static auto ", T, "(){ typeof(this) vt; vt.set", G, "(); return vt; }");
|
||||||
|
}
|
||||||
|
static foreach(s; members){
|
||||||
|
mixin funcs!(s.asCapitalized.to!string, s);
|
||||||
|
}
|
||||||
|
string toString() const{
|
||||||
|
final switch(_type){
|
||||||
|
static foreach(s; members){
|
||||||
|
mixin("case Type.", s.asCapitalized.to!string, ": return _type.to!string ~ ':' ~ get", s.asCapitalized.to!string, ".to!string ;");
|
||||||
|
}
|
||||||
|
case Type.None: return "None";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest{
|
||||||
|
import std.exception, std.stdio;
|
||||||
|
union Test{
|
||||||
|
uint u;
|
||||||
|
int i;
|
||||||
|
double d;
|
||||||
|
}
|
||||||
|
auto i = VarType!Test.i(-5);
|
||||||
|
assert(i.getI == -5);
|
||||||
|
assert(i.type == i.Type.I);
|
||||||
|
assert(i.isI);
|
||||||
|
assert(!i.isD);
|
||||||
|
i.setD(0.5);
|
||||||
|
assert(i.getD == 0.5);
|
||||||
|
assert(i.type == i.Type.D);
|
||||||
|
assert(i.isD);
|
||||||
|
assert(!i.isU);
|
||||||
|
|
||||||
|
auto i2 = VarType!Test.i();
|
||||||
|
assert(i2.isI);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -13,7 +13,16 @@ import clox.util;
|
||||||
import clox.container.varint;
|
import clox.container.varint;
|
||||||
import clox.container.int24;
|
import clox.container.int24;
|
||||||
|
|
||||||
private ulong simpleInstruction(string name, ulong offset){
|
private ulong simpleInstruction(alias op)(string name, ulong offset){
|
||||||
|
static if(isValueOp!op)
|
||||||
|
writeln(name.cyan);
|
||||||
|
else static if(isLogicOp!op)
|
||||||
|
writeln(name.lightRed);
|
||||||
|
else static if(isCompOp!op)
|
||||||
|
writeln(name.red);
|
||||||
|
else static if(isArithOp!op)
|
||||||
|
writeln(name.yellow);
|
||||||
|
else
|
||||||
writeln(name.lightCyan);
|
writeln(name.lightCyan);
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
}
|
}
|
||||||
|
|
@ -41,12 +50,13 @@ ulong disassembleInstruction(Chunk* chunk, const ulong offset){
|
||||||
}
|
}
|
||||||
ubyte instruction = chunk.code[offset];
|
ubyte instruction = chunk.code[offset];
|
||||||
with(OpCode) switch(instruction){
|
with(OpCode) switch(instruction){
|
||||||
|
import std.meta, std.traits;
|
||||||
case Constant:
|
case Constant:
|
||||||
return constantInstruction("OP_CONSTANT", chunk, offset);
|
return constantInstruction("OP_CONSTANT", chunk, offset);
|
||||||
static foreach(k; [ Return, Negate, Add, Subtract, Multiply, Divide ]){
|
static foreach(k; Filter!(isSize1Op, EnumMembers!OpCode)){
|
||||||
case k:
|
case k:
|
||||||
static name = "OP_" ~ (k.to!string).toUpper;
|
static name = "OP_" ~ (k.to!string).toUpper;
|
||||||
return simpleInstruction(name, offset);
|
return simpleInstruction!k(name, offset);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
writefln("Unknown opcode %d", instruction);
|
writefln("Unknown opcode %d", instruction);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import clox.container.varint;
|
||||||
struct Emitter{
|
struct Emitter{
|
||||||
Compiler* compiler;
|
Compiler* compiler;
|
||||||
Chunk* chunk;
|
Chunk* chunk;
|
||||||
private uint line;
|
private uint line = 1;
|
||||||
Chunk* currentChunk(){
|
Chunk* currentChunk(){
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,30 +3,32 @@ module clox.parserules;
|
||||||
import clox.compiler;
|
import clox.compiler;
|
||||||
import clox.chunk;
|
import clox.chunk;
|
||||||
import clox.scanner;
|
import clox.scanner;
|
||||||
|
import clox.value;
|
||||||
|
|
||||||
alias ParseFn = void function(Compiler* compiler);
|
alias ParseFn = void function(Compiler* compiler);
|
||||||
|
|
||||||
void number(Compiler* compiler){
|
private void number(Compiler* compiler){
|
||||||
import core.stdc.stdlib : strtod;
|
import core.stdc.stdlib : strtod;
|
||||||
Token token = compiler.parser.previous;
|
Token token = compiler.parser.previous;
|
||||||
double value = strtod(token.lexeme.ptr, null);
|
double value = strtod(token.lexeme.ptr, null);
|
||||||
compiler.emitter.setLine(token.line);
|
compiler.emitter.setLine(token.line);
|
||||||
compiler.emitter.emitConstant(value);
|
compiler.emitter.emitConstant(Value.num(value));
|
||||||
}
|
}
|
||||||
void grouping(Compiler* compiler){
|
private void grouping(Compiler* compiler){
|
||||||
compiler.parser.expression();
|
compiler.parser.expression();
|
||||||
compiler.parser.consume(Token.Type.RightParen, "Expect ')' after expression.");
|
compiler.parser.consume(Token.Type.RightParen, "Expect ')' after expression.");
|
||||||
}
|
}
|
||||||
void unary(Compiler* compiler){
|
private void unary(Compiler* compiler){
|
||||||
Token operator = compiler.parser.previous;
|
Token operator = compiler.parser.previous;
|
||||||
compiler.parser.parsePrecedence(Precedence.Unary);
|
compiler.parser.parsePrecedence(Precedence.Unary);
|
||||||
compiler.emitter.setLine(operator.line);
|
compiler.emitter.setLine(operator.line);
|
||||||
switch(operator.type){
|
switch(operator.type){
|
||||||
case Token.Type.Minus: compiler.emitter.emit(OpCode.Negate); break;
|
case Token.Type.Minus: compiler.emitter.emit(OpCode.Negate); break;
|
||||||
|
case Token.Type.Bang: compiler.emitter.emit(OpCode.Not); break;
|
||||||
default: assert(0);
|
default: assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void binary(Compiler* compiler){
|
private void binary(Compiler* compiler){
|
||||||
Token operator = compiler.parser.previous;
|
Token operator = compiler.parser.previous;
|
||||||
immutable(ParseRule)* rule = ParseRule.get(operator.type);
|
immutable(ParseRule)* rule = ParseRule.get(operator.type);
|
||||||
compiler.parser.parsePrecedence(cast(Precedence)(rule.precedence + 1));
|
compiler.parser.parsePrecedence(cast(Precedence)(rule.precedence + 1));
|
||||||
|
|
@ -36,6 +38,24 @@ void binary(Compiler* compiler){
|
||||||
case Token.Type.Minus: compiler.emitter.emit(OpCode.Subtract); break;
|
case Token.Type.Minus: compiler.emitter.emit(OpCode.Subtract); break;
|
||||||
case Token.Type.Star: compiler.emitter.emit(OpCode.Multiply); break;
|
case Token.Type.Star: compiler.emitter.emit(OpCode.Multiply); break;
|
||||||
case Token.Type.Slash: compiler.emitter.emit(OpCode.Divide); break;
|
case Token.Type.Slash: compiler.emitter.emit(OpCode.Divide); break;
|
||||||
|
|
||||||
|
case Token.Type.BangEqual: compiler.emitter.emit(OpCode.NotEqual); break;
|
||||||
|
case Token.Type.EqualEqual: compiler.emitter.emit(OpCode.Equal); break;
|
||||||
|
|
||||||
|
case Token.Type.Greater: compiler.emitter.emit(OpCode.Greater); break;
|
||||||
|
case Token.Type.GreaterEqual: compiler.emitter.emit(OpCode.GreaterEqual); break;
|
||||||
|
|
||||||
|
case Token.Type.Less: compiler.emitter.emit(OpCode.Less); break;
|
||||||
|
case Token.Type.LessEqual: compiler.emitter.emit(OpCode.LessEqual); break;
|
||||||
|
|
||||||
|
default: assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void literal(Compiler* compiler){
|
||||||
|
switch(compiler.parser.previous.type){
|
||||||
|
case Token.Type.True: compiler.emitter.emit(OpCode.True); break;
|
||||||
|
case Token.Type.False: compiler.emitter.emit(OpCode.False); break;
|
||||||
|
case Token.Type.Nil: compiler.emitter.emit(OpCode.Nil); break;
|
||||||
default: assert(0);
|
default: assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,31 +95,31 @@ immutable ParseRule[Token.Type.max+1] rules = [
|
||||||
Token.Type.Semicolon : ParseRule(null, null, Precedence.None),
|
Token.Type.Semicolon : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Slash : ParseRule(null, &binary, Precedence.Factor),
|
Token.Type.Slash : ParseRule(null, &binary, Precedence.Factor),
|
||||||
Token.Type.Star : ParseRule(null, &binary, Precedence.Factor),
|
Token.Type.Star : ParseRule(null, &binary, Precedence.Factor),
|
||||||
Token.Type.Bang : ParseRule(null, null, Precedence.None),
|
Token.Type.Bang : ParseRule(&unary, null, Precedence.None),
|
||||||
Token.Type.BangEqual : ParseRule(null, null, Precedence.None),
|
Token.Type.BangEqual : ParseRule(null, &binary, Precedence.Equality),
|
||||||
Token.Type.Equal : ParseRule(null, null, Precedence.None),
|
Token.Type.Equal : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.EqualEqual : ParseRule(null, null, Precedence.None),
|
Token.Type.EqualEqual : ParseRule(null, &binary, Precedence.Equality),
|
||||||
Token.Type.Greater : ParseRule(null, null, Precedence.None),
|
Token.Type.Greater : ParseRule(null, &binary, Precedence.Comparison),
|
||||||
Token.Type.GreaterEqual : ParseRule(null, null, Precedence.None),
|
Token.Type.GreaterEqual : ParseRule(null, &binary, Precedence.Comparison),
|
||||||
Token.Type.Less : ParseRule(null, null, Precedence.None),
|
Token.Type.Less : ParseRule(null, &binary, Precedence.Comparison),
|
||||||
Token.Type.LessEqual : ParseRule(null, null, Precedence.None),
|
Token.Type.LessEqual : ParseRule(null, &binary, Precedence.Comparison),
|
||||||
Token.Type.Identifier : ParseRule(null, null, Precedence.None),
|
Token.Type.Identifier : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.String : ParseRule(null, null, Precedence.None),
|
Token.Type.String : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Number : ParseRule(&number, null, Precedence.None),
|
Token.Type.Number : ParseRule(&number, null, Precedence.None),
|
||||||
Token.Type.And : ParseRule(null, null, Precedence.None),
|
Token.Type.And : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Class : ParseRule(null, null, Precedence.None),
|
Token.Type.Class : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Else : ParseRule(null, null, Precedence.None),
|
Token.Type.Else : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.False : ParseRule(null, null, Precedence.None),
|
Token.Type.False : ParseRule(&literal, null, Precedence.None),
|
||||||
Token.Type.For : ParseRule(null, null, Precedence.None),
|
Token.Type.For : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Fun : ParseRule(null, null, Precedence.None),
|
Token.Type.Fun : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.If : ParseRule(null, null, Precedence.None),
|
Token.Type.If : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Nil : ParseRule(null, null, Precedence.None),
|
Token.Type.Nil : ParseRule(&literal, null, Precedence.None),
|
||||||
Token.Type.Or : ParseRule(null, null, Precedence.None),
|
Token.Type.Or : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Print : ParseRule(null, null, Precedence.None),
|
Token.Type.Print : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Return : ParseRule(null, null, Precedence.None),
|
Token.Type.Return : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Super : ParseRule(null, null, Precedence.None),
|
Token.Type.Super : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.This : ParseRule(null, null, Precedence.None),
|
Token.Type.This : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.True : ParseRule(null, null, Precedence.None),
|
Token.Type.True : ParseRule(&literal, null, Precedence.None),
|
||||||
Token.Type.Var : ParseRule(null, null, Precedence.None),
|
Token.Type.Var : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.While : ParseRule(null, null, Precedence.None),
|
Token.Type.While : ParseRule(null, null, Precedence.None),
|
||||||
Token.Type.Error : ParseRule(null, null, Precedence.None),
|
Token.Type.Error : ParseRule(null, null, Precedence.None),
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,52 @@ module clox.value;
|
||||||
|
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
|
|
||||||
alias Value = double;
|
import clox.container.vartype;
|
||||||
|
|
||||||
|
/* struct Value{ */
|
||||||
|
/* alias T = VarType!u; */
|
||||||
|
/* private union U{ */
|
||||||
|
/* bool bln; */
|
||||||
|
/* bool nil; */
|
||||||
|
/* double num; */
|
||||||
|
/* } */
|
||||||
|
/* T v; */
|
||||||
|
/* alias v this; */
|
||||||
|
/* } */
|
||||||
|
private union U{
|
||||||
|
bool bln;
|
||||||
|
bool nil;
|
||||||
|
double num;
|
||||||
|
}
|
||||||
|
alias Value = VarType!U;
|
||||||
|
|
||||||
void printValue(Value value){
|
void printValue(Value value){
|
||||||
writef("%g", value);
|
final switch(value.type){
|
||||||
|
case value.Type.Bln: writef("%s", value.getBln); break;
|
||||||
|
case value.Type.Num: writef("%g", value.getNum); break;
|
||||||
|
case value.Type.Nil: writef("nil"); break;
|
||||||
|
case value.Type.None: assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool isTruthy(Value value) nothrow @nogc {
|
||||||
|
final switch(value.type){
|
||||||
|
case value.Type.Bln: return value.getBln;
|
||||||
|
case value.Type.Num: return true;
|
||||||
|
case value.Type.Nil: return false;
|
||||||
|
case value.Type.None: assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool isFalsey(Value value) nothrow @nogc {
|
||||||
|
return !isTruthy(value);
|
||||||
|
}
|
||||||
|
bool compare(string op)(Value a, Value b){
|
||||||
|
if(a.type != b.type)
|
||||||
|
return false;
|
||||||
|
final switch(a.type){
|
||||||
|
case a.Type.Bln: return mixin("a.getBln", op, "b.getBln");
|
||||||
|
case a.Type.Num: return mixin("a.getNum", op, "b.getNum");
|
||||||
|
case a.Type.Nil: return true;
|
||||||
|
case a.Type.None: assert(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import clox.util;
|
||||||
import clox.compiler;
|
import clox.compiler;
|
||||||
import clox.container.stack;
|
import clox.container.stack;
|
||||||
import clox.container.varint;
|
import clox.container.varint;
|
||||||
|
import clox.container.int24;
|
||||||
|
|
||||||
enum stackMax = 256;
|
enum stackMax = 256;
|
||||||
|
|
||||||
|
|
@ -33,7 +34,16 @@ struct VM{
|
||||||
ip = &chunk.code[0];
|
ip = &chunk.code[0];
|
||||||
return run();
|
return run();
|
||||||
}
|
}
|
||||||
private InterpretResult run() @nogc nothrow {
|
private void runtimeError(Args...)(string format, Args args) nothrow {
|
||||||
|
size_t instruction = ip - (&chunk.code[0]) - 1;
|
||||||
|
uint line = chunk.lines[instruction].toUint;
|
||||||
|
try{
|
||||||
|
stderr.writef("[line %d] ", line);
|
||||||
|
stderr.writefln(format, args);
|
||||||
|
} catch(Exception){}
|
||||||
|
/* stack.reset(); */
|
||||||
|
}
|
||||||
|
private InterpretResult run() nothrow {
|
||||||
auto readByte() => *ip++;
|
auto readByte() => *ip++;
|
||||||
auto readIns() => cast(OpCode)readByte();
|
auto readIns() => cast(OpCode)readByte();
|
||||||
Value readConstant(){
|
Value readConstant(){
|
||||||
|
|
@ -41,6 +51,9 @@ struct VM{
|
||||||
ip += constant.len;
|
ip += constant.len;
|
||||||
return chunk.constants[constant.i];
|
return chunk.constants[constant.i];
|
||||||
}
|
}
|
||||||
|
Value peek(int distance = 0){
|
||||||
|
return stack.top[-1 - distance];
|
||||||
|
}
|
||||||
while(true){
|
while(true){
|
||||||
debug(traceExec){
|
debug(traceExec){
|
||||||
writeln(" ", stack.live);
|
writeln(" ", stack.live);
|
||||||
|
|
@ -52,15 +65,42 @@ struct VM{
|
||||||
Value constant = readConstant();
|
Value constant = readConstant();
|
||||||
stack.push(constant);
|
stack.push(constant);
|
||||||
break;
|
break;
|
||||||
|
case True:
|
||||||
|
stack.push(Value.bln(true));
|
||||||
|
break;
|
||||||
|
case False:
|
||||||
|
stack.push(Value.bln(false));
|
||||||
|
break;
|
||||||
|
case Nil:
|
||||||
|
stack.push(Value.nil());
|
||||||
|
break;
|
||||||
static foreach(k, op; [ Add: "+", Subtract: "-", Multiply: "*", Divide: "/" ]){
|
static foreach(k, op; [ Add: "+", Subtract: "-", Multiply: "*", Divide: "/" ]){
|
||||||
|
case k:
|
||||||
|
if(!peek(0).isNum || !peek(1).isNum){
|
||||||
|
runtimeError("Operands must be numbers.");
|
||||||
|
return InterpretResult.RuntimeError;
|
||||||
|
}
|
||||||
|
double b = stack.pop().getNum;
|
||||||
|
double a = stack.pop().getNum;
|
||||||
|
stack.push(Value.num(mixin("a", op, "b")));
|
||||||
|
break opSwitch;
|
||||||
|
}
|
||||||
|
static foreach(k, op; [ NotEqual: "!=", Equal: "==", Greater: ">", GreaterEqual: ">=", Less: "<", LessEqual: "<=" ]){
|
||||||
case k:
|
case k:
|
||||||
Value b = stack.pop();
|
Value b = stack.pop();
|
||||||
Value a = stack.pop();
|
Value a = stack.pop();
|
||||||
stack.push(mixin("a", op, "b"));
|
stack.push(Value.bln(compare!op(a, b)));
|
||||||
break opSwitch;
|
break opSwitch;
|
||||||
}
|
}
|
||||||
|
case Not:
|
||||||
|
stack.push(Value.bln(stack.pop().isFalsey));
|
||||||
|
break;
|
||||||
case Negate:
|
case Negate:
|
||||||
stack.push(-stack.pop());
|
if(!peek(0).isNum){
|
||||||
|
runtimeError("Operand must be a number.");
|
||||||
|
return InterpretResult.RuntimeError;
|
||||||
|
}
|
||||||
|
stack.push(Value.num(-stack.pop().getNum));
|
||||||
break;
|
break;
|
||||||
case Return:
|
case Return:
|
||||||
debug printValue(stack.pop());
|
debug printValue(stack.pop());
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue