fullseye

Fullseye Script — Language / Runtime / Watch IDE Design Specification (North Star)

日本語 · English

Goal = evolve Fullseye Studio’s Program from “a linear (op,a,b) pipeline wearing a scripting skin” into a real programming environment on par with HALCON HDevelop. User-decided direction (2026-08-15 dialogue):

  1. A dedicated language (HDevEngine syntax is the first choice, C/C++ style is also acceptable), with named variables + real if/for/while (branching on measured values) + per-object iteration + I/O.
  2. Fullseye = image-processing library, the language is the layer that calls it (do not build the logic into the language).
  3. A compiler approach is preferred (over an interpreter).
  4. Rigorous watches: in addition to variable watches, image watches / Region watches / watches on a specific image domain (ROI).
  5. Ultimately, turning Fullseye into a DLL is the most natural end state.
  6. It should be possible to write robot-control programs (perception → decision → action loops).

External AI (Codex read-only) design consultation done (20 items, reflected in this document). Discipline = no blind acceptance, back it up with code.


0. Honest reality and the phased strategy

Conclusion (Codex #1): make the language primary, and keep the linear pipeline as a “branch-free, side-effect-free serial subset (LinearSubset)” for reuse in evolution, acceleration, and the old UI.

★Cross-platform (Linux) and the phases of the “custom foundation” (user note 2026-08-15)

User insight = “if we’re going to use it on Linux too, isn’t it a form of putting the language on top of a fairly custom foundation?” → Correct, but the foundation changes by phase:

★★A central requirements fork: Path A (Python-native IDE) vs Path B (custom language) (user note 2026-08-15)

User insight = “A form where a Python development platform has convenient watch features for image processing is completely viable too.” This is actually the wiser first goal. Next session’s requirements definition should decide this A/B first.

★Resolving interpreter vs compiler (user notes: watches are structurally hard / PyBind11 / DLL feasibility)


1. The 3-layer architecture

┌─────────────────────────────────────────────────────────────┐
│ L3  Studio IDE  … editor / run (debug|run|profile) /          │
│                   ★watch panel (§4) / breakpoints /            │
│                   execution cursor / Variable & Object panes   │
├─────────────────────────────────────────────────────────────┤
│ L2  Fullseye Script language … lexer→parser→typed AST→        │
│     bytecode compiler→VM (= the compiler). Source position     │
│     first-class. Control flow, variable env, per-object        │
│     iteration, exceptions, cancellation.                       │
│     ★Holds no logic. Only calls L1 (LanguageOperatorSpec).     │
├─────────────────────────────────────────────────────────────┤
│ L1  Fullseye library … vision/measurement/geometry/device      │
│     functions with real parameters (read_image/gauss/threshold/│
│     connection/area_center/…/comm/device/acquire). numpy/scipy │
│     implementation.                                            │
│     ★Future: hot paths to C codegen → native DLL (C ABI).      │
│     Contract kept separate from the evolution engine's          │
│     normalized-knob registry (§3).                             │
└─────────────────────────────────────────────────────────────┘

2. Language specification (HDevEngine style, Codex #2–4)

2b. The finalized behavior of the increment-1 implementation (fscript.py) —— making spec and implementation agree (finalized in the 2026-09-03 audit)

§2 above is the North Star (design). How the current implementation actually behaves is authoritatively the following table, which tests/test_fscript.py / tests/test_fsruntime.py lock one item at a time. Under the discipline of “never silently return a wrong answer”, every ambiguous input is a FScriptError (with a line number).

Item Finalized behavior
Gray-value units The language’s gray value is a fraction of the image’s declared range (0 = range bottom, 1 = top). threshold(Image, lo, hi) and mean_gray/min_gray/max_gray use the same units. Pixel 128 of an 8-bit image reads as 0.502. ∴ threshold(Image, mean_gray(Image), max_gray(Image)) means the same for 8-bit and float alike (previously only statistics used raw pixel values, and on 8-bit it silently returned area 0).
Tuple arithmetic + - * / % and unary - are all elementwise (length 1↔N broadcast; N↔N requires equal length, else an error). [1,2] * 2 = [2,4] (not Python’s repetition [1,2,1,2]). Concatenation is [t1, t2].
String arithmetic string + string = concatenation only. 'ab' * 3 and 'a' + 1 are errors (Python’s repetition / type mixing are not language features).
Scalar = a length-1 tuple [1] = 1 is true, if ([0]) is false, not [0] is true. A tuple of length ≠ 1 cannot be placed in a condition (an error). In comparisons < > <= >=, mixing a tuple and a scalar is an error; =/# are whole-tuple equality.
Indexing The i of t[i] / s[i] is a non-negative integer (an integer-valued real like 2.0 is allowed; 1.9, negatives, and strings are errors). Out of range is an error (index 3 out of range (length 3)). There is no negative index.
Index assignment Name[i] := expr is implemented (expr is a scalar; a tuple is flat so nesting is impossible). Tuples are values: B := A is a copy, and B[0] := 9 does not reach A. A Python list passed via images= is also duplicated, and the script does not rewrite the caller’s list.
Numeric literals ASCII digits only: 12 / 1.5 / 1. / .5 / 1e-3 / 2.5E+4. 1.2.3 / 2e / 1e5e3 / (full-width) / ² are syntax errors. A literal that falls to infinity like 1e400 is also an error.
String literals '...' fits on one line (crossing a line is unterminated string). The only escapes are \' and \\. Any other backslash is that literal character ('<a local working path>\images\a.png' reads as-is; '<a local working path>\dir\' has \' become an escape → an unterminated error, so write '<a local working path>\dir\\').
break / continue Outside a loop, a syntax error ('break' outside loop).
Chained comparison 0 <= X <= 10 is forbidden (an error). A parenthesized comparison is an explicit operand, so (X > 3) = true / (1 < 2) = (2 < 3) are allowed.
Condition header The condition of if/elseif/while/until runs to end of line. if (X = 1) or (Y = 1) is one condition. A header starting with (...) can only continue with and/or, so if (X = 1) -1 or for I := 0 to 2 X := I (a statement on the header line) is unexpected ... after statement.
for bounds start/stop/step are numeric (a string or a tuple of length ≠ 1 is an error). step 0 is an error.
The radius of dilation / erosion / mean_image A non-negative integer (pixels). 0 is the identity (returns the region unchanged); negative or fractional (0.4) is an error (previously max(1, int(r)) turned everything into 1).
op arguments Passing a string or a tuple (length ≠ 1) to a numeric argument raises ... must be a number, got string 'x' as a FScriptError (no bare ValueError surfaces). An error always carries the calling line.
The path of read_image If base_dir exists it confines to that subtree (a relative path is base_dir-relative; .. is judged after resolution; an absolute path is allowed only within the subtree). Otherwise path ... is outside the script's base directory. Only without base_dir (the caller explicitly omitting it) is it opened as-is. The industrial-profile Runtime rejects loading a recipe containing read_image (no file access within a cycle = FSCRIPT_DECISION.md §3.1 R4; frames are passed via images=).
Nesting limit The nesting of parentheses/unary/blocks and the expression depth at evaluation time go up to 200 (nesting too deep (limit 200)). An expression chaining 200+ 1+1+…+1 terms hits the same error. Python’s RecursionError never escapes. check() never throws and returns a string.
The golden digest (fsruntime) The manifest digest is not repr() but a canonical byte sequence (type tag + length + float.hex() / dtype.str + shape + tobytes()). It does not depend on np.printoptions, and detects a single-element difference even in an array of over 1000 elements. The types that can be placed on GoldenVector.expect are only bool/int/float/str / their list · tuple / numpy arrays (others are a construction-time TypeError). There is no compatibility with the old digest (re-sign a signed recipe with sign).

3. The L1 library = the language operator specification (Codex #6–9)


4. ★The watch model (user’s top priority, rigorous design)

User requirement = not just variable watches but image watches / Region watches / watches on a specific image domain (ROI). The core of the debugger.


5. Coexistence with the evolution North Star (Codex #16, #17)

6. Robot control (Codex #20)


7. Sample layout (Codex #14–15)

samples/
  scripts/     01_threshold_and_measure.fsh …            # sample code (dedicated folder)
  images/      parts_01.png defects_scratches.png …       # synthetic images (procedurally generated)
  generators/  make_inspection_samples.py                 # generators (seed/conditions)
  manifests/   samples.json                               # generation conditions / expected results / license

8. The phased roadmap (Codex #18 adjusted to imgevolve reality)

At each stage, regress on parser golden tests / type errors / empty objects / cancellation / same-seed images / old-pipeline parity.


9. The current prototype (measured and verified 2026-08-15)

★What the measurements changed about the design priority

The conclusions of docs/FSCRIPT_MEASUREMENTS.md:

  1. The language’s execution method (AST interpreter) has essentially no effect on cycle time (+0.4–4.7%). → The motive for a bytecode VM is not speed but the debugging experience (step/breakpoint/span/watch). Distinguish this honestly.
  2. The object model matters 5.8×, and the pixel-kernel implementation 23×. 134× in total. → The priority order is types·semantics → ObjectSet → native kernel contract → VM-ization.
  3. 5 defects that silently return wrong values (misreading * as a comment = fixed, content-dependent normalization of the value range, Tuple +, implicit truth-coercion of iconics, .any() collapse in comparisons). All are semantic problems that remain even if the implementation language changes.“Type system first, then the VM.” Native-ization comes after.