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)
|
- Dictionary operations (MAKE_DICT, DICT_GET, DICT_SET, DICT_HAS)
|
||||||
- Function operations (MAKE_FUNCTION, CALL, TAIL_CALL, RETURN) with parameter binding
|
- Function operations (MAKE_FUNCTION, CALL, TAIL_CALL, RETURN) with parameter binding
|
||||||
- Variadic functions with positional rest parameters (`...rest`)
|
- 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
|
- Mixed positional and named arguments with proper priority binding
|
||||||
- Tail call optimization with unbounded recursion (10,000+ iterations without stack overflow)
|
- 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
|
- 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
|
// Check if named argument was provided for this param
|
||||||
if (namedArgs.has(paramName)) {
|
if (namedArgs.has(paramName)) {
|
||||||
this.scope.set(paramName, namedArgs.get(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) {
|
} else if (positionalArgs[i] !== undefined) {
|
||||||
this.scope.set(paramName, positionalArgs[i]!)
|
this.scope.set(paramName, positionalArgs[i]!)
|
||||||
} else if (fn.defaults[paramName] !== undefined) {
|
} 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)
|
// Handle named parameter (collect remaining named args that didn't match params)
|
||||||
if (fn.named) {
|
if (fn.named) {
|
||||||
const namedParamName = fn.params[fn.params.length - 1]!
|
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) {
|
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
|
// subtract 1 because pc was incremented
|
||||||
|
|
@ -488,11 +488,11 @@ export class VM {
|
||||||
// Handle named parameter
|
// Handle named parameter
|
||||||
if (tailFn.named) {
|
if (tailFn.named) {
|
||||||
const namedParamName = tailFn.params[tailFn.params.length - 1]!
|
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) {
|
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
|
// 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 () => {
|
test("CALL - named args function with no fixed params", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
MAKE_FUNCTION (@kwargs) #9
|
MAKE_FUNCTION (@named) #9
|
||||||
PUSH "name"
|
PUSH "name"
|
||||||
PUSH "Bob"
|
PUSH "Bob"
|
||||||
PUSH "age"
|
PUSH "age"
|
||||||
|
|
@ -210,7 +210,7 @@ test("CALL - named args function with no fixed params", async () => {
|
||||||
PUSH 2
|
PUSH 2
|
||||||
CALL
|
CALL
|
||||||
HALT
|
HALT
|
||||||
LOAD kwargs
|
LOAD named
|
||||||
RETURN
|
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 () => {
|
test("CALL - named args function with one fixed param", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
MAKE_FUNCTION (x @kwargs) #8
|
MAKE_FUNCTION (x @named) #8
|
||||||
PUSH 10
|
PUSH 10
|
||||||
PUSH "name"
|
PUSH "name"
|
||||||
PUSH "Alice"
|
PUSH "Alice"
|
||||||
|
|
@ -232,7 +232,7 @@ test("CALL - named args function with one fixed param", async () => {
|
||||||
PUSH 1
|
PUSH 1
|
||||||
CALL
|
CALL
|
||||||
HALT
|
HALT
|
||||||
LOAD kwargs
|
LOAD named
|
||||||
RETURN
|
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(`
|
const bytecode = toBytecode(`
|
||||||
MAKE_FUNCTION (name @kwargs) #8
|
MAKE_FUNCTION (name @named) #8
|
||||||
PUSH "Bob"
|
PUSH "Bob"
|
||||||
PUSH "age"
|
PUSH "age"
|
||||||
PUSH 50
|
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()
|
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' })
|
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(`
|
const bytecode = toBytecode(`
|
||||||
MAKE_FUNCTION (name age @kwargs) #9
|
MAKE_FUNCTION (name age @named) #9
|
||||||
PUSH "name"
|
PUSH "name"
|
||||||
PUSH "Bob"
|
PUSH "Bob"
|
||||||
PUSH "city"
|
PUSH "city"
|
||||||
|
|
@ -274,14 +274,14 @@ test("CALL - named args that match param names should not be in kwargs", async (
|
||||||
PUSH 2
|
PUSH 2
|
||||||
CALL
|
CALL
|
||||||
HALT
|
HALT
|
||||||
LOAD kwargs
|
LOAD named
|
||||||
RETURN
|
RETURN
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const result = await new VM(bytecode).run()
|
const result = await new VM(bytecode).run()
|
||||||
expect(result.type).toBe('dict')
|
expect(result.type).toBe('dict')
|
||||||
if (result.type === '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.get('city')).toEqual({ type: 'string', value: 'NYC' })
|
||||||
expect(result.value.has('name')).toBe(false)
|
expect(result.value.has('name')).toBe(false)
|
||||||
expect(result.value.size).toBe(1)
|
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 () => {
|
test("CALL - mixed variadic and named args", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
MAKE_FUNCTION (x ...rest @kwargs) #10
|
MAKE_FUNCTION (x ...rest @named) #10
|
||||||
PUSH 1
|
PUSH 1
|
||||||
PUSH 2
|
PUSH 2
|
||||||
PUSH 3
|
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(`
|
const bytecode = toBytecode(`
|
||||||
MAKE_FUNCTION (x ...rest @kwargs) #10
|
MAKE_FUNCTION (x ...rest @named) #10
|
||||||
PUSH 1
|
PUSH 1
|
||||||
PUSH 2
|
PUSH 2
|
||||||
PUSH 3
|
PUSH 3
|
||||||
|
|
@ -327,7 +327,7 @@ test("CALL - mixed variadic and named args, check kwargs", async () => {
|
||||||
PUSH 1
|
PUSH 1
|
||||||
CALL
|
CALL
|
||||||
HALT
|
HALT
|
||||||
LOAD kwargs
|
LOAD named
|
||||||
RETURN
|
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 () => {
|
test("CALL - named args with no extra named args", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
MAKE_FUNCTION (x @kwargs) #6
|
MAKE_FUNCTION (x @named) #6
|
||||||
PUSH 10
|
PUSH 10
|
||||||
PUSH 1
|
PUSH 1
|
||||||
PUSH 0
|
PUSH 0
|
||||||
CALL
|
CALL
|
||||||
HALT
|
HALT
|
||||||
LOAD kwargs
|
LOAD named
|
||||||
RETURN
|
RETURN
|
||||||
`)
|
`)
|
||||||
|
|
||||||
const result = await new VM(bytecode).run()
|
const result = await new VM(bytecode).run()
|
||||||
// kwargs should be empty dict
|
// named should be empty dict
|
||||||
expect(result.type).toBe('dict')
|
expect(result.type).toBe('dict')
|
||||||
if (result.type === 'dict') {
|
if (result.type === 'dict') {
|
||||||
expect(result.value.size).toBe(0)
|
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 () => {
|
test("CALL - named args with defaults on fixed params", async () => {
|
||||||
const bytecode = toBytecode(`
|
const bytecode = toBytecode(`
|
||||||
MAKE_FUNCTION (x=5 @kwargs) #7
|
MAKE_FUNCTION (x=5 @named) #7
|
||||||
PUSH "name"
|
PUSH "name"
|
||||||
PUSH "Alice"
|
PUSH "Alice"
|
||||||
PUSH 0
|
PUSH 0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user