diff --git a/docs/WAM_JAVASCRIPT_STATUS.md b/docs/WAM_JAVASCRIPT_STATUS.md index bd8d68d84..171c062da 100644 --- a/docs/WAM_JAVASCRIPT_STATUS.md +++ b/docs/WAM_JAVASCRIPT_STATUS.md @@ -63,8 +63,11 @@ Key, `@<`/`@>`/`<`/`>`), **`predsort/3`** (`compare/3` and 3-arg callables), Atom/string: **`atom_concat/3`**, **`string_concat/3`**, **`atom_length/2`**, **`atom_chars/2`**, **`string_chars/2`**, **`atom_codes/2`**, **`char_code/2`**, **`sub_atom/5`** (ground-Atom; enumerates unbound Before/Length/After), -**`atom_string/2`**, **`number_codes/2`**, **`number_string/2`**, -**`split_string/4`**, **`upcase_atom/2`**, **`downcase_atom/2`**. +**`atom_string/2`**, **`string_to_atom/2`**, **`string/1`**, +**`number_codes/2`**, **`number_string/2`**, **`split_string/4`**, +**`upcase_atom/2`**, **`downcase_atom/2`**. Distinct **`string` tag** +(`V.String`): string-producing builtins yield strings; `atom/1` is +false for them. `write/1` prints the text; `format` `~q` quotes with `"`. I/O: `write/1`, `nl/0`, `writeln/1`, **`format/2`**, **`format/3`** (`~w ~a ~d ~p ~q ~n ~s ~t ~~`; `atom(A)` / `string(S)` sinks), **`tab/1`**. @@ -86,8 +89,8 @@ builtin metacall), **`bagof/3`** / **`setof/3`** (ISO free-var grouping, `^/2` existential quantification, empty-goal failure), **`aggregate_all/3`** for `count` / `sum(X)` / `bag(X)` / `set(X)`. -Types: `atom/1`, `integer/1`, `float/1`, `number/1`, `compound/1`, `var/1`, -`nonvar/1`, `is_list/1`, `ground/1`. +Types: `atom/1`, `integer/1`, `float/1`, `number/1`, `string/1`, `compound/1`, +`var/1`, `nonvar/1`, `is_list/1`, `ground/1`. ## Runtime term parser (G-W2) @@ -97,7 +100,8 @@ goes through `parse_cli_atom_or_int`, which now calls the same reader (unreadable text still interned as an atom). **Full:** integers (including a leading `-` after start/`(`/`[`/`,`/`|`), -floats (`3.14`, `-1.5`, `1.0e2`), bare atoms, quoted atoms (`'hi there'` +floats (`3.14`, `-1.5`, `1.0e2`), bare atoms, quoted atoms (`'hi there'`), +double-quoted strings (`"hi"` → string tag), with `\'` / `\\` escapes), variables (`X`, `_`, shared names), `[]`, proper lists `[a,b,c]`, partial lists `[H|T]`, compounds `foo(a, bar(b), 3)`, and parentheses. Cons intern as `[|]/2` + the `[]` @@ -133,13 +137,13 @@ parser, not the bundled portable `compiled(prolog_term_parser)`. | Builtin | Status | |---|---| | `bagof/3` | **Implemented.** ISO witness grouping (one bag per distinct free-var binding, SWI encounter order), `Var^Goal` / nested `V1^V2^Goal` stripped from the witness set, fails when Goal has no solutions. | -| `setof/3` | **Implemented.** `bagof` then per-group standard-order sort + dedup. Order: Var < Number < Atom < String < Compound; compounds by arity, functor **name**, then args L-to-R (matches SWI mixed-type lists). | +| `setof/3` | **Implemented.** `bagof` then per-group standard-order sort + dedup. Order: Var < Number < **String** < Atom < Compound (SWI 9.0.4); compounds by arity, functor **name**, then args L-to-R (matches SWI mixed-type lists). | | `term_variables/2` | **Implemented.** Distinct unbound vars, first-occurrence L-to-R depth-first. Cyclic compounds are visited once (same `seen` walk as `copy_term`). | | `numbervars/3` | **Implemented.** Binds+trails each distinct unbound var to `'$VAR'(N)` from Start; End is Start+count. `write/1` prints `'$VAR'(N)` literally — it does **not** letter-style SWI rendering (`A`, `B`, …). | | `=@=/2` / `\=@=/2` | **Implemented.** Variant equality: ground as `==`; vars match via a consistent bijection. Cyclic struct pairs are treated as already-equal once seen. | | `format/2` `/3` | **Implemented** for `~w ~a ~d ~p ~q ~n ~s ~t ~~`. Not ported: `~f`, `~r`, `~D`, positioning (`~N|`, `~+`, `t~`), aliases, and stream sinks other than stdout / `atom(A)` / `string(S)`. | | `sub_atom/5` | **Implemented** when Atom is ground; enumerates unbound Before/Length/After (and filters a ground SubAtom). | -| `atom_string/2` / `split_string/4` | **Implemented** but the runtime has no distinct string tag — results intern as atoms (write/== match SWI for the probe suite). | +| String term tag | **Implemented.** `V.String` is a distinct tag. Unify/`==` require equal strings (not atoms). Standard order / `compare/3` / `sort` matches SWI 9.0.4: Var < Number < **String** < Atom < Compound (`"foo" @< foo`). `atom_string/2`, `string_concat/3`, `string_chars/2` (construct), `string_to_atom/2`, `number_string/2`, `split_string/4` produce strings. `string/1` is true only for the tag. `write/1` prints text; `~q` quotes with `"`. The shared WAM tokeniser stores constants as text, so compiled `"foo"` literals collapse to atoms; construct strings via builtins or the Pratt `"..."` reader. Fact-source JSON/TSV values still intern as atoms. | | `library(assoc)` | **Implemented** as a Prolog `assoc/1` list of Key-Value pairs (not SWI's AVL tree). get/put/list/keys match SWI for unique-key maps. | | First-arg indexing | **Implemented.** `switch_on_constant` / `_fallthrough` / `_a2`, `switch_on_structure` / `_a2`, and `switch_on_term` / `_a2` jump to the matching clause group. Ground first-arg with a unique clause leaves no choice point (`deterministic/0`). Unbound first arg falls through to the try/retry/trust chain (no lost solutions). Exclusive miss fails; fallthrough variants keep the chain for variable-headed clauses. Dedicated `try`/`retry`/`trust` dispatch chains are emitted for multi-clause groups. | | Second-arg / deep indexing | A2 switches are implemented; deep (argument >2) indexing is not. | @@ -169,14 +173,15 @@ CONFORMANCE_TARGETS=javascript swipl -q -g run_tests -t halt \ Residual ISO corners not covered: bagof/setof of *unbound* free vars (two solutions that leave the same witness unbound) is grouped by copied -variable name rather than `@=`; the runtime has no distinct string tag, -so String vs Atom order is unused and `atom_string`/`split_string` -intern results as atoms; `^/2` as a standalone metacall just +variable name rather than `@=`; `^/2` as a standalone metacall just runs the RHS; `format` does not implement `~f` / `~r` / column positioning; assoc is a list-of-pairs, not SWI's AVL tree; `numbervars` does not letter-render `'$VAR'(N)` on `write/1`; `op/3` is process-global (no module-local ops) and `current_op/3` is -not implemented. +not implemented; `writeq/1` as a standalone builtin is not registered +(quoted string rendering is via `format` `~q`); compiled `"foo"` +literals become atoms (shared WAM constant tokens); fact-source cells +stay atoms even when the host file looks like a quoted string. ## Document status @@ -186,5 +191,6 @@ breadth (sort, lists, atom/string, format, assoc), the G-W2 runtime term parser, G-W4 file-backed fact sources (TSV/CSV/JSONL; LMDB/CSR out of scope), the G-W3 term-meta family (`term_variables/2`, `numbervars/3`, `=@=/2`, `\=@=/2`), then G-W2 `op/3` (dynamic Pratt -table: infix + prefix + postfix). Source-verified against SWI-Prolog -as the oracle (2026-08-30). +table: infix + prefix + postfix), then a distinct string term tag +(`V.String`; string-producing builtins + standard order). +Source-verified against SWI-Prolog as the oracle (2026-08-30). diff --git a/src/unifyweaver/bindings/javascript_wam_bindings.pl b/src/unifyweaver/bindings/javascript_wam_bindings.pl index c6d01c66b..ab53bd9e5 100644 --- a/src/unifyweaver/bindings/javascript_wam_bindings.pl +++ b/src/unifyweaver/bindings/javascript_wam_bindings.pl @@ -13,6 +13,7 @@ % (read_term_from_atom, atom_to_term, term_to_atom) % and term-meta (term_variables, numbervars, =@=, \=@=) % plus op/3 (dynamic Pratt operator table) +% and a distinct string term tag (atom_string, split_string, …) :- module(javascript_wam_bindings, [ javascript_wam_builtin/3, % Name, Arity, Status @@ -92,6 +93,8 @@ javascript_wam_builtin(char_code, 2, implemented). javascript_wam_builtin(sub_atom, 5, implemented). javascript_wam_builtin(atom_string, 2, implemented). +javascript_wam_builtin(string_to_atom, 2, implemented). +javascript_wam_builtin(string, 1, implemented). javascript_wam_builtin(number_codes, 2, implemented). javascript_wam_builtin(number_string, 2, implemented). javascript_wam_builtin(split_string, 4, implemented). diff --git a/templates/targets/javascript_wam/runtime.js.mustache b/templates/targets/javascript_wam/runtime.js.mustache index a32adb2bd..75ab8a7d2 100644 --- a/templates/targets/javascript_wam/runtime.js.mustache +++ b/templates/targets/javascript_wam/runtime.js.mustache @@ -18,6 +18,7 @@ const Runtime = {}; V.Atom = function (id) { return { tag: "atom", id: id }; }; V.Int = function (n) { return { tag: "int", val: n | 0 }; }; V.Float = function (n) { return { tag: "float", val: +n }; }; +V.String = function (s) { return { tag: "string", val: String(s) }; }; V.Unbound = function (name) { return { tag: "unbound", name: name }; }; V.Struct = function (fid, args) { return { tag: "struct", fid: fid, args: args || [] }; }; @@ -208,6 +209,7 @@ Runtime.copy_term = function (state, v, seen) { if (v.tag === "atom") return V.Atom(v.id); if (v.tag === "int") return V.Int(v.val); if (v.tag === "float") return V.Float(v.val); + if (v.tag === "string") return V.String(v.val); if (v.tag === "unbound") return V.Unbound(v.name); return v; }; @@ -253,6 +255,7 @@ function same_atomic(a, b) { if (a.tag !== b.tag) return false; if (a.tag === "atom") return a.id === b.id; if (a.tag === "int" || a.tag === "float") return a.val === b.val; + if (a.tag === "string") return a.val === b.val; return false; } @@ -549,6 +552,7 @@ function term_to_string(program, state, term) { if (typeof term !== "object" || term === null) return String(term); if (term.tag === "atom") return Runtime.string_of(program.intern_table, term.id); if (term.tag === "int" || term.tag === "float") return String(term.val); + if (term.tag === "string") return String(term.val); if (term.tag === "unbound") return "_" + String(term.name); if (term.tag === "struct") { const items = list_items(program, state, term); @@ -594,7 +598,8 @@ function builtin_functor(program, state) { name = V.Atom(term.fid); arity = (term.args || []).length; } else if (typeof term === "object" && term !== null - && (term.tag === "atom" || term.tag === "int" || term.tag === "float")) { + && (term.tag === "atom" || term.tag === "int" || term.tag === "float" + || term.tag === "string")) { name = term; arity = 0; } else { @@ -634,7 +639,8 @@ function builtin_univ(program, state) { const items = [V.Atom(term.fid)].concat(term.args || []); out = Runtime.list_from_terms(items, program.intern_table); } else if (typeof term === "object" && term !== null - && (term.tag === "atom" || term.tag === "int" || term.tag === "float")) { + && (term.tag === "atom" || term.tag === "int" || term.tag === "float" + || term.tag === "string")) { out = Runtime.list_from_terms([term], program.intern_table); } else { return false; @@ -664,6 +670,7 @@ function copy_term_fresh(state, term, varMap, seen) { if (term.tag === "atom") return V.Atom(term.id); if (term.tag === "int") return V.Int(term.val); if (term.tag === "float") return V.Float(term.val); + if (term.tag === "string") return V.String(term.val); return term; } @@ -792,6 +799,7 @@ function exact_equal(state, a, b) { if (a.tag === "unbound") return a.name === b.name; if (a.tag === "atom") return a.id === b.id; if (a.tag === "int" || a.tag === "float") return a.val === b.val; + if (a.tag === "string") return a.val === b.val; if (a.tag === "struct") { if (a.fid !== b.fid || (a.args || []).length !== (b.args || []).length) return false; for (let i = 0; i < a.args.length; i++) { @@ -805,7 +813,7 @@ function exact_equal(state, a, b) { function type_builtin(program, state, pred) { const p = strip_trailing_arity(pred); const known = { - atom: true, integer: true, float: true, number: true, + atom: true, integer: true, float: true, number: true, string: true, compound: true, var: true, nonvar: true, is_list: true, ground: true }; if (!known[p]) return null; @@ -813,6 +821,7 @@ function type_builtin(program, state, pred) { if (p === "ground") return is_ground(state, val); if (typeof val !== "object" || val === null) return false; if (p === "atom") return val.tag === "atom"; + if (p === "string") return val.tag === "string"; if (p === "integer") return val.tag === "int"; if (p === "float") return val.tag === "float"; if (p === "number") return val.tag === "int" || val.tag === "float"; @@ -842,6 +851,7 @@ function term_key(state, v) { if (v.tag === "atom") return "a:" + String(v.id); if (v.tag === "int") return "i:" + String(v.val); if (v.tag === "float") return "f:" + String(v.val); + if (v.tag === "string") return "str:" + String(v.val); if (v.tag === "unbound") return "u:" + String(v.name); if (v.tag === "struct") { const parts = ["s:" + String(v.fid) + "/" + String((v.args || []).length)]; @@ -857,12 +867,13 @@ function term_ord(state, v) { if (typeof v !== "object" || v === null) return [0, String(v)]; if (v.tag === "unbound") return [1, v.name]; if (v.tag === "int" || v.tag === "float") return [2, v.val]; - if (v.tag === "atom") return [3, v.id]; + if (v.tag === "string") return [3, v.val]; + if (v.tag === "atom") return [4, v.id]; if (v.tag === "struct") { const args = v.args || []; const inner = []; for (let i = 0; i < args.length; i++) inner.push(term_ord(state, args[i])); - return [4, v.fid, args.length, inner]; + return [5, v.fid, args.length, inner]; } return [9, String(v)]; } @@ -930,13 +941,14 @@ function term_class_rank(v) { if (typeof v !== "object" || v === null) return 5; if (v.tag === "unbound") return 0; if (v.tag === "int" || v.tag === "float") return 1; - if (v.tag === "atom") return 2; - if (v.tag === "string") return 3; + // SWI 9.0.4: String @< Atom (compare(foo,"foo") = >). Match the oracle. + if (v.tag === "string") return 2; + if (v.tag === "atom") return 3; if (v.tag === "struct") return 4; return 5; } -// ISO §7.2 / SWI standard order: Var < Number < Atom < String < Compound. +// SWI 9.x standard order (oracle): Var < Number < String < Atom < Compound. // Atoms compare by name (not intern id). Compounds compare by arity, // then functor name, then arguments left-to-right. function compare_terms(intern, state, a, b) { @@ -954,15 +966,15 @@ function compare_terms(intern, state, a, b) { return a.val < b.val ? -1 : 1; } if (ca === 2) { + if (a.val === b.val) return 0; + return a.val < b.val ? -1 : 1; + } + if (ca === 3) { const na = Runtime.string_of(intern, a.id); const nb = Runtime.string_of(intern, b.id); if (na === nb) return 0; return na < nb ? -1 : 1; } - if (ca === 3) { - if (a.val === b.val) return 0; - return a.val < b.val ? -1 : 1; - } if (ca === 4) { const aa = (a.args || []).length; const ba = (b.args || []).length; @@ -1186,7 +1198,8 @@ function find_matching_end_aggregate(program, startPc) { function is_switch_constant(v) { return typeof v === "object" && v !== null - && (v.tag === "atom" || v.tag === "int" || v.tag === "float"); + && (v.tag === "atom" || v.tag === "int" || v.tag === "float" + || v.tag === "string"); } function switch_values_equal(a, b) { @@ -1199,6 +1212,7 @@ function switch_values_equal(a, b) { } if (a.tag === "atom") return a.id === b.id; if (a.tag === "int" || a.tag === "float") return a.val === b.val; + if (a.tag === "string") return a.val === b.val; return false; } @@ -1842,32 +1856,44 @@ function builtin_include_exclude(program, state, keep) { return bind_list(program, state, 3, out); } -function builtin_atom_concat(program, state) { +function concat_box(program, text, asString) { + return asString ? V.String(text) : intern_atom(program, text); +} + +function builtin_concat_family(program, state, asString) { const a = atomic_text(program, state, Runtime.get_reg(state, 1)); const b = atomic_text(program, state, Runtime.get_reg(state, 2)); const c = atomic_text(program, state, Runtime.get_reg(state, 3)); if (a !== null && b !== null) { - return bind_or_compare(state, 3, intern_atom(program, a + b), program); + return bind_or_compare(state, 3, concat_box(program, a + b, asString), program); } if (c === null) return false; if (a !== null) { if (c.indexOf(a) !== 0) return false; - return bind_or_compare(state, 2, intern_atom(program, c.slice(a.length)), program); + return bind_or_compare(state, 2, concat_box(program, c.slice(a.length), asString), program); } if (b !== null) { if (c.length < b.length || c.slice(c.length - b.length) !== b) return false; - return bind_or_compare(state, 1, intern_atom(program, c.slice(0, c.length - b.length)), program); + return bind_or_compare(state, 1, concat_box(program, c.slice(0, c.length - b.length), asString), program); } const sols = []; for (let i = 0; i <= c.length; i++) { sols.push([ - [1, intern_atom(program, c.slice(0, i))], - [2, intern_atom(program, c.slice(i))] + [1, concat_box(program, c.slice(0, i), asString)], + [2, concat_box(program, c.slice(i), asString)] ]); } return dispatch_enum(program, state, sols); } +function builtin_atom_concat(program, state) { + return builtin_concat_family(program, state, false); +} + +function builtin_string_concat(program, state) { + return builtin_concat_family(program, state, true); +} + function builtin_atom_length(program, state) { const t = atomic_text(program, state, Runtime.get_reg(state, 1)); if (t === null) return false; @@ -1884,6 +1910,16 @@ function builtin_atom_chars(program, state) { return bind_or_compare(state, 1, intern_atom(program, s), program); } +function builtin_string_chars(program, state) { + const t = atomic_text(program, state, Runtime.get_reg(state, 1)); + if (t !== null) return bind_list(program, state, 2, string_to_chars(program, t)); + const items = list_items(program, state, Runtime.get_reg(state, 2)); + if (items === null) return false; + const s = chars_to_string(program, state, items); + if (s === null) return false; + return bind_or_compare(state, 1, V.String(s), program); +} + function builtin_atom_codes(program, state) { const t = atomic_text(program, state, Runtime.get_reg(state, 1)); if (t !== null) { @@ -1949,10 +1985,20 @@ function builtin_sub_atom(program, state) { } function builtin_atom_string(program, state) { - const a = atomic_text(program, state, Runtime.get_reg(state, 1)); - const b = atomic_text(program, state, Runtime.get_reg(state, 2)); - if (a !== null) return bind_or_compare(state, 2, intern_atom(program, a), program); - if (b !== null) return bind_or_compare(state, 1, intern_atom(program, b), program); + const aText = atomic_text(program, state, Runtime.get_reg(state, 1)); + const bText = atomic_text(program, state, Runtime.get_reg(state, 2)); + if (aText !== null && bText !== null) return aText === bText; + if (aText !== null) return bind_or_compare(state, 2, V.String(aText), program); + if (bText !== null) return bind_or_compare(state, 1, intern_atom(program, bText), program); + return false; +} + +function builtin_string_to_atom(program, state) { + const sText = atomic_text(program, state, Runtime.get_reg(state, 1)); + const aText = atomic_text(program, state, Runtime.get_reg(state, 2)); + if (sText !== null && aText !== null) return sText === aText; + if (sText !== null) return bind_or_compare(state, 2, intern_atom(program, sText), program); + if (aText !== null) return bind_or_compare(state, 1, V.String(aText), program); return false; } @@ -1976,7 +2022,7 @@ function builtin_number_codes(program, state) { function builtin_number_string(program, state) { const n = Runtime.deref(state, Runtime.get_reg(state, 1)); if (typeof n === "object" && n !== null && (n.tag === "int" || n.tag === "float")) { - return bind_or_compare(state, 2, intern_atom(program, String(n.val)), program); + return bind_or_compare(state, 2, V.String(String(n.val)), program); } const s = atomic_text(program, state, Runtime.get_reg(state, 2)); if (s === null) return false; @@ -2017,9 +2063,9 @@ function builtin_split_string(program, state) { } parts.push(trimPad(buf)); } - const atoms = []; - for (let i = 0; i < parts.length; i++) atoms.push(intern_atom(program, parts[i])); - return bind_list(program, state, 4, atoms); + const strs = []; + for (let i = 0; i < parts.length; i++) strs.push(V.String(parts[i])); + return bind_list(program, state, 4, strs); } function builtin_case_atom(program, state, up) { @@ -2034,11 +2080,18 @@ function quote_atom_text(s) { return "'" + String(s).replace(/\\/g, "\\\\").replace(/'/g, "\\'") + "'"; } +function quote_string_text(s) { + return "\"" + String(s).replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\""; +} + function format_write_term(program, state, term, quoted) { term = Runtime.deref(state, term); if (quoted && term && term.tag === "atom") { return quote_atom_text(Runtime.string_of(program.intern_table, term.id)); } + if (quoted && term && term.tag === "string") { + return quote_string_text(term.val); + } return term_to_string(program, state, term); } @@ -2477,13 +2530,16 @@ Runtime.builtin_call = function (program, state, inst) { if (name === "select") return builtin_select(program, state); if (name === "include") return builtin_include_exclude(program, state, true); if (name === "exclude") return builtin_include_exclude(program, state, false); - if (name === "atom_concat" || name === "string_concat") return builtin_atom_concat(program, state); + if (name === "atom_concat") return builtin_atom_concat(program, state); + if (name === "string_concat") return builtin_string_concat(program, state); if (name === "atom_length") return builtin_atom_length(program, state); - if (name === "atom_chars" || name === "string_chars") return builtin_atom_chars(program, state); + if (name === "atom_chars") return builtin_atom_chars(program, state); + if (name === "string_chars") return builtin_string_chars(program, state); if (name === "atom_codes") return builtin_atom_codes(program, state); if (name === "char_code") return builtin_char_code(program, state); if (name === "sub_atom") return builtin_sub_atom(program, state); if (name === "atom_string") return builtin_atom_string(program, state); + if (name === "string_to_atom") return builtin_string_to_atom(program, state); if (name === "number_codes") return builtin_number_codes(program, state); if (name === "number_string") return builtin_number_string(program, state); if (name === "split_string") return builtin_split_string(program, state); @@ -3392,6 +3448,23 @@ Runtime.tokenize_term = function (text) { toks.push({ type: "atom", value: buf }); continue; } + if (c === "\"") { + i += 1; + let buf = ""; + while (i < n && s.charAt(i) !== "\"") { + if (s.charAt(i) === "\\" && i + 1 < n) { + buf += s.charAt(i + 1); + i += 2; + } else { + buf += s.charAt(i); + i += 1; + } + } + if (i >= n) return null; + i += 1; + toks.push({ type: "string", value: buf }); + continue; + } const after_sep = last_type() === null || last_type() === "comma" || last_type() === "semicolon" || last_type() === "lparen" || last_type() === "lbracket" || @@ -3495,6 +3568,10 @@ Runtime.wam_parse_primary = function (p, maxPrec) { if (tok.is_float) return V.Float(num); return V.Int(num); } + if (tok.type === "string") { + p.pos += 1; + return V.String(tok.value); + } if (tok.type === "var") { p.pos += 1; const name = tok.value; @@ -3525,7 +3602,8 @@ Runtime.wam_parse_primary = function (p, maxPrec) { if (op && op.prec <= maxPrec && p.pos < p.tokens.length) { const next = p.tokens[p.pos]; const starts = next.type === "number" || next.type === "var" || - next.type === "atom" || next.type === "lparen" || next.type === "lbracket"; + next.type === "atom" || next.type === "string" || + next.type === "lparen" || next.type === "lbracket"; if (starts) { const rhsMax = op.assoc === "fy" ? op.prec : op.prec - 1; const operand = Runtime.wam_parse_expr(p, rhsMax); diff --git a/tests/test_wam_javascript_builtins.pl b/tests/test_wam_javascript_builtins.pl index b35c71aaa..b48965e29 100644 --- a/tests/test_wam_javascript_builtins.pl +++ b/tests/test_wam_javascript_builtins.pl @@ -61,6 +61,7 @@ :- dynamic user:probe_term_meta/0. :- dynamic user:probe_op3/0. :- dynamic user:probe_parse_likes/0. +:- dynamic user:probe_string_tag/0. install_probes :- retractall(user:probe_findall), @@ -103,6 +104,7 @@ retractall(user:probe_term_meta), retractall(user:probe_op3), retractall(user:probe_parse_likes), + retractall(user:probe_string_tag), assertz((user:probe_findall :- findall(X, member(X, [1,2,3]), L), write(L), nl, L == [1,2,3])), assertz((user:probe_functor :- @@ -269,6 +271,20 @@ assertz((user:probe_parse_likes :- read_term_from_atom('alice likes bob', T), T == likes(alice, bob), + write(ok), nl)), + assertz((user:probe_string_tag :- + atom_string(a, S), string(S), \+ atom(S), + string_to_atom(S, A0), A0 == a, + split_string('a,b,c', ',', '', Parts), + Parts = [P1, P2, P3], + string(P1), string(P2), string(P3), + atom_string(a, EA), atom_string(b, EB), atom_string(c, EC), + P1 == EA, P2 == EB, P3 == EC, + string_concat(x, y, Z), string(Z), + atom_string(xy, EZ), Z == EZ, + atom_string(foo, SFoo), + sort([foo, SFoo, 1, bar], Ord), + Ord == [1, SFoo, bar, foo], write(ok), nl)). probe_preds([ @@ -306,7 +322,8 @@ user:probe_assoc/0, user:probe_parse_atom/0, user:probe_term_meta/0, - user:probe_op3/0 + user:probe_op3/0, + user:probe_string_tag/0 ]). :- dynamic user:ctw_js/0.