diff --git a/docs/grammar.ebnf b/docs/grammar.ebnf new file mode 100644 index 0000000..dbe4efb --- /dev/null +++ b/docs/grammar.ebnf @@ -0,0 +1,119 @@ + +chunk ::= statement* EOF + +statement ::= import_statement + | variable_declaration ';' + | assign_call ';' + | struct_declaration + | function_declaration + | delete_statement + | if_statement + | match_statement + | loop_statement + | while_statement + | for_statement + | break_statement + | return_statement + | block + +import_statement ::= 'import' name ( 'as' name )? ';' + | 'import' STRING 'as' name ';' + | 'import' '{' name ( ',' name )* '}' 'from' ( name | STRING ) ';' + +variable_declaration ::= 'let' name '=' expression + | 'mut' name ( '=' expression )? + | ( 'let' | 'mut' ) '[' '_' | name ( ',' '_' | name )* ']' '=' expression + | ( 'let' | 'mut' ) '{' name ( ',' name )* '}' '=' expression + +assign_call ::= name subscript* assign_op expression + | name subscript* ( '++' | '--' ) + | name subscript* '[' ']' '=' expression + | name subscript* subscript '=' expression + | name ( subscript | call )* call + +struct_declaration ::= 'struct' name '{' ( string | name ( ',' string | name )* )? '}' + +function_declaration ::= 'fn' name '(' ( 'mut'? name ( ',' 'mut'? name )* )? ')' ( '=>' expression | block ) + +delete_statement ::= 'del' name subscript* '[' expression ']' ';' + +if_statement ::= ( 'if' | 'if!' ) '(' ( ( variable_declaration | assign_call ) ';' )? expression ')' + statement ( 'else' statement )? + +match_statement ::= 'match' '(' ( ( variable_declaration | assign_call ) ';' )? expression ')' + '{' ( expression '=>' statement )+ ( '_' '=>' statement )? '}' + +loop_statement ::= 'loop' statement + +while_statement ::= ( 'while' | 'while!' ) '(' expression ')' statement + | 'do' statement ( 'while' | 'while!' ) '(' expression ')' ';' + +for_statement ::= 'for' '(' ( variable_declaration | assign_call )? ';' expression? ';' assign_call? ')' statement + | 'foreach' '(' name 'in' expression ')' statement + +break_statement ::= ( 'break' | 'continue' ) ';' + +return_statement ::= 'return' expression? ';' + +block ::= '{' stmt* '}' + +assign_op ::= '=' | '|=' | '^=' | '&=' | '<<=' | '>>=' + | '+=' | '-=' | '*=' | '/=' | '~/=' | '%=' + +subscript ::= '[' expression ']' | '.' name + +call ::= '(' ( expression ( ',' expression )* )? ')' + +expression ::= and_expression ( '||' and_expression )* + +and_expression ::= equal_expression ( '&&' equal_expression )* + +equal_expression ::= comp_expression ( ( '==' | '!=' ) comp_expression )* + +comp_expression ::= bor_expression ( ( '>' | '>=' | '<' | '<=' ) bor_expression )* + +bor_expression ::= bxor_expression ( '|' bxor_expression )* + +bxor_expression ::= band_expression ( '^' band_expression )* + +band_expression ::= shift_expression ( '&' shift_expression )* + +shift_expression ::= range_expression ( ( '<<' | '>>' ) range_expression )* + +range_expression ::= add_expression ( '..' add_expression )? + +add_expression ::= mul_expression ( ( '+' | '-' ) mul_expression )* + +mul_expression ::= unary_expression ( ( '*' | '/' | '~/' | '%' ) unary_expression )* + +unary_expression ::= ( '-' | '!' | '~' ) unary_expression | primary_expression + +primary_expression ::= literal + | array_constructor + | struct_constructor + | anonymous_struct + | anonymous_function + | if_expression + | match_expression + | subscript_call + | group_expression + +literal ::= 'nil' | 'false' | 'true' | number | string + +array_constructor ::= '[' ( expression ( ',' expression )* )? ']' + +struct_constructor ::= '{' ( string | name ':' expression ( ',' string | name ':' expression )* )? '}' + +anonymous_struct ::= 'struct' '{' ( string | name ( ',' string | name )* )? '}' + +anonymous_function ::= '|' ( 'mut'? name ( ',' 'mut'? name )* )? '|' ( '=>' expression | block ) + | '||' ( '=>' expression | block ) + +if_expression ::= ( 'if' | 'if!' ) '(' expression ')' expression 'else' expression + +match_expression ::= 'match' '(' expression ')' '{' expression '=>' expression ( ',' expression '=>' expression )* + ',' '_' '=>' expression '}' + +subscript_call ::= name ( subscript | call )* ( '{' ( expression ( ',' expression )* )? '}' )? + +group_expression ::= '(' expression ')' diff --git a/docs/grammar.md b/docs/grammar.md index d26f8db..da71ceb 100644 --- a/docs/grammar.md +++ b/docs/grammar.md @@ -179,7 +179,7 @@ In addition, Hook uses a special token to indicate the end of a file. This token The complete syntactic grammar of Hook is defined by the following EBNF grammar: -``` +```ebnf chunk ::= statement* EOF statement ::= import_statement @@ -218,16 +218,18 @@ function_declaration ::= 'fn' name '(' ( 'mut'? name ( ',' 'mut'? name )* )? ')' delete_statement ::= 'del' name subscript* '[' expression ']' ';' -if_statement ::= ( 'if' | 'if!' ) '(' ( variable_declaration ';' )? expression ')' statement ( 'else' statement )? +if_statement ::= ( 'if' | 'if!' ) '(' ( ( variable_declaration | assign_call ) ';' )? expression ')' + statement ( 'else' statement )? -match_statement ::= 'match' '(' ( variable_declaration ';' )? expression ')' '{' ( expression '=>' statement )+ ( '_' '=>' statement )? '}' +match_statement ::= 'match' '(' ( ( variable_declaration | assign_call ) ';' )? expression ')' + '{' ( expression '=>' statement )+ ( '_' '=>' statement )? '}' loop_statement ::= 'loop' statement while_statement ::= ( 'while' | 'while!' ) '(' expression ')' statement | 'do' statement ( 'while' | 'while!' ) '(' expression ')' ';' -for_statement ::= 'for' '(' variable_declaration | assign_call? ';' expression? ';' assign_call? ')' statement +for_statement ::= 'for' '(' ( variable_declaration | assign_call )? ';' expression? ';' assign_call? ')' statement | 'foreach' '(' name 'in' expression ')' statement break_statement ::= ( 'break' | 'continue' ) ';' diff --git a/docs/grammar.txt b/docs/grammar.txt deleted file mode 100644 index 0bc1f63..0000000 --- a/docs/grammar.txt +++ /dev/null @@ -1,79 +0,0 @@ - -chunk ::= stmt* EOF - -stmt ::= 'import' NAME ( 'as' NAME )? ';' - | 'import' STRING 'as' NAME ';' - | 'import' '{' NAME ( ',' NAME )* '}' 'from' ( NAME | STRING ) ';' - | var_decl ';' - | assign_call ';' - | 'struct' NAME '{' ( STRING | NAME ( ',' STRING | NAME )* )? '}' - | 'fn' NAME '(' ( 'mut'? NAME ( ',' 'mut'? NAME )* )? ')' - ( '=>' expr | block ) - | 'del' NAME subscr* '[' expr ']' ';' - | ( 'if' | 'if!' ) '(' ( var_decl ';' )? expr ')' stmt ( 'else' stmt )? - | 'match' '(' ( var_decl ';' )? expr ')' '{' ( expr '=>' stmt )+ ( '_' '=>' stmt )? '}' - | 'loop' stmt - | ( 'while' | 'while!' ) '(' expr ')' stmt - | 'do' stmt ( 'while' | 'while!' ) '(' expr ')' ';' - | 'for' '(' var_decl | assign_call? ';' expr? ';' assign_call? ')' stmt - | 'foreach' '(' NAME 'in' expr ')' stmt - | 'continue' ';' - | 'break' ';' - | 'return' expr? ';' - | block - -var_decl ::= 'let' NAME '=' expr - | 'mut' NAME ( '=' expr )? - | ( 'let' | 'mut' ) '[' '_' | NAME ( ',' '_' | NAME )* ']' '=' expr - | ( 'let' | 'mut' ) '{' NAME ( ',' NAME )* '}' '=' expr - -assign_call ::= NAME subscr* assign_op expr - | NAME subscr* ( '++' | '--' ) - | NAME subscr* '[' ']' '=' expr - | NAME subscr* subscr '=' expr - | NAME ( subscr | call )* call - -assign_op ::= '=' | '|=' | '^=' | '&=' | '<<=' | '>>=' - | '+=' | '-=' | '*=' | '/=' | '~/=' | '%=' - -subscr ::= '[' expr ']' | '.' NAME - -call ::= '(' ( expr ( ',' expr )* )? ')' - -block ::= '{' stmt* '}' - -expr ::= and_expr ( '||' and_expr )* - -and_expr ::= equal_expr ( '&&' equal_expr )* - -equal_expr ::= comp_expr ( ( '==' | '!=' ) comp_expr )* - -comp_expr ::= bor_expr ( ( '>' | '>=' | '<' | '<=' ) bor_expr )* - -bor_expr ::= bxor_expr ( '|' bxor_expr )* - -bxor_expr ::= band_expr ( '^' band_expr )* - -band_expr ::= shift_expr ( '&' shift_expr )* - -shift_expr ::= range_expr ( ( '<<' | '>>' ) range_expr )* - -range_expr ::= add_expr ( '..' add_expr )? - -add_expr ::= mul_expr ( ( '+' | '-' ) mul_expr )* - -mul_expr ::= unary_expr ( ( '*' | '/' | '~/' | '%' ) unary_expr )* - -unary_expr ::= ( '-' | '!' | '~' ) unary_expr | prim_expr - -prim_expr ::= 'nil' | 'false' | 'true' | INT | FLOAT | STRING - | '[' ( expr ( ',' expr )* )? ']' - | '{' ( STRING | NAME ':' expr ( ',' STRING | NAME ':' expr )* )? '}' - | 'struct' '{' ( STRING | NAME ( ',' STRING | NAME )* )? '}' - | '|' ( 'mut'? NAME ( ',' 'mut'? NAME )* )? '|' ( '=>' expr | block ) - | '||' ( '=>' expr | block ) - | ( 'if' | 'if!' ) '(' expr ')' expr 'else' expr - | 'match' '(' expr ')' '{' expr '=>' expr ( ',' expr '=>' expr )* - ',' '_' '=>' expr '}' - | NAME ( subscr | call )* ( '{' ( expr ( ',' expr )* )? '}' )? - | '(' expr ')' diff --git a/src/compiler.c b/src/compiler.c index beee142..8cd4d39 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -1278,6 +1278,13 @@ static void compile_if_statement(Compiler *comp, bool not) compile_variable_declaration(comp); consume(comp, TOKEN_SEMICOLON); } + else if (match(scan, TOKEN_NAME)) + { + Token tk = scan->token; + scanner_next_token(scan); + compile_assign_statement(comp, &tk); + consume(comp, TOKEN_SEMICOLON); + } compile_expression(comp); consume(comp, TOKEN_RPAREN); HkOpCode op = not ? HK_OP_JUMP_IF_TRUE : HK_OP_JUMP_IF_FALSE; @@ -1311,6 +1318,13 @@ static void compile_match_statement(Compiler *comp) compile_variable_declaration(comp); consume(comp, TOKEN_SEMICOLON); } + else if (match(scan, TOKEN_NAME)) + { + Token tk = scan->token; + scanner_next_token(scan); + compile_assign_statement(comp, &tk); + consume(comp, TOKEN_SEMICOLON); + } compile_expression(comp); consume(comp, TOKEN_RPAREN); consume(comp, TOKEN_LBRACE); diff --git a/tests/if_statement_var_declaration_test.hk b/tests/if_statement_var_declaration_test.hk index fc01905..210344c 100644 --- a/tests/if_statement_var_declaration_test.hk +++ b/tests/if_statement_var_declaration_test.hk @@ -28,3 +28,9 @@ if (mut { ok, err } = baz(); err) { } else { println(ok); } + +mut result; +if (result = baz(); result.err) { + println(result.err); +} +println(result.ok); diff --git a/tests/match_statement_var_declaration_test.hk b/tests/match_statement_var_declaration_test.hk index 0bd28de..362de82 100644 --- a/tests/match_statement_var_declaration_test.hk +++ b/tests/match_statement_var_declaration_test.hk @@ -36,3 +36,11 @@ match (let { lvl, msg } = error("error message"); lvl) { "error" => println(msg); _ => println("unknown log level"); } + +mut result; +match (result = info("info message"); result.lvl) { + "info" => println(result.msg); + "warn" => println(result.msg); + "error" => println(result.msg); + _ => println("unknown log level"); +}