Support var

This commit is contained in:
2026-01-19 22:07:25 +08:00
parent 57def6137b
commit 0833a2229b
6 changed files with 93 additions and 55 deletions

View File

@@ -133,12 +133,13 @@ struct ExprStmt : public Stmt {
};
struct VarDecl : public Stmt {
std::string type_name;
std::string type_name; // Can be empty if type inference is needed
std::string name;
ExprPtr initializer;
bool infer_type; // true if type should be inferred from initializer
VarDecl(std::string type_name, std::string name, ExprPtr initializer = nullptr)
: type_name(std::move(type_name)), name(std::move(name)), initializer(std::move(initializer)) {}
VarDecl(std::string type_name, std::string name, ExprPtr initializer = nullptr, bool infer_type = false)
: type_name(std::move(type_name)), name(std::move(name)), initializer(std::move(initializer)), infer_type(infer_type) {}
};
struct BlockStmt : public Stmt {

View File

@@ -367,7 +367,23 @@ void Interpreter::exec_var_decl(const VarDecl& stmt) {
if (stmt.initializer) {
value = evaluate(*stmt.initializer);
// If type inference is needed, we use the value directly
if (stmt.infer_type) {
// Type is automatically inferred from the initializer value
environment->define(stmt.name, value);
return;
}
// If explicit type is provided, verify the value matches
// (This is a simple check, could be more sophisticated)
if (!stmt.type_name.empty()) {
// Just use the value; type checking could be added here
environment->define(stmt.name, value);
return;
}
} else {
// No initializer, use default value based on type
value = create_default_value(stmt.type_name);
}

View File

@@ -94,14 +94,9 @@ StmtPtr Parser::declaration() {
if (match({TokenType::FUNC})) {
return function_declaration();
}
if (check(TokenType::NUMBER) || check(TokenType::STRING) ||
check(TokenType::BOOL) || check(TokenType::LIST) ||
check(TokenType::MAP) || check(TokenType::IDENTIFIER)) {
// Type name followed by identifier
Token lookahead = tokens_[current_];
if (current_ + 1 < tokens_.size() && tokens_[current_ + 1].type == TokenType::IDENTIFIER) {
return var_declaration();
}
// Handle var keyword for variable declaration
if (match({TokenType::VAR})) {
return var_declaration();
}
return statement();
@@ -118,8 +113,10 @@ StmtPtr Parser::class_declaration() {
if (check(TokenType::FUNC)) {
advance();
members.push_back(function_declaration());
} else {
} else if (match({TokenType::VAR})) {
members.push_back(var_declaration());
} else {
throw error(peek(), "Expected 'var' or 'func' in class body.");
}
}
@@ -161,17 +158,35 @@ StmtPtr Parser::function_declaration() {
}
StmtPtr Parser::var_declaration() {
Token type_token = advance();
std::string type_name = type_token.lexeme;
Token name = consume(TokenType::IDENTIFIER, "Expected variable name.");
// var keyword has already been consumed
Token name = consume(TokenType::IDENTIFIER, "Expected variable name after 'var'.");
std::string type_name;
bool infer_type = false;
ExprPtr initializer = nullptr;
if (match({TokenType::EQUAL})) {
// Check for explicit type annotation: var x : type
if (match({TokenType::COLON})) {
Token type_token = advance();
type_name = type_token.lexeme;
// Optional initializer
if (match({TokenType::EQUAL})) {
initializer = expression();
}
} else {
// No type annotation, must have initializer for type inference
if (!match({TokenType::EQUAL})) {
throw error(peek(), "Variable declaration without type annotation must have an initializer.");
}
initializer = expression();
infer_type = true;
type_name = ""; // Will be inferred at runtime
}
consume(TokenType::SEMICOLON, "Expected ';' after variable declaration.");
return std::make_unique<VarDecl>(type_name, name.lexeme, std::move(initializer));
return std::make_unique<VarDecl>(type_name, name.lexeme, std::move(initializer), infer_type);
}
StmtPtr Parser::statement() {