diff options
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | docs/help.txt | 2 | ||||
| -rw-r--r-- | docs/stats.txt | 6 | ||||
| -rw-r--r-- | docs/version.txt | 2 | ||||
| -rw-r--r-- | examples/all-examples.txt | 46 | ||||
| -rw-r--r-- | examples/func_args_ret.fy | 45 | ||||
| -rw-r--r-- | src/build.h | 2 | ||||
| -rw-r--r-- | src/core/functions.c | 33 | ||||
| -rw-r--r-- | src/core/interpret.c | 102 | ||||
| -rw-r--r-- | src/core/interpret.h | 1 | ||||
| -rw-r--r-- | src/core/symbol.c | 26 | ||||
| -rw-r--r-- | src/core/symbol.h | 12 | ||||
| -rw-r--r-- | tags | 2 |
13 files changed, 261 insertions, 21 deletions
@@ -6,7 +6,8 @@ SRCS=$(shell find ./src -name '*.c') OBJS=$(SRCS:.c=.o) CC?=cc DEBUG=-g3 -ggdb3 -CFLAGS+=-c -Wall -std=c99 -pedantic $(DEBUG) +# Enable POSIX extensions (e.g. strdup) before any system headers are seen +CFLAGS+=-c -Wall -std=c99 -pedantic -D_POSIX_C_SOURCE=200809L $(DEBUG) LDADD+= HEADER?=docs/header.txt # Detect the OS name for updating build.h diff --git a/docs/help.txt b/docs/help.txt index 37ccbb1..481c057 100644 --- a/docs/help.txt +++ b/docs/help.txt @@ -1,4 +1,4 @@ -Fype Superalpha Build 9673 +Fype Superalpha Build 9676 (c) Paul C. Buetow (2005 - 2008) <fype@dev.buetow.org> -e Executes given code string (see synopses) -h Prints this help diff --git a/docs/stats.txt b/docs/stats.txt index d0571bc..087de31 100644 --- a/docs/stats.txt +++ b/docs/stats.txt @@ -1,6 +1,6 @@ make[1]: Entering directory '/home/paul/git/fype' ===> Num of C source files : 46 -===> Num of C source lines : 8344 -===> Num of Fype source examples : 16 -===> Num of Fype source lines : 419 +===> Num of C source lines : 8490 +===> Num of Fype source examples : 17 +===> Num of Fype source lines : 464 make[1]: Leaving directory '/home/paul/git/fype' diff --git a/docs/version.txt b/docs/version.txt index c825cd1..aa5c59c 100644 --- a/docs/version.txt +++ b/docs/version.txt @@ -1 +1 @@ -Fype Superalpha Build 9673 +Fype Superalpha Build 9676 diff --git a/examples/all-examples.txt b/examples/all-examples.txt index 966fc52..6e02967 100644 --- a/examples/all-examples.txt +++ b/examples/all-examples.txt @@ -202,6 +202,52 @@ ifnot pid { say "I am the child process"; } +# Test: function named arguments, explicit ret, and multiple return values + +# zero-arg function with explicit return +func answer() { + ret 42; +} +assert 42 == say answer(); + +# single-arg function — factorial with a while loop and ret +func factorial(n) { + my result = 1; + while n > 1 { + result = result * n; + decr n; + } + ret result; +} +assert 120 == say factorial(5); + +# two-arg function +func add(a, b) { + ret a + b; +} +assert 8 == say add(3, 5); + +# conditional return inside if +func absval(n) { + if n < 0 { ret neg n; } + ret n; +} +assert 5 == say absval(5); +assert 5 == say absval(neg 5); + +# multiple return values — both land on the caller's stack +func minmax(a, b) { + if a < b { ret a, b; } + ret b, a; +} +say minmax(3, 7); + +# old-style zero-arg function without parens still works +func greet { + say "hello"; +} +greet; + #* * Examples of how to use functions *# diff --git a/examples/func_args_ret.fy b/examples/func_args_ret.fy new file mode 100644 index 0000000..54088e8 --- /dev/null +++ b/examples/func_args_ret.fy @@ -0,0 +1,45 @@ +# Test: function named arguments, explicit ret, and multiple return values + +# zero-arg function with explicit return +func answer() { + ret 42; +} +assert 42 == say answer(); + +# single-arg function — factorial with a while loop and ret +func factorial(n) { + my result = 1; + while n > 1 { + result = result * n; + decr n; + } + ret result; +} +assert 120 == say factorial(5); + +# two-arg function +func add(a, b) { + ret a + b; +} +assert 8 == say add(3, 5); + +# conditional return inside if +func absval(n) { + if n < 0 { ret neg n; } + ret n; +} +assert 5 == say absval(5); +assert 5 == say absval(neg 5); + +# multiple return values — both land on the caller's stack +func minmax(a, b) { + if a < b { ret a, b; } + ret b, a; +} +say minmax(3, 7); + +# old-style zero-arg function without parens still works +func greet { + say "hello"; +} +greet; diff --git a/src/build.h b/src/build.h index aac60de..9125e92 100644 --- a/src/build.h +++ b/src/build.h @@ -36,7 +36,7 @@ #ifndef BUILD_H #define BUILD_H -#define BUILDNR 9675 +#define BUILDNR 9680 #define OS_LINUX #endif diff --git a/src/core/functions.c b/src/core/functions.c index 792065a..76b4eed 100644 --- a/src/core/functions.c +++ b/src/core/functions.c @@ -971,9 +971,38 @@ function_process_self_defined(Interpret *p_interpret, Token *p_token_ident) { break; case SYM_FUNCTION: { - List *p_list_token = symbol_get_val(p_symbol); + FuncDef *p_funcdef = (FuncDef*) symbol_get_val(p_symbol); scope_up(p_interpret->p_scope); - interpret_subprocess(p_interpret, p_list_token); + + /* Bind each named parameter to its argument value. + * Args were pushed left-to-right so the last arg is on top (LIFO). + * Reverse-fill a temp array so index 0 = first param = first arg. */ + if (p_funcdef->i_nparams > 0) { + Token **pp_args = malloc( + sizeof(Token*) * p_funcdef->i_nparams); + + for (int i = p_funcdef->i_nparams - 1; i >= 0; --i) + pp_args[i] = stack_pop(p_interpret->p_stack); + + ListIterator *p_iter = listiterator_new(p_funcdef->p_params); + + for (int i = 0; i < p_funcdef->i_nparams; ++i) { + char *c_name = (char*) listiterator_next(p_iter); + Symbol *p_sym = symbol_new(SYM_VARIABLE, pp_args[i]); + scope_newset(p_interpret->p_scope, c_name, p_sym); + } + + listiterator_delete(p_iter); + free(pp_args); + } + + interpret_subprocess(p_interpret, p_funcdef->p_body); + + /* Consume CONTROL_RET here; return value(s) are already on the stack + * (merged by interpret_subprocess when CONTROL_RET was active). */ + if (p_interpret->ct == CONTROL_RET) + p_interpret->ct = CONTROL_NONE; + scope_down(p_interpret->p_scope); } NO_DEFAULT; diff --git a/src/core/interpret.c b/src/core/interpret.c index f19dec4..0911ef7 100644 --- a/src/core/interpret.c +++ b/src/core/interpret.c @@ -414,18 +414,42 @@ _func_decl(Interpret *p_interpret) { _INTERPRET_ERROR("Symbol already defined", p_token_ident); } - List *p_list_proc = list_new(); + List *p_list_params = list_new(); + int i_nparams = 0; + + /* Optional comma-separated parameter list: func name(p1, p2) { body } + * Without parens, zero params — old-style syntax remains valid. */ + if (p_interpret->tt == TT_PARANT_L) { + _NEXT /* past '(' */ + while (p_interpret->tt != TT_PARANT_R + && p_interpret->tt != TT_NONE) { + if (p_interpret->tt != TT_IDENT) + _INTERPRET_ERROR("Expected parameter name", + p_interpret->p_token); + list_add_back(p_list_params, + strdup(token_get_val(p_interpret->p_token))); + ++i_nparams; + _NEXT + if (p_interpret->tt == TT_COMMA) _NEXT /* skip comma */ + } + _NEXT /* past ')' */ + } - if (_block_get(p_interpret, p_list_proc)) { + List *p_list_body = list_new(); - Symbol *p_symbol = symbol_new(SYM_FUNCTION, p_list_proc); + if (_block_get(p_interpret, p_list_body)) { + FuncDef *p_funcdef = funcdef_new(p_list_body, p_list_params, + i_nparams); + Symbol *p_symbol = symbol_new(SYM_FUNCTION, p_funcdef); scope_newset(p_interpret->p_scope, token_get_val(p_token_ident), p_symbol); - return (1); } - list_delete(p_list_proc); + /* Block parse failed; clean up params and body */ + list_delete(p_list_body); + list_iterate(p_list_params, free); + list_delete(p_list_params); } return (0); @@ -498,6 +522,22 @@ _control(Interpret *p_interpret) { Token *p_token = p_interpret->p_token; switch (p_interpret->tt) { + /* ret; — no return value (stack unchanged after clearing intermediates) + * ret expr; — single return value pushed to stack + * ret a, b; — multiple return values; all pushed left-to-right */ + case TT_RET: + _NEXT /* past 'ret' */ + /* Clear any intermediate values accumulated during the function body + * so only the explicit return expressions remain on the stack. */ + stack_clear(p_interpret->p_stack); + while (p_interpret->tt != TT_SEMICOLON + && p_interpret->tt != TT_NONE) { + _expression_(p_interpret); + if (p_interpret->tt == TT_COMMA) + _NEXT /* past ',' between multiple return values */ + } + p_interpret->ct = CONTROL_RET; + return (1); /* break; — set the break flag; the statement loop in _program() will * stop and the flag propagates up to the enclosing while/until. */ case TT_BREAK: @@ -598,15 +638,24 @@ _control(Interpret *p_interpret) { } } - /* Act on any break/next flag set during loop body execution. + /* Act on any break/next/ret flag set during loop body execution. * break clears the flag and stops iteration; next clears it - * and lets the loop re-evaluate the condition naturally. */ + * and lets the loop re-evaluate the condition naturally. + * ret does not clear the flag so it propagates to the caller; + * the return value is rescued from cond_stack to p_stack_backup + * before the cond_stack is deleted below. */ if (p_interpret->ct == CONTROL_BREAK) { p_interpret->ct = CONTROL_NONE; b_flag = false; } else if (p_interpret->ct == CONTROL_NEXT) { p_interpret->ct = CONTROL_NONE; /* b_flag stays true; condition re-evaluated next iteration */ + } else if (p_interpret->ct == CONTROL_RET) { + /* Rescue any return values from cond_stack into the outer + * stack before cond_stack is destroyed at the end of the + * do-while iteration. */ + stack_merge(p_stack_backup, p_interpret->p_stack); + b_flag = false; } } else { @@ -649,6 +698,10 @@ _control(Interpret *p_interpret) { } else if (p_interpret->ct == CONTROL_NEXT) { p_interpret->ct = CONTROL_NONE; /* skip the rest of the body; re-run from the top */ + } else if (p_interpret->ct == CONTROL_RET) { + /* ret inside loop: propagate CONTROL_RET up to the enclosing + * function without clearing it; return values are on the stack. */ + break; } } @@ -693,7 +746,9 @@ _control(Interpret *p_interpret) { interpret_subprocess(p_interpret, p_list_block); scope_down(p_interpret->p_scope); - /* Handle break/next before re-evaluating the condition */ + /* Handle break/next/ret before re-evaluating the condition. + * For ret: stop the loop without touching the return value on + * p_interpret->p_stack; skip condition eval via continue. */ if (p_interpret->ct == CONTROL_BREAK) { p_interpret->ct = CONTROL_NONE; b_flag = false; @@ -701,6 +756,11 @@ _control(Interpret *p_interpret) { } else if (p_interpret->ct == CONTROL_NEXT) { p_interpret->ct = CONTROL_NONE; /* fall through to condition check */ + } else if (p_interpret->ct == CONTROL_RET) { + /* ret inside do-loop: propagate CONTROL_RET; skip condition + * re-eval to avoid corrupting the stack with cond results. */ + b_flag = false; + continue; } /* Re-evaluate condition using a temp stack/iterator over @@ -980,8 +1040,21 @@ _term(Interpret *p_interpret) { Stack *p_stack = p_interpret->p_stack; p_interpret->p_stack = stack_new(); - _NEXT - if (_expression_(p_interpret)); + _NEXT /* advance past function name */ + if (p_interpret->tt == TT_PARANT_L) { + /* Parenthesised call: func(arg1, arg2) — collect args */ + _NEXT /* past '(' */ + while (p_interpret->tt != TT_PARANT_R + && p_interpret->tt != TT_NONE) { + _expression_(p_interpret); + if (p_interpret->tt == TT_COMMA) + _NEXT /* past ',' between arguments */ + } + _NEXT /* past ')' */ + } else { + /* Old-style call without parens (procedures; zero-arg funcs) */ + if (_expression_(p_interpret)); + } function_process_self_defined(p_interpret, p_token); @@ -1158,14 +1231,19 @@ interpret_process(Interpret *p_interpret) { int interpret_subprocess(Interpret *p_interpret, List *p_list_token) { - Interpret *p_interpret_sub = interpret_new(p_list_token, - NULL); + Interpret *p_interpret_sub = interpret_new(p_list_token, NULL); p_interpret_sub->p_scope = p_interpret->p_scope; int i_ret = interpret_process(p_interpret_sub); p_interpret->ct = p_interpret_sub->ct; + /* When a 'ret' fired, move return values from the sub's stack into + * the parent's stack so they survive subprocess teardown. The merge + * empties p_interpret_sub->p_stack, so interpret_delete is safe. */ + if (p_interpret->ct == CONTROL_RET) + stack_merge(p_interpret->p_stack, p_interpret_sub->p_stack); + interpret_delete(p_interpret_sub); return (i_ret); diff --git a/src/core/interpret.h b/src/core/interpret.h index f31d100..972c49d 100644 --- a/src/core/interpret.h +++ b/src/core/interpret.h @@ -50,6 +50,7 @@ typedef enum { CONTROL_NONE, CONTROL_NEXT, CONTROL_BREAK, + CONTROL_RET, /* explicit return from a function */ } ControlType; typedef struct { diff --git a/src/core/symbol.c b/src/core/symbol.c index 2a3c865..0195107 100644 --- a/src/core/symbol.c +++ b/src/core/symbol.c @@ -38,6 +38,26 @@ #include "../data/list.h" #include "token.h" +FuncDef* +funcdef_new(List *p_body, List *p_params, int i_nparams) { + FuncDef *p_fd = malloc(sizeof(FuncDef)); + p_fd->p_body = p_body; + p_fd->p_params = p_params; + p_fd->i_nparams = i_nparams; + return (p_fd); +} + +void +funcdef_delete(FuncDef *p_funcdef) { + /* list_delete only frees list/node structs, not the token pointers + * inside p_body — those tokens are GC-managed by the scanner. */ + list_delete(p_funcdef->p_body); + /* Each param name was strdup'd at declaration time; free each one. */ + list_iterate(p_funcdef->p_params, free); + list_delete(p_funcdef->p_params); + free(p_funcdef); +} + Symbol* symbol_new(SymbolType sym, void *p_val) { Symbol *p_symbol = malloc(sizeof(Symbol)); @@ -61,6 +81,12 @@ symbol_delete(Symbol *p_symbol) { case SYM_ARRAY: symbol_delete(symbol_get_val(p_symbol)); break; + case SYM_FUNCTION: + { + FuncDef *p_funcdef = (FuncDef*) symbol_get_val(p_symbol); + funcdef_delete(p_funcdef); + } + break; NO_DEFAULT; } free(p_symbol); diff --git a/src/core/symbol.h b/src/core/symbol.h index b3b3b29..e5a331c 100644 --- a/src/core/symbol.h +++ b/src/core/symbol.h @@ -37,6 +37,7 @@ #define SYMBOL_H #include "../defines.h" +#include "../data/list.h" #define symbol_set_val(s,v) s->p_val = v #define symbol_set_sym(s,st) s->sym = st @@ -55,6 +56,17 @@ typedef enum { SYM_VARIABLE, } SymbolType; +/* Wraps a function's body token list and its named parameter names. + * Every SYM_FUNCTION symbol stores a FuncDef* as its p_val. */ +typedef struct { + List *p_body; /* function body token list */ + List *p_params; /* list of strdup'd char* param names */ + int i_nparams; /* cached count of p_params */ +} FuncDef; + +FuncDef* funcdef_new(List *p_body, List *p_params, int i_nparams); +void funcdef_delete(FuncDef *p_funcdef); + typedef struct { SymbolType sym; void *p_val; @@ -160,6 +160,8 @@ datiter_new ./src/data/dat.c /^datiter_new(Dat *p_dat) {$/;" f typeref:typename: datiter_next ./src/data/dat.c /^datiter_next(DatIter *p_iter) {$/;" f typeref:typename:void * datiter_next_t ./src/data/dat.c /^datiter_next_t(DatIter *p_iter, TYPE *p_type) {$/;" f typeref:typename:void * datiter_skip ./src/data/dat.c /^datiter_skip(DatIter *p_iter, unsigned i_num) {$/;" f typeref:typename:void +funcdef_delete ./src/core/symbol.c /^funcdef_delete(FuncDef *p_funcdef) {$/;" f typeref:typename:void +funcdef_new ./src/core/symbol.c /^funcdef_new(List *p_body, List *p_params, int i_nparams) {$/;" f typeref:typename:FuncDef * function_delete ./src/core/function.c /^function_delete(Function *p_function) {$/;" f typeref:typename:void function_is_buildin ./src/core/functions.c /^function_is_buildin(Token *p_token_ident) {$/;" f typeref:typename:_Bool function_is_self_defined ./src/core/functions.c /^function_is_self_defined(Interpret *p_interpret) {$/;" f typeref:typename:_Bool |
