Skip to content

Commit ed208d1

Browse files
committed
feat: introduce upvalues to catch local variables used inside closures to be used when the enclosing function goes from stack
1 parent e7b0a37 commit ed208d1

6 files changed

Lines changed: 87 additions & 3 deletions

File tree

src/chunk.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ typedef enum
1616
OP_GET_GLOBAL,
1717
OP_DEFINE_GLOBAL,
1818
OP_SET_GLOBAL,
19+
OP_GET_UPVALUE,
20+
OP_SET_UPVALUE,
1921
OP_EQUAL,
2022
OP_GREATER,
2123
OP_LESS,

src/compiler.c

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ typedef struct
5555
bool is_immutable;
5656
} Local;
5757

58+
typedef struct
59+
{
60+
u8 index;
61+
bool islocal;
62+
} Upvalue;
63+
5864
typedef enum
5965
{
6066
TYPE_FUNCTION,
@@ -67,9 +73,10 @@ typedef struct Compiler
6773
ObjFunction* function;
6874
FunctionType type;
6975

70-
Local locals[UINT8_COUNT];
71-
int local_count;
72-
int scope_depth;
76+
Local locals[UINT8_COUNT];
77+
int local_count;
78+
Upvalue upvalues[UINT8_COUNT];
79+
int scope_depth;
7380
} Compiler;
7481

7582
static bool immutable_globals[UINT8_MAX];
@@ -303,6 +310,43 @@ static int resolve_local(Compiler* compiler, Token* name)
303310
return -1;
304311
}
305312

313+
static int add_upvalue(Compiler* compiler, u8 index, bool islocal)
314+
{
315+
int upvalue_count = compiler->function->upvalue_count;
316+
for (int i = 0; i < upvalue_count; i++)
317+
{
318+
Upvalue* upvalue = &compiler->upvalues[i];
319+
if (upvalue->index == index && upvalue->islocal == islocal)
320+
return i;
321+
}
322+
323+
if (upvalue_count == UINT8_COUNT)
324+
{
325+
error("Too many closure variables in function");
326+
return 0;
327+
}
328+
329+
compiler->upvalues[upvalue_count].islocal = islocal;
330+
compiler->upvalues[upvalue_count].index = index;
331+
return compiler->function->upvalue_count++;
332+
}
333+
334+
static int resolve_upvalue(Compiler* compiler, Token* name)
335+
{
336+
if (compiler->enclosing == NULL)
337+
return -1;
338+
339+
int local = resolve_local(compiler->enclosing, name);
340+
if (local != -1)
341+
return add_upvalue(compiler, (u8)local, true);
342+
343+
int upvalue = resolve_upvalue(compiler->enclosing, name);
344+
if (upvalue != -1)
345+
return add_upvalue(compiler, (u8)upvalue, false);
346+
347+
return -1;
348+
}
349+
306350
static void add_local(Token name, bool is_immutable)
307351
{
308352
if (current->local_count == UINT8_COUNT)
@@ -503,6 +547,11 @@ static void named_variable(Token name, bool can_assign)
503547
get_op = OP_GET_LOCAL;
504548
set_op = OP_SET_LOCAL;
505549
}
550+
else if ((arg = resolve_upvalue(current, &name)) != -1)
551+
{
552+
get_op = OP_GET_UPVALUE;
553+
set_op = OP_SET_UPVALUE;
554+
}
506555
else
507556
{
508557
arg = identifier_constant(&name);
@@ -680,6 +729,12 @@ static void function(FunctionType type)
680729

681730
ObjFunction* function = end_compiler();
682731
emit_bytes(OP_CLOSURE, make_constant(OBJ_VAL(function)));
732+
733+
for (int i = 0; i < function->upvalue_count; i++)
734+
{
735+
emit_byte(compiler.upvalues[i].islocal ? 1 : 1);
736+
emit_byte(compiler.upvalues[i].index);
737+
}
683738
}
684739

685740
static void fun_declaration()

src/debug.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "chunk.h"
44
#include "debug.h"
5+
#include "object.h"
56
#include "value.h"
67

78
static int simple_instruction(const char* name, int offset);
@@ -52,6 +53,10 @@ int disassemble_instruction(Chunk* chunk, int offset)
5253
return constant_instruction("OP_DEFINE_GLOBAL", chunk, offset);
5354
case OP_SET_GLOBAL:
5455
return constant_instruction("OP_SET_GLOBAL", chunk, offset);
56+
case OP_GET_UPVALUE:
57+
return byte_instruction("OP_GET_UPVALUE", chunk, offset);
58+
case OP_SET_UPVALUE:
59+
return byte_instruction("OP_SET_UPVALUE", chunk, offset);
5560
case OP_EQUAL:
5661
return simple_instruction("OP_EQUAL", offset);
5762
case OP_GREATER:
@@ -87,6 +92,15 @@ int disassemble_instruction(Chunk* chunk, int offset)
8792
print_value(chunk->constants.values[constant]);
8893
printf("\n");
8994

95+
ObjFunction* function = AS_FUNCTION(chunk->constants.values[constant]);
96+
for (int j = 0; j < function->upvalue_count; j++)
97+
{
98+
int islocal = chunk->code[offset++];
99+
int index = chunk->code[offset++];
100+
printf("%04 | %s %d\n", offset - 2,
101+
islocal ? "local" : "upvalue", index);
102+
}
103+
90104
return offset;
91105
}
92106
case OP_RETURN:

src/memory.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ static void free_object(Obj* object)
4848
FREE(ObjString, object);
4949
break;
5050
}
51+
case OBJ_UPVALUE:
52+
{
53+
FREE(ObjUpvalue, object);
54+
break;
55+
}
5156
}
5257
}
5358
void free_objects()

src/object.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ ObjFunction* new_function()
3535
ObjFunction* function = ALLOCATE_OBJ(ObjFunction, OBJ_FUNCTION);
3636

3737
function->arity = 0;
38+
function->upvalue_count = 0;
3839
function->name = NULL;
3940
init_chunk(&function->chunk);
4041
return function;
@@ -136,5 +137,10 @@ void print_object(Value value)
136137
printf("%s", AS_CSTRING(value));
137138
break;
138139
}
140+
case OBJ_UPVALUE:
141+
{
142+
printf("upvalue");
143+
break;
144+
}
139145
}
140146
}

src/object.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ typedef enum
2626
OBJ_FUNCTION,
2727
OBJ_NATIVE,
2828
OBJ_STRING,
29+
OBJ_UPVALUE,
2930
} ObjType;
3031

3132
struct Obj
@@ -38,6 +39,7 @@ typedef struct
3839
{
3940
Obj obj;
4041
int arity;
42+
int upvalue_count;
4143
Chunk chunk;
4244
ObjString* name;
4345
} ObjFunction;

0 commit comments

Comments
 (0)