Skip to content

Commit dc85dc8

Browse files
committed
feat: Complete local variables resolution at compile time
1 parent e70bc2d commit dc85dc8

4 files changed

Lines changed: 126 additions & 12 deletions

File tree

src/chunk.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ typedef enum
1111
OP_TRUE,
1212
OP_FALSE,
1313
OP_POP,
14+
OP_GET_LOCAL,
15+
OP_SET_LOCAL,
1416
OP_GET_GLOBAL,
1517
OP_DEFINE_GLOBAL,
1618
OP_SET_GLOBAL,

src/compiler.c

Lines changed: 100 additions & 7 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 <string.h>
56
#include <sys/types.h>
67

78
#include "chunk.h"
@@ -58,7 +59,7 @@ typedef struct
5859
{
5960
Local locals[UINT8_COUNT];
6061
int local_count;
61-
int local_depth;
62+
int scope_depth;
6263

6364
} Compiler;
6465

@@ -175,7 +176,7 @@ static void emit_constant(Value value)
175176
static void init_compiler(Compiler* compiler)
176177
{
177178
compiler->local_count = 0;
178-
compiler->local_depth = 0;
179+
compiler->scope_depth = 0;
179180
current = compiler;
180181
}
181182

@@ -190,12 +191,20 @@ static void end_compiler()
190191

191192
static void begin_scope()
192193
{
193-
current->local_depth++;
194+
current->scope_depth++;
194195
}
195196

196197
static void end_scope()
197198
{
198-
current->local_depth--;
199+
current->scope_depth--;
200+
201+
while (current->local_count > 0 &&
202+
current->locals[current->local_count - 1].depth >
203+
current->scope_depth)
204+
{
205+
emit_byte(OP_POP);
206+
current->local_count--;
207+
}
199208
}
200209

201210
static void expression();
@@ -209,14 +218,84 @@ static u8 identifier_constant(Token* name)
209218
return make_constant(OBJ_VAL(copy_string(name->start, name->length)));
210219
}
211220

221+
static bool identifiers_equal(Token* t1, Token* t2)
222+
{
223+
if (t1->length != t2->length)
224+
return false;
225+
226+
return memcmp(t1->start, t2->start, t1->length) == 0;
227+
}
228+
229+
static int resolve_local(Compiler* compiler, Token* name)
230+
{
231+
for (int i = compiler->local_count - 1; i >= 0; i--)
232+
{
233+
Local* local = &compiler->locals[i];
234+
if (identifiers_equal(name, &local->name))
235+
{
236+
if (local->depth == -1)
237+
error("Can't read local variable in its initializer");
238+
239+
return i;
240+
}
241+
}
242+
return -1;
243+
}
244+
245+
static void add_local(Token name)
246+
{
247+
if (current->local_count == UINT8_COUNT)
248+
{
249+
error("Too many variables in function");
250+
return;
251+
}
252+
Local* local = &current->locals[current->local_count++];
253+
local->name = name;
254+
local->depth = -1;
255+
}
256+
257+
static void declare_variable()
258+
{
259+
if (current->scope_depth == 0)
260+
return;
261+
262+
Token* name = &parser.previous;
263+
264+
for (int i = current->local_count - 1; i >= 0; i--)
265+
{
266+
Local* local = &current->locals[i];
267+
if (local->depth != -1 && local->depth < current->scope_depth)
268+
break;
269+
270+
if (identifiers_equal(name, &local->name))
271+
error("Already variable with this name in this scope");
272+
}
273+
add_local(*name);
274+
}
275+
212276
static u8 parse_variable(char* error_message)
213277
{
214278
consume(TOKEN_IDENTIFIER, error_message);
279+
280+
declare_variable();
281+
if (current->scope_depth > 0)
282+
return 0;
283+
215284
return identifier_constant(&parser.previous);
216285
}
217286

287+
static void mark_initialized()
288+
{
289+
current->locals[current->local_count - 1].depth = current->scope_depth;
290+
}
291+
218292
static void define_variable(u8 global)
219293
{
294+
if (current->scope_depth > 0)
295+
{
296+
mark_initialized();
297+
return;
298+
}
220299
emit_bytes(OP_DEFINE_GLOBAL, global);
221300
}
222301

@@ -302,15 +381,29 @@ static void string(bool can_assign)
302381

303382
static void named_variable(Token name, bool can_assign)
304383
{
305-
u8 arg = identifier_constant(&name);
384+
u8 get_op, set_op;
385+
int arg = resolve_local(current, &name);
386+
387+
if (arg != -1)
388+
{
389+
get_op = OP_GET_LOCAL;
390+
set_op = OP_SET_LOCAL;
391+
}
392+
else
393+
{
394+
arg = identifier_constant(&name);
395+
get_op = OP_GET_GLOBAL;
396+
set_op = OP_SET_GLOBAL;
397+
}
398+
306399
if (can_assign && match(TOKEN_EQUAL))
307400
{
308401
expression();
309-
emit_bytes(OP_SET_GLOBAL, arg);
402+
emit_bytes(set_op, (u8)arg);
310403
}
311404
else
312405
{
313-
emit_bytes(OP_GET_GLOBAL, arg);
406+
emit_bytes(get_op, (u8)arg);
314407
}
315408
}
316409

src/debug.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@
44
#include "debug.h"
55
#include "value.h"
66

7-
/**
8-
* NOTE: I'm noob into c, and i don't know if it's best practice to do this or
9-
* not but I like to keep impl details at buttom so i put this signatures here
10-
* to be easy for reader / reviewer to read code fluently
11-
**/
127
static int simple_instruction(const char* name, int offset);
138
static int constant_instruction(const char* name, Chunk* chunk, int offset);
9+
static int byte_instruction(const char* name, Chunk* chunk, int offset);
1410

1511
void disassemble_chunk(Chunk* chunk, const char* name)
1612
{
@@ -44,6 +40,10 @@ int disassemble_instruction(Chunk* chunk, int offset)
4440
return simple_instruction("OP_TRUE", offset);
4541
case OP_POP:
4642
return simple_instruction("OP_POP", offset);
43+
case OP_GET_LOCAL:
44+
return byte_instruction("OP_GET_LOCAL", chunk, offset);
45+
case OP_SET_LOCAL:
46+
return byte_instruction("OP_SET_LOCAL", chunk, offset);
4747
case OP_GET_GLOBAL:
4848
return constant_instruction("OP_GET_GLOBAL", chunk, offset);
4949
case OP_DEFINE_GLOBAL:
@@ -84,6 +84,13 @@ static int simple_instruction(const char* name, int offset)
8484
return offset + 1;
8585
}
8686

87+
static int byte_instruction(const char* name, Chunk* chunk, int offset)
88+
{
89+
uint8_t slot = chunk->code[offset + 1];
90+
printf("%-16s %4d\n", name, slot);
91+
return offset + 2;
92+
}
93+
8794
static int constant_instruction(const char* name, Chunk* chunk, int offset)
8895
{
8996
u8 constantIndex = chunk->code[offset + 1];

src/vm.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,18 @@ static InterpretResult run()
138138
case OP_POP:
139139
pop();
140140
break;
141+
case OP_GET_LOCAL:
142+
{
143+
u8 slot = READ_BYTE();
144+
push(vm.stack[slot]);
145+
break;
146+
}
147+
case OP_SET_LOCAL:
148+
{
149+
u8 slot = READ_BYTE();
150+
vm.stack[slot] = peek(0);
151+
break;
152+
}
141153
case OP_GET_GLOBAL:
142154
{
143155
ObjString* name = READ_STRING();

0 commit comments

Comments
 (0)