|
|
@@ -43,17 +43,33 @@ typedef struct {
|
|
|
Precedence precedence;
|
|
|
} ParseRule;
|
|
|
|
|
|
+typedef struct {
|
|
|
+ Token name;
|
|
|
+ int depth;
|
|
|
+} Local;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ Local locals[UINT8_COUNT];
|
|
|
+ int localCount;/// how many locals are in scope
|
|
|
+ int scopeDepth;
|
|
|
+} Compiler;
|
|
|
+
|
|
|
Parser parser;
|
|
|
+Compiler *current = NULL;
|
|
|
Chunk *compilingChunk;
|
|
|
|
|
|
static void parsePrecedence(Precedence);
|
|
|
static uint8_t parseVariable(const char *);
|
|
|
static void defineVariable(uint8_t);
|
|
|
static uint8_t identifierConstant(Token *name);
|
|
|
+static bool identifiersEqual(Token *a, Token *b);
|
|
|
static ParseRule *getRule(TokenType);
|
|
|
static void statement();
|
|
|
static void declaration();
|
|
|
|
|
|
+static void beginScope();
|
|
|
+static void endScope();
|
|
|
+
|
|
|
static Chunk *currentChunk() {
|
|
|
return compilingChunk;
|
|
|
}
|
|
|
@@ -131,6 +147,7 @@ static void expression() {
|
|
|
static void varDeclaration() {
|
|
|
uint8_t global = parseVariable("Expect variable name.");
|
|
|
|
|
|
+ // 变量初始化
|
|
|
if (match(TOKEN_EQUAL)) {
|
|
|
expression();
|
|
|
} else {
|
|
|
@@ -184,6 +201,13 @@ static void declaration() {
|
|
|
|
|
|
if (parser.panicMode) synchronize();
|
|
|
}
|
|
|
+static void block() {
|
|
|
+ while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
|
|
|
+ declaration();
|
|
|
+ }
|
|
|
+
|
|
|
+ consume(TOKEN_RIGHT_BRACE, "Expect '}' after block.");
|
|
|
+}
|
|
|
///statement → exprStmt
|
|
|
/// | forStmt
|
|
|
/// | ifStmt
|
|
|
@@ -194,13 +218,35 @@ static void declaration() {
|
|
|
static void statement() {
|
|
|
if (match(TOKEN_PRINT)) {
|
|
|
printStatement();
|
|
|
+ } else if (match(TOKEN_LEFT_BRACE)) {
|
|
|
+ ///! block → "{" declaration* "}" ;
|
|
|
+ beginScope();
|
|
|
+ block();
|
|
|
+ endScope();
|
|
|
} else {
|
|
|
expressionStatement();
|
|
|
}
|
|
|
}
|
|
|
+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 emitConstant(Value value) {
|
|
|
emitBytes(OP_CONSTANT, makeConstant(value));
|
|
|
}
|
|
|
+static void initCompiler(Compiler *compiler) {
|
|
|
+ compiler->localCount = 0;
|
|
|
+ compiler->scopeDepth = 0;
|
|
|
+ current = compiler;
|
|
|
+}
|
|
|
static void endCompiler() {
|
|
|
emitReturn();
|
|
|
#ifdef DEBUG_PRINT_CODE
|
|
|
@@ -223,15 +269,36 @@ static void string(__attribute__((unused)) bool canAssign) {
|
|
|
emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
|
|
|
parser.previous.length - 2)));
|
|
|
}
|
|
|
+static int resolveLocal(Compiler *compile, Token *name) {
|
|
|
+ for (int i = compile->localCount - 1; i >= 0; i--) {
|
|
|
+ Local *local = &compile->locals[i];
|
|
|
+ if (identifiersEqual(name, &local->name)) {
|
|
|
+ if (local->depth == -1) {
|
|
|
+ error("Can't read local variable in ints own initializer.");
|
|
|
+ }
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
static void namedVariable(Token name, bool canAssign) {
|
|
|
- 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)) {
|
|
|
// 如 menu.brunch(sunday).beverage = "mimosa";
|
|
|
expression();
|
|
|
- emitBytes(OP_SET_GLOBAL, arg);
|
|
|
+ emitBytes(setOp, (uint8_t) arg);
|
|
|
} else {
|
|
|
- emitBytes(OP_GET_GLOBAL, arg);
|
|
|
+ emitBytes(getOp, (uint8_t) arg);
|
|
|
}
|
|
|
}
|
|
|
static void variable(bool canAssign) {
|
|
|
@@ -378,11 +445,60 @@ static void parsePrecedence(Precedence precedence) {
|
|
|
static uint8_t identifierConstant(Token *name) {
|
|
|
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 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;// 未初始化
|
|
|
+}
|
|
|
+static void declareVariable() {
|
|
|
+ if (current->scopeDepth == 0) return;
|
|
|
+
|
|
|
+ Token *name = &parser.previous;
|
|
|
+
|
|
|
+ // we detect the error like:
|
|
|
+ // {
|
|
|
+ // var a = "first";
|
|
|
+ // var a = "second";
|
|
|
+ // }
|
|
|
+ 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 the name in this scope.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ addLocal(*name);
|
|
|
+}
|
|
|
static uint8_t parseVariable(const char *errorMessage) {
|
|
|
consume(TOKEN_IDENTIFIER, errorMessage);
|
|
|
+
|
|
|
+ declareVariable();// 处理本地变量
|
|
|
+ if (current->scopeDepth > 0) return 0;
|
|
|
+
|
|
|
return identifierConstant(&parser.previous);
|
|
|
}
|
|
|
static void defineVariable(uint8_t global) {
|
|
|
+
|
|
|
+ if (current->scopeDepth > 0) {
|
|
|
+ // markInitialized 未初始化时值 为 -1
|
|
|
+ current->locals[current->localCount - 1].depth = current->scopeDepth;
|
|
|
+ // 本地变量,直接退出
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
emitBytes(OP_DEFINE_GLOBAL, global);
|
|
|
}
|
|
|
static ParseRule *getRule(TokenType type) {
|
|
|
@@ -398,6 +514,7 @@ static ParseRule *getRule(TokenType type) {
|
|
|
| returnStmt
|
|
|
| whileStmt
|
|
|
| block ;
|
|
|
+ block → "{" declaration* "}" ;
|
|
|
|
|
|
declaration → classDecl
|
|
|
| funDecl
|
|
|
@@ -406,7 +523,9 @@ static ParseRule *getRule(TokenType type) {
|
|
|
******************************************************************************
|
|
|
*/
|
|
|
bool compile(const char *source, Chunk *chunk) {
|
|
|
+ Compiler compiler;
|
|
|
initScanner(source);
|
|
|
+ initCompiler(&compiler);
|
|
|
compilingChunk = chunk;
|
|
|
|
|
|
parser.hadError = false;
|