| Age | Commit message (Collapse) | Author |
|
|
|
Two bugs combined to break uber.fy and any function called more than once:
1. Scanner split identifiers at '_' boundaries (e.g. arr_sum → arr + _sum).
Fixed by adding d != '_' to the token-splitting guard condition so
underscores are treated as part of identifiers.
2. Dead keyword TT_ARR reserved the name 'arr' as a keyword token,
preventing its use as a function parameter. Removed TT_ARR from the
enum, keyword table, and name table.
3. (Root cause of the assert failure) _var_assign stored the symbol for a
newly declared variable pointing directly at the literal token from the
function body token list. incr/decr modify tokens in place via
stack_top, so a loop like `my i = 0; while i < n { incr i; }` would
corrupt the body's '0' literal — on the next call i starts at 10 instead
of 0. Fixed by allocating a fresh token_new_integer / token_new_dummy
for TT_INTEGER and TT_DOUBLE initialisers so body literals are never
mutated. Arrays and strings retain reference semantics unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
_statement() wrapped its six parse rules in `for (int i = 0; i < 2; ++i)`
with no explanation. Investigation shows every rule is atomic: it either
consumes tokens and returns 1, or leaves the stream untouched and returns 0.
A second pass would see identical parser state, so the retry was a no-op.
Replace the loop with a flat priority-ordered try sequence and add a comment
explaining the invariant so future readers are not confused.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Three issues resolved:
1. scanner_run() no longer takes Fype*. New signature:
scanner_run(List*, Tupel*, char **c_filename_out)
Verbose-mode list_iterate() and basename extraction moved to
fype_run() (the composition root), leaving scanner_run() as pure
tokenizer: open source -> tokenize -> post-process -> return filename.
2. interpret_run() no longer takes Fype*. New signature:
interpret_run(List *p_list_token, Hash *p_hash_syms)
Fype* unpacking happens in fype_run(); interpret.h no longer includes
fype.h. scanner.h no longer includes fype.h either.
3. _CODESTR_INDEX file-global (which made the scanner non-reentrant)
moved into the Scanner struct as i_codestr_index, initialized to 0
in scanner_new(). _scanner_has_next_char() and _scanner_get_next_char()
use p_scanner->i_codestr_index instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
_term_paren() [SRP]
_term() was ~130 lines mixing literal push, array literal construction,
identifier resolution, parenthesized expressions, and defined/undef/syms.
Extracted three static helpers:
- _term_ident(): resolves TT_IDENT as array access, builtin call,
self-defined function/proc call, or variable/procedure (~90 lines;
acceptable given the complexity of identifier resolution)
- _term_array_literal(): builds an array from [expr, ...] syntax
- _term_paren(): evaluates a parenthesised expression and checks ')'
_term() is now a ~100-line dispatcher. The defined/undef/syms cases
remain inline as they are self-contained ~10-line blocks.
Also fixes a latent double-assignment bug in _term_array_literal():
the original `TokenType tt = tt = p_interpret->tt;` is now `= p_interpret->tt;`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
functions.c::_op_assign() was reading p_interpret->p_token_array_lhs and
p_interpret->i_array_lhs_index directly — a hidden cross-module coupling
where the functions module reached into interpreter-private state.
Pass the array-LHS context as explicit parameters instead:
- _op_assign() gains Token *p_array_lhs, int i_lhs_idx params
- _process() propagates them to _op_assign()
- function_process() propagates them from the call site
In interpret.c the "restore" lines that set p_interpret->p_token_array_lhs
and p_interpret->i_array_lhs_index before calling function_process() are
removed; instead p_lhs and i_lhs_idx are passed directly. The three
non-array-LHS call sites pass NULL / 0. _op_assign() no longer touches
Interpret internals for the array assignment path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
_process() was a 510-line function with a nested switch(operator) x
switch(type) structure. Extracted 17 static helper functions:
- _resolve_composite_op(): maps two-token operator pairs (!=, ==, <=,
>=, <<, >>) to their canonical single TokenType
- _op_assign(): handles variable and array-element assignment
- _op_add(), _op_sub(), _op_mult(), _op_div(): arithmetic operators
- _op_eq(), _op_neq(), _op_lt(), _op_gt(), _op_le(), _op_ge():
comparison operators (result always TT_INTEGER)
- _op_and(), _op_or(), _op_xor(), _op_lshift(), _op_rshift():
bitwise operators (doubles/strings coerced to int)
_process() is now a ~70-line dispatcher. Assignment is guarded by
`else if` so it only fires when p_token_op2 == NULL, preserving the
original semantics exactly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add _eval_expr_list() to isolate condition evaluation in a temporary
stack+iterator, restoring the outer stack before returning. This
eliminates the duplicate stack/iter backup boilerplate that existed
separately in the while/until and do-while paths.
Extract four static handler functions from the 280-line _control() body:
_control_if_ifnot() — TT_IF / TT_IFNOT
_control_while_until() — TT_WHILE / TT_UNTIL; uses _eval_expr_list
_control_loop() — TT_LOOP
_control_do() — TT_DO; uses _eval_expr_list for post-condition
_control() itself shrinks to a ~30-line dispatcher (ret/break/next inline;
loops and conditionals delegated to their handlers).
Semantic improvement in while/until: the original code ran the body with
a temp stack active and then did a manual stack_merge for CONTROL_RET.
Since _eval_expr_list now restores the outer stack before the body runs,
interpret_subprocess merges return values into the outer stack directly —
the extra stack_merge is no longer needed.
Also removes the leftover debug printf("FOO")/exit(0) guard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Extract each of the 21 built-in function handlers (assert, decr, double,
end, exit, fork, gc, incr, ind, integer, len, ln, neg, no, not, put,
refs, say, scope, string, yes) into individual static _builtin_XXX()
functions, each focused on a single responsibility.
Add a null-terminated BuiltinEntry g_builtins[] dispatch table mapping
name strings to handler function pointers. Rewrite function_is_buildin()
and function_process_buildin() to iterate the table instead of
maintaining parallel 21-branch strcmp chains.
This satisfies OCP (adding a built-in only requires a new table row),
SRP (each handler owns exactly one built-in's logic), and DRY (name
lookup done in one place). The array special-case (len/ind on TT_ARRAY
vs element-wise iteration) is preserved inline before the table dispatch.
Also add missing NO_DEFAULT; to _builtin_say's switch for style
consistency with all other handlers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Remove IS_A_FUNCTION/IS_NOT_A_FUNCTION macros from symbol.h: referenced
non-existent SYM_INLINEFUNCTION enum value (latent compile error) and
were never used anywhere in the codebase
- Remove TT_BOOL from TokenType enum: marked "temporarily disabled" but
unused for years; YAGNI
- Remove TT_NOTEQ from TokenType enum: duplicate of TT_NEQ ("!="), never
referenced
- Remove IS_NON_TERMINAL/IS_NOT_NON_TERMINAL macros: referenced
START_NON_TERMINALS/END_NON_TERMINALS sentinels that don't exist in the
enum — would cause compile errors if ever used
- Remove token_new_couble declaration (typo) and token_new_double
implementation: function had no callers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
Replace the `func` language keyword with `fun` throughout: update the
scanner's string-to-token mapping, all .fy example scripts, and all
documentation files (.txt, .pod, .tex, .man, .html).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Add FuncDef struct to symbol.h/symbol.c: holds body token list,
strdup'd param names, and param count; replaces the bare List* that
SYM_FUNCTION used to store; funcdef_delete frees param strings and
body list (tokens are GC-managed)
- Add CONTROL_RET to ControlType so ret can propagate cleanly through
the interpreter's control-flow stack
- Update _func_decl to parse optional (p1, p2, ...) param list and store
a FuncDef* instead of a raw List*
- Implement case TT_RET in _control: clears intermediate stack values,
evaluates comma-separated return expressions, sets CONTROL_RET
- Propagate CONTROL_RET in all loop bodies (while/until, loop, do)
without clearing it; while/until rescues return values from the
temporary condition stack before it is destroyed
- Update _term call site to support parenthesised func(arg1, arg2)
syntax alongside the existing no-parens style for backward compat
- Modify interpret_subprocess to merge the sub's stack into the parent
when CONTROL_RET is active so return values survive teardown
- Update function_process_self_defined SYM_FUNCTION case to pop and
bind named args, run the body, then clear CONTROL_RET
- Add -D_POSIX_C_SOURCE=200809L to Makefile CFLAGS for strdup under
-std=c99 -pedantic
- Add examples/func_args_ret.fy covering zero-arg, single-arg,
two-arg, conditional ret, multiple return values, and old-style
no-parens backward compat
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Replace bare say statements with assert expected == say value; so that
each example self-verifies its output at runtime — matching the
convention used in expressions.fy and types.fy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Completes the 'loop, next, break, do' TODO entry — break and next
were landed in the previous commit; this adds the remaining two forms:
- loop { body } — infinite loop; the only exit is break. Simpler than
while/until since there is no condition expression to evaluate.
- do { body } while expr; / do { body } until expr; — post-condition
loop: body always executes at least once, condition is checked at the
bottom of each iteration. Condition tokens are collected until ';'
(mirrors _expression_get but stops at semicolon instead of '{'), then
replayed each iteration using the same temp-stack/temp-iterator
technique as while/until. Both break and next are supported.
Token changes: TT_LOOP and TT_DO added to the keyword enum in token.h,
registered in get_tt() and tt_get_name() in token.c.
Add examples/loop_do.fy; expected output: 5 / 12 / 11 / 5.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
TT_BREAK and TT_NEXT were already tokenised; this wires them up:
- _program(): guard statement loop with ct == CONTROL_NONE so a break/next
flag set inside a loop body stops block execution and propagates upward
- _control(): add TT_BREAK and TT_NEXT cases that set CONTROL_BREAK /
CONTROL_NEXT on p_interpret->ct and return immediately
- while/until loop: replace the commented-out switch with active if/else
logic that clears the flag and either stops iteration (break) or lets
the loop re-evaluate its condition (next)
Add examples/break_next.fy to exercise both keywords in while and until
loops; expected output: 5 / 12 / 7.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Replace BSD make != shell assignment with $(shell ...) idiom
- Replace static $(OBJS): rule + sed hack with %.o: %.c pattern rule
- Use $(MAKE) for all recursive make invocations
- Update CLAUDE.md to document GNU make instead of pmake
- Build verified clean on Fedora (GCC 15, all 14 examples pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array_new_copy
implemented
|
|
|
|
|
|
|
|
|
|
|
|
run "make headers"
|
|
|
|
|
|
|
|
|
|
more BSD style in return (FOO);
|