no kwargs
This commit is contained in:
parent
d8e97c0f20
commit
078fc37a02
|
|
@ -19,7 +19,7 @@ It's where Shrimp live.
|
|||
- Dictionary operations (MAKE_DICT, DICT_GET, DICT_SET, DICT_HAS)
|
||||
- Function operations (MAKE_FUNCTION, CALL, TAIL_CALL, RETURN) with parameter binding
|
||||
- Variadic functions with positional rest parameters (`...rest`)
|
||||
- Named arguments (kwargs) that collect unmatched named args into a dict (`@named`)
|
||||
- Named arguments (named) that collect unmatched named args into a dict (`@named`)
|
||||
- Mixed positional and named arguments with proper priority binding
|
||||
- Tail call optimization with unbounded recursion (10,000+ iterations without stack overflow)
|
||||
- Exception handling (PUSH_TRY, PUSH_FINALLY, POP_TRY, THROW) with nested try/finally blocks and call stack unwinding
|
||||
|
|
|
|||
14
src/vm.ts
14
src/vm.ts
|
|
@ -386,7 +386,7 @@ export class VM {
|
|||
// Check if named argument was provided for this param
|
||||
if (namedArgs.has(paramName)) {
|
||||
this.scope.set(paramName, namedArgs.get(paramName)!)
|
||||
namedArgs.delete(paramName) // Remove from named args so it won't go to kwargs
|
||||
namedArgs.delete(paramName) // Remove from named args so it won't go to named
|
||||
} else if (positionalArgs[i] !== undefined) {
|
||||
this.scope.set(paramName, positionalArgs[i]!)
|
||||
} else if (fn.defaults[paramName] !== undefined) {
|
||||
|
|
@ -410,11 +410,11 @@ export class VM {
|
|||
// Handle named parameter (collect remaining named args that didn't match params)
|
||||
if (fn.named) {
|
||||
const namedParamName = fn.params[fn.params.length - 1]!
|
||||
const kwargsDict = new Map<string, Value>()
|
||||
const namedDict = new Map<string, Value>()
|
||||
for (const [key, value] of namedArgs) {
|
||||
kwargsDict.set(key, value)
|
||||
namedDict.set(key, value)
|
||||
}
|
||||
this.scope.set(namedParamName, { type: 'dict', value: kwargsDict })
|
||||
this.scope.set(namedParamName, { type: 'dict', value: namedDict })
|
||||
}
|
||||
|
||||
// subtract 1 because pc was incremented
|
||||
|
|
@ -488,11 +488,11 @@ export class VM {
|
|||
// Handle named parameter
|
||||
if (tailFn.named) {
|
||||
const namedParamName = tailFn.params[tailFn.params.length - 1]!
|
||||
const kwargsDict = new Map<string, Value>()
|
||||
const namedDict = new Map<string, Value>()
|
||||
for (const [key, value] of tailNamedArgs) {
|
||||
kwargsDict.set(key, value)
|
||||
namedDict.set(key, value)
|
||||
}
|
||||
this.scope.set(namedParamName, { type: 'dict', value: kwargsDict })
|
||||
this.scope.set(namedParamName, { type: 'dict', value: namedDict })
|
||||
}
|
||||
|
||||
// subtract 1 because PC was incremented
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ test("TAIL_CALL - variadic function", async () => {
|
|||
|
||||
test("CALL - named args function with no fixed params", async () => {
|
||||
const bytecode = toBytecode(`
|
||||
MAKE_FUNCTION (@kwargs) #9
|
||||
MAKE_FUNCTION (@named) #9
|
||||
PUSH "name"
|
||||
PUSH "Bob"
|
||||
PUSH "age"
|
||||
|
|
@ -210,7 +210,7 @@ test("CALL - named args function with no fixed params", async () => {
|
|||
PUSH 2
|
||||
CALL
|
||||
HALT
|
||||
LOAD kwargs
|
||||
LOAD named
|
||||
RETURN
|
||||
`)
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ test("CALL - named args function with no fixed params", async () => {
|
|||
|
||||
test("CALL - named args function with one fixed param", async () => {
|
||||
const bytecode = toBytecode(`
|
||||
MAKE_FUNCTION (x @kwargs) #8
|
||||
MAKE_FUNCTION (x @named) #8
|
||||
PUSH 10
|
||||
PUSH "name"
|
||||
PUSH "Alice"
|
||||
|
|
@ -232,7 +232,7 @@ test("CALL - named args function with one fixed param", async () => {
|
|||
PUSH 1
|
||||
CALL
|
||||
HALT
|
||||
LOAD kwargs
|
||||
LOAD named
|
||||
RETURN
|
||||
`)
|
||||
|
||||
|
|
@ -244,9 +244,9 @@ test("CALL - named args function with one fixed param", async () => {
|
|||
}
|
||||
})
|
||||
|
||||
test("CALL - named args with matching param name should bind to param not kwargs", async () => {
|
||||
test("CALL - named args with matching param name should bind to param not named", async () => {
|
||||
const bytecode = toBytecode(`
|
||||
MAKE_FUNCTION (name @kwargs) #8
|
||||
MAKE_FUNCTION (name @named) #8
|
||||
PUSH "Bob"
|
||||
PUSH "age"
|
||||
PUSH 50
|
||||
|
|
@ -259,13 +259,13 @@ test("CALL - named args with matching param name should bind to param not kwargs
|
|||
`)
|
||||
|
||||
const result = await new VM(bytecode).run()
|
||||
// name should be bound as regular param, not collected in kwargs
|
||||
// name should be bound as regular param, not collected in named
|
||||
expect(result).toEqual({ type: 'string', value: 'Bob' })
|
||||
})
|
||||
|
||||
test("CALL - named args that match param names should not be in kwargs", async () => {
|
||||
test("CALL - named args that match param names should not be in named", async () => {
|
||||
const bytecode = toBytecode(`
|
||||
MAKE_FUNCTION (name age @kwargs) #9
|
||||
MAKE_FUNCTION (name age @named) #9
|
||||
PUSH "name"
|
||||
PUSH "Bob"
|
||||
PUSH "city"
|
||||
|
|
@ -274,14 +274,14 @@ test("CALL - named args that match param names should not be in kwargs", async (
|
|||
PUSH 2
|
||||
CALL
|
||||
HALT
|
||||
LOAD kwargs
|
||||
LOAD named
|
||||
RETURN
|
||||
`)
|
||||
|
||||
const result = await new VM(bytecode).run()
|
||||
expect(result.type).toBe('dict')
|
||||
if (result.type === 'dict') {
|
||||
// Only city should be in kwargs, name should be bound to param
|
||||
// Only city should be in named, name should be bound to param
|
||||
expect(result.value.get('city')).toEqual({ type: 'string', value: 'NYC' })
|
||||
expect(result.value.has('name')).toBe(false)
|
||||
expect(result.value.size).toBe(1)
|
||||
|
|
@ -290,7 +290,7 @@ test("CALL - named args that match param names should not be in kwargs", async (
|
|||
|
||||
test("CALL - mixed variadic and named args", async () => {
|
||||
const bytecode = toBytecode(`
|
||||
MAKE_FUNCTION (x ...rest @kwargs) #10
|
||||
MAKE_FUNCTION (x ...rest @named) #10
|
||||
PUSH 1
|
||||
PUSH 2
|
||||
PUSH 3
|
||||
|
|
@ -315,9 +315,9 @@ test("CALL - mixed variadic and named args", async () => {
|
|||
})
|
||||
})
|
||||
|
||||
test("CALL - mixed variadic and named args, check kwargs", async () => {
|
||||
test("CALL - mixed variadic and named args, check named", async () => {
|
||||
const bytecode = toBytecode(`
|
||||
MAKE_FUNCTION (x ...rest @kwargs) #10
|
||||
MAKE_FUNCTION (x ...rest @named) #10
|
||||
PUSH 1
|
||||
PUSH 2
|
||||
PUSH 3
|
||||
|
|
@ -327,7 +327,7 @@ test("CALL - mixed variadic and named args, check kwargs", async () => {
|
|||
PUSH 1
|
||||
CALL
|
||||
HALT
|
||||
LOAD kwargs
|
||||
LOAD named
|
||||
RETURN
|
||||
`)
|
||||
|
||||
|
|
@ -340,18 +340,18 @@ test("CALL - mixed variadic and named args, check kwargs", async () => {
|
|||
|
||||
test("CALL - named args with no extra named args", async () => {
|
||||
const bytecode = toBytecode(`
|
||||
MAKE_FUNCTION (x @kwargs) #6
|
||||
MAKE_FUNCTION (x @named) #6
|
||||
PUSH 10
|
||||
PUSH 1
|
||||
PUSH 0
|
||||
CALL
|
||||
HALT
|
||||
LOAD kwargs
|
||||
LOAD named
|
||||
RETURN
|
||||
`)
|
||||
|
||||
const result = await new VM(bytecode).run()
|
||||
// kwargs should be empty dict
|
||||
// named should be empty dict
|
||||
expect(result.type).toBe('dict')
|
||||
if (result.type === 'dict') {
|
||||
expect(result.value.size).toBe(0)
|
||||
|
|
@ -360,7 +360,7 @@ test("CALL - named args with no extra named args", async () => {
|
|||
|
||||
test("CALL - named args with defaults on fixed params", async () => {
|
||||
const bytecode = toBytecode(`
|
||||
MAKE_FUNCTION (x=5 @kwargs) #7
|
||||
MAKE_FUNCTION (x=5 @named) #7
|
||||
PUSH "name"
|
||||
PUSH "Alice"
|
||||
PUSH 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user