Browse Source

Sol Part 69: They told me I couldn't ship this! THEY WERE WRONG!

master
Graham Northup 4 years ago
parent
commit
84aed45860
  1. 4
      builtins.c
  2. 50
      runtime.c
  3. 8
      sol.h
  4. 1
      state.c
  5. 13
      tests/_crasher_tco.sol
  6. 15
      tests/crasher_tco.sol

4
builtins.c

@ -567,6 +567,10 @@ sol_object_t *sol_f_debug_getops(sol_state_t *state, sol_object_t *args) {
return res;
}
sol_object_t *sol_f_debug_fnstack(sol_state_t *state, sol_object_t *args) {
return sol_incref(state->fnstack);
}
#ifndef NO_READLINE
sol_object_t *sol_f_readline_readline(sol_state_t *state, sol_object_t *args) {
sol_object_t *obj, *objstr, *res;

50
runtime.c

@ -939,6 +939,7 @@ sol_object_t *sol_eval(sol_state_t *state, expr_node *expr) {
void sol_exec(sol_state_t *state, stmt_node *stmt) {
sol_object_t *value = NULL, *vint = NULL, *list, *iter, *item;
stmtlist_node *curs;
exprlist_node *cure;
if(!stmt) {
sol_obj_free(sol_set_error_string(state, "Execute NULL statement"));
return;
@ -969,7 +970,47 @@ void sol_exec(sol_state_t *state, stmt_node *stmt) {
case ST_RET:
if(stmt->ret->ret) {
state->ret = sol_eval(state, stmt->ret->ret);
if(stmt->ret->ret->type == EX_CALL) {
value = sol_eval(state, stmt->ret->ret->call->expr);
iter = sol_new_list(state);
if(stmt->ret->ret->call->method) {
list = sol_new_list(state);
sol_list_insert(state, list, 0, value);
item = sol_new_string(state, stmt->ret->ret->call->method);
sol_list_insert(state, list, 1, item);
sol_obj_free(item);
item = CALL_METHOD(state, value, index, list);
sol_obj_free(value);
sol_list_insert(state, iter, 0, value);
value = item;
}
cure = stmt->ret->ret->call->args;
while(cure) {
if(cure->expr) {
if(value->ops->tflags & SOL_TF_NO_EVAL_CALL_ARGS) {
sol_list_insert(state, iter, sol_list_len(state, iter), sol_new_exprnode(state, cure->expr));
} else {
sol_list_insert(state, iter, sol_list_len(state, iter), sol_eval(state, cure->expr));
}
}
cure = cure->next;
}
sol_list_insert(state, iter, 0, value);
vint = sol_list_get_index(state, state->fnstack, 0);
if(vint == value) {
sol_obj_free(vint);
sol_obj_free(value);
state->topargs = iter;
longjmp(state->topfunc, 1);
}
sol_obj_free(vint);
vint = CALL_METHOD(state, value, call, iter);
sol_obj_free(value);
sol_obj_free(iter);
state->ret = vint;
} else {
state->ret = sol_eval(state, stmt->ret->ret);
}
} else {
state->ret = sol_incref(state->None);
}
@ -1010,6 +1051,12 @@ sol_object_t *sol_f_func_call(sol_state_t *state, sol_object_t *args) {
identlist_node *curi;
dsl_seq_iter *iter;
int argcnt = 0;
char was_jumped = 0;
if(setjmp(state->topfunc)) {
//sol_obj_free(args);
args = state->topargs;
was_jumped = 1;
}
iter = dsl_new_seq_iter(args->seq);
if(!args || dsl_seq_iter_is_invalid(iter) || sol_is_none(state, args)) {
printf("WARNING: No parameters to function call (expecting function)\n");
@ -1018,6 +1065,7 @@ sol_object_t *sol_f_func_call(sol_state_t *state, sol_object_t *args) {
value = dsl_seq_iter_at(iter);
if(!value || !(sol_is_func(value) || sol_is_macro(value))) {
printf("WARNING: Function call without function as first parameter\n");
ob_print(value);
return sol_incref(state->None);
}
if(!value->func) {

8
sol.h

@ -7,10 +7,11 @@
#include <stdio.h>
#include <stdarg.h>
#include <setjmp.h>
#include "dsl/dsl.h"
/** The version of the project, as made available through `debug.version`. */
#define SOL_VERSION "0.5a3"
#define SOL_VERSION "0.5a4"
/** The hexadecimal version of the project, formatted 0xAAIIRPP where:
*
* - AA is the two-digit major version
@ -23,7 +24,7 @@
* version shall be available in all versions numerically greater than it
* (unless they are later deprecated or removed).
*/
#define SOL_HEXVER 0x0005A03
#define SOL_HEXVER 0x0005A04
#ifndef SOL_BUILD_HOST
#define SOL_BUILD_HOST "(unknown host)"
@ -426,6 +427,8 @@ typedef struct sol_tag_state_t {
sol_object_t *ret; ///< Return value of this function, for early return
sol_object_t *traceback; ///< The last stack of statement (nodes) in the last error, or NULL
sol_object_t *fnstack; ///< The stack of function objects (`SOL_FUNCTION`, `SOL_CFUNCTION`) in the current call stack
jmp_buf topfunc; ///< A jump buffer pointing to the most recent `SOL_FUNCTION` call, used for tail calls
sol_object_t *topargs; ///< The new arguments passed before jumping in a tail call
sol_state_flag_t sflag; ///< Used to implement break/continue
sol_object_t *error; ///< Some arbitrary error descriptor, `None` if no error
sol_object_t *_stdout; ///< Standard output stream object (for print(), type `SOL_STREAM`)
@ -775,6 +778,7 @@ sol_object_t *sol_f_debug_globals(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_debug_locals(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_debug_scopes(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_debug_getops(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_debug_fnstack(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_iter_str(sol_state_t *, sol_object_t *);
sol_object_t *sol_f_iter_buffer(sol_state_t *, sol_object_t *);

1
state.c

@ -286,6 +286,7 @@ int sol_state_init(sol_state_t *state) {
sol_map_borrow_name(state, mod, "globals", sol_new_cfunc(state, sol_f_debug_globals, "debug.globals"));
sol_map_borrow_name(state, mod, "locals", sol_new_cfunc(state, sol_f_debug_locals, "debug.locals"));
sol_map_borrow_name(state, mod, "scopes", sol_new_cfunc(state, sol_f_debug_scopes, "debug.scopes"));
sol_map_borrow_name(state, mod, "fnstack", sol_new_cfunc(state, sol_f_debug_fnstack, "debug.fnstack"));
sol_map_borrow_name(state, mod, "version", sol_new_buffer(state, SOL_VERSION, strlen(SOL_VERSION), OWN_NONE, NULL, NULL));
sol_map_borrow_name(state, mod, "hexversion", sol_new_int(state, SOL_HEXVER));
#ifdef SOL_ICACHE

13
tests/_crasher_tco.sol

@ -1,13 +0,0 @@
execfile("tests/_lib.sol")
func blow_up_stack(n)
return if n > 0 then
1 + blow_up_stack(n - 1)
else
0
end
end
assert_eq(blow_up_stack(5), 5, "blow_up_stack 5 deep")
assert_eq(blow_up_stack(5000), 5000, "blow_up_stack 5000 deep")
assert_eq(blow_up_stack(5000000), 5000000, "blow_up_stack 5000000 deep")

15
tests/crasher_tco.sol

@ -0,0 +1,15 @@
execfile("tests/_lib.sol")
func blow_up_stack(n, accum)
if n > 0 then
return blow_up_stack(n - 1, accum + 1)
else
return accum
end
end
print('TCO test (will take a long time, sorry)')
assert_eq(blow_up_stack(5, 0), 5, "blow_up_stack 5 deep")
assert_eq(blow_up_stack(5000, 0), 5000, "blow_up_stack 5000 deep")
assert_eq(blow_up_stack(50000, 0), 50000, "blow_up_stack 50000 deep")
assert_eq(assert.closure._test_count, 3, "ran three tests")
Loading…
Cancel
Save