|
|
@@ -35,7 +35,7 @@ typedef enum {
|
|
|
PREC_PRIMARY
|
|
|
} Precedence;
|
|
|
|
|
|
-typedef void (*ParseFn)();
|
|
|
+typedef void (*ParseFn)(bool canAssign);
|
|
|
|
|
|
typedef struct {
|
|
|
ParseFn prefix;
|
|
|
@@ -47,7 +47,12 @@ Parser parser;
|
|
|
Chunk *compilingChunk;
|
|
|
|
|
|
static void parsePrecedence(Precedence);
|
|
|
+static uint8_t parseVariable(const char *);
|
|
|
+static void defineVariable(uint8_t);
|
|
|
+static uint8_t identifierConstant(Token *name);
|
|
|
static ParseRule *getRule(TokenType);
|
|
|
+static void statement();
|
|
|
+static void declaration();
|
|
|
|
|
|
static Chunk *currentChunk() {
|
|
|
return compilingChunk;
|
|
|
@@ -93,6 +98,14 @@ 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);
|
|
|
}
|
|
|
@@ -114,6 +127,77 @@ static uint8_t makeConstant(Value value) {
|
|
|
static void expression() {
|
|
|
parsePrecedence(PREC_ASSIGNMENT);
|
|
|
}
|
|
|
+/// for example: var a; var b = 10;
|
|
|
+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();
|
|
|
+ }
|
|
|
+}
|
|
|
+///declaration → classDecl
|
|
|
+/// | funDecl
|
|
|
+/// | varDecl
|
|
|
+/// | statement ;
|
|
|
+static void declaration() {
|
|
|
+ if (match(TOKEN_VAR)) {
|
|
|
+ varDeclaration();
|
|
|
+ } else {
|
|
|
+ statement();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (parser.panicMode) synchronize();
|
|
|
+}
|
|
|
+///statement → exprStmt
|
|
|
+/// | forStmt
|
|
|
+/// | ifStmt
|
|
|
+/// | printStmt
|
|
|
+/// | returnStmt
|
|
|
+/// | whileStmt
|
|
|
+/// | block ;
|
|
|
+static void statement() {
|
|
|
+ if (match(TOKEN_PRINT)) {
|
|
|
+ printStatement();
|
|
|
+ } else {
|
|
|
+ expressionStatement();
|
|
|
+ }
|
|
|
+}
|
|
|
static void emitConstant(Value value) {
|
|
|
emitBytes(OP_CONSTANT, makeConstant(value));
|
|
|
}
|
|
|
@@ -125,22 +209,36 @@ static void endCompiler() {
|
|
|
}
|
|
|
#endif
|
|
|
}
|
|
|
-static void grouping() {
|
|
|
+static void grouping(__attribute__((unused)) bool canAssign) {
|
|
|
expression();
|
|
|
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
|
|
|
}
|
|
|
/// \brief parse number token
|
|
|
/// Number literals: 123
|
|
|
-static void number() {
|
|
|
+static void number(__attribute__((unused)) bool canAssign) {
|
|
|
double value = strtod(parser.previous.start, NULL);
|
|
|
emitConstant(NUMBER_VAL(value));
|
|
|
}
|
|
|
-static void string() {
|
|
|
+static void string(__attribute__((unused)) bool canAssign) {
|
|
|
emitConstant(OBJ_VAL(copyString(parser.previous.start + 1,
|
|
|
parser.previous.length - 2)));
|
|
|
}
|
|
|
+static void namedVariable(Token name, bool canAssign) {
|
|
|
+ uint8_t arg = identifierConstant(&name);
|
|
|
+
|
|
|
+ if (canAssign && match(TOKEN_EQUAL)) {
|
|
|
+ // 如 menu.brunch(sunday).beverage = "mimosa";
|
|
|
+ expression();
|
|
|
+ emitBytes(OP_SET_GLOBAL, arg);
|
|
|
+ } else {
|
|
|
+ emitBytes(OP_GET_GLOBAL, arg);
|
|
|
+ }
|
|
|
+}
|
|
|
+static void variable(bool canAssign) {
|
|
|
+ namedVariable(parser.previous, canAssign);
|
|
|
+}
|
|
|
/// Unary negation: -123
|
|
|
-static void unary() {
|
|
|
+static void unary(__attribute__((unused)) bool canAssign) {
|
|
|
TokenType operatorType = parser.previous.type;
|
|
|
|
|
|
// Compile the operand.
|
|
|
@@ -158,7 +256,7 @@ static void unary() {
|
|
|
}
|
|
|
}
|
|
|
/// \brief infix parser
|
|
|
-static void binary() {
|
|
|
+static void binary(__attribute__((unused)) bool canAssign) {
|
|
|
TokenType operatorType = parser.previous.type;
|
|
|
ParseRule *rule = getRule(operatorType);
|
|
|
parsePrecedence((Precedence) rule->precedence + 1);
|
|
|
@@ -197,7 +295,7 @@ static void binary() {
|
|
|
default: return;// Unreachable.
|
|
|
}
|
|
|
}
|
|
|
-static void literal() {
|
|
|
+static void literal(__attribute__((unused)) bool canAssign) {
|
|
|
switch (parser.previous.type) {
|
|
|
case TOKEN_FALSE:
|
|
|
emitByte(OP_FALSE);
|
|
|
@@ -231,7 +329,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},
|
|
|
@@ -264,17 +362,49 @@ 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.");
|
|
|
}
|
|
|
}
|
|
|
+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 ParseRule *getRule(TokenType type) {
|
|
|
return &rules[type];
|
|
|
}
|
|
|
+
|
|
|
+/**
|
|
|
+ ******************************************************************************
|
|
|
+ * statement → exprStmt
|
|
|
+ | forStmt
|
|
|
+ | ifStmt
|
|
|
+ | printStmt
|
|
|
+ | returnStmt
|
|
|
+ | whileStmt
|
|
|
+ | block ;
|
|
|
+
|
|
|
+ declaration → classDecl
|
|
|
+ | funDecl
|
|
|
+ | varDecl
|
|
|
+ | statement ;
|
|
|
+ ******************************************************************************
|
|
|
+ */
|
|
|
bool compile(const char *source, Chunk *chunk) {
|
|
|
initScanner(source);
|
|
|
compilingChunk = chunk;
|
|
|
@@ -283,8 +413,11 @@ bool compile(const char *source, Chunk *chunk) {
|
|
|
parser.panicMode = false;
|
|
|
|
|
|
advance();
|
|
|
- expression();
|
|
|
- consume(TOKEN_EOF, "Expect end of expression");
|
|
|
+
|
|
|
+ while (!match(TOKEN_EOF)) {
|
|
|
+ declaration();
|
|
|
+ }
|
|
|
+
|
|
|
endCompiler();//! return opcode
|
|
|
return !parser.hadError;
|
|
|
}
|