Commit Graph
18 Commits
Author SHA1 Message Date
dotCooCoo f526c5310c JS: XML and XMLList xpath evaluates XPath 1.0 expressions
xml.xpath(expression[, variables]) evaluates an XPath 1.0 expression
(https://www.w3.org/TR/1999/REC-xpath-19991116/) with the XML value as
the context node and returns the selected nodes as an XMLList, in
document order and without repeats. On an XMLList the expression runs
on each element in turn, and the results are joined.

- All 13 axes and the 27 core functions. id() selects nothing, since
  E4X trees carry no DTD data.
- Each property of `variables` binds the variable of its name. A
  prefix in a name test resolves through the context element's
  in-scope namespaces; `xml` is always bound.
- An expression that breaks the grammar gives a SyntaxError. An
  unknown function, a wrong argument count, an unbound prefix or
  variable, a result that is not a node-set, and a result that holds
  the root or a namespace node give a TypeError.
- The parser and the evaluator keep their state on their own stacks,
  so expressions of any depth use no native stack; nesting past
  Options::max_depth gives "nesting too deep". Traversal stacks, node
  lists, strings and the values that string functions build count
  against the heap limit, and every node visited and string read takes
  fuel.

Docs: docs/target-app-parity.md (E4X line), crate doc in lib.rs.
2026-11-11 21:01:32 -08:00
BFlat 1e76791ae1 Merge pull request #6615 from dotCooCoo/feat/script-file-folder
Scripts: File and Folder read files in the script's own folder
2026-11-12 01:35:45 +07:00
dotCooCoo c4d323eb28 Merge main into feat/js-e4x-xml
# Conflicts:
#	crates/js/src/interp.rs
2026-11-11 07:48:21 -08:00
dotCooCoo ba05554c64 Merge main into feat/script-file-folder 2026-11-11 07:47:42 -08:00
dotCooCoo b7a6196994 Scripts: File and Folder read files in the script's own folder
Scripts get the File and Folder classes as built-ins of the script
engine. A script run from a file can test whether files exist, read
their properties, list folders with getFiles (a mask or a function),
and open files to read them with read, readln, readch, seek and tell.

- Reads stay inside the script's folder and, when automation read
  roots are set, inside them too. A script given as text reads no
  files. Network and device paths, and paths outside the folder by
  their names, are refused before anything is looked up. Links and
  junctions that lead out are refused, and getFiles leaves them out.
- The encoding comes from the byte order mark, then the encoding the
  script set, then UTF-8 when the bytes are valid, then CP1252.
- Limits: 8 MiB a file, 32 MiB a run, 256 files open at once, 2,048
  getFiles entries and 65,536 names looked at, and paths of at most
  32,767 characters.
- Writing, the file dialogs and execute are not available yet: they
  return false or null and set error.

The report's hostCallCounts gains fileCalls and fileExecutes.
Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 07:46:33 -08:00
dotCooCoo 0e4010d26b Merge main into feat/script-dollar-object 2026-11-11 02:35:43 -08:00
dotCooCoo 179bd81566 Scripts: the $ object's other members, localize and toSource
A script's $ object now has the other members the Dollar object page
lists: about, appEncoding, bp, build, buildDate, colorPicker,
decimalPoint, engineName, error, flags, gc, getenv, setenv, global,
hiresTimer, level, line, locale, localize, memCache, os, screens, sleep,
stack, strict and version. Scripts also get the global
localize(object, ...values), toSource(value) and
Object.prototype.toSource().

Each run starts with the interface's locale and its own $ state;
$.setenv changes only the run's copy of LANG, LC_MESSAGES, LC_ALL and
OS, and $.getenv returns null for every other name. $.sleep waits from
0 to 1,000 ms between frames, and script.stop ends the wait. toSource
writes plain data only and throws on cycles, getters, host objects and
values past the data limits.

The script checker reports $.localize as a $ member instead of the
global localize function.
2026-11-11 01:59:37 -08:00
dotCooCoo c0adc6e9d7 Merge main into feat/script-alert-confirm-prompt 2026-11-11 01:09:54 -08:00
dotCooCoo df0373aa26 Scripts: alert, confirm and prompt open dialogs with Stop, and headless runs take their answers from script.run
alert(message[, title, errorIcon]), confirm(message[, noAsDflt, title])
and prompt(message, preset[, title]) are globals in a script run. In the
app the run waits at the call for the person's answer, spends no
instructions while it waits, and every dialog has Stop, which ends the
run and puts every document back. A run over MCP or the command line
takes its answers in order from script.run's answers, a list of
{kind, value} records; a missing, mismatched or unused answer ends the
run. The report lists each dialog with its answer in dialogs, with
dialogCount and dialogsTruncated.

The interpreter gains a host call that waits at the call for its
result, from the script's own code only; a dialog called in a callback
of a built-in, a getter or a conversion throws in the app.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-10 22:14:01 -08:00
dotCooCoo 693031c509 Scripts: #include and the other preprocessor lines run, and $.evalFile runs a file
Before a script runs, `script.run` reads its preprocessor lines (`#` or
`//@` at the start of a line, outside strings and comments):

- `#include` and `//@include` put another file's text in place of the
  line, looked for beside the including file, then in each
  `#includepath` folder, then in each `JSINCLUDE` folder. A later
  `#includepath` replaces the list.
- `#script` names the run unless `name` is given; `#target` takes any
  value; `#strict off` is taken. `#strict on`, `#targetengine` and
  `#engine` are refused as unsupported, and any other `#name` as
  unknown.

`$.evalFile(path, timeout?)` runs a file in the same global environment
and returns its last value, `$.fileName` is the file being run and
`$.includePath` the include folders.

A script run from a file reads these files only in its folder and the
folders below it. Paths with a `..` name, links or junctions that lead
out, device names, streams and network paths are refused; a script
given as text reads none. A run reads at most 64 files 16 levels deep,
32 MiB of source in all and 1,024 paths looked up. An error in an
included or evaluated file is reported at that file's line and column,
and the report's new `file` field names it.

Docs: docs/mcp.md (script.run, Script Console), docs/target-app-parity.md.
2026-11-10 21:59:22 -08:00
dotCooCoo 2c0d09e079 Scripts: File › Scripts › Other Script… runs JavaScript files
File › Scripts › Other Script… picks a .js or .jsx file, shows a warning
that can be turned off in Preferences › General, and runs the script
against the open documents through the `vc` object. The new
`script.run` and `script.stop` commands run and stop a script over MCP.

- A run is one transaction. Each document it changed gets one undo step
  named Run Script. An uncaught error, a limit or a stop puts every
  document, the paint, the clipboard, menu state and preferences back.
- A command that fails inside a run, or whose result is too large to
  reach the script, is undone and throws an error the script can catch.
- Files a command wrote stay on disk and the report names them, also
  when the run is stopped from the quit question.
- While a script runs, other commands that change anything fail with a
  busy error; MCP reports it as JSON-RPC error -32003.
- In the app the run advances one slice per frame with a Stop button in
  the status bar. A run that adds a great many objects asks whether to
  continue; Stop and Escape end it.
- Commands that change state outside the documents, such as prefs.*,
  plugin.* and file.place.queue, are refused to scripts.

Docs: docs/mcp.md, docs/gaps.md, docs/roadmap.md,
docs/target-app-parity.md.
2026-11-09 18:35:41 -08:00
dotCooCoo f85c555014 JavaScript: run E4X XML literals and the XML, XMLList, Namespace and QName objects behind an option
With `Options::e4x` set, vectorcraft-js runs ECMA-357 (2nd edition):
XML and XMLList literals with embedded expressions, the `..`, `.@`,
`.*`, `.()` and `::` selectors, assignment and `delete` on selected
nodes, `for each`, `default xml namespace`, and the four objects with
their methods and settings. The option is off by default, and without
it the syntax and globals do not exist.

Parsing, serializing, comparing and copying trees use no native stack,
and each takes fuel and counts against the heap limit. Inserting a node
into itself or its descendants throws a TypeError before the tree
changes. A literal or XML string nested past `Options::max_depth` gives
a SyntaxError, and a tree built past that depth stops at the heap limit.
`XML.prototype.xpath` is not provided.
2026-11-09 06:50:32 -08:00
dotCooCoo 501d82ece4 Merge main into feat/js-builtins-core 2026-11-08 16:25:35 -08:00
dotCooCoo 02fffcd00c JavaScript: String, Number, RegExp and Date built-ins
A realm now has String, Number, RegExp and Date as ECMAScript 5.1
sections 15.5, 15.7, 15.9 and 15.10 define them, with Annex B's substr,
getYear, setYear and toGMTString.

- String: every ES5 prototype method; split, replace, match and search
  take strings and RegExps. Each new string's length is checked against
  the heap limit before it is built, and scans take fuel.
- Number: toString(radix), toFixed, toExponential and toPrecision
  format from the number's exact decimal value.
- RegExp: a backtracking matcher keeps its choice points and capture
  state on the heap, within the heap room left, and takes fuel for every
  step, so a catastrophic pattern stops at the fuel limit. Patterns
  nested past Options::max_depth give a SyntaxError. Flags g, i and m,
  lastIndex, exec, test and toString.
- Date: chrono converts days to calendar dates, shifting by whole
  400-year cycles past its years. Realm::set_clock and
  Realm::set_time_zone set the clock and local time (UTC by default).
  Without a hint, a Date converts to a primitive as a string.

cargo xtask test262 also runs the suite's built-ins tests for the four
and prints their counts.
2026-11-07 18:24:27 -08:00
dotCooCoo 7436a38f44 JavaScript: Object, Function, Array, Error, Math and JSON built-ins
vectorcraft-js now provides the ECMAScript 5.1 built-ins: the Object
property-descriptor functions, create, keys, seal and freeze (15.2),
Function.prototype.bind (15.3.4.5), Array with every ES5 method (15.4),
the error types (15.11), Math (15.8), JSON with revivers, replacers and
indentation (15.12), and the global functions with Annex B's escape and
unescape (15.1, B.2). Prototype methods are writable and configurable.

The array methods work on array-like objects and visit only the indices
that exist, so a sparse array of length 2^32-1 costs as much as its
elements, and they see indices a callback adds. join writes a cycle back
to an array as an empty string; JSON.stringify throws a TypeError on a
cycle. JSON parsing, writing and the reviver walk use no native
recursion. Every loop takes fuel and every result is checked against
the heap limit, so new Array(4294967295).join("x") ends with the heap
limit and a huge sort or stringify ends with a limit.

cargo xtask test262 also runs the suite's ES5 built-in tests and fetches
json2.js at a pinned commit, checked against its SHA-256, to compare its
output with the built-in JSON once Date, String.prototype.replace and
RegExp exist.
2026-11-07 16:49:38 -08:00
dotCooCoo 70cfdcb43d JavaScript: kept code and function source text count toward the heap
A collection counts each piece of code a closure or frame keeps: its instructions, tables,
names, nested functions and its source text, each shared buffer once. Code that eval and the
Function constructor compile at run time is charged when it is made, after a heap reservation.
Function.prototype.toString reserves and charges the text it copies.
2026-11-07 14:39:21 -08:00
dotCooCoo d026e6ae26 JavaScript: interpreter with a garbage-collected heap and execution limits
vectorcraft-js gains a Realm that runs ECMAScript 5.1 scripts. The parsed
script compiles to stack-machine bytecode, a verifier checks every jump
target, handler, constant index and stack depth before the code runs, and
the interpreter keeps JavaScript calls on its own frame stack. Closures,
this, arguments, with, direct and indirect eval, new Function,
try/catch/finally, labeled break and continue, and for-in in insertion
order follow sections 10 to 14 of ECMA-262 5.1. Variable references are
resolved before the right-hand side of an assignment runs (11.13).

Objects and environments live in an arena with a mark-and-sweep
collector and index handles. Built-ins are limited to Object and
Object.prototype's basic methods, Function with call and apply, eval
and the error constructors.

Every run is bounded by fuel, heap bytes, frames, nested host calls and
an interrupt flag another thread can set, with smaller defaults in the
wasm build. A run that reaches a limit ends with a limit error that a
script's try cannot catch. Source passed to eval and Function counts
against Options::max_source_units.

cargo xtask test262 also runs the suite's ES5 test/language tests that
need only these built-ins and prints their counts apart from the parse
counts. Nothing in the app calls the interpreter yet.
2026-11-07 12:36:48 -08:00
dotCooCoo 05c1be3548 JavaScript: parser for ES5 scripts, with cargo xtask test262
New crate vectorcraft-js reads ECMAScript 5.1 source into a syntax tree
with source positions: a UTF-16 lexer (line and column tracking, automatic
semicolon insertion, the regexp and division goals), preprocessor lines
read as directive tokens, a recursive-descent parser to an arena tree, and
the ES5 early errors, strict mode included. Nesting deeper than
Options::max_depth (200 by default) gives a SyntaxError with its line and
column rather than overflowing the stack. Nothing runs scripts yet.

cargo xtask test262 downloads test262 at a pinned commit, checks its
SHA-256, unpacks it into the gitignored corpus/ folder and runs the ES5
syntax tests. A differential test in the engine compares the lexer with
scriptcheck on line counts, directives and unterminated literals.
2026-11-07 10:29:51 -08:00