From 2c15d680b2fa5f3e6f99842014ac53da8689ac52 Mon Sep 17 00:00:00 2001 From: Richard Ward Date: Wed, 31 Dec 2025 12:57:23 +0000 Subject: [PATCH] docs: shared,script,packages (2 files) --- .../shared/seed/SCRIPT_JSON_SPEC_ISSUES.md | 323 ++++++++++++++++++ .../seed/scripts/runtime/script_executor.lua | 93 ++++- 2 files changed, 408 insertions(+), 8 deletions(-) create mode 100644 packages/shared/seed/SCRIPT_JSON_SPEC_ISSUES.md diff --git a/packages/shared/seed/SCRIPT_JSON_SPEC_ISSUES.md b/packages/shared/seed/SCRIPT_JSON_SPEC_ISSUES.md new file mode 100644 index 000000000..9ee37d4d5 --- /dev/null +++ b/packages/shared/seed/SCRIPT_JSON_SPEC_ISSUES.md @@ -0,0 +1,323 @@ +# Script.json Spec Issues & Improvements + +Issues discovered while converting real Lua code to script.json format. + +## Critical Missing Features + +### 1. **Logical Expressions (||, &&, !)** +**Issue**: Original spec only has `binary_expression` but logical operators need different semantics. + +**Current (Wrong)**: +```json +{ + "type": "binary_expression", + "left": "a", + "operator": "&&", + "right": "b" +} +``` + +**Fixed (New)**: +```json +{ + "type": "logical_expression", + "left": "a", + "operator": "&&", + "right": "b" +} +``` + +**Reason**: Logical operators short-circuit, binary operators don't. + +### 2. **Unary Expressions (!, -, ~)** +**Missing**: No way to express negation `!condition` + +**Added**: +```json +{ + "type": "unary_expression", + "operator": "!", + "argument": "$ref:params.databaseEnabled" +} +``` + +**Operators**: `!`, `-`, `+`, `~`, `typeof`, `void` + +### 3. **For-Each Loops (ipairs/for in)** +**Missing**: No iteration over arrays/tables + +**Added**: +```json +{ + "type": "for_each_loop", + "iterator": "flag", + "iterable": "$ref:params.permissions.featureFlags", + "body": [...] +} +``` + +**Maps to Lua**: +```lua +for _, flag in ipairs(permissions.featureFlags) do + -- body +end +``` + +### 4. **Comments** +**Missing**: No way to preserve code comments + +**Added**: +```json +{ + "type": "comment", + "text": "Check if resource is enabled" +} +``` + +### 5. **Default Parameter Handling** +**Issue**: Parameters with `optional: true` and `default` value not handled + +**Current**: +```json +{ + "name": "featureFlags", + "type": "table", + "optional": true, + "default": {} +} +``` + +**Runtime Needs**: Auto-assign default when undefined + +### 6. **Dynamic Property Access** +**Issue**: Can't access object properties using variable keys + +**Current (Wrong)**: +```json +{ + "type": "member_access", + "object": "$ref:params.featureFlags", + "property": "hardcoded_key" +} +``` + +**Needed**: +```json +{ + "type": "member_access", + "object": "$ref:params.featureFlags", + "property": "$ref:local.flag", + "computed": true +} +``` + +## Spec Improvements + +### 1. **Null Coalescing (??)** +**Added**: +```json +{ + "type": "logical_expression", + "operator": "??", + "left": "$ref:params.value", + "right": "default" +} +``` + +### 2. **Optional Chaining (?.)** +**Added**: +```json +{ + "type": "member_access", + "object": "$ref:params.obj", + "property": "nested", + "optional": true +} +``` + +### 3. **Spread Operator (...)** +**Added**: +```json +{ + "type": "spread_expression", + "argument": "$ref:params.array" +} +``` + +### 4. **Ternary Conditional (? :)** +**Added**: +```json +{ + "type": "conditional_expression", + "test": "$ref:params.condition", + "consequent": "value_if_true", + "alternate": "value_if_false" +} +``` + +## Type System Enhancements + +### 1. **Lua-Specific Types** +**Added**: `table`, `userdata`, `thread`, `nil` + +### 2. **Union Types** +**Current**: `"type": "string | number"` +**Better**: +```json +{ + "type": { + "kind": "union", + "types": ["string", "number"] + } +} +``` + +### 3. **Generic Constraints** +**Added**: +```json +{ + "generic": ["T extends any[]"], + "constraints": { + "T": { + "extends": "any[]" + } + } +} +``` + +## Compatibility Fixes + +### 1. **Lua vs JS Operators** +| Lua | JS | JSON | +|-----|----| -----| +| `~=` | `!=` | Use `!=` | +| `and` | `&&` | Use `&&` | +| `or` | `\|\|` | Use `\|\|` | +| `not` | `!` | Use `!` | +| `..` (concat) | `+` | Use `+` for strings | +| `#` (length) | `.length` | Use member access | + +### 2. **ipairs vs for...of** +**Lua**: +```lua +for _, item in ipairs(array) do +end +``` + +**JSON**: +```json +{ + "type": "for_each_loop", + "iterator": "item", + "iterable": "$ref:params.array" +} +``` + +### 3. **Table vs Object** +Lua `table` → JS `object` or `Map` + +**JSON uses**: `table` type for Lua compatibility + +## Runtime Requirements + +### Must Support: +1. ✅ Logical expressions with short-circuiting +2. ✅ Unary operators (!condition) +3. ⏳ For-each loops (ipairs iteration) +4. ⏳ Comments (ignored at runtime) +5. ⏳ Default parameters +6. ⏳ Computed property access + +### Should Support: +1. ⏳ Null coalescing (??) +2. ⏳ Optional chaining (?.) +3. ⏳ Spread operator (...) +4. ⏳ Ternary conditional (? :) + +### Nice to Have: +1. ⏳ Destructuring assignment +2. ⏳ Array/Object methods (map, filter, reduce) +3. ⏳ Regular expressions +4. ⏳ Switch/case statements + +## Migration Path + +### Phase 1: Core Features (Current) +- Basic expressions and statements +- Function calls +- Conditionals +- Object/array literals + +### Phase 2: Logical Operations (Next) +- Logical expressions (&&, ||, ??) +- Unary operators (!, -, +) +- Short-circuit evaluation + +### Phase 3: Iteration (Required for check_access) +- For-each loops +- For loops with counter +- While loops + +### Phase 4: Advanced Features +- Computed properties +- Spread operator +- Optional chaining +- Destructuring + +## Recommended Spec Changes + +### New Version: v2.1.0 + +```json +{ + "schema_version": "2.1.0", + + "expressions": { + "logical_expression": { + "operators": ["&&", "||", "??"], + "short_circuit": true + }, + "unary_expression": { + "operators": ["!", "-", "+", "~", "typeof", "void", "delete"] + }, + "conditional_expression": { + "ternary": true + }, + "computed_member_access": { + "dynamic_keys": true + } + }, + + "statements": { + "for_each_loop": { + "lua_ipairs": true, + "js_for_of": true + }, + "comment": { + "preserve": true, + "runtime_ignore": true + } + }, + + "compatibility": { + "lua": { + "operators": {"~=": "!=", "and": "&&", "or": "||", "not": "!"}, + "loops": {"ipairs": "for_each_loop", "pairs": "for_in_loop"} + }, + "typescript": { + "types": true, + "generics": true, + "decorators": false + } + } +} +``` + +## Testing Checklist + +- [x] Parse check_access.lua +- [ ] Execute check_access via runtime +- [ ] Validate all logical operators +- [ ] Test default parameters +- [ ] Verify for-each loops work +- [ ] Compare Lua output vs JSON output diff --git a/packages/shared/seed/scripts/runtime/script_executor.lua b/packages/shared/seed/scripts/runtime/script_executor.lua index bec6f09a6..59d40644d 100644 --- a/packages/shared/seed/scripts/runtime/script_executor.lua +++ b/packages/shared/seed/scripts/runtime/script_executor.lua @@ -67,14 +67,53 @@ local function eval_expression(expr, context) elseif op == "*" then return left * right elseif op == "/" then return left / right elseif op == "%" then return left % right - elseif op == "==" then return left == right - elseif op == "!=" or op == "~=" then return left ~= right + elseif op == "==" or op == "===" then return left == right + elseif op == "!=" or op == "~=" or op == "!==" then return left ~= right elseif op == "<" then return left < right elseif op == ">" then return left > right elseif op == "<=" then return left <= right elseif op == ">=" then return left >= right - elseif op == "&&" or op == "and" then return left and right - elseif op == "||" or op == "or" then return left or right + end + end + + -- Logical expressions (short-circuit evaluation) + if expr_type == "logical_expression" then + local op = expr.operator + + if op == "&&" or op == "and" then + local left = eval_expression(expr.left, context) + if not left then return left end -- Short-circuit + return eval_expression(expr.right, context) + elseif op == "||" or op == "or" then + local left = eval_expression(expr.left, context) + if left then return left end -- Short-circuit + return eval_expression(expr.right, context) + elseif op == "??" then + local left = eval_expression(expr.left, context) + if left ~= nil then return left end -- Null coalescing + return eval_expression(expr.right, context) + end + end + + -- Unary expressions + if expr_type == "unary_expression" then + local arg = eval_expression(expr.argument, context) + local op = expr.operator + + if op == "!" or op == "not" then return not arg + elseif op == "-" then return -arg + elseif op == "+" then return arg + elseif op == "~" then return ~arg -- Bitwise NOT (Lua 5.3+) + end + end + + -- Conditional expression (ternary) + if expr_type == "conditional_expression" then + local test = eval_expression(expr.test, context) + if test then + return eval_expression(expr.consequent, context) + else + return eval_expression(expr.alternate, context) end end @@ -82,7 +121,13 @@ local function eval_expression(expr, context) if expr_type == "member_access" then local obj = eval_expression(expr.object, context) if type(obj) == "table" then - return obj[expr.property] + -- Computed property access + if expr.computed then + local prop = eval_expression(expr.property, context) + return obj[prop] + else + return obj[expr.property] + end end return nil end @@ -222,6 +267,29 @@ local function execute_statement(stmt, context) return nil end + -- For-each loop (ipairs/for...of) + if stmt_type == "for_each_loop" then + local iterable = eval_expression(stmt.iterable, context) + if type(iterable) == "table" then + context.local_vars = context.local_vars or {} + for _, item in ipairs(iterable) do + context.local_vars[stmt.iterator] = item + for _, s in ipairs(stmt.body or {}) do + local result = execute_statement(s, context) + if result and result.type == "return" then + return result + end + end + end + end + return nil + end + + -- Comment (ignored at runtime) + if stmt_type == "comment" then + return nil + end + return nil end @@ -254,13 +322,22 @@ function M.execute_function(script_json, function_name, args) context.constants[const.name] = const.value end - -- Set parameters + -- Set parameters with defaults args = args or {} for i, param in ipairs(func_def.params or {}) do local value = args[i] - if value == nil and param.default then - value = resolve_ref(param.default, context) + + -- Handle default values + if value == nil and param.default ~= nil then + if type(param.default) == "string" and param.default:match("^%$ref:") then + value = resolve_ref(param.default, context) + elseif type(param.default) == "table" then + value = eval_expression(param.default, context) + else + value = param.default + end end + context.params[param.name] = value end