1+ /* src/runtime/vm.c */
2+ #include <stdarg.h>
13#include <stdio.h>
4+ #include <string.h>
5+ #include <time.h>
26
3- #include "common.h"
4- #include "debug.h"
5- #include "vm.h"
7+ #include "../include/common.h"
8+ #include "../include/compiler.h"
9+ #include "../include/debug.h"
10+ #include "../include/object.h"
11+ #include "../include/memory.h"
12+ #include "../include/vm.h"
13+ #include "../include/table.h"
614
7- void initVM (VM * vm ) { vm -> stackTop = vm -> stack ; }
15+ // Helper to reset the stack
16+ static void resetStack (VM * vm ) {
17+ vm -> stackTop = vm -> stack ;
18+ }
19+
20+ // Runtime Error Helper
21+ static void runtimeError (VM * vm , const char * format , ...) {
22+ va_list args ;
23+ va_start (args , format );
24+ vfprintf (stderr , format , args );
25+ va_end (args );
26+ fputs ("\n" , stderr );
27+
28+ size_t instruction = vm -> ip - vm -> chunk -> code - 1 ;
29+ int line = vm -> chunk -> lines [instruction ];
30+ fprintf (stderr , "[line %d] in script\n" , line );
31+ resetStack (vm );
32+ }
833
9- void freeVM (VM * vm ) {
10- // Nothing to free yet
34+ // Initialize the VM
35+ void initVM (VM * vm ) {
36+ resetStack (vm );
37+ vm -> objects = NULL ;
38+ initTable (& vm -> globals );
39+ initTable (& vm -> strings );
40+
41+ // You can register native functions here if you want them globally available
42+ // defineNative(vm, "clock", clockNative);
1143}
1244
13- void push (VM * vm , Value value ) {
45+ // Free the VM resources
46+ void freeVM (VM * vm ) {
47+ freeTable (& vm -> globals );
48+ freeTable (& vm -> strings );
49+ freeObjects (); // Frees the linked list of objects
50+ }
51+
52+ void push (VM * vm , Value value ) {
1453 * vm -> stackTop = value ;
1554 vm -> stackTop ++ ;
1655}
1756
18- Value pop (VM * vm ) {
57+ Value pop (VM * vm ) {
1958 vm -> stackTop -- ;
2059 return * vm -> stackTop ;
2160}
2261
23- static InterpretResult run (VM * vm ) {
62+ static Value peek (VM * vm , int distance ) {
63+ return vm -> stackTop [-1 - distance ];
64+ }
65+
66+ // Helper to check for false/nil
67+ static bool isFalsey (Value value ) {
68+ return IS_NIL (value ) || (IS_BOOL (value ) && !AS_BOOL (value ));
69+ }
70+
71+ // Concatenate two strings
72+ static void concatenate (VM * vm ) {
73+ ObjString * b = AS_STRING (peek (vm , 0 ));
74+ ObjString * a = AS_STRING (peek (vm , 1 ));
75+
76+ int length = a -> length + b -> length ;
77+ char * chars = ALLOCATE (char , length + 1 );
78+ memcpy (chars , a -> chars , a -> length );
79+ memcpy (chars + a -> length , b -> chars , b -> length );
80+ chars [length ] = '\0' ;
81+
82+ ObjString * result = takeString (chars , length );
83+ pop (vm );
84+ pop (vm );
85+ push (vm , OBJ_VAL (result ));
86+ }
87+
88+ // Define a native function
89+ void defineNative (VM * vm , const char * name , NativeFn function ) {
90+ push (vm , OBJ_VAL (copyString (name , (int )strlen (name ))));
91+ push (vm , OBJ_VAL (newNative (function )));
92+ tableSet (& vm -> globals , AS_STRING (vm -> stack [0 ]), vm -> stack [1 ]);
93+ pop (vm );
94+ pop (vm );
95+ }
96+
97+ // Main execution loop
98+ static InterpretResult run (VM * vm ) {
2499#define READ_BYTE () (*vm->ip++)
25100#define READ_CONSTANT () (vm->chunk->constants.values[READ_BYTE()])
26- #define BINARY_OP (valueType , op ) \
27- do { \
28- if (!IS_NUMBER((*(vm->stackTop - 1))) || \
29- !IS_NUMBER((*(vm->stackTop - 2)))) { \
30- printf("Operands must be numbers.\n"); \
31- return INTERPRET_RUNTIME_ERROR; \
32- } \
33- double b = AS_NUMBER(pop(vm)); \
34- double a = AS_NUMBER(pop(vm)); \
35- push(vm, valueType(a op b)); \
36- } while (false)
101+ #define BINARY_OP (valueType , op ) \
102+ do { \
103+ if (!IS_NUMBER(peek(vm, 0)) || !IS_NUMBER(peek(vm, 1))) { \
104+ runtimeError(vm, "Operands must be numbers."); \
105+ return INTERPRET_RUNTIME_ERROR; \
106+ } \
107+ double b = AS_NUMBER(pop(vm)); \
108+ double a = AS_NUMBER(pop(vm)); \
109+ push(vm, valueType(a op b)); \
110+ } while (false)
37111
38112 for (;;) {
39113#ifdef DEBUG_TRACE_EXECUTION
40114 printf (" " );
41- for (Value * slot = vm -> stack ; slot < vm -> stackTop ; slot ++ ) {
115+ for (Value * slot = vm -> stack ; slot < vm -> stackTop ; slot ++ ) {
42116 printf ("[ " );
43117 printValue (* slot );
44118 printf (" ]" );
@@ -47,57 +121,52 @@ static InterpretResult run(VM *vm) {
47121 disassembleInstruction (vm -> chunk , (int )(vm -> ip - vm -> chunk -> code ));
48122#endif
49123
50- u8 instruction ;
124+ uint8_t instruction ;
51125 switch (instruction = READ_BYTE ()) {
52- case OP_CONSTANT : {
53- Value constant = READ_CONSTANT ();
54- push (vm , constant );
55- break ;
56- }
57- case OP_TRUE :
58- push (vm , BOOL_VAL (true));
59- break ;
60- case OP_FALSE :
61- push (vm , BOOL_VAL (false));
62- break ;
63- case OP_NULL :
64- push (vm , NULL_VAL );
65- break ;
66- case OP_POP :
67- pop (vm );
68- break ;
69- case OP_ADD :
70- BINARY_OP (NUMBER_VAL , + );
71- break ;
72- case OP_SUBTRACT :
73- BINARY_OP (NUMBER_VAL , - );
74- break ;
75- case OP_MULTIPLY :
76- BINARY_OP (NUMBER_VAL , * );
77- break ;
78- case OP_DIVIDE :
79- BINARY_OP (NUMBER_VAL , /);
80- break ;
81- case OP_NOT :
82- push (vm , BOOL_VAL (IS_NULL (* (vm -> stackTop - 1 )) ||
83- (IS_BOOL (* (vm -> stackTop - 1 )) &&
84- !AS_BOOL (* (vm -> stackTop - 1 )))));
85- break ;
86- case OP_NEGATE :
87- if (!IS_NUMBER (* (vm -> stackTop - 1 ))) {
88- printf ("Operand must be a number.\n" );
89- return INTERPRET_RUNTIME_ERROR ;
126+ case OP_CONSTANT : {
127+ Value constant = READ_CONSTANT ();
128+ push (vm , constant );
129+ break ;
130+ }
131+ case OP_TRUE : push (vm , BOOL_VAL (true)); break ;
132+ case OP_FALSE : push (vm , BOOL_VAL (false)); break ;
133+ case OP_POP : pop (vm ); break ;
134+ case OP_NULL : push (vm , NULL_VAL ); break ;
135+
136+ case OP_ADD : {
137+ if (IS_STRING (peek (vm , 0 )) && IS_STRING (peek (vm , 1 ))) {
138+ concatenate (vm );
139+ } else if (IS_NUMBER (peek (vm , 0 )) && IS_NUMBER (peek (vm , 1 ))) {
140+ double b = AS_NUMBER (pop (vm ));
141+ double a = AS_NUMBER (pop (vm ));
142+ push (vm , NUMBER_VAL (a + b ));
143+ } else {
144+ runtimeError (vm , "Operands must be two numbers or two strings." );
145+ return INTERPRET_RUNTIME_ERROR ;
146+ }
147+ break ;
148+ }
149+ case OP_SUBTRACT : BINARY_OP (NUMBER_VAL , - ); break ;
150+ case OP_MULTIPLY : BINARY_OP (NUMBER_VAL , * ); break ;
151+ case OP_DIVIDE : BINARY_OP (NUMBER_VAL , /); break ;
152+ case OP_NOT :
153+ push (vm , BOOL_VAL (isFalsey (pop (vm ))));
154+ break ;
155+ case OP_NEGATE :
156+ if (!IS_NUMBER (peek (vm , 0 ))) {
157+ runtimeError (vm , "Operand must be a number." );
158+ return INTERPRET_RUNTIME_ERROR ;
159+ }
160+ push (vm , NUMBER_VAL (- AS_NUMBER (pop (vm ))));
161+ break ;
162+ case OP_PRINT : {
163+ printValue (pop (vm ));
164+ printf ("\n" );
165+ break ;
166+ }
167+ case OP_RETURN : {
168+ return INTERPRET_OK ;
90169 }
91- push (vm , NUMBER_VAL (- AS_NUMBER (pop (vm ))));
92- break ;
93- case OP_PRINT : {
94- printValue (pop (vm ));
95- printf ("\n" );
96- break ;
97- }
98- case OP_RETURN : {
99- return INTERPRET_OK ;
100- }
101170 }
102171 }
103172
@@ -106,8 +175,22 @@ static InterpretResult run(VM *vm) {
106175#undef BINARY_OP
107176}
108177
109- InterpretResult interpret (VM * vm , Chunk * chunk ) {
110- vm -> chunk = chunk ;
178+ // Compile and Run
179+ InterpretResult interpret (VM * vm , const char * source ) {
180+ Chunk chunk ;
181+ initChunk (& chunk );
182+
183+ if (!compile (source , & chunk )) {
184+ freeChunk (& chunk );
185+ return INTERPRET_COMPILE_ERROR ;
186+ }
187+
188+ vm -> chunk = & chunk ;
111189 vm -> ip = vm -> chunk -> code ;
112- return run (vm );
190+
191+ InterpretResult result = run (vm );
192+
193+ freeChunk (& chunk );
194+ return result ;
113195}
196+
0 commit comments