[Chapter22&Locals] - Adding locals
This commit is contained in:
parent
a40570768a
commit
8bce027f37
@ -1,5 +1,7 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -g
|
INC_DIR = ./
|
||||||
|
CFLAGS = -Wall -Wextra -g -I$(INC_DIR)
|
||||||
|
DEPS = common.h
|
||||||
SOURCES = main.c chunk.c memory.c debug.c value.c vm.c compiler.c scanner.c object.c table.c
|
SOURCES = main.c chunk.c memory.c debug.c value.c vm.c compiler.c scanner.c object.c table.c
|
||||||
OBJECTS = $(SOURCES:.c=.o)
|
OBJECTS = $(SOURCES:.c=.o)
|
||||||
EXECUTABLE = clox
|
EXECUTABLE = clox
|
||||||
@ -9,7 +11,7 @@ all: $(EXECUTABLE)
|
|||||||
$(EXECUTABLE): $(OBJECTS)
|
$(EXECUTABLE): $(OBJECTS)
|
||||||
$(CC) $(CFLAGS) -o $@ $(OBJECTS)
|
$(CC) $(CFLAGS) -o $@ $(OBJECTS)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c $(DEPS)
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
@ -10,6 +10,8 @@ typedef enum {
|
|||||||
OP_TRUE,
|
OP_TRUE,
|
||||||
OP_FALSE,
|
OP_FALSE,
|
||||||
OP_POP,
|
OP_POP,
|
||||||
|
OP_GET_LOCAL,
|
||||||
|
OP_SET_LOCAL,
|
||||||
OP_GET_GLOBAL,
|
OP_GET_GLOBAL,
|
||||||
OP_SET_GLOBAL,
|
OP_SET_GLOBAL,
|
||||||
OP_DEFINE_GLOBAL,
|
OP_DEFINE_GLOBAL,
|
||||||
|
BIN
clox/chunk.o
BIN
clox/chunk.o
Binary file not shown.
BIN
clox/clox
BIN
clox/clox
Binary file not shown.
@ -6,5 +6,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define DEBUG_TRACE_EXECUTION
|
#define DEBUG_TRACE_EXECUTION
|
||||||
|
#define UINT8_COUNT (UINT8_MAX + 1)
|
||||||
#define DEBUG_PRINT_CODE
|
#define DEBUG_PRINT_CODE
|
||||||
#endif
|
#endif
|
||||||
|
121
clox/compiler.c
121
clox/compiler.c
@ -1,5 +1,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
@ -38,7 +39,19 @@ typedef struct {
|
|||||||
Precedence precedence;
|
Precedence precedence;
|
||||||
} ParseRule;
|
} ParseRule;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Token name;
|
||||||
|
int depth;
|
||||||
|
} Local;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Local locals[UINT8_COUNT];
|
||||||
|
int localCount;
|
||||||
|
int scopeDepth;
|
||||||
|
} Compiler;
|
||||||
|
|
||||||
Parser parser;
|
Parser parser;
|
||||||
|
Compiler* current = NULL;
|
||||||
Chunk* compilingChunk;
|
Chunk* compilingChunk;
|
||||||
|
|
||||||
static Chunk* currentChunk() {
|
static Chunk* currentChunk() {
|
||||||
@ -127,6 +140,12 @@ static void emitConstant(Value value) {
|
|||||||
emitBytes(OP_CONSTANT, makeConstant(value));
|
emitBytes(OP_CONSTANT, makeConstant(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void initCompiler(Compiler* compiler) {
|
||||||
|
compiler->localCount = 0;
|
||||||
|
compiler->scopeDepth = 0;
|
||||||
|
current = compiler;
|
||||||
|
}
|
||||||
|
|
||||||
static void endCompiler() {
|
static void endCompiler() {
|
||||||
emitReturn();
|
emitReturn();
|
||||||
#ifdef DEBUG_PRINT_CODE
|
#ifdef DEBUG_PRINT_CODE
|
||||||
@ -135,6 +154,20 @@ static void endCompiler() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void beginScope() {
|
||||||
|
current->scopeDepth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void endScope() {
|
||||||
|
current->scopeDepth--;
|
||||||
|
while (current->localCount > 0 &&
|
||||||
|
current->locals[current->localCount - 1].depth >
|
||||||
|
current->scopeDepth) {
|
||||||
|
emitByte(OP_POP);
|
||||||
|
current->localCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
static void expression();
|
static void expression();
|
||||||
static void statement();
|
static void statement();
|
||||||
static void declaration();
|
static void declaration();
|
||||||
@ -144,13 +177,69 @@ static void parsePrecedence(Precedence precedence);
|
|||||||
static uint8_t identifierConstant(Token* name) {
|
static uint8_t identifierConstant(Token* name) {
|
||||||
return makeConstant(OBJ_VAL(copyString(name->start,name->length)));
|
return makeConstant(OBJ_VAL(copyString(name->start,name->length)));
|
||||||
}
|
}
|
||||||
|
static bool identifiersEqual(Token* a, Token* b) {
|
||||||
|
if (a->length != b->length) return false;
|
||||||
|
return memcmp(a->start, b->start, a->length) == 0;
|
||||||
|
}
|
||||||
|
static int resolveLocal(Compiler* compiler, Token* name) {
|
||||||
|
for (int i = compiler->localCount - 1; i >= 0; i--) {
|
||||||
|
Local* local = &compiler->locals[i];
|
||||||
|
if (identifiersEqual(name, &local->name)) {
|
||||||
|
if (local->depth == -1) {
|
||||||
|
error("Can't read local variable in its own initializer.");
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
static void addLocal(Token name) {
|
||||||
|
if (current->localCount == UINT8_COUNT) {
|
||||||
|
error("Too many local variables in function.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Local* local = ¤t->locals[current->localCount++];
|
||||||
|
local->name = name;
|
||||||
|
local->depth = -1;
|
||||||
|
// local->depth = current->scopeDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void declareVariable() {
|
||||||
|
if (current->scopeDepth == 0) return;
|
||||||
|
|
||||||
|
Token* name = &parser.previous;
|
||||||
|
for (int i = current->localCount - 1; i >= 0; i--) {
|
||||||
|
Local* local = ¤t->locals[i];
|
||||||
|
if (local->depth != -1 && local->depth < current->scopeDepth) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifiersEqual(name, &local->name)) {
|
||||||
|
error("Already a variable with this name in this scope.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addLocal(*name);
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t parseVariable(const char* errorMessage) {
|
static uint8_t parseVariable(const char* errorMessage) {
|
||||||
consume(TOKEN_IDENTIFIER, errorMessage);
|
consume(TOKEN_IDENTIFIER, errorMessage);
|
||||||
|
|
||||||
|
declareVariable();
|
||||||
|
if (current->scopeDepth > 0) return 0;
|
||||||
|
|
||||||
return identifierConstant(&parser.previous);
|
return identifierConstant(&parser.previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void markInitialized() {
|
||||||
|
current->locals[current->localCount - 1].depth = current->scopeDepth;
|
||||||
|
}
|
||||||
|
|
||||||
static void defineVariable(uint8_t global) {
|
static void defineVariable(uint8_t global) {
|
||||||
|
if (current->scopeDepth > 0) {
|
||||||
|
markInitialized();
|
||||||
|
return;
|
||||||
|
}
|
||||||
emitBytes(OP_DEFINE_GLOBAL, global);
|
emitBytes(OP_DEFINE_GLOBAL, global);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,6 +277,13 @@ static void expression() {
|
|||||||
// What goes here?
|
// What goes here?
|
||||||
parsePrecedence(PREC_ASSIGNMENT);
|
parsePrecedence(PREC_ASSIGNMENT);
|
||||||
}
|
}
|
||||||
|
static void block() {
|
||||||
|
while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
|
||||||
|
declaration();
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TOKEN_RIGHT_BRACE, "Expect '}' after block.");
|
||||||
|
}
|
||||||
|
|
||||||
static void varDeclaration() {
|
static void varDeclaration() {
|
||||||
uint8_t global = parseVariable("Expect variable name.");
|
uint8_t global = parseVariable("Expect variable name.");
|
||||||
@ -253,6 +349,11 @@ static void statement() {
|
|||||||
if (match(TOKEN_PRINT)) {
|
if (match(TOKEN_PRINT)) {
|
||||||
printStatement();
|
printStatement();
|
||||||
}
|
}
|
||||||
|
else if (match(TOKEN_LEFT_BRACE)) {
|
||||||
|
beginScope();
|
||||||
|
block();
|
||||||
|
endScope();
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
expressionStatement();
|
expressionStatement();
|
||||||
}
|
}
|
||||||
@ -274,13 +375,23 @@ static void string(bool canAssign) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void namedVariable(Token name, bool canAssign) {
|
static void namedVariable(Token name, bool canAssign) {
|
||||||
uint8_t arg = identifierConstant(&name);
|
// uint8_t arg = identifierConstant(&name);
|
||||||
|
uint8_t getOp, setOp;
|
||||||
|
int arg = resolveLocal(current, &name);
|
||||||
|
if (arg != -1) {
|
||||||
|
getOp = OP_GET_LOCAL;
|
||||||
|
setOp = OP_SET_LOCAL;
|
||||||
|
} else {
|
||||||
|
arg = identifierConstant(&name);
|
||||||
|
getOp = OP_GET_GLOBAL;
|
||||||
|
setOp = OP_SET_GLOBAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (canAssign && match(TOKEN_EQUAL)) {
|
if (canAssign && match(TOKEN_EQUAL)) {
|
||||||
expression();
|
expression();
|
||||||
emitBytes(OP_SET_GLOBAL, arg);
|
emitBytes(setOp, (uint8_t)arg);
|
||||||
} else {
|
} else {
|
||||||
emitBytes(OP_GET_GLOBAL, arg);
|
emitBytes(getOp, (uint8_t)arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,6 +484,8 @@ static ParseRule* getRule(TokenType type) {
|
|||||||
|
|
||||||
bool compile(const char* source, Chunk* chunk) {
|
bool compile(const char* source, Chunk* chunk) {
|
||||||
initScanner(source);
|
initScanner(source);
|
||||||
|
Compiler compiler;
|
||||||
|
initCompiler(&compiler);
|
||||||
compilingChunk = chunk;
|
compilingChunk = chunk;
|
||||||
parser.hadError = false;
|
parser.hadError = false;
|
||||||
parser.panicMode = false;
|
parser.panicMode = false;
|
||||||
@ -380,8 +493,6 @@ bool compile(const char* source, Chunk* chunk) {
|
|||||||
while (!match(TOKEN_EOF)) {
|
while (!match(TOKEN_EOF)) {
|
||||||
declaration();
|
declaration();
|
||||||
}
|
}
|
||||||
// expression();
|
|
||||||
// consume(TOKEN_EOF, "Expect end of expression.");
|
|
||||||
endCompiler();
|
endCompiler();
|
||||||
return !parser.hadError;
|
return !parser.hadError;
|
||||||
}
|
}
|
||||||
|
BIN
clox/compiler.o
BIN
clox/compiler.o
Binary file not shown.
11
clox/debug.c
11
clox/debug.c
@ -22,6 +22,13 @@ static int simpleInstruction(const char* name, int offset) {
|
|||||||
printf("%s\n", name);
|
printf("%s\n", name);
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
}
|
}
|
||||||
|
static int byteInstruction(const char* name, Chunk* chunk,
|
||||||
|
int offset) {
|
||||||
|
uint8_t slot = chunk->code[offset + 1];
|
||||||
|
printf("%-16s %4d\n", name, slot);
|
||||||
|
return offset + 2;
|
||||||
|
}
|
||||||
|
|
||||||
int disassembleInstruction(Chunk* chunk, int offset) {
|
int disassembleInstruction(Chunk* chunk, int offset) {
|
||||||
printf("%04d ", offset);
|
printf("%04d ", offset);
|
||||||
|
|
||||||
@ -44,6 +51,10 @@ int disassembleInstruction(Chunk* chunk, int offset) {
|
|||||||
return simpleInstruction("OP_FALSE", offset);
|
return simpleInstruction("OP_FALSE", offset);
|
||||||
case OP_POP:
|
case OP_POP:
|
||||||
return simpleInstruction("OP_POP", offset);
|
return simpleInstruction("OP_POP", offset);
|
||||||
|
case OP_GET_LOCAL:
|
||||||
|
return byteInstruction("OP_GET_LOCAL", chunk, offset);
|
||||||
|
case OP_SET_LOCAL:
|
||||||
|
return byteInstruction("OP_SET_LOCAL", chunk, offset);
|
||||||
case OP_GET_GLOBAL:
|
case OP_GET_GLOBAL:
|
||||||
return constantInstruction("OP_GET_GLOBAL", chunk, offset);
|
return constantInstruction("OP_GET_GLOBAL", chunk, offset);
|
||||||
case OP_DEFINE_GLOBAL:
|
case OP_DEFINE_GLOBAL:
|
||||||
|
BIN
clox/debug.o
BIN
clox/debug.o
Binary file not shown.
BIN
clox/main.o
BIN
clox/main.o
Binary file not shown.
BIN
clox/scanner.o
BIN
clox/scanner.o
Binary file not shown.
4
clox/tests/chapter22/case2.lox
Normal file
4
clox/tests/chapter22/case2.lox
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
var a = "first";
|
||||||
|
var a = "second";
|
||||||
|
}
|
6
clox/tests/chapter22/case3.lox
Normal file
6
clox/tests/chapter22/case3.lox
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
var a = "outer";
|
||||||
|
{
|
||||||
|
var a = "inner";
|
||||||
|
}
|
||||||
|
}
|
6
clox/tests/chapter22/case4.lox
Normal file
6
clox/tests/chapter22/case4.lox
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
var a = "outer";
|
||||||
|
{
|
||||||
|
var a = a;
|
||||||
|
}
|
||||||
|
}
|
BIN
clox/value.o
BIN
clox/value.o
Binary file not shown.
10
clox/vm.c
10
clox/vm.c
@ -145,6 +145,16 @@ static InterpretResult run() {
|
|||||||
push(BOOL_VAL(isFalsey(pop())));
|
push(BOOL_VAL(isFalsey(pop())));
|
||||||
break;
|
break;
|
||||||
case OP_POP: pop(); break;
|
case OP_POP: pop(); 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_GET_GLOBAL: {
|
case OP_GET_GLOBAL: {
|
||||||
ObjString* name = READ_STRING();
|
ObjString* name = READ_STRING();
|
||||||
Value value;
|
Value value;
|
||||||
|
BIN
clox/vm.o
BIN
clox/vm.o
Binary file not shown.
Loading…
Reference in New Issue
Block a user