| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- /**
- ******************************************************************************
- * @file : vm.c
- * @author : simon
- * @brief : Interpret the bytecode from chunk
- * @attention : None
- * @date : 2023/8/17
- ******************************************************************************
- */
- #include "vm.h"
- #include "common.h"
- #include "compiler.h"
- #include "debug.h"
- #include "memory.h"
- #include <stdarg.h>
- VM vm;
- /// \brief 重置栈
- static void resetStack() {
- 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 initVM() {
- resetStack();
- vm.objects = NULL;
- initTable(&vm.globals);
- initTable(&vm.strings);
- }
- static Value peek(int distance) {
- return vm.stackTop[-1 - distance];
- }
- static bool isFalse(Value value) {
- // the nil and false will return true
- return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
- }
- static void concatenate() {
- ObjString *b = AS_STRING(pop());
- ObjString *a = AS_STRING(pop());
- int length = a->length + b->length;
- char *chars = ALLOCATE(char, length + 1);
- memcpy(chars, a->chars, a->length);
- memcpy(chars + a->length, b->chars, b->length);
- chars[length] = '\0';
- ObjString *result = takeString(chars, length);
- push(OBJ_VAL(result));
- }
- static InterpretResult run() {
- //! <macro> reads the byte currently pointed at by ip
- //! and then advances the instruction pointer
- #define READ_BYTE() (*vm.ip++)
- //! reads the next byte from the bytecode
- //! treats the resulting number as an index
- #define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
- #define READ_STRING() AS_STRING(READ_CONSTANT())
- #define BINARY_OP(valueType, op) \
- do { \
- if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
- runtimeError("Operands must be numbers."); \
- return INTERPRET_RUNTIME_ERROR; \
- } \
- double b = AS_NUMBER(pop()); \
- double a = AS_NUMBER(pop()); \
- push(valueType(a op b)); \
- } while (false)
- #ifdef DEBUG_TRACE_EXECUTION
- printf("!!! <Stack tracing> start\n");
- #endif
- for (;;) {
- #ifdef DEBUG_TRACE_EXECUTION
- //! <Stack tracing> start
- printf("------------------------\n|");
- for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
- printf("[ ");
- printValue(*slot);
- printf(" ]");
- }
- printf("\n------------------------");
- printf("\n");
- //! <Stack tracing> end
- disassembleInstruction(vm.chunk, (int) (vm.ip - vm.chunk->code));
- #endif
- switch (READ_BYTE()) {
- case OP_CONSTANT: {
- Value constant = READ_CONSTANT();
- push(constant);
- break;
- }
- case OP_NIL:
- push(NIL_VAL);
- break;
- case OP_TRUE:
- push(BOOL_VAL(true));
- break;
- case OP_FALSE:
- push(BOOL_VAL(false));
- break;
- case OP_POP:
- pop();
- break;
- case OP_DEFINE_GLOBAL: {
- ObjString *name = READ_STRING();
- tableSet(&vm.globals, name, peek(0));
- pop();
- break;
- }
- case OP_GET_GLOBAL: {
- ObjString *name = READ_STRING();
- Value value;
- if (!tableGet(&vm.globals, name, &value)) {
- runtimeError("Undefined variable '%s'.", name->chars);
- return INTERPRET_RUNTIME_ERROR;
- }
- push(value);
- break;
- }
- case OP_SET_GLOBAL: {
- ObjString *name = READ_STRING();
- if (tableSet(&vm.globals, name, peek(0))) {
- tableDelete(&vm.globals, name);
- runtimeError("Undefined variable '%s'.", name->chars);
- return INTERPRET_RUNTIME_ERROR;
- }
- break;
- }
- case OP_GET_LOCAL: {
- uint8_t slot = READ_BYTE();
- push(vm.stack[slot]);
- break;
- }
- case OP_SET_LOCAL: {
- uint8_t slot = READ_BYTE();
- vm.stack[slot] = peek(0);
- break;
- }
- case OP_ADD: {
- if (IS_STRING(peek(0)) && IS_STRING(peek(1))) {
- // 字串拼接
- concatenate();
- } else if (IS_NUMBER(peek(0)) && IS_NUMBER(peek(1))) {
- double b = AS_NUMBER(pop());
- double a = AS_NUMBER(pop());
- push(NUMBER_VAL(a + b));
- } else {
- runtimeError("Operands must be two numbers or two strings.");
- return INTERPRET_RUNTIME_ERROR;
- }
- 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(isFalse(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_EQUAL: {
- 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_PRINT:
- printValue(pop());
- printf("\n");
- break;
- case OP_RETURN: {
- // Exit interpreter.
- return INTERPRET_OK;
- }
- }
- #ifdef DEBUG_TRACE_EXECUTION
- printf("!!! <Stack tracing> end\n");
- #endif
- }
- #undef READ_BYTE
- #undef READ_CONSTANT
- #undef READ_STRING
- #undef BINARY_OP
- }
- void freeVM() {
- freeTable(&vm.globals);
- freeTable(&vm.strings);
- freeObjects();
- }
- InterpretResult interpret(const char *source) {
- Chunk chunk;
- initChunk(&chunk);
- if (!compile(source, &chunk)) {
- freeChunk(&chunk);
- return INTERPRET_COMPILE_ERROR;
- }
- vm.chunk = &chunk;
- vm.ip = vm.chunk->code;
- InterpretResult result = run();
- freeChunk(&chunk);
- return result;
- }
- void push(Value value) {
- *vm.stackTop = value;
- vm.stackTop++;
- }
- Value pop() {
- vm.stackTop--;
- //! NOTE: 出栈后,里面的值没有清除
- return *vm.stackTop;
- }
|