diff --git a/docs/grammar.txt b/docs/grammar.txt index 8090378..32d8fcd 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -13,6 +13,9 @@ Non-terminals are lowercase words. program -> declaration* EOF ; +/* returns a SlashType, not a an AST node */ +type_hint -> ":" TYPE ; + newline -> ( "\n" | ";" )+ ; expr_promotion -> newline | "}" | "&&" | "||" ; @@ -20,11 +23,12 @@ expr_promotion -> newline | "}" | "&&" | "||" ; declaration -> var_declaration | and_or ; -var_declaration -> "var" IDENTIFIER "=" expression newline ; +var_declaration -> "var" IDENTIFIER type_hint? "=" expression newline ; and_or -> statement ( ( "&&" | "||" ) statement )* ; + /* statements */ statement -> loop_stmt diff --git a/include/interpreter/ast.h b/include/interpreter/ast.h index 8f2ac46..7653400 100644 --- a/include/interpreter/ast.h +++ b/include/interpreter/ast.h @@ -173,6 +173,7 @@ typedef struct { typedef struct { StmtType type; StrView name; + SlashType type_hint; // optional: defaults to SLASH_ANY Expr *initializer; } VarStmt; diff --git a/include/interpreter/types/slash_value.h b/include/interpreter/types/slash_value.h index 776aaad..f1b7917 100644 --- a/include/interpreter/types/slash_value.h +++ b/include/interpreter/types/slash_value.h @@ -40,7 +40,8 @@ typedef enum { SLASH_TUPLE, SLASH_MAP, SLASH_NONE, - SLASH_TYPE_COUNT, + SLASH_TYPE_COUNT, /* not any actual type */ + SLASH_ANY, /* special type that a value cannot hold in runtime */ } SlashType; #define SLASH_TYPE_DYNAMIC(slash_type) \ diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index 4907ef3..867a910 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -430,6 +430,10 @@ static void exec_expr(Interpreter *interpreter, ExpressionStmt *stmt) static void exec_var(Interpreter *interpreter, VarStmt *stmt) { SlashValue value = eval(interpreter, stmt->initializer); + if (stmt->type_hint != SLASH_ANY && stmt->type_hint != value.type) { + report_runtime_error("Type mismatch. Expected X, but got Y."); + ASSERT_NOT_REACHED; + } var_define(interpreter->scope, &stmt->name, &value); } diff --git a/src/interpreter/parser.c b/src/interpreter/parser.c index 9432b2b..c29d1e1 100644 --- a/src/interpreter/parser.c +++ b/src/interpreter/parser.c @@ -33,6 +33,7 @@ static void report_err_and_sync(Parser *parser, char *err_msg); * non-terminal grammar rule functions */ +static SlashType type_hint(Parser *parser); static void newline(Parser *parser); static void expr_promotion(Parser *parser); static Stmt *declaration(Parser *parser); @@ -216,6 +217,16 @@ static void report_err_and_sync(Parser *parser, char *err_msg) /* grammar functions */ +static SlashType type_hint(Parser *parser) +{ + /* came from ':' */ + if (!match(parser, t_num, t_str, t_bool)) { + report_err_and_sync(parser, "Expected type keyword after ':'"); + return SLASH_ANY; + } + return token_type_to_slash_type(previous(parser)->type); +} + static void newline(Parser *parser) { consume(parser, t_newline, "Expected newline or semicolon"); @@ -248,13 +259,20 @@ static Stmt *declaration(Parser *parser) static Stmt *var_decl(Parser *parser) { /* came from 'var' */ + VarStmt *stmt = (VarStmt *)stmt_alloc(parser->ast_arena, STMT_VAR); Token *name = consume(parser, t_ident, "Expected variable name"); + stmt->name = name->lexeme; + + /* optional type */ + if (match(parser, t_colon)) + stmt->type_hint = type_hint(parser); + else + stmt->type_hint = SLASH_ANY; + consume(parser, t_equal, "Expected variable definition"); Expr *initializer = expression(parser); expr_promotion(parser); - VarStmt *stmt = (VarStmt *)stmt_alloc(parser->ast_arena, STMT_VAR); - stmt->name = name->lexeme; stmt->initializer = initializer; return (Stmt *)stmt; } diff --git a/src/test.slash b/src/test.slash index be0a144..b707d88 100644 --- a/src/test.slash +++ b/src/test.slash @@ -1,6 +1,2 @@ -var list1 = ["Nicolai", "Callum", 2, "Aidan", 4, "Zach", "Brendan", 1] -$list1.sort() -var i = 0 -loop item in $list1 { - echo $item -} +var s: str = "Nicolai" +echo $s