Match OpenSCAD's argument and arithmetic diagnostics - #99
Merged
Conversation
`ord(undef)` printed no warning where real OpenSCAD does. Chasing that found
the same hole everywhere: we emitted the right undef VALUE for a bad
argument or a bad operand type, silently, for every builtin and every
arithmetic operator. A misspelled variable reaching abs(), len() or `+` said
nothing at all.
Checked case-by-case against OpenSCAD 2026.02.01 (~/Desktop/OpenSCAD-dev.app,
not the 2022 build in /Applications, which differs) over 1002 generated
cases: every ordered type pair for + - * / % ^ and unary minus, every builtin
with each argument wrong in turn, and search() across needle/haystack/
num_returns/index_col combinations. All 1002 now agree on both value and
warning, bar three rands() cases (random) and version() (the binary is
2026.02.01, we report 2026.01.01).
Diagnostics added, all reference-exact:
undefined operation (undefined + number) -- + - * / % ^, unary -
NAME() parameter could not be converted: argument N: expected T, found T (v)
NAME() parameter could not be converted: vector element N: ... -- max/min
NAME() number of parameters does not match: expected N, found M
vector*vector requires matching lengths (2 != 3), and its matrix siblings
Multiplication is undefined on empty vectors
Matrix must be rectangular / must contain only numbers. Problem at row N
Incorrect arguments to norm()
Invalid vector size of parameter for cross()
Invalid value in parameter vector for cross()
Invalid entry in search vector at index N, required ...
Parent module index (N) greater than the number of modules on the stack
Value fixes found while doing it -- each one verified against the binary,
not inferred:
- `%` was a floored/Python modulo. The reference is C's fmod, so the result
takes the LEFT operand's sign: -7 % 3 is -1, not 2. `x % 0` is nan, not
undef. This is the one change here that can alter rendered output; a real
BOSL2 model (spur_gear + prismoid + path_sweep) exports byte-identical
before and after, so nothing in normal use depends on the old sign.
- `0 ^ -1` is inf, not undef -- pow() straight through, no zero special-case.
- `number / vector` divides element-wise: 5 / [1,2] is [5, 2.5], was undef.
- `[] * []` is undef with a message, was 0.
- search() dispatches on what is searched FOR (number/string/vector only,
everything else undef) and never type-checks what it searches IN (a
non-vector is an empty table). Both were backwards: search(undef, "abc")
answered [] and search("a", undef) answered undef. A string needle in a
vector table also requires vector entries, so search("a", ["a","b"]) is []
with a warning, not [0].
- has_key(obj, undef) is undef, not false -- false reads as "no such key"
rather than "that is not a key".
- chr() is variadic (chr(65,66) == "AB") and range-checked. An out-of-range
or surrogate codepoint contributes nothing; it used to emit raw invalid
UTF-8 that escaped and broke the caller's own decoding.
- Objects echo as `{ a = 1; b = "x"; }`, not `object(a = 1, b = "x")`.
- oscTypeName called ranges and functions "undefined", which put a phantom
undef operand into every message naming one.
Comparators were already correct and are unchanged: < > <= >= warn and
answer undef, == and != never warn.
The extra-argument case now answers undef as the reference does, so
abs(1, 2) and is_num(1, 2) are undef rather than quietly using the first
argument. search()'s port also picks up an upstream crash fix (f2be6d0d2,
2026-07-02) that the 2026.02.01 binary predates -- a non-string entry at
index_col aborts that binary with bad_variant_access.
878 tests pass under both engines; BelfrySCAD's own 794 pass against this.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
ord(undef)printed no warning where real OpenSCAD does. Chasing that found the same hole everywhere: we produced the right undef value for a bad argument or operand — silently — for every builtin and every arithmetic operator. A misspelled variable reachingabs(),len()or+said nothing at all.How it was checked
Generated 1002 cases and ran them through OpenSCAD 2026.02.01 (
~/Desktop/OpenSCAD-dev.app, not the 2022 build in/Applications— it differs) and through us, comparing both value and warning text:+ - * / % ^and unary-search()num_returns×index_colAll 1002 now agree, except three
rands()cases (random) andversion()(the binary is 2026.02.01; we report 2026.01.01).Diagnostics added
All reference-exact, none invented:
Value fixes found along the way
Each verified against the binary, not inferred:
-7 % 32-15 % 0undefnan0 ^ -1undefinf5 / [1,2]undef[5, 2.5][] * []0undef+ warningsearch(undef, "abc")[]undefsearch("a", undef)undef[]search("a", ["a","b"])[0][]+ warninghas_key(obj, undef)falseundefchr(65, 66)"A""AB"chr(-1),chr(1e9)""echo(object(a=1))object(a = 1){ a = 1; }abs(1, 2)1undef+ warning%is the one change that can alter rendered output. Ours was a floored/Python modulo; the reference is C'sfmod, so the result takes the left operand's sign. A real BOSL2 model (spur_gear+prismoid+path_sweep) exports byte-identical before and after, so nothing in normal use depended on the old sign.Also:
oscTypeNamecalled ranges and functions"undefined", which put a phantom undef operand into every message naming one.Comparators were already correct and are untouched —
<><=>=warn and answer undef;==!=never warn.Note on
search()The port follows current upstream, which includes a crash fix (
f2be6d0d2, 2026-07-02) that the 2026.02.01 binary predates — a non-string entry atindex_colaborts that binary withbad_variant_access. Those shapes are excluded from the binary comparison and follow the fixed source.Tests
878 pass under
OSCAD_BYTECODE_VM=0and=1. BelfrySCAD's own 794 pass against this build. Five existing tests encoded the old wrong behavior and were corrected with the reference output alongside.Version 0.32.2 → 0.33.0 (minor: real behavior changes).
🤖 Generated with Claude Code