add logical ops, add numbers
This commit is contained in:
parent
bf38431165
commit
9d65790e5d
47
src/'
47
src/'
|
@ -1,47 +0,0 @@
|
||||||
#include "common.h"
|
|
||||||
#include "vm.h"
|
|
||||||
|
|
||||||
VM vm;
|
|
||||||
|
|
||||||
void newVM() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeVM() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static InterpretResult run() {
|
|
||||||
#define READ_BYTE() (*vm.ip++)
|
|
||||||
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
#ifdef DEBUG_TRACE_EXECUTION
|
|
||||||
disassembleInstruction(vm.chunk,
|
|
||||||
(int)(vm.ip - vm.chunk->code));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint8_t instruction;
|
|
||||||
switch (instruction = READ_BYTE()) {
|
|
||||||
case OP_CONSTANT: {
|
|
||||||
Value constant = READ_CONSTANT();
|
|
||||||
printValue(constant);
|
|
||||||
printf("\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OP_RETURN: {
|
|
||||||
return INTERPRET_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef READ_BYTE
|
|
||||||
#undef READ_CONSTANT
|
|
||||||
}
|
|
||||||
|
|
||||||
InterpretResult interpret(Chunk* chunk) {
|
|
||||||
vm.chunk = chunk;
|
|
||||||
vm.ip = vm.chunk->code;
|
|
||||||
return run();
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,10 +8,17 @@
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OP_NOOP,
|
OP_NOOP,
|
||||||
OP_CONSTANT,
|
OP_CONSTANT,
|
||||||
|
OP_NIL,
|
||||||
|
OP_TRUE,
|
||||||
|
OP_FALSE,
|
||||||
|
OP_EQUAL,
|
||||||
|
OP_GREATER,
|
||||||
|
OP_LESS,
|
||||||
OP_ADD,
|
OP_ADD,
|
||||||
OP_SUBTRACT,
|
OP_SUBTRACT,
|
||||||
OP_MULTIPLY,
|
OP_MULTIPLY,
|
||||||
OP_DIVIDE,
|
OP_DIVIDE,
|
||||||
|
OP_NOT,
|
||||||
OP_NEGATE,
|
OP_NEGATE,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
} OpCode;
|
} OpCode;
|
||||||
|
|
|
@ -141,7 +141,7 @@ static void grouping() {
|
||||||
|
|
||||||
static void number() {
|
static void number() {
|
||||||
double value = strtod(parser.previous.start, NULL);
|
double value = strtod(parser.previous.start, NULL);
|
||||||
emitConstant(value);
|
emitConstant(NUMBER_VAL(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void binary() {
|
static void binary() {
|
||||||
|
@ -150,6 +150,12 @@ static void binary() {
|
||||||
parsePrecedence((Precedence)(rule->precedence + 1));
|
parsePrecedence((Precedence)(rule->precedence + 1));
|
||||||
|
|
||||||
switch (operatorType) {
|
switch (operatorType) {
|
||||||
|
case TOKEN_BANG_EQUAL: emitBytes(OP_EQUAL, OP_NOT); break;
|
||||||
|
case TOKEN_EQUAL_EQUAL: emitByte(OP_EQUAL); break;
|
||||||
|
case TOKEN_GREATER: emitByte(OP_GREATER); break;
|
||||||
|
case TOKEN_GREATER_EQUAL: emitBytes(OP_LESS, OP_NOT); break;
|
||||||
|
case TOKEN_LESS: emitByte(OP_LESS); break;
|
||||||
|
case TOKEN_LESS_EQUAL: emitBytes(OP_GREATER, OP_NOT); break;
|
||||||
case TOKEN_PLUS: emitByte(OP_ADD); break;
|
case TOKEN_PLUS: emitByte(OP_ADD); break;
|
||||||
case TOKEN_MINUS: emitByte(OP_SUBTRACT); break;
|
case TOKEN_MINUS: emitByte(OP_SUBTRACT); break;
|
||||||
case TOKEN_STAR: emitByte(OP_MULTIPLY); break;
|
case TOKEN_STAR: emitByte(OP_MULTIPLY); break;
|
||||||
|
@ -158,6 +164,15 @@ static void binary() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void literal() {
|
||||||
|
switch (parser.previous.type) {
|
||||||
|
case TOKEN_FALSE: emitByte(OP_FALSE); break;
|
||||||
|
case TOKEN_NIL: emitByte(OP_NIL); break;
|
||||||
|
case TOKEN_TRUE: emitByte(OP_TRUE); break;
|
||||||
|
default: return; // Unreachable.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void unary() {
|
static void unary() {
|
||||||
TokenType operatorType = parser.previous.type;
|
TokenType operatorType = parser.previous.type;
|
||||||
|
|
||||||
|
@ -166,6 +181,7 @@ static void unary() {
|
||||||
|
|
||||||
// Emit the operator instruction.
|
// Emit the operator instruction.
|
||||||
switch (operatorType) {
|
switch (operatorType) {
|
||||||
|
case TOKEN_BANG: emitByte(OP_NOT); break;
|
||||||
case TOKEN_MINUS: emitByte(OP_NEGATE); break;
|
case TOKEN_MINUS: emitByte(OP_NEGATE); break;
|
||||||
default: return; // Unreachable.
|
default: return; // Unreachable.
|
||||||
}
|
}
|
||||||
|
@ -183,31 +199,31 @@ ParseRule rules[] = {
|
||||||
[TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE},
|
[TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_SLASH] = {NULL, binary, PREC_FACTOR},
|
[TOKEN_SLASH] = {NULL, binary, PREC_FACTOR},
|
||||||
[TOKEN_STAR] = {NULL, binary, PREC_FACTOR},
|
[TOKEN_STAR] = {NULL, binary, PREC_FACTOR},
|
||||||
[TOKEN_BANG] = {NULL, NULL, PREC_NONE},
|
[TOKEN_BANG] = {unary, NULL, PREC_NONE},
|
||||||
[TOKEN_BANG_EQUAL] = {NULL, NULL, PREC_NONE},
|
[TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY},
|
||||||
[TOKEN_EQUAL] = {NULL, NULL, PREC_NONE},
|
[TOKEN_EQUAL] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_EQUAL_EQUAL] = {NULL, NULL, PREC_NONE},
|
[TOKEN_EQUAL_EQUAL] = {NULL, binary, PREC_EQUALITY},
|
||||||
[TOKEN_GREATER] = {NULL, NULL, PREC_NONE},
|
[TOKEN_GREATER] = {NULL, binary, PREC_COMPARISON},
|
||||||
[TOKEN_GREATER_EQUAL] = {NULL, NULL, PREC_NONE},
|
[TOKEN_GREATER_EQUAL] = {NULL, binary, PREC_COMPARISON},
|
||||||
[TOKEN_LESS] = {NULL, NULL, PREC_NONE},
|
[TOKEN_LESS] = {NULL, binary, PREC_COMPARISON},
|
||||||
[TOKEN_LESS_EQUAL] = {NULL, NULL, PREC_NONE},
|
[TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON},
|
||||||
[TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
|
[TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_STRING] = {NULL, NULL, PREC_NONE},
|
[TOKEN_STRING] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
|
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
|
||||||
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
|
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_TYPE] = {NULL, NULL, PREC_NONE},
|
[TOKEN_TYPE] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
[TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_FALSE] = {NULL, NULL, PREC_NONE},
|
[TOKEN_FALSE] = {literal, NULL, PREC_NONE},
|
||||||
[TOKEN_FOR] = {NULL, NULL, PREC_NONE},
|
[TOKEN_FOR] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_FN] = {NULL, NULL, PREC_NONE},
|
[TOKEN_FN] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_NIL] = {NULL, NULL, PREC_NONE},
|
[TOKEN_NIL] = {literal, NULL, PREC_NONE},
|
||||||
[TOKEN_OR] = {NULL, NULL, PREC_NONE},
|
[TOKEN_OR] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
|
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
|
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
|
[TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_THIS] = {NULL, NULL, PREC_NONE},
|
[TOKEN_THIS] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_TRUE] = {NULL, NULL, PREC_NONE},
|
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
|
||||||
[TOKEN_LET] = {NULL, NULL, PREC_NONE},
|
[TOKEN_LET] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
|
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
|
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
|
||||||
|
|
14
src/debug.c
14
src/debug.c
|
@ -42,6 +42,18 @@ int disassembleInstruction(Chunk* chunk, int offset) {
|
||||||
return simpleInstruction("OP_NEGATE", offset);
|
return simpleInstruction("OP_NEGATE", offset);
|
||||||
case OP_CONSTANT:
|
case OP_CONSTANT:
|
||||||
return constantInstruction("OP_CONSTANT", chunk, offset);
|
return constantInstruction("OP_CONSTANT", chunk, offset);
|
||||||
|
case OP_NIL:
|
||||||
|
return simpleInstruction("OP_NIL", offset);
|
||||||
|
case OP_TRUE:
|
||||||
|
return simpleInstruction("OP_TRUE", offset);
|
||||||
|
case OP_FALSE:
|
||||||
|
return simpleInstruction("OP_FALSE", offset);
|
||||||
|
case OP_EQUAL:
|
||||||
|
return simpleInstruction("OP_EQUAL", offset);
|
||||||
|
case OP_GREATER:
|
||||||
|
return simpleInstruction("OP_GREATER", offset);
|
||||||
|
case OP_LESS:
|
||||||
|
return simpleInstruction("OP_LESS", offset);
|
||||||
case OP_ADD:
|
case OP_ADD:
|
||||||
return simpleInstruction("OP_ADD", offset);
|
return simpleInstruction("OP_ADD", offset);
|
||||||
case OP_SUBTRACT:
|
case OP_SUBTRACT:
|
||||||
|
@ -50,6 +62,8 @@ int disassembleInstruction(Chunk* chunk, int offset) {
|
||||||
return simpleInstruction("OP_MULTIPLY", offset);
|
return simpleInstruction("OP_MULTIPLY", offset);
|
||||||
case OP_DIVIDE:
|
case OP_DIVIDE:
|
||||||
return simpleInstruction("OP_DIVIDE", offset);
|
return simpleInstruction("OP_DIVIDE", offset);
|
||||||
|
case OP_NOT:
|
||||||
|
return simpleInstruction("OP_NOT", offset);
|
||||||
case OP_RETURN:
|
case OP_RETURN:
|
||||||
return simpleInstruction("OP_RETURN", offset);
|
return simpleInstruction("OP_RETURN", offset);
|
||||||
default:
|
default:
|
||||||
|
|
18
src/value.c
18
src/value.c
|
@ -27,5 +27,21 @@ void freeValueArray(ValueArray *array) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void printValue(Value value) {
|
void printValue(Value value) {
|
||||||
printf("%g", value);
|
switch (value.type) {
|
||||||
|
case VAL_BOOL:
|
||||||
|
printf(AS_BOOL(value) ? "true" : "false");
|
||||||
|
break;
|
||||||
|
case VAL_NIL: printf("nil"); break;
|
||||||
|
case VAL_NUMBER: printf("%g", AS_NUMBER(value)); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valuesEqual(Value a, Value b) {
|
||||||
|
if (a.type != b.type) return false;
|
||||||
|
switch (a.type) {
|
||||||
|
case VAL_BOOL: return AS_BOOL(a) == AS_BOOL(b);
|
||||||
|
case VAL_NIL: return true;
|
||||||
|
case VAL_NUMBER: return AS_NUMBER(a) == AS_NUMBER(b);
|
||||||
|
default: return false; // Unreachable.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
26
src/value.h
26
src/value.h
|
@ -3,7 +3,30 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
typedef double Value;
|
typedef enum {
|
||||||
|
VAL_BOOL,
|
||||||
|
VAL_NIL,
|
||||||
|
VAL_NUMBER,
|
||||||
|
} ValueType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ValueType type;
|
||||||
|
union {
|
||||||
|
bool boolean;
|
||||||
|
double number;
|
||||||
|
} as;
|
||||||
|
} Value;
|
||||||
|
|
||||||
|
#define BOOL_VAL(value) ((Value){VAL_BOOL,{.boolean = value}})
|
||||||
|
#define NIL_VAL ((Value){VAL_NIL, {.number = 0}})
|
||||||
|
#define NUMBER_VAL(value) ((Value){VAL_NUMBER, {.number = value}})
|
||||||
|
|
||||||
|
#define AS_BOOL(value) ((value).as.boolean)
|
||||||
|
#define AS_NUMBER(value) ((value).as.number)
|
||||||
|
|
||||||
|
#define IS_BOOL(value) ((value).type == VAL_BOOL)
|
||||||
|
#define IS_NIL(value) ((value).type == VAL_NIL)
|
||||||
|
#define IS_NUMBER(value) ((value).type == VAL_NUMBER)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int capacity;
|
int capacity;
|
||||||
|
@ -11,6 +34,7 @@ typedef struct {
|
||||||
Value *values;
|
Value *values;
|
||||||
} ValueArray;
|
} ValueArray;
|
||||||
|
|
||||||
|
bool valuesEqual(Value a, Value b);
|
||||||
void newValueArray(ValueArray *array);
|
void newValueArray(ValueArray *array);
|
||||||
void writeValueArray(ValueArray *array, Value value);
|
void writeValueArray(ValueArray *array, Value value);
|
||||||
void freeValueArray(ValueArray *array);
|
void freeValueArray(ValueArray *array);
|
||||||
|
|
64
src/vm.c
64
src/vm.c
|
@ -4,6 +4,7 @@
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
VM vm;
|
VM vm;
|
||||||
|
|
||||||
|
@ -11,6 +12,19 @@ static void resetStack() {
|
||||||
vm.stackTop = vm.stack;
|
vm.stackTop = vm.stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void runtimeError(const char* format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vfprintf(stderr, format, args);
|
||||||
|
va_end(args);
|
||||||
|
fputs("\n", stderr);
|
||||||
|
|
||||||
|
size_t instruction = vm.ip - vm.chunk->code - 1;
|
||||||
|
int line = vm.chunk->lines[instruction];
|
||||||
|
fprintf(stderr, "[line %d] in script\n", line);
|
||||||
|
resetStack();
|
||||||
|
}
|
||||||
|
|
||||||
void newVM() {
|
void newVM() {
|
||||||
resetStack();
|
resetStack();
|
||||||
}
|
}
|
||||||
|
@ -29,14 +43,26 @@ Value pop() {
|
||||||
return *vm.stackTop;
|
return *vm.stackTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Value peek(int distance) {
|
||||||
|
return vm.stackTop[-1 - distance];
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isFalsey(Value value) {
|
||||||
|
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
|
||||||
|
}
|
||||||
|
|
||||||
static InterpretResult run() {
|
static InterpretResult run() {
|
||||||
#define READ_BYTE() (*vm.ip++)
|
#define READ_BYTE() (*vm.ip++)
|
||||||
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
|
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
|
||||||
#define BINARY_OP(op) \
|
#define BINARY_OP(valueType, op) \
|
||||||
do { \
|
do { \
|
||||||
double b = pop(); \
|
if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
|
||||||
double a = pop(); \
|
runtimeError("Operands must be numbers."); \
|
||||||
push(a op b); \
|
return INTERPRET_RUNTIME_ERROR; \
|
||||||
|
} \
|
||||||
|
double b = AS_NUMBER(pop()); \
|
||||||
|
double a = AS_NUMBER(pop()); \
|
||||||
|
push(valueType(a op b)); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -59,11 +85,31 @@ static InterpretResult run() {
|
||||||
push(constant);
|
push(constant);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OP_ADD: BINARY_OP(+); break;
|
case OP_NIL: push(NIL_VAL); break;
|
||||||
case OP_SUBTRACT: BINARY_OP(-); break;
|
case OP_TRUE: push(BOOL_VAL(true)); break;
|
||||||
case OP_MULTIPLY: BINARY_OP(*); break;
|
case OP_FALSE: push(BOOL_VAL(false)); break;
|
||||||
case OP_DIVIDE: BINARY_OP(/); break;
|
case OP_EQUAL: {
|
||||||
case OP_NEGATE: push(-pop()); break;
|
Value b = pop();
|
||||||
|
Value a = pop();
|
||||||
|
push(BOOL_VAL(valuesEqual(a, b)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_GREATER: BINARY_OP(BOOL_VAL, >); break;
|
||||||
|
case OP_LESS: BINARY_OP(BOOL_VAL, <); break;
|
||||||
|
case OP_ADD: BINARY_OP(NUMBER_VAL, +); break;
|
||||||
|
case OP_SUBTRACT: BINARY_OP(NUMBER_VAL, -); break;
|
||||||
|
case OP_MULTIPLY: BINARY_OP(NUMBER_VAL, *); break;
|
||||||
|
case OP_DIVIDE: BINARY_OP(NUMBER_VAL, /); break;
|
||||||
|
case OP_NOT:
|
||||||
|
push(BOOL_VAL(isFalsey(pop())));
|
||||||
|
break;
|
||||||
|
case OP_NEGATE:
|
||||||
|
if (!IS_NUMBER(peek(0))) {
|
||||||
|
runtimeError("Operand must be a number.");
|
||||||
|
return INTERPRET_RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
push(NUMBER_VAL(-AS_NUMBER(pop())));
|
||||||
|
break;
|
||||||
case OP_RETURN: {
|
case OP_RETURN: {
|
||||||
printValue(pop());
|
printValue(pop());
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1 + 2;
|
1 + 2
|
||||||
|
|
Loading…
Reference in New Issue