From 6eab5ff09dbc8ad37279f4d143685186fbd7f05e Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Thu, 27 Feb 2025 16:55:37 -0500 Subject: [PATCH 1/2] Implement compound statements (`{...}`). Closes #1538 The functionality was there, just not available in the parser. --- doc/programming/language/variables.rst | 34 ++++++++++++++++++- hilti/toolchain/src/compiler/parser/parser.yy | 10 ++++-- spicy/toolchain/src/compiler/parser/parser.yy | 10 ++++-- tests/Baseline/hilti.statements.block/output | 9 +++++ tests/Baseline/spicy.statements.block/output | 9 +++++ tests/hilti/statements/block.hlt | 31 +++++++++++++++++ tests/spicy/statements/block.spicy | 28 +++++++++++++++ 7 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 tests/Baseline/hilti.statements.block/output create mode 100644 tests/Baseline/spicy.statements.block/output create mode 100644 tests/hilti/statements/block.hlt create mode 100644 tests/spicy/statements/block.spicy diff --git a/doc/programming/language/variables.rst b/doc/programming/language/variables.rst index 9cc62ce7a..132087a2f 100644 --- a/doc/programming/language/variables.rst +++ b/doc/programming/language/variables.rst @@ -36,9 +36,41 @@ prefixed with ``local`` this time: function f() { local x: bytes; local y = "Y"; - } Usual scoping rules apply to locals. Just like globals, locals are always initialized to a well-defined value: either their default if given, or the type's null value. + +Local variables can also have their visibility limited to a block +enclosed by curly braces: + +.. spicy-code:: + + function f() { + { + local x = "One"; + print x; + } + { + local x = "Two"; + print x; + } + + # Shadowing variables also works + local shadowed = "Outer"; + { + local shadowed = "Inner"; + print shadowed; + } + print shadowed; + } + +The above function would print: + +.. code:: + + One + Two + Inner + Outer diff --git a/hilti/toolchain/src/compiler/parser/parser.yy b/hilti/toolchain/src/compiler/parser/parser.yy index 7c469522a..2fa24ec20 100644 --- a/hilti/toolchain/src/compiler/parser/parser.yy +++ b/hilti/toolchain/src/compiler/parser/parser.yy @@ -36,7 +36,7 @@ namespace hilti { namespace detail { class Parser; } } %glr-parser %expect 113 -%expect-rr 209 +%expect-rr 211 %{ @@ -467,8 +467,11 @@ opt_func_default_expr : '=' expr { $$ = std::move($2); } /* Statements */ -block : braced_block { $$ = std::move($1); } - | stmt { $$ = builder->statementBlock({$1}, __loc__); } +block : stmt { if ( ! $1->isA() ) + $$ = builder->statementBlock({std::move($1)}, __loc__); + else + $$ = std::move($1); + } ; braced_block : '{' opt_stmts '}' { $$ = builder->statementBlock(std::move($2), __loc__); } @@ -481,6 +484,7 @@ stmts : stmts stmt { $$ = std::move($1); $$.push_b stmt : stmt_expr ';' { $$ = std::move($1); } | stmt_decl { $$ = std::move($1); } + | braced_block { $$ = std::move($1); } | RETURN ';' { $$ = builder->statementReturn(__loc__); } | RETURN expr ';' { $$ = builder->statementReturn(std::move($2), __loc__); } | THROW expr ';' { $$ = builder->statementThrow(std::move($2), __loc__); } diff --git a/spicy/toolchain/src/compiler/parser/parser.yy b/spicy/toolchain/src/compiler/parser/parser.yy index f9b592d93..e1632e3c2 100644 --- a/spicy/toolchain/src/compiler/parser/parser.yy +++ b/spicy/toolchain/src/compiler/parser/parser.yy @@ -36,7 +36,7 @@ namespace spicy { namespace detail { class Parser; } } %glr-parser %expect 119 -%expect-rr 170 +%expect-rr 172 %{ @@ -521,8 +521,11 @@ opt_init_expression : '=' expr { $$ = std::move($2); } /* Statements */ -block : braced_block { $$ = std::move($1); } - | stmt { $$ = builder->statementBlock({$1}, __loc__); } +block : stmt { if ( ! $1->isA() ) + $$ = builder->statementBlock({std::move($1)}, __loc__); + else + $$ = std::move($1); + } ; braced_block : '{' opt_stmts '}' { $$ = builder->statementBlock(std::move($2), __loc__); } @@ -535,6 +538,7 @@ stmts : stmts stmt { $$ = std::move($1); $$.push_b stmt : stmt_expr ';' { $$ = std::move($1); } | stmt_decl { $$ = std::move($1); } + | braced_block { $$ = std::move($1); } | ASSERT expr ';' { $$ = builder->statementAssert(std::move($2), {}, __loc__); } | ASSERT_EXCEPTION expr_no_or_error ':' expr ';' { $$ = builder->statementAssert(hilti::statement::assert::Exception(), std::move($2), {}, std::move($4), __loc__); } diff --git a/tests/Baseline/hilti.statements.block/output b/tests/Baseline/hilti.statements.block/output new file mode 100644 index 000000000..edc3e027d --- /dev/null +++ b/tests/Baseline/hilti.statements.block/output @@ -0,0 +1,9 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +Testing block: +1 +hello :) + +Testing shadowing: +outer +inner +outer diff --git a/tests/Baseline/spicy.statements.block/output b/tests/Baseline/spicy.statements.block/output new file mode 100644 index 000000000..edc3e027d --- /dev/null +++ b/tests/Baseline/spicy.statements.block/output @@ -0,0 +1,9 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +Testing block: +1 +hello :) + +Testing shadowing: +outer +inner +outer diff --git a/tests/hilti/statements/block.hlt b/tests/hilti/statements/block.hlt new file mode 100644 index 000000000..150f8dbba --- /dev/null +++ b/tests/hilti/statements/block.hlt @@ -0,0 +1,31 @@ +# @TEST-EXEC: ${HILTIC} -j %INPUT >output +# @TEST-EXEC: btest-diff output + +module Block { + +import hilti; + +function void block() { + hilti::print("Testing block:"); + { + local x = 1; + hilti::print(x); + } + local x = "hello :)"; + hilti::print(x); +} + +function void shadowed() { + hilti::print("\nTesting shadowing:"); + local shadowed = "outer"; + hilti::print(shadowed); + { + local shadowed = "inner"; + hilti::print(shadowed); + } + hilti::print(shadowed); +} + +block(); +shadowed(); +} diff --git a/tests/spicy/statements/block.spicy b/tests/spicy/statements/block.spicy new file mode 100644 index 000000000..0f713bb38 --- /dev/null +++ b/tests/spicy/statements/block.spicy @@ -0,0 +1,28 @@ +# @TEST-EXEC: ${SPICYC} -j %INPUT >output +# @TEST-EXEC: btest-diff output + +module Block; + +function block() { + print "Testing block:"; + { + local x = 1; + print x; + } + local x = "hello :)"; + print x; +} + +function shadow() { + print "\nTesting shadowing:"; + local shadowed = "outer"; + print shadowed; + { + local shadowed = "inner"; + print shadowed; + } + print shadowed; +} + +block(); +shadow(); From 9152a067b61999571ccbc87bb9ff0f0a77dcb520 Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Fri, 28 Feb 2025 09:33:19 -0500 Subject: [PATCH 2/2] Fix `for` loop snippet to run as-is. --- doc/programming/language/examples/_statement-for.spicy | 2 +- doc/programming/language/statements.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/programming/language/examples/_statement-for.spicy b/doc/programming/language/examples/_statement-for.spicy index 1b89c946e..b249d7360 100644 --- a/doc/programming/language/examples/_statement-for.spicy +++ b/doc/programming/language/examples/_statement-for.spicy @@ -8,7 +8,7 @@ for ( i in b"abc" ) { print i; } -local v = vector("a", "b", "c"); +global v = vector("a", "b", "c"); for ( i in v ) print i; \ No newline at end of file diff --git a/doc/programming/language/statements.rst b/doc/programming/language/statements.rst index 3f8cbe650..eeddeba05 100644 --- a/doc/programming/language/statements.rst +++ b/doc/programming/language/statements.rst @@ -118,7 +118,7 @@ Examples: print i; } - local v = vector("a", "b", "c"); + global v = vector("a", "b", "c"); for ( i in v ) print i;