Skip to content

Commit 4735f04

Browse files
committed
feat: add support for variable (creation, assignment, usage)
1 parent 55cded0 commit 4735f04

5 files changed

Lines changed: 112 additions & 17 deletions

File tree

src/chunk.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ typedef enum
1111
OP_TRUE,
1212
OP_FALSE,
1313
OP_POP,
14+
OP_GET_GLOBAL,
15+
OP_DEFINE_GLOBAL,
16+
OP_SET_GLOBAL,
1417
OP_EQUAL,
1518
OP_GREATER,
1619
OP_LESS,

src/compiler.c

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <stdint.h>
33
#include <stdio.h>
44
#include <stdlib.h>
5+
#include <sys/types.h>
56

67
#include "chunk.h"
78
#include "common.h"
@@ -37,7 +38,7 @@ typedef enum
3738
PREC_PRIMARY
3839
} Precedence;
3940

40-
typedef void (*ParseFn)();
41+
typedef void (*ParseFn)(bool can_assign);
4142

4243
typedef struct
4344
{
@@ -178,7 +179,23 @@ static void declaration();
178179
static ParseRule* get_rule(TokenType type);
179180
static void parse_precedence(Precedence precedence);
180181

181-
static void binary()
182+
static uint8_t identifier_constant(Token* name)
183+
{
184+
return make_constant(OBJ_VAL(copy_string(name->start, name->length)));
185+
}
186+
187+
static uint8_t parse_variable(const char* error_message)
188+
{
189+
consume(TOKEN_IDENTIFIER, error_message);
190+
return identifier_constant(&parser.previous);
191+
}
192+
193+
static void define_variable(uint8_t global)
194+
{
195+
emit_bytes(OP_DEFINE_GLOBAL, global);
196+
}
197+
198+
static void binary(bool can_assign)
182199
{
183200
TokenType operator_type = parser.previous.type;
184201

@@ -222,7 +239,7 @@ static void binary()
222239
}
223240
}
224241

225-
static void literal()
242+
static void literal(bool can_assign)
226243
{
227244
switch (parser.previous.type)
228245
{
@@ -240,25 +257,42 @@ static void literal()
240257
}
241258
}
242259

243-
static void grouping()
260+
static void grouping(bool can_assign)
244261
{
245262
expression();
246263
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression");
247264
}
248265

249-
static void number()
266+
static void number(bool can_assign)
250267
{
251268
double value = strtod(parser.previous.start, NULL);
252269
emit_constant(NUMBER_VAL(value));
253270
}
254271

255-
static void string()
272+
static void string(bool can_assign)
256273
{
257274
emit_constant(OBJ_VAL(
258275
copy_string(parser.previous.start + 1, parser.previous.length - 2)));
259276
}
260277

261-
static void unary()
278+
static void named_variable(Token name, bool can_assign)
279+
{
280+
uint8_t arg = identifier_constant(&name);
281+
if (can_assign && match(TOKEN_EQUAL))
282+
{
283+
expression();
284+
emit_bytes(OP_SET_GLOBAL, arg);
285+
}
286+
else
287+
emit_bytes(OP_GET_GLOBAL, arg);
288+
}
289+
290+
static void variable(bool can_assign)
291+
{
292+
named_variable(parser.previous, can_assign);
293+
}
294+
295+
static void unary(bool can_assign)
262296
{
263297
TokenType operator_type = parser.previous.type;
264298

@@ -297,9 +331,9 @@ ParseRule rules[] = {
297331
[TOKEN_GREATER_EQUAL] = { NULL, binary, PREC_COMPARAISON },
298332
[TOKEN_LESS] = { NULL, binary, PREC_COMPARAISON },
299333
[TOKEN_LESS_EQUAL] = { NULL, binary, PREC_COMPARAISON },
300-
[TOKEN_STRING] = { string, NULL, PREC_NONE },
334+
[TOKEN_STRING] = { string, NULL, PREC_NONE },
301335
[TOKEN_NUMBER] = { number, NULL, PREC_NONE },
302-
[TOKEN_IDENTIFIER] = { NULL, NULL, PREC_NONE },
336+
[TOKEN_IDENTIFIER] = { variable, NULL, PREC_NONE },
303337
[TOKEN_AND] = { NULL, NULL, PREC_NONE },
304338
[TOKEN_CLASS] = { NULL, NULL, PREC_NONE },
305339
[TOKEN_ELSE] = { NULL, NULL, PREC_NONE },
@@ -330,13 +364,19 @@ static void parse_precedence(Precedence precedence)
330364
error("Expect expression");
331365
return;
332366
}
333-
prefix_rule();
367+
bool can_assign = precedence <= PREC_ASSIGNMENT;
368+
prefix_rule(can_assign);
334369

335370
while (precedence <= get_rule(parser.current.type)->precedence)
336371
{
337372
advance();
338373
ParseFn infix_rule = get_rule(parser.previous.type)->infix;
339-
infix_rule();
374+
infix_rule(can_assign);
375+
}
376+
377+
if (can_assign && match(TOKEN_EQUAL))
378+
{
379+
error("Invalid assignment target");
340380
}
341381
}
342382

@@ -352,6 +392,19 @@ static void expression()
352392

353393
// ---------------------- statements --------------------------
354394

395+
static void var_declaration()
396+
{
397+
uint8_t global = parse_variable("Expect variable name");
398+
399+
if (match(TOKEN_EQUAL))
400+
expression();
401+
else
402+
emit_byte(OP_NIL);
403+
404+
consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration");
405+
define_variable(global);
406+
}
407+
355408
static void expression_statement()
356409
{
357410
expression();
@@ -397,7 +450,10 @@ static void synchronize()
397450

398451
static void declaration()
399452
{
400-
statement();
453+
if (match(TOKEN_VAR))
454+
var_declaration();
455+
else
456+
statement();
401457

402458
if (parser.panic_mode)
403459
synchronize();
@@ -406,13 +462,9 @@ static void declaration()
406462
static void statement()
407463
{
408464
if (match(TOKEN_PRINT))
409-
{
410465
print_statement();
411-
}
412466
else
413-
{
414467
expression_statement();
415-
}
416468
}
417469

418470
static inline void init_parser()

src/debug.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ int disassemble_instruction(Chunk* chunk, int offset)
4444
return simple_instruction("OP_TRUE", offset);
4545
case OP_POP:
4646
return simple_instruction("OP_POP", offset);
47+
case OP_GET_GLOBAL:
48+
return constant_instruction("OP_GET_GLOBAL", chunk, offset);
49+
case OP_DEFINE_GLOBAL:
50+
return constant_instruction("OP_DEFINE_GLOBAL", chunk, offset);
51+
case OP_SET_GLOBAL:
52+
return constant_instruction("OP_SET_GLOBAL", chunk, offset);
4753
case OP_EQUAL:
4854
return simple_instruction("OP_EQUAL", offset);
4955
case OP_GREATER:

src/vm.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ void init_VM()
3838
{
3939
reset_stack();
4040
vm.objects = NULL;
41+
init_table(&vm.globals);
4142
init_table(&vm.strings);
4243
}
4344

4445
void free_VM()
4546
{
47+
free_table(&vm.globals);
4648
free_table(&vm.strings);
4749
free_objects();
4850
}
@@ -88,6 +90,7 @@ static InterpretResult run()
8890
{
8991
#define READ_BYTE() (*vm.ip++)
9092
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
93+
#define READ_STRING() AS_STRING(READ_CONSTANT())
9194
#define BINARY_OP(value_type, op) \
9295
do \
9396
{ \
@@ -135,6 +138,35 @@ static InterpretResult run()
135138
case OP_POP:
136139
pop();
137140
break;
141+
case OP_GET_GLOBAL:
142+
{
143+
ObjString* name = READ_STRING();
144+
Value value;
145+
if (!table_get(&vm.globals, name, &value))
146+
{
147+
runtime_error("Undefined Variable %s", name->chars);
148+
return INTERPRET_RUNTIME_ERROR;
149+
}
150+
push(value);
151+
break;
152+
}
153+
case OP_DEFINE_GLOBAL:
154+
{
155+
ObjString* name = READ_STRING();
156+
table_set(&vm.globals, name, peek(0));
157+
pop();
158+
break;
159+
}
160+
case OP_SET_GLOBAL:
161+
{
162+
ObjString* name = READ_STRING();
163+
if (table_set(&vm.globals, name, peek(0)))
164+
{
165+
table_delete(&vm.globals, name);
166+
return INTERPRET_RUNTIME_ERROR;
167+
}
168+
break;
169+
}
138170
case OP_EQUAL:
139171
{
140172
Value v2 = pop();
@@ -200,6 +232,7 @@ static InterpretResult run()
200232

201233
#undef READ_BYTE
202234
#undef READ_CONSTANT
235+
#undef READ_STRING
203236
#undef BINARY_OP
204237
}
205238

src/vm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ typedef struct
1313
uint8_t* ip; // aka of Instrction Pointer
1414
Value stack[STACK_MAX];
1515
Value* stack_top;
16-
Table* strings;
16+
Table globals;
17+
Table* strings; // for string interning just like (string pool in java)
1718
Obj* objects;
1819
} VM;
1920

0 commit comments

Comments
 (0)