Skip to content

Commit 485f915

Browse files
gh-12: Fix optional kwargs.
1 parent d5d117c commit 485f915

File tree

3 files changed

+28
-3
lines changed

3 files changed

+28
-3
lines changed

docs/SPECIFICATION.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@
280280

281281
In addition to named functions, the language provides an anonymous function literal form `LAMBDA` which constructs a `FUNC` value without binding it to a function name in the global function table. The syntax is `LAMBDA(T1: arg1, T2: arg2, ..., TN: argN):R{ block }`. Parameter typing, default-value rules, call semantics, and return-type rules are the same as for `FUNC`. Evaluating a `LAMBDA` expression captures (closes over) the current lexical environment, producing a first-class `FUNC` value that can be assigned to variables, stored in tensors, passed as an argument, or returned.
282282

283-
A user-defined function is called with the same syntax as a built-in: `callee(expr1, expr2, ..., exprN)`. The callee may be any expression that evaluates to `FUNC`, including identifiers, tensor elements, or intermediate expressions. Calls may supply zero or more positional arguments (left-to-right) followed by zero or more keyword arguments of the form `param=expr`. Keyword arguments can only appear after all positional arguments. At the call site, every positional argument is bound to the next positional parameter; keyword arguments must match the name of a parameter that declared a default value. Duplicate keyword names, supplying too many positional arguments, or providing a keyword for an unknown parameter are runtime errors. If a keyword-capable parameter is omitted from the call, its default expression is evaluated at call time in the function's lexical environment after earlier parameters have been bound. The evaluated default must match the parameter's declared type. Built-in functions do not accept keyword arguments except that `READFILE` and `WRITEFILE` allow a single optional `coding=` keyword; attempting to pass any other keyword raises a runtime error. Arguments are evaluated left-to-right. The function body executes in a new environment (activation record) that closes over the defining environment. If a `RETURN(v)` statement is executed, the function terminates immediately and yields `v`; the returned value must match the declared return type. If control reaches the end of the body without `RETURN`, the function returns a default value of the declared return type (0 for `INT`, 0.0 for `FLT`, "" for `STR`). Functions whose return type is `TNS` or `FUNC` must execute an explicit `RETURN` of the declared type; reaching the end of the body without returning is a runtime error for `TNS`- or `FUNC`-returning functions.
283+
A user-defined function is called with the same syntax as a built-in: `callee(expr1, expr2, ..., exprN)`. The callee may be any expression that evaluates to `FUNC`, including identifiers, tensor elements, or intermediate expressions. Calls may supply zero or more positional arguments (left-to-right) followed by zero or more keyword arguments of the form `param=expr`. Keyword arguments can only appear after all positional arguments. At the call site, every positional argument is bound to the next positional parameter; keyword arguments must match the name of a parameter that declared a default value. Duplicate keyword names, supplying too many positional arguments, or providing a keyword for an unknown parameter are runtime errors. If a keyword-capable parameter is omitted from the call, its default expression is evaluated at call time in the function's lexical environment after earlier parameters have been bound. The evaluated default must match the parameter's declared type. Arguments are evaluated left-to-right. The function body executes in a new environment (activation record) that closes over the defining environment. If a `RETURN(v)` statement is executed, the function terminates immediately and yields `v`; the returned value must match the declared return type. If control reaches the end of the body without `RETURN`, the function returns a default value of the declared return type (0 for `INT`, 0.0 for `FLT`, "" for `STR`). Functions whose return type is `TNS` or `FUNC` must execute an explicit `RETURN` of the declared type; reaching the end of the body without returning is a runtime error for `TNS`- or `FUNC`-returning functions.
284284

285285
Because `FUNC` is a first-class type, functions can be assigned to variables, stored inside tensors, passed as arguments, or returned from other functions. Calling `alias()` invokes the function bound to `alias`, while `tns[1]()` invokes the `FUNC` stored in the first tensor slot. Equality compares identity: two `FUNC` values are equal only if they refer to the same function object.
286286
@@ -810,7 +810,7 @@
810810
811811
### 12.14 Rounding
812812
813-
- `ROUND(FLT: float, STR: mode="floor", INT: ndigits=0):FLT` - Round `float` to `ndigits` places right of the radix point (binary places; `ndigits` may be negative). When exactly two arguments are supplied and the second is an `INT`, it is treated as `ndigits` with the mode defaulting to `"floor"`. Modes are:
813+
- `ROUND(FLT: float, STR: mode = "floor", INT: ndigits = 0):FLT` - Round `float` to `ndigits` places right of the radix point (binary places; `ndigits` may be negative). When exactly two arguments are supplied and the second is an `INT`, it is treated as `ndigits` with the mode defaulting to `"floor"`. Modes are:
814814

815815
- `"floor"` - round toward -
816816

src/interpreter.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,31 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
795795
}
796796
}
797797

798+
// Count positional-only parameters (those without a default value).
799+
// Per spec: "A parameter without a default is positional; a parameter
800+
// with a default is keyword-capable." Positional arguments may only
801+
// bind to positional parameters.
802+
int num_pos_params = 0;
803+
for (size_t pi = 0; pi < user_func->params.count; pi++) {
804+
if (!user_func->params.items[pi].default_value) num_pos_params++;
805+
else break; // positional params must precede keyword-capable ones
806+
}
807+
808+
if (pos_argc > num_pos_params) {
809+
char buf[128];
810+
snprintf(buf, sizeof(buf), "Too many positional arguments for '%s'",
811+
user_func->name ? user_func->name : "<lambda>");
812+
interp->error = strdup(buf);
813+
interp->error_line = expr->line;
814+
interp->error_col = expr->column;
815+
for (int t = 0; t < pos_argc; t++) value_free(pos_vals[t]);
816+
free(pos_vals);
817+
for (int t = 0; t < kwc; t++) value_free(kw_vals[t]);
818+
free(kw_vals);
819+
if (kw_used) free(kw_used);
820+
return value_null();
821+
}
822+
798823
// Create new environment for function call
799824
Env* call_env = env_create(user_func->closure);
800825

test2.pre

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ FUNC GREET(STR: name, STR: prefix = "Hello"):STR{
605605
RETURN(JOIN(" ", prefix, name))
606606
}
607607
ASSERT(EQ(GREET("World"), "Hello World"))
608-
ASSERT(EQ(GREET("World", "Hi"), "Hi World"))
608+
ASSERT(EQ(GREET("World", prefix = "Hi"), "Hi World"))
609609
PRINT("Functions: PASS\n")
610610

611611
PRINT("Testing SIGNATURE...")

0 commit comments

Comments
 (0)