chore: merge v2
This commit is contained in:
commit
34b4fa9543
156 changed files with 10182 additions and 5610 deletions
|
|
@ -237,8 +237,8 @@ A host cannot define its own `$codemode` top-level namespace.
|
|||
|
||||
CodeMode executes a deliberately bounded JavaScript subset. It supports:
|
||||
|
||||
- Plain data literals, property access, assignment, and destructuring.
|
||||
- `if`, conditional expressions, `switch`, `for`, `for...of` (arrays, strings, Maps, Sets), `for...in` (own keys of plain objects, index strings of arrays, and namespace/tool names of `tools` references - anything else is an error suggesting `for...of` or `Object.keys`, rather than real JS's surprising behavior of indices for strings and zero iterations for Maps/Sets), `while`, and `do...while`.
|
||||
- Plain data literals, property access, assignment, destructuring, and sequence expressions (the comma operator, evaluated left to right with the final value returned).
|
||||
- `if`, conditional expressions, `switch`, `for`, `for...of` (arrays, strings, Maps, Sets, including assignment-form destructuring such as `for ([key, value] of entries)`), `for...in` (own keys of plain objects, index strings of arrays, and namespace/tool names of `tools` references - anything else is an error suggesting `for...of` or `Object.keys`, rather than real JS's surprising behavior of indices for strings and zero iterations for Maps/Sets), `while`, and `do...while`.
|
||||
- Arrow functions and function declarations with closures, defaults, rest parameters, and destructuring.
|
||||
- Optional chaining, nullish coalescing, templates, spread (arrays, strings, Maps, Sets), and `try`/`catch`.
|
||||
- Common array, string, number, `Object`, `Math`, and `JSON` operations. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
|
||||
|
|
|
|||
|
|
@ -1135,7 +1135,7 @@ class Interpreter<R> {
|
|||
}
|
||||
|
||||
let declaration: { readonly pattern: AstNode; readonly mutable: boolean } | undefined
|
||||
let assignmentName: string | undefined
|
||||
let assignment: AstNode | undefined
|
||||
|
||||
if (left.type === "VariableDeclaration") {
|
||||
const declarations = getArray(left, "declarations")
|
||||
|
|
@ -1145,8 +1145,13 @@ class Interpreter<R> {
|
|||
|
||||
const declarator = asNode(declarations[0], "declarations[0]")
|
||||
declaration = { pattern: getNode(declarator, "id"), mutable: getString(left, "kind") !== "const" }
|
||||
} else if (left.type === "Identifier") {
|
||||
assignmentName = getString(left, "name")
|
||||
} else if (
|
||||
left.type === "Identifier" ||
|
||||
left.type === "MemberExpression" ||
|
||||
left.type === "ArrayPattern" ||
|
||||
left.type === "ObjectPattern"
|
||||
) {
|
||||
assignment = left
|
||||
} else {
|
||||
throw new InterpreterRuntimeError("Unsupported for...of binding.", left)
|
||||
}
|
||||
|
|
@ -1155,8 +1160,8 @@ class Interpreter<R> {
|
|||
if (declaration) {
|
||||
self.pushScope()
|
||||
yield* self.declarePattern(declaration.pattern, value, declaration.mutable, left)
|
||||
} else if (assignmentName) {
|
||||
self.setIdentifierValue(assignmentName, value, left)
|
||||
} else if (assignment) {
|
||||
yield* self.assignPattern(assignment, value, left)
|
||||
}
|
||||
|
||||
const result = yield* self.evaluateStatement(body).pipe(
|
||||
|
|
@ -1554,6 +1559,16 @@ class Interpreter<R> {
|
|||
return this.evaluateUnaryExpression(node)
|
||||
case "AssignmentExpression":
|
||||
return this.evaluateAssignmentExpression(node)
|
||||
case "SequenceExpression": {
|
||||
const self = this
|
||||
return Effect.gen(function* () {
|
||||
let result: unknown
|
||||
for (const expression of getArray(node, "expressions")) {
|
||||
result = yield* self.evaluateExpression(asNode(expression, "expressions"))
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
case "CallExpression":
|
||||
return this.evaluateCallExpression(node)
|
||||
case "ArrowFunctionExpression":
|
||||
|
|
|
|||
|
|
@ -464,6 +464,54 @@ describe("H5: builtin coercion functions work as array callbacks", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("for...of assignment destructuring", () => {
|
||||
test("assigns entry pairs into predeclared variables", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
let key
|
||||
let item
|
||||
const out = []
|
||||
for ([key, item] of Object.entries({ a: 1, b: 2 })) out.push(key + item)
|
||||
return { key, item, out }
|
||||
`),
|
||||
).toEqual({ key: "b", item: 2, out: ["a1", "b2"] })
|
||||
})
|
||||
|
||||
test("assigns object patterns and defaults", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
let id
|
||||
let label
|
||||
const labels = []
|
||||
for ({ id, label = "unknown" } of [{ id: 1 }, { id: 2, label: "two" }]) labels.push(label)
|
||||
return { id, label, labels }
|
||||
`),
|
||||
).toEqual({ id: 2, label: "two", labels: ["unknown", "two"] })
|
||||
})
|
||||
})
|
||||
|
||||
describe("sequence expressions", () => {
|
||||
test("evaluate left to right and return the final value", async () => {
|
||||
expect(await value(`let x = 0; const result = (x += 1, x *= 3, x + 2); return { x, result }`)).toEqual({
|
||||
x: 3,
|
||||
result: 5,
|
||||
})
|
||||
})
|
||||
|
||||
test("support comma-separated for-loop updates", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
const pairs = []
|
||||
for (let left = 0, right = 3; left < right; left++, right--) pairs.push([left, right])
|
||||
return pairs
|
||||
`),
|
||||
).toEqual([
|
||||
[0, 3],
|
||||
[1, 2],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("destructuring assignment", () => {
|
||||
test("assigns object and array patterns to existing bindings", async () => {
|
||||
expect(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue