Skip to content

Commit 3b5647f

Browse files
gh-191: Make EXTEND use STR.
Closes #191.
1 parent df9f37d commit 3b5647f

19 files changed

Lines changed: 27 additions & 111 deletions

docs/SPECIFICATION.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@
647647

648648
The interpreter MUST NOT load `.prex` pointer files.
649649

650-
Runtime extension loading from Prefix source MUST use `EXTEND(EXTENSION ext)`, defined in [9.1.8](#918-function-and-module-operators). The `ext` specifier MUST exclude the platform filename suffix (`.dll`, `.so`, `.dylib`) and MAY use package semantics with `..`. When `ext` names a package, the loader MUST attempt `ext..init`.
650+
Runtime extension loading from Prefix source MUST use `EXTEND(STR ext)`, defined in [9.1.8](#918-function-and-module-operators). The `ext` specifier MUST exclude the platform filename suffix (`.dll`, `.so`, `.dylib`) and MAY use package semantics with `..`. When `ext` names a package, the loader MUST attempt `ext..init`.
651651

652652
`EXTEND` MUST resolve extension libraries using the same extension search roots used by compiled-library loading: the calling module directory when available, then current working directory, then interpreter `ext/std`, `ext/usr`, `lib/std`, and `lib/usr` roots with bundled roots consulted before user roots.
653653

@@ -687,8 +687,6 @@
687687

688688
`MODULE` is a pseudo-type indicating that the argument MUST be a plain unquoted module identifier or a package-qualified module name using the language's `..` separator. A comma-separated signature such as `+, -, *` denotes a family of distinct operators that share the same argument rules and differ only in the named operation.
689689

690-
`EXTENSION` is a pseudo-type indicating that the argument MUST be a plain unquoted extension specifier used by `EXTEND`, excluding the platform filename suffix and optionally using package-style `..` separators.
691-
692690
`INT` and `FLT` are not interoperable. Unless an operator explicitly permits or requires type mixing, all numeric operands MUST share the same numeric type; supplying mismatched types MUST raise a runtime error. The numeric base of an operation's result MUST be the highest base present among its numeric operands.
693691

694692
Built-in operator names MUST be matched case-sensitively and MUST be written in their canonical form. A user-defined function MUST NOT share a name with any built-in operator; such a conflict MUST raise a runtime error. Extensions MAY register additional operators, which are dispatched through the same call syntax and MAY be qualified with a dotted extension-name prefix, but whose names MUST NOT conflict with those of built-in operators.
@@ -847,7 +845,7 @@
847845
848846
- `BOOL MAIN()` = MUST return `TRUE` when the call site is in the primary program source and `FALSE` when the call site is executing from imported module code. The result MUST depend on the source location of the call expression rather than on the dynamic caller stack.
849847
850-
- `BOOL EXTEND(EXTENSION ext)` = MUST load the compiled extension designated by `ext`. The specifier `ext` MUST exclude the platform filename suffix and MAY use package-style `..` separators. If `ext` resolves to a package, the loader MUST attempt `ext..init`. Relative extension names MUST resolve using the calling module's source directory when available, then the current working directory, then the configured extension roots `ext/std`, `ext/usr`, `lib/std`, and `lib/usr` (with bundled roots searched before user roots). Repeating an `EXTEND` request for an already-loaded extension exposure MUST be idempotent. On success, `EXTEND` MUST return `FALSE`.
848+
-- `BOOL EXTEND(STR ext)` = MUST load the compiled extension designated by `ext`. The specifier `ext` MUST exclude the platform filename suffix and MAY use package-style `..` separators. If `ext` resolves to a package, the loader MUST attempt `ext..init`. Relative extension names MUST resolve using the calling module's source directory when available, then the current working directory, then the configured extension roots `ext/std`, `ext/usr`, `lib/std`, and `lib/usr` (with bundled roots searched before user roots). Repeating an `EXTEND` request for an already-loaded extension exposure MUST be idempotent. On success, `EXTEND` MUST return `FALSE`.
851849

852850
Operators registered with the module-restriction flag (for example `PREFIX_EXTENSION_MODULE_RESTRICTED`) MUST be exposed only under the extending module's namespace. Importing that module MUST expose the extension namespace qualifier under the importing module's qualified name. `PREFIX_EXTENSION_ASMODULE` by itself MUST NOT restrict an operator to the extending module's namespace or cause that qualified exposure. Extension side effects outside operator registration (for example process-global hooks or host-side state) remain global.
853851

lib/std/gui/init.pre

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
! `image`-interoperable GUI operations for Prefix
22

3-
EXTEND(EXTENSION: gui)
3+
EXTEND("gui")
44

55
FUNC INT SCREEN_WIDTH(){
66
RETURN(SCREEN()[0d1])

lib/std/image/init.pre

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
! The returned tensor layout is [column][row][channel] (width, height, channel)
55
! in user code. Channels are ordered r, g, b, a and values are 0..255.
66

7-
EXTEND(EXTENSION: win32)
7+
EXTEND("win32")
88

9-
EXTEND(EXTENSION: image)
9+
EXTEND("image")
1010

1111
IMPORT("path")
1212

src/builtins.c

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9736,20 +9736,15 @@ static Value builtin_exit(Interpreter *interp, Value *args, int argc, Expr **arg
97369736
}
97379737

97389738
static Value builtin_extend(Interpreter *interp, Value *args, int argc, Expr **arg_nodes, Env *env, int line, int col) {
9739-
if (argc < 1 || !arg_nodes || !arg_nodes[0]) {
9740-
RUNTIME_ERROR(interp, "EXTEND expects EXTENSION: name", line, col);
9739+
(void)arg_nodes;
9740+
9741+
if (argc < 1 || !args) {
9742+
RUNTIME_ERROR(interp, "EXTEND expects STR argument", line, col);
97419743
}
97429744

9743-
const char *spec = NULL;
9744-
Expr *spec_node = arg_nodes[0];
9745+
EXPECT_STR(args[0], "EXTEND", interp, line, col);
97459746

9746-
if (spec_node->type == EXPR_TYPED_IDENT) {
9747-
spec = spec_node->as.typed_ident.name;
9748-
} else if (spec_node->type == EXPR_IDENT) {
9749-
spec = spec_node->as.ident;
9750-
} else if (args && args[0].type == VAL_STR) {
9751-
spec = args[0].as.s;
9752-
}
9747+
const char *spec = args[0].as.s;
97539748

97549749
if (!spec || spec[0] == '\0') {
97559750
RUNTIME_ERROR(interp, "EXTEND expects a non-empty extension specifier", line, col);

src/interpreter.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,10 +1406,6 @@ Value eval_expr(Interpreter *interp, Expr *expr, Env *env) {
14061406
deferred_async_arg0 = true;
14071407
continue;
14081408
}
1409-
if (strcmp(func_name, "EXTEND") == 0 && i == 0 && arg_expr->type == EXPR_TYPED_IDENT) {
1410-
// EXTEND(EXTENSION: name) passes the symbolic extension specifier.
1411-
continue;
1412-
}
14131409
if ((strcmp(func_name, "DEL") == 0 || strcmp(func_name, "EXIST") == 0 ||
14141410
strcmp(func_name, "ASSIGN") == 0) &&
14151411
i == 0) {

src/parser.c

Lines changed: 2 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -556,72 +556,6 @@ static Expr *parse_typed_ident_expr(Parser *parser) {
556556
return expr_typed_ident(dtype, name, type_tok.line, type_tok.column);
557557
}
558558

559-
static Expr *parse_extension_spec_expr(Parser *parser) {
560-
Token type_tok = parser->current_token;
561-
562-
if (type_tok.type == TOKEN_IDENT && type_tok.literal && strcmp(type_tok.literal, "EXTENSION") == 0 &&
563-
parser->next_token.type == TOKEN_COLON) {
564-
advance(parser);
565-
consume(parser, TOKEN_COLON, "Expected ':' after EXTENSION");
566-
567-
if (parser->current_token.type != TOKEN_IDENT) {
568-
report_error(parser, "Expected extension specifier after EXTENSION:");
569-
return NULL;
570-
}
571-
} else if (parser->current_token.type != TOKEN_IDENT) {
572-
report_error(parser, "Expected extension specifier");
573-
return NULL;
574-
}
575-
576-
char *name = parser->current_token.literal;
577-
advance(parser);
578-
579-
while (parser->current_token.type == TOKEN_DOT) {
580-
advance(parser); // consume first dot
581-
582-
int is_package_sep = 0;
583-
if (parser->current_token.type == TOKEN_DOT) {
584-
is_package_sep = 1;
585-
advance(parser); // consume second dot
586-
}
587-
588-
if (parser->current_token.type != TOKEN_IDENT) {
589-
report_error(parser, "Expected identifier after extension separator");
590-
return NULL;
591-
}
592-
593-
const char *part = parser->current_token.literal;
594-
size_t cur_len = strlen(name);
595-
size_t part_len = strlen(part);
596-
size_t sep_len = is_package_sep ? 2 : 1;
597-
size_t new_len = cur_len + sep_len + part_len + 1;
598-
char *tmp = realloc(name, new_len);
599-
if (!tmp) {
600-
fprintf(stderr, "Out of memory\n");
601-
exit(1);
602-
}
603-
name = tmp;
604-
if (is_package_sep) {
605-
name[cur_len] = '.';
606-
name[cur_len + 1] = '.';
607-
if (part_len) {
608-
memcpy(name + cur_len + 2, part, part_len);
609-
}
610-
name[cur_len + 2 + part_len] = '\0';
611-
} else {
612-
name[cur_len] = '.';
613-
if (part_len) {
614-
memcpy(name + cur_len + 1, part, part_len);
615-
}
616-
name[cur_len + 1 + part_len] = '\0';
617-
}
618-
619-
advance(parser);
620-
}
621-
622-
return expr_typed_ident(TYPE_UNKNOWN, name, type_tok.line, type_tok.column);
623-
}
624-
625559
static Expr *parse_primary(Parser *parser) {
626560
Token token = parser->current_token;
627561
// Recognize FLT literal names `INF` and `NaN` as primary expressions
@@ -830,15 +764,8 @@ static Expr *parse_call(Parser *parser) {
830764
call->as.call.kw_count == 0 && is_type_token(parser->current_token.type) &&
831765
(parser->next_token.type == TOKEN_IDENT || parser->next_token.type == TOKEN_COLON)) != 0;
832766

833-
bool is_extend_specifier =
834-
(call->as.call.callee->type == EXPR_IDENT &&
835-
strcmp(call->as.call.callee->as.ident, "EXTEND") == 0 && call->as.call.args.count == 0 &&
836-
call->as.call.kw_count == 0 && parser->current_token.type == TOKEN_IDENT &&
837-
parser->next_token.type != TOKEN_EQUALS) != 0;
838-
839-
if (is_typed_assign_target || is_extend_specifier) {
840-
Expr *arg = (int)is_extend_specifier ? parse_extension_spec_expr(parser)
841-
: parse_typed_ident_expr(parser);
767+
if (is_typed_assign_target) {
768+
Expr *arg = parse_typed_ident_expr(parser);
842769
if (!arg) {
843770
return NULL;
844771
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
EXTEND(prefix_missing_extension_target)
1+
EXTEND("prefix_missing_extension_target")

tests/cases/passing/extend-explicit-suffix-rejected.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ try {
1313
Copy-Item -Path $coreExtension -Destination $stagedExtension -Force
1414

1515
Set-Content -Path $programPath -Encoding Ascii -Value @"
16-
BOOL loaded = EXTEND($extensionSpecifier)
16+
BOOL loaded = EXTEND("$extensionSpecifier")
1717
REFUTE(loaded)
1818
"@
1919

tests/cases/passing/extend-search-cwd-before-roots.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ try {
2828
Add-StagedProbe $libStdProbe
2929
Add-StagedProbe $libUsrProbe
3030

31-
$sourceText = 'BOOL loaded=EXTEND(search_probe);REFUTE(loaded);PRINT(TST_SEARCH_PROBE_PATH())'
31+
$sourceText = 'BOOL loaded=EXTEND("search_probe");REFUTE(loaded);PRINT(TST_SEARCH_PROBE_PATH())'
3232
$result = Invoke-PrefixWithArguments -Arguments @('-source', $sourceText) -WorkingDirectory $cwdDir
3333
Assert-PrefixSuccess $result
3434
Assert-NoErrorOutput $result

tests/cases/passing/extend-search-ext-std-before-ext-usr.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ try {
2525
Add-StagedProbe $libUsrProbe
2626

2727
Set-Content -Path $programPath -Encoding Ascii -Value @'
28-
BOOL loaded = EXTEND(search_probe)
28+
BOOL loaded = EXTEND("search_probe")
2929
REFUTE(loaded)
3030
PRINT(TST_SEARCH_PROBE_PATH())
3131
'@

0 commit comments

Comments
 (0)