[Chapter21&Globals] - Adding global variables
This commit is contained in:
parent
eafabfcb00
commit
a40570768a
@ -1,6 +1,6 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -g
|
||||
SOURCES = main.c chunk.c memory.c debug.c value.c vm.c compiler.c scanner.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)
|
||||
EXECUTABLE = clox
|
||||
|
||||
|
@ -9,11 +9,16 @@ typedef enum {
|
||||
OP_NIL,
|
||||
OP_TRUE,
|
||||
OP_FALSE,
|
||||
OP_POP,
|
||||
OP_GET_GLOBAL,
|
||||
OP_SET_GLOBAL,
|
||||
OP_DEFINE_GLOBAL,
|
||||
OP_EQUAL,
|
||||
OP_GREATER,
|
||||
OP_LESS,
|
||||
OP_RETURN,
|
||||
OP_LESS,
|
||||
OP_NEGATE,
|
||||
OP_PRINT,
|
||||
OP_RETURN,
|
||||
OP_ADD,
|
||||
OP_SUBTRACT,
|
||||
OP_MULTIPLY,
|
||||
|
BIN
clox/clox
BIN
clox/clox
Binary file not shown.
141
clox/compiler.c
141
clox/compiler.c
@ -30,7 +30,7 @@ typedef enum {
|
||||
PREC_PRIMARY
|
||||
} Precedence;
|
||||
|
||||
typedef void (*ParseFn)();
|
||||
typedef void (*ParseFn)(bool canAssign);
|
||||
|
||||
typedef struct {
|
||||
ParseFn prefix;
|
||||
@ -90,6 +90,16 @@ static void consume(TokenType type, const char* message) {
|
||||
errorAtCurrent(message);
|
||||
}
|
||||
|
||||
static bool check(TokenType type) {
|
||||
return parser.current.type == type;
|
||||
}
|
||||
|
||||
static bool match(TokenType type) {
|
||||
if (!check(type)) return false;
|
||||
advance();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void emitByte(uint8_t byte) {
|
||||
writeChunk(currentChunk(), byte, parser.previous.line);
|
||||
}
|
||||
@ -126,10 +136,25 @@ static void endCompiler() {
|
||||
#endif
|
||||
}
|
||||
static void expression();
|
||||
static void statement();
|
||||
static void declaration();
|
||||
static ParseRule* getRule(TokenType type);
|
||||
static void parsePrecedence(Precedence precedence);
|
||||
|
||||
static void binary() {
|
||||
static uint8_t identifierConstant(Token* name) {
|
||||
return makeConstant(OBJ_VAL(copyString(name->start,name->length)));
|
||||
}
|
||||
|
||||
static uint8_t parseVariable(const char* errorMessage) {
|
||||
consume(TOKEN_IDENTIFIER, errorMessage);
|
||||
return identifierConstant(&parser.previous);
|
||||
}
|
||||
|
||||
static void defineVariable(uint8_t global) {
|
||||
emitBytes(OP_DEFINE_GLOBAL, global);
|
||||
}
|
||||
|
||||
static void binary(bool canAssign) {
|
||||
TokenType operatorType = parser.previous.type;
|
||||
ParseRule* rule = getRule(operatorType);
|
||||
parsePrecedence((Precedence)(rule->precedence + 1));
|
||||
@ -150,7 +175,7 @@ static void binary() {
|
||||
}
|
||||
}
|
||||
|
||||
static void literal() {
|
||||
static void literal(bool canAssign) {
|
||||
switch (parser.previous.type) {
|
||||
case TOKEN_FALSE: emitByte(OP_FALSE); break;
|
||||
case TOKEN_NIL: emitByte(OP_NIL); break;
|
||||
@ -163,22 +188,107 @@ static void expression() {
|
||||
// What goes here?
|
||||
parsePrecedence(PREC_ASSIGNMENT);
|
||||
}
|
||||
static void grouping() {
|
||||
|
||||
static void varDeclaration() {
|
||||
uint8_t global = parseVariable("Expect variable name.");
|
||||
|
||||
if (match(TOKEN_EQUAL)) {
|
||||
expression();
|
||||
} else {
|
||||
emitByte(OP_NIL);
|
||||
}
|
||||
consume(TOKEN_SEMICOLON,
|
||||
"Expect ';' after variable declaration.");
|
||||
|
||||
defineVariable(global);
|
||||
}
|
||||
|
||||
static void expressionStatement() {
|
||||
expression();
|
||||
consume(TOKEN_SEMICOLON, "Expect ';' after expression.");
|
||||
emitByte(OP_POP);
|
||||
}
|
||||
|
||||
static void printStatement() {
|
||||
expression();
|
||||
consume(TOKEN_SEMICOLON, "Expect ';' after value.");
|
||||
emitByte(OP_PRINT);
|
||||
}
|
||||
|
||||
static void synchronize() {
|
||||
parser.panicMode = false;
|
||||
|
||||
while (parser.current.type != TOKEN_EOF) {
|
||||
if (parser.previous.type == TOKEN_SEMICOLON) return;
|
||||
switch (parser.current.type) {
|
||||
case TOKEN_CLASS:
|
||||
case TOKEN_FUN:
|
||||
case TOKEN_VAR:
|
||||
case TOKEN_FOR:
|
||||
case TOKEN_IF:
|
||||
case TOKEN_WHILE:
|
||||
case TOKEN_PRINT:
|
||||
case TOKEN_RETURN:
|
||||
return;
|
||||
|
||||
default:
|
||||
; // Do nothing.
|
||||
}
|
||||
|
||||
advance();
|
||||
}
|
||||
}
|
||||
|
||||
static void declaration() {
|
||||
if (match(TOKEN_VAR)) {
|
||||
varDeclaration();
|
||||
} else {
|
||||
statement();
|
||||
}
|
||||
|
||||
if (parser.panicMode) synchronize();
|
||||
}
|
||||
|
||||
static void statement() {
|
||||
if (match(TOKEN_PRINT)) {
|
||||
printStatement();
|
||||
}
|
||||
else {
|
||||
expressionStatement();
|
||||
}
|
||||
}
|
||||
|
||||
static void grouping(bool canAssign) {
|
||||
expression();
|
||||
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
|
||||
}
|
||||
|
||||
static void number() {
|
||||
static void number(bool canAssign) {
|
||||
double value = strtod(parser.previous.start, NULL);
|
||||
emitConstant(NUMBER_VAL(value));
|
||||
}
|
||||
|
||||
static void string() {
|
||||
static void string(bool canAssign) {
|
||||
emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
|
||||
parser.previous.length - 2)));
|
||||
}
|
||||
|
||||
static void unary() {
|
||||
static void namedVariable(Token name, bool canAssign) {
|
||||
uint8_t arg = identifierConstant(&name);
|
||||
|
||||
if (canAssign && match(TOKEN_EQUAL)) {
|
||||
expression();
|
||||
emitBytes(OP_SET_GLOBAL, arg);
|
||||
} else {
|
||||
emitBytes(OP_GET_GLOBAL, arg);
|
||||
}
|
||||
}
|
||||
|
||||
static void variable(bool canAssign) {
|
||||
namedVariable(parser.previous, canAssign);
|
||||
}
|
||||
|
||||
static void unary(bool canAssign) {
|
||||
TokenType operatorType = parser.previous.type;
|
||||
|
||||
// Compile the operand.
|
||||
@ -201,12 +311,16 @@ static void parsePrecedence(Precedence precedence) {
|
||||
return;
|
||||
}
|
||||
|
||||
prefixRule();
|
||||
bool canAssign = precedence <= PREC_ASSIGNMENT;
|
||||
prefixRule(canAssign);
|
||||
|
||||
while (precedence <= getRule(parser.current.type)->precedence) {
|
||||
advance();
|
||||
ParseFn infixRule = getRule(parser.previous.type)->infix;
|
||||
infixRule();
|
||||
infixRule(canAssign);
|
||||
}
|
||||
if (canAssign && match(TOKEN_EQUAL)) {
|
||||
error("Invalid assignment target.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +344,7 @@ ParseRule rules[] = {
|
||||
[TOKEN_GREATER_EQUAL] = {NULL, binary, PREC_COMPARISON},
|
||||
[TOKEN_LESS] = {NULL, binary, PREC_COMPARISON},
|
||||
[TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON},
|
||||
[TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE},
|
||||
[TOKEN_STRING] = {string, NULL, PREC_NONE},
|
||||
[TOKEN_NUMBER] = {number, NULL, PREC_NONE},
|
||||
[TOKEN_AND] = {NULL, NULL, PREC_NONE},
|
||||
@ -263,8 +377,11 @@ bool compile(const char* source, Chunk* chunk) {
|
||||
parser.hadError = false;
|
||||
parser.panicMode = false;
|
||||
advance();
|
||||
expression();
|
||||
consume(TOKEN_EOF, "Expect end of expression.");
|
||||
while (!match(TOKEN_EOF)) {
|
||||
declaration();
|
||||
}
|
||||
// expression();
|
||||
// consume(TOKEN_EOF, "Expect end of expression.");
|
||||
endCompiler();
|
||||
return !parser.hadError;
|
||||
}
|
||||
|
BIN
clox/compiler.o
BIN
clox/compiler.o
Binary file not shown.
10
clox/debug.c
10
clox/debug.c
@ -42,6 +42,14 @@ int disassembleInstruction(Chunk* chunk, int offset) {
|
||||
return simpleInstruction("OP_TRUE", offset);
|
||||
case OP_FALSE:
|
||||
return simpleInstruction("OP_FALSE", offset);
|
||||
case OP_POP:
|
||||
return simpleInstruction("OP_POP", offset);
|
||||
case OP_GET_GLOBAL:
|
||||
return constantInstruction("OP_GET_GLOBAL", chunk, offset);
|
||||
case OP_DEFINE_GLOBAL:
|
||||
return constantInstruction("OP_DEFINE_GLOBAL", chunk,offset);
|
||||
case OP_SET_GLOBAL:
|
||||
return constantInstruction("OP_SET_GLOBAL", chunk, offset);
|
||||
case OP_EQUAL:
|
||||
return simpleInstruction("OP_EQUAL", offset);
|
||||
case OP_GREATER:
|
||||
@ -60,6 +68,8 @@ int disassembleInstruction(Chunk* chunk, int offset) {
|
||||
return simpleInstruction("OP_NEGATE", offset);
|
||||
case OP_NOT:
|
||||
return simpleInstruction("OP_NOT", offset);
|
||||
case OP_PRINT:
|
||||
return simpleInstruction("OP_PRINT", offset);
|
||||
case OP_RETURN:
|
||||
return simpleInstruction("OP_RETURN", offset);
|
||||
default:
|
||||
|
BIN
clox/debug.o
BIN
clox/debug.o
Binary file not shown.
BIN
clox/memory.o
BIN
clox/memory.o
Binary file not shown.
5
clox/tests/chapter21_globals.lox
Normal file
5
clox/tests/chapter21_globals.lox
Normal file
@ -0,0 +1,5 @@
|
||||
var breakfast = "beignets";
|
||||
var beverage = "cafe au lait";
|
||||
breakfast = "beignets with " + beverage;
|
||||
|
||||
print breakfast;
|
@ -28,6 +28,7 @@ typedef struct {
|
||||
#define IS_NUMBER(value) ((value).type == VAL_NUMBER)
|
||||
#define IS_OBJ(value) ((value).type == VAL_OBJ)
|
||||
|
||||
#define AS_OBJ(value) ((value).as.obj)
|
||||
#define AS_BOOL(value) ((value).as.boolean)
|
||||
#define AS_NUMBER(value) ((value).as.number)
|
||||
#define BOOL_VAL(value) ((Value){VAL_BOOL, {.boolean = value}})
|
||||
|
39
clox/vm.c
39
clox/vm.c
@ -7,6 +7,7 @@
|
||||
#include "object.h"
|
||||
#include "memory.h"
|
||||
#include "vm.h"
|
||||
#include "table.h"
|
||||
VM vm;
|
||||
static void resetStack() {
|
||||
vm.stackTop = vm.stack;
|
||||
@ -28,10 +29,12 @@ static void runtimeError(const char* format, ...) {
|
||||
void initVM() {
|
||||
resetStack();
|
||||
vm.objects = NULL;
|
||||
initTable(&vm.globals);
|
||||
initTable(&vm.strings);
|
||||
}
|
||||
|
||||
void freeVM() {
|
||||
freeTable(&vm.globals);
|
||||
freeTable(&vm.strings);
|
||||
freeObjects();
|
||||
}
|
||||
@ -69,6 +72,7 @@ static void concatenate() {
|
||||
static InterpretResult run() {
|
||||
#define READ_BYTE() (*vm.ip++)
|
||||
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
|
||||
#define READ_STRING() AS_STRING(READ_CONSTANT())
|
||||
#define BINARY_OP(valueType, op) \
|
||||
do { \
|
||||
if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
|
||||
@ -103,6 +107,15 @@ static InterpretResult run() {
|
||||
case OP_NIL: push(NIL_VAL); break;
|
||||
case OP_TRUE: push(BOOL_VAL(true)); break;
|
||||
case OP_FALSE: push(BOOL_VAL(false)); break;
|
||||
case OP_SET_GLOBAL: {
|
||||
ObjString* name = READ_STRING();
|
||||
if (tableSet(&vm.globals, name, peek(0))) {
|
||||
tableDelete(&vm.globals, name);
|
||||
runtimeError("Undefined variable '%s'.", name->chars);
|
||||
return INTERPRET_RUNTIME_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_EQUAL: {
|
||||
Value b = pop();
|
||||
Value a = pop();
|
||||
@ -131,6 +144,23 @@ static InterpretResult run() {
|
||||
case OP_NOT:
|
||||
push(BOOL_VAL(isFalsey(pop())));
|
||||
break;
|
||||
case OP_POP: pop(); break;
|
||||
case OP_GET_GLOBAL: {
|
||||
ObjString* name = READ_STRING();
|
||||
Value value;
|
||||
if (!tableGet(&vm.globals, name, &value)) {
|
||||
runtimeError("Undefined variable '%s'.", name->chars);
|
||||
return INTERPRET_RUNTIME_ERROR;
|
||||
}
|
||||
push(value);
|
||||
break;
|
||||
}
|
||||
case OP_DEFINE_GLOBAL: {
|
||||
ObjString* name = READ_STRING();
|
||||
tableSet(&vm.globals, name, peek(0));
|
||||
pop();
|
||||
break;
|
||||
}
|
||||
case OP_NEGATE:
|
||||
if (!IS_NUMBER(peek(0))) {
|
||||
runtimeError("Operand must be a number.");
|
||||
@ -138,14 +168,21 @@ static InterpretResult run() {
|
||||
}
|
||||
push(NUMBER_VAL(-AS_NUMBER(pop())));
|
||||
break;
|
||||
case OP_RETURN: {
|
||||
case OP_PRINT: {
|
||||
printValue(pop());
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
case OP_RETURN: {
|
||||
// printValue(pop());
|
||||
// printf("\n");
|
||||
return INTERPRET_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#undef READ_CONSTANT
|
||||
#undef READ_STRING
|
||||
#undef BINARY_OP
|
||||
#undef READ_BYTE
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ typedef struct {
|
||||
uint8_t* ip;
|
||||
Value stack[STACK_MAX];
|
||||
Value* stackTop;
|
||||
Table globals;
|
||||
Table strings;
|
||||
Obj* objects;
|
||||
} VM;
|
||||
|
BIN
clox/vm.o
BIN
clox/vm.o
Binary file not shown.
Loading…
Reference in New Issue
Block a user