From cae39fb62f9519ce5cea0ee484393ef8df48dd43 Mon Sep 17 00:00:00 2001 From: Ramb Memburg <46289413+memburg@users.noreply.github.com> Date: Thu, 11 Jun 2026 13:43:39 -0400 Subject: [PATCH] reject unsupported let/const declarations explicitly --- reference/Language.md | 2 ++ reference/REFERENCE.md | 2 ++ spec/giavascript_spec.cr | 11 ++++++++--- src/giavascript/interpreter.cr | 22 ++++++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/reference/Language.md b/reference/Language.md index cb9ec44..5188533 100644 --- a/reference/Language.md +++ b/reference/Language.md @@ -83,3 +83,5 @@ Status of core JavaScript language features in GiavaScript. ## Notes - This reflects current behavior in the interpreter and specs. +- `let` and `const` declarations return explicit errors: `Error: unsupported declaration 'let'` and `Error: unsupported declaration 'const'`. +- Use `var` for variable declarations. diff --git a/reference/REFERENCE.md b/reference/REFERENCE.md index 0abfa73..4f5dbee 100644 --- a/reference/REFERENCE.md +++ b/reference/REFERENCE.md @@ -107,6 +107,8 @@ Status of core JavaScript language features in GiavaScript. ### Notes - This reflects current behavior in the interpreter and specs. +- `let` and `const` declarations return explicit errors: `Error: unsupported declaration 'let'` and `Error: unsupported declaration 'const'`. +- Use `var` for variable declarations. ## Type Methods and Properties diff --git a/spec/giavascript_spec.cr b/spec/giavascript_spec.cr index b041ecc..eeb5715 100644 --- a/spec/giavascript_spec.cr +++ b/spec/giavascript_spec.cr @@ -1562,11 +1562,16 @@ describe GiavaScript do interpreter.eval("continue;").should eq(["Error: continue can only be used inside loops"]) end - it "rejects let and const declarations" do + it "rejects let declarations with an explicit unsupported-declaration error" do interpreter = GiavaScript::Interpreter.new - interpreter.eval("let value = 1;").should eq(["Error: invalid assignment target 'let value'"]) - interpreter.eval("const total = 2;").should eq(["Error: invalid assignment target 'const total'"]) + interpreter.eval("let value = 1;").should eq(["Error: unsupported declaration 'let'"]) + end + + it "rejects const declarations with an explicit unsupported-declaration error" do + interpreter = GiavaScript::Interpreter.new + + interpreter.eval("const total = 2;").should eq(["Error: unsupported declaration 'const'"]) end it "rejects class declarations" do diff --git a/src/giavascript/interpreter.cr b/src/giavascript/interpreter.cr index 73ef6c9..a95e84d 100644 --- a/src/giavascript/interpreter.cr +++ b/src/giavascript/interpreter.cr @@ -85,6 +85,13 @@ module GiavaScript end end + class UnsupportedDeclarationRawStatement < CompiledRawStatement + getter keyword : String + + def initialize(@keyword : String) + end + end + def initialize(@console_output : IO = STDOUT) @env = build_global_env @function_runtime = FunctionRuntime.new @@ -185,6 +192,10 @@ module GiavaScript return "Error: return can only be used inside functions" end + if keyword = unsupported_declaration_keyword(stmt) + return "Error: unsupported declaration '#{keyword}'" + end + if match = stmt.match(/^(.+?)\s*(\+\+|--)$/) target_source = match[1].strip operator = match[2] @@ -811,6 +822,8 @@ module GiavaScript rescue ex : ExpressionError ex.message || "Error: invalid right-hand side '#{compiled.source}'" end + when UnsupportedDeclarationRawStatement + "Error: unsupported declaration '#{compiled.keyword}'" end end @@ -843,6 +856,8 @@ module GiavaScript rescue FallbackRawStatement.new(source) end + elsif keyword = unsupported_declaration_keyword(key) + UnsupportedDeclarationRawStatement.new(keyword) elsif assignment = split_assignment_statement(key) begin target = parse_assignment_target(assignment[:lhs]) @@ -948,6 +963,13 @@ module GiavaScript next_char.nil? || !(next_char.ascii_letter? || next_char.ascii_number? || next_char == '_') end + private def unsupported_declaration_keyword(source : String) : String? + return "let" if starts_with_keyword?(source, "let") + return "const" if starts_with_keyword?(source, "const") + + nil + end + private def call_function(name : String, args : Array(Value), env : Environment) : Value @function_runtime.invoke_function(name, args, env) do |stmt, local_env, inside_function, inside_loop| eval_statement(stmt, local_env, inside_function, inside_loop)