Lhogho
0.0.027
|
Defines | |
#define | SRC_EXT_COUNT (sizeof source_extensions)/(sizeof source_extensions[0]) |
#define | SRC_EXT (source_extensions[i]) |
#define | BUF_SIZE 1024 |
#define | RETURN_NO_CHECK(INSTR) |
#define | RETURN_NO_CHECK_EL(INSTR) |
#define | RETURN_CHECK(INSTR) |
Functions | |
atom_t | compile_expr (context_t *ctx, atom_t lisp, int mode) |
compiles expression or constant | |
atom_t | compile_block (context_t *ctx, atom_t lisp, int mode) |
compiles block of statements | |
atom_t | compile_if (context_t *ctx, atom_t source, int mode) |
compiles IF statement | |
atom_t | compile_repeat (context_t *ctx, atom_t source) |
compiles REPEAT statement | |
atom_t | compile_while (context_t *ctx, atom_t source, int is_while, int is_do) |
compiles WHILE statement | |
atom_t | compile_forever (context_t *ctx, atom_t source) |
compiles FOREVER statement | |
atom_t | compile_catch (context_t *ctx, atom_t source, int mode) |
compiles CATCH statement | |
atom_t | compile_tag (context_t *ctx, atom_t source) |
compiles TAG statement | |
atom_t | compile_goto (context_t *ctx, atom_t source, atom_t var) |
compiles GOTO statement | |
atom_t | compile_iftest (context_t *ctx, atom_t source, int criteria) |
atom_t | compile_for (context_t *ctx, atom_t source) |
compiles FOR statement | |
void | init_compiler (outter_t outter, inner_t inner, inner_eof_t inner_eof) |
initializes the compiler | |
void | finit_compiler () |
finalizes the compiler | |
int | compile_from_options () |
compiles according to options | |
atom_t | compile_to_file () |
compiles into executable file | |
atom_t | instruction_list (atom_t data) |
ensured that input is a list in brackets | |
int | run_function (atom_t function) |
runs the compiled code of a function | |
int | run_source (chars_t source) |
compiles and runs source code | |
atom_t | compile_function (atom_t func, int mode, int is_macro) |
compiles a function | |
atom_t | compile_external_function (atom_t func) |
compiles an external function | |
atom_t | compile_internal_function (atom_t func, int static_link) |
compiles an internal function | |
int | is_constant (atom_t lisp) |
determines whether lisp is a constant | |
int | is_reference (atom_t lisp) |
determines whether lisp is a reference | |
atom_t | compile_lisp_const (context_t *ctx, atom_t lisp) |
compiles constant | |
atom_t | compile_local (context_t *ctx, atom_t source, int *processed) |
compiles LOCAL statement | |
atom_t | compile_make (context_t *ctx, atom_t source, int is_name, int *processed) |
compiles MAKE statement | |
atom_t | compile_output (context_t *ctx, atom_t source) |
compiles OUTPUT statement | |
atom_t | compile_maybeoutput (context_t *ctx, atom_t source) |
compiles MAYBEOUTPUT statement | |
atom_t | compile_stop (context_t *ctx, atom_t source) |
compiles STOP statement | |
atom_t | compile_test (context_t *ctx, atom_t source, int criteria) |
compiles IFTRUE and IFFALSE statements | |
atom_t | compile_lisp_reference (context_t *ctx, atom_t source) |
compiles reference | |
Variables | |
int | running_compiled_code |
indicate whether generated code is currently running | |
int | compiling_code |
indicate whether source code is currently compiling | |
char * | source_extensions [] = { ".lgo", ".log", ".lg", ".logo", ".lho", ".lhogho" } |
#define SRC_EXT_COUNT (sizeof source_extensions)/(sizeof source_extensions[0]) |
#define SRC_EXT (source_extensions[i]) |
#define BUF_SIZE 1024 |
#define RETURN_NO_CHECK | ( | INSTR | ) |
{ \ result = INSTR; \ needs_check = 0; \ goto finalize; \ }
#define RETURN_NO_CHECK_EL | ( | INSTR | ) |
{ \ result = INSTR; \ if( !IS_ERROR(result) ) result = empty_list;\ needs_check = 0; \ goto finalize; \ }
#define RETURN_CHECK | ( | INSTR | ) |
{ \ result = INSTR; \ needs_check = 1; \ goto finalize; \ }
atom_t compile_expr | ( | context_t * | ctx, |
atom_t | lisp, | ||
int | mode | ||
) |
ctx | compilation context |
lisp | statement to compile |
mode | compilation mode (COMPILE_AS_FUNC/COMPILE_AS_PROC) |
Compiles a single expression or constant. If needed calls itself recursively to process nested expressions. Expression result is in the stack.
Depending on mode
the result is checked with rt_cmdchk(), rt_funchk() or rt_exprchk().
If the result of compile_expr
is empty list, then the expression is a constant which is left in the stack. The caller may want to pop it in EAX register.
{ #define RETURN_NO_CHECK(INSTR) \ { \ result = INSTR; \ needs_check = 0; \ goto finalize; \ } #define RETURN_NO_CHECK_EL(INSTR) \ { \ result = INSTR; \ if( !IS_ERROR(result) ) result = empty_list;\ needs_check = 0; \ goto finalize; \ } #define RETURN_CHECK(INSTR) \ { \ result = INSTR; \ needs_check = 1; \ goto finalize; \ } atom_t orig_lisp = lisp; lisp = CAR(lisp); int needs_check = 1; atom_t result = unbound; #ifdef DEBUG_COMPILE printf("<COMPILE> Compile expression: "); dumpln(lisp); #endif // remove extra parentheses if( IS_LIST(lisp) ) if( !IS_EMPTY(lisp) ) if( IS_EXPRESSION(lisp) ) while( IS_LIST(CAR(lisp)) && IS_EXPRESSION(CAR(lisp)) && IS_EMPTY(CDR(lisp)) ) lisp = CAR(lisp); #ifdef DEBUG_COMPILE printf("<COMPILE> Normalized expression: "); dumpln(lisp); if( IS_LIST(lisp) && IS_EXTENDED(lisp) ) { printf("<COMPILE>It's source is: |"); dump(POS(lisp)); printf("|\n"); } #endif // if the source is a constant, then just push it if( is_constant(lisp) ) { if( mode==COMPILE_AS_PROC ) { if( IS_EXTENDED(lisp) && !IS_UNBOUND(POS(lisp)) ) return new_error( ERROR_UNUSED_VALUE, POS(lisp) ); else if( IS_EXTENDED(orig_lisp) && !IS_UNBOUND(POS(orig_lisp)) ) return new_error( ERROR_UNUSED_VALUE, POS(orig_lisp) ); else return new_error( ERROR_UNUSED_VALUE, lisp ); } RETURN_NO_CHECK_EL( compile_lisp_const(ctx,lisp) ); } // test whether the source is a reference if( is_reference( lisp ) ) { if( mode==COMPILE_AS_PROC ) return new_error( ERROR_UNUSED_VALUE, lisp ); RETURN_NO_CHECK_EL( compile_lisp_reference(ctx,lisp) ); } // the source is not a constant and cannot be just pushed INFO( "" ); INFO( "code for %a", lisp ); // find the main function of the expression // it must be known variable atom_t varname = CAR( lisp ); #ifdef SAFEMODE assert( IS_ANY_WORD(varname) ); #endif if( LENGTH(varname)==1 && *STRING(varname)==TEXT('"') ) { return new_error( ERROR_DO_NOT_KNOW, lisp ); } atom_t var = find_var( varname, ctx->parent ); #ifdef SAFEMODE assert( var ); #endif // variables cannot be commands and procedures if( IS_VARIABLE(var) ) return new_error( ERROR_NOT_A_FUNCTION, lisp ); // commands do not return values if( (mode==COMPILE_AS_FUNC) && !IS_FUNCTION(var) ) return new_error( ERROR_MISSING_VALUE, lisp ); //printf("var="); dumpln(NAME(var)); int addr = ADDRESS(var); #ifdef SAFEMODE if( IS_PRIMITIVE(var) ) assert( addr ); #endif // dump source which is about to be executed or evaluated #ifdef ADVANCED if( OPTION_RUNTIME ) { asm_dump_source( ctx, lisp ); } #endif // SPECIAL COMPILATION CASES { // 0. _int3_ if( addr==(int)rt_int3 ) { asm_int_3( ctx ); RETURN_NO_CHECK( unbound ); } // 1. local <name> if( addr==(int)rt_local ) { int processed; result = compile_local( ctx, lisp, &processed ); if( processed ) RETURN_NO_CHECK( result ); } // 2. make <name> <value> if( addr==(int)rt_make ) { int processed; result = compile_make( ctx, lisp, 0, &processed ); //printf("processed make=%d\n",processed); if( processed ) RETURN_NO_CHECK( result ); } // 3. name <value> <name> if( addr==(int)rt_name ) { int processed; result = compile_make( ctx, lisp, 1, &processed ); if( processed ) RETURN_NO_CHECK( result ); } // 4a. output <value> if( addr==(int)rt_output ) RETURN_NO_CHECK( compile_output(ctx,lisp) ); // 4b. maybeoutput <value> if( addr==(int)rt_maybeoutput ) RETURN_NO_CHECK( compile_maybeoutput(ctx,lisp) ); // 5. stop if( addr==(int)rt_stop ) RETURN_NO_CHECK( compile_stop(ctx,lisp) ); // 6. if if( addr==(int)rt_if ) RETURN_NO_CHECK( compile_if(ctx,lisp,mode) ); // 7. repeat if( addr==(int)rt_repeat ) RETURN_NO_CHECK( compile_repeat(ctx,lisp) ); // 8. forever if( addr==(int)rt_forever ) RETURN_NO_CHECK( compile_forever(ctx,lisp) ); // 9. while if( addr==(int)rt_while ) RETURN_NO_CHECK( compile_while(ctx,lisp,1,0) ); // 10. until if( addr==(int)rt_until ) RETURN_NO_CHECK( compile_while(ctx,lisp,0,0) ); // 11. do.while if( addr==(int)rt_dowhile ) RETURN_NO_CHECK( compile_while(ctx,lisp,1,1) ); // 12. do.until if( addr==(int)rt_dountil ) RETURN_NO_CHECK( compile_while(ctx,lisp,0,1) ); // 13. catch if( addr==(int)rt_catch ) RETURN_CHECK( compile_catch(ctx,lisp,mode) ); // 14. tag if( addr==(int)rt_tag ) RETURN_NO_CHECK( compile_tag(ctx,lisp) ); // 15. goto if( addr==(int)rt_goto ) RETURN_NO_CHECK( compile_goto(ctx,lisp,var) ); // 16. iftrue if( addr==(int)rt_iftrue ) RETURN_NO_CHECK( compile_test(ctx,lisp,1) ); // 17. iffalse if( addr==(int)rt_iffalse ) RETURN_NO_CHECK( compile_test(ctx,lisp,0) ); // 18. for if( addr==(int)rt_for ) RETURN_NO_CHECK( compile_for(ctx,lisp) ); } // END OF SPECIAL COMPILATION CASES if( GET_FLAGS(var,FLAG_PUSH_MODE) ) asm_push_mode( ctx, mode ); if( GET_FLAGS(var,FLAG_PUSH_FRAME) ) asm_push_frame( ctx ); int params = 0; atom_t x; atom_t y; if( !IS_PRIMITIVE(var) && GET_FLAGS(var,FLAG_INFINITE_ARGS) ) { //printf("<COMPILE> Compile func: "); dumpln(NAME(var)); //printf("<COMPILE> LARGS=%d RARGS=%d\n",LARGS(var),RARGS(var)); // skip number of compulsory params int skip; x = CDR(lisp); for( skip = LARGS(var)+RARGS(var); skip; skip-- ) { if( IS_NOT_EMPTY(x) ) x = CDR(x); } // process all extra parameters for( ; IS_NOT_EMPTY(x); x=CDR(x) ) { #ifdef DEBUG_COMPILE printf("<COMPILE> Compile parameter: "); dumpln(CAR(x)); #endif y = compile_expr( ctx, x, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; params++; } // process compulsory params x = CDR(lisp); for( skip = LARGS(var)+RARGS(var); skip; skip-- ) { if( IS_NOT_EMPTY(x) ) { #ifdef DEBUG_COMPILE printf("<COMPILE> Compile parameter: "); dumpln(CAR(x)); #endif y = compile_expr( ctx, x, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; x = CDR(x); } else { // Compile dummy parameter asm_push_atom( ctx, empty_list ); } params++; } } else { for( x=CDR(lisp); IS_NOT_EMPTY(x); x=CDR(x) ) { #ifdef DEBUG_COMPILE printf("<COMPILE> Compile parameter: "); dumpln(CAR(x)); #endif y = compile_expr( ctx, x, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; params++; } } asm_call_atom( ctx, var, params ); int i; for( i=params-1; i>=0; i-- ) { asm_pop_atom( ctx ); } if( GET_FLAGS(var,FLAG_PUSH_FRAME) ) asm_pop_frame( ctx ); if( GET_FLAGS(var,FLAG_PUSH_MODE) ) asm_pop_dummy( ctx ); // special case for RUN, RUNMACRO and RUNRESULT - they return // a var atom containing the actual code to execute. if( addr==(int)rt_run ) { asm_run_epilogue( ctx ); } if( addr==(int)rt_runmacro ) { asm_run_epilogue( ctx ); } if( addr==(int)rt_runresult ) { asm_runresult_epilogue( ctx ); } asm_push_result( ctx ); //disasm_atom( ctx, NAME(var) ); finalize: if( needs_check ) { if( mode==COMPILE_AS_FUNC ) asm_result_func( ctx, lisp ); if( mode==COMPILE_AS_PROC ) asm_result_proc( ctx, lisp ); if( mode==COMPILE_AS_UNKNOWN ) asm_result_unknown( ctx, lisp ); } return result; }
atom_t compile_block | ( | context_t * | ctx, |
atom_t | lisp, | ||
int | mode | ||
) |
ctx | compilation context |
lisp | statements to compile |
mode | compilation mode (COMPILE_AS_macro) |
Compiles a block of expressions. If mode
is COMPILE_AS_UNKNOWN
then the number of expressions determines how to compile. If the number is >1, then compile as procedure.
{ //printf("compile_block "); dumpln(lisp); //printf("mode=%d (func=%d cmd=%d unknown=%d)\n",mode,COMPILE_AS_FUNC,COMPILE_AS_PROC,COMPILE_AS_UNKNOWN); if( (mode==COMPILE_AS_FUNC) && IS_NOT_EMPTY(CDR(lisp)) ) return new_error( ERROR_CROWDED_EXPRESSION, CDR(lisp) ); if( mode==COMPILE_AS_UNKNOWN ) if( IS_NOT_EMPTY(lisp) && IS_NOT_EMPTY(CDR(lisp)) ) mode = COMPILE_AS_PROC; // there is a word if( IS_ANY_WORD(lisp) ) { return new_error(ERROR_NOT_A_LIST_CONST,lisp); } // there is a lisp which is not produced by // a constant list if( !GET_FLAGS(lisp,FLAG_WAS_LIST_CONST) ) { return new_error(ERROR_NOT_A_LIST_CONST,lisp); } atom_t x; for( x=lisp; IS_NOT_EMPTY(x); x=CDR(x) ) { #ifdef DEBUG_COMPILE printf("<COMPILE> Compile command: "); dumpln(CAR(x)); #endif atom_t y = compile_expr( ctx, x, mode ); if( IS_ERROR(y) ) return y; // do not leave results in the stack if( IS_EMPTY(y) && mode==COMPILE_AS_UNKNOWN ) asm_pop_result( ctx ); #ifdef DEBUG_COMPILE printf("<COMPILE> Command compiled!\n"); #endif } if( IS_EMPTY(lisp) ) { if( mode==COMPILE_AS_FUNC ) { asm_empty_body( ctx ); asm_push_result( ctx ); } } return unbound; }
atom_t compile_if | ( | context_t * | ctx, |
atom_t | source, | ||
int | mode | ||
) |
ctx | compilation context |
source | IF's source |
mode | compilation mode (COMPILE_AS_... macro) |
Compiles an if
statement. The generated code depends on the parameters - whether they are constants or expressions. At the end of execution of generated code the result of if
should be in the stack (the result is either error atom or unbound atom).
{ atom_t params = CDR(source); atom_t condition_src = params; atom_t condition = CAR(params); atom_t then_lisp = instruction_list(CAR(CDR(params))); atom_t else_lisp = instruction_list(CAR(CDR(CDR(params)))); int has_else = !IS_EMPTY(else_lisp); if( mode==COMPILE_AS_FUNC && !has_else ) return( new_error( ERROR_MISSING_RIGHTS, source ) ); int branch = 0; int ifend = 0; //printf("IF STATEMENT\n"); //printf(" COND="); dumpln(condition); //printf(" THEN="); dumpln(then_lisp); //printf(" ELSE="); dumpln(else_lisp); // compile condition of IF atom_t y = compile_expr( ctx, condition_src, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; // add checker for boolean value asm_boolean( ctx, condition ); branch = asm_if_prologue( ctx ); // compile THEN of IF asm_label( ctx, TEXT("$then") ); y = compile_block( ctx, then_lisp, mode ); if( IS_ERROR(y) ) return y; if( has_else ) ifend = asm_if_epilogue( ctx ); asm_fix( ctx, branch ); // compile ELSE of IF asm_label( ctx, TEXT("$else") ); if( has_else ) { y = compile_block( ctx, else_lisp, mode ); if( IS_ERROR(y) ) return y; asm_fix( ctx, ifend ); asm_label( ctx, TEXT("$ifend") ); } if( mode==COMPILE_AS_PROC ) asm_adjust_result( ctx ); return unbound; }
atom_t compile_repeat | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | REPEAT's source |
Compiles a repeat
statement. The generated code depends on the first parameter of repeat
- if it is a constant, then a shorter code is generated. If it is an expression, then the code should contain instructions for calculating the number of repetitions.
At the end of execution of generated code the result of repeat
should be in the stack (the result is either error atom or unbound atom).
{ atom_t params = CDR(source); atom_t repcount_src = params; atom_t repcount = CAR(params); atom_t commands_lisp = CAR(CDR(params)); int branch; int branch2 = -1; if( IS_LIST(repcount) ) { try_expr: // REPEAT {expr} [...] branch = 0; atom_t y = compile_expr( ctx, repcount_src, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; branch = asm_repeat_prologue_expr( ctx, repcount, &branch2 ); } else { // REPEAT {const} [...] int64_t cnt; if( !atom_to_int( repcount, &cnt ) ) goto try_expr; // return new_error_atom( ERROR_NOT_AN_INTEGER, repcount ); if( cnt==0 ) return unbound; if( cnt>INT_MAX ) return new_error( ERROR_TOO_BIG_NUMBER, repcount ); if( cnt<1 ) return new_error( ERROR_TOO_SMALL_NUMBER, repcount ); branch = asm_repeat_prologue_const( ctx, cnt ); } atom_t y = compile_block( ctx, commands_lisp, COMPILE_AS_PROC ); if( IS_ERROR(y) ) return y; asm_repeat_epilogue( ctx, branch, branch2 ); return unbound; }
atom_t compile_while | ( | context_t * | ctx, |
atom_t | source, | ||
int | is_while, | ||
int | is_do | ||
) |
ctx | compilation context |
source | WHILE's source |
is_while | while=1, until=0 |
is_do | do.while/do.until=1, while/until=0 |
Compiles a while
or a until
statement. If the value of is_while
is !0, then generated code is for while. If it is 0, then the code is for repeat. If is_do
is 1, then parameters must be swapped as in do.while
and do.until
.
{ atom_t condition_src; //atom_t condition_lisp; atom_t commands_lisp; if( is_do ) { condition_src = CDR(CDR(source)); commands_lisp = CAR(CDR(source)); } else { condition_src = CDR(source); commands_lisp = CAR(CDR(CDR(source))); } //condition_lisp = CAR(condition_src); //printf("condition = "); dumpln(condition_lisp); //printf("commands = "); dumpln(commands_lisp); int loop_branch = asm_while_prologue( ctx, is_while, is_do ); if( is_do ) { atom_t y = compile_block( ctx, commands_lisp, COMPILE_AS_PROC ); if( IS_ERROR(y) ) return y; } atom_t x = compile_expr( ctx, condition_src, COMPILE_AS_FUNC ); if( IS_ERROR(x) ) return x; int skip_branch = asm_while_inlogue( ctx, commands_lisp, is_while ); if( !is_do ) { atom_t y = compile_block( ctx, commands_lisp, COMPILE_AS_PROC ); if( IS_ERROR(y) ) return y; } asm_while_epilogue( ctx, loop_branch, skip_branch, is_while ); return unbound; }
atom_t compile_forever | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | FOREVER's source |
Compiles a forever
statement.
{ atom_t commands_lisp = CAR(CDR(source)); int branch = asm_forever_prologue( ctx ); atom_t y = compile_block( ctx, commands_lisp, COMPILE_AS_PROC ); if( IS_ERROR(y) ) return y; asm_forever_epilogue( ctx, branch ); return unbound; }
atom_t compile_catch | ( | context_t * | ctx, |
atom_t | source, | ||
int | mode | ||
) |
ctx | compilation context |
source | CATCH's source |
mode | compilation mode (COMPILE_AS_... macro) |
Compiles a catch
statement. The generated code executes the commands and catches forced exits. If they are caused by a throw
with the same tag, then the exits are masked.
{ //printf("commands="); dumpln(source); atom_t params = CDR(source); atom_t tag_src = params; atom_t commands = CAR(CDR(params)); // generate catch trampoline (prologue changes exit_addr) int old_exit_addr = ctx->exit_addr; int branch = asm_catch_prologue( ctx ); // compile catch body atom_t y; y = compile_block( ctx, commands, mode ); if( IS_ERROR(y) ) return y; // generate catch prologue and restore exit_addr ctx->exit_addr = old_exit_addr; // compile tag asm_label( ctx, TEXT("exit_catch:") ); asm_fix( ctx, branch ); //asm_int_3( ctx ); if( mode==COMPILE_AS_PROC ) asm_push_result( ctx ); else asm_set_output_status( ctx, 0 ); y = compile_expr( ctx, tag_src, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; asm_catch_epilogue( ctx ); //asm_exit_if_output( ctx ); return empty_list; }
atom_t compile_tag | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | TAG's source |
Compiles a tag
statement. Creates a new variable of type VAR_TYPE_TAG.
{ atom_t tag = CAR(CDR(source)); if( !IS_ANY_WORD(tag) ) return new_error( ERROR_NOT_A_WORD, tag ); tag = new_subword( tag, STRING(tag)+1, LENGTH(tag)-1 ); // remember the current position in generated code // only suring the first pass if( !ctx->generate ) { atom_t var = new_var( tag, ctx->parent, 1 ); VARTYPE( var ) = VAR_TYPE_TAG; VALUE( var ) = new_integer( ctx->size ); SET_FLAGS( var, FLAG_VARIABLE ); } else { if( OPTION_ASSEMBLER ) asm_label( ctx, STRING(tag) ); } DEUSE( tag ); return unbound; }
atom_t compile_goto | ( | context_t * | ctx, |
atom_t | source, | ||
atom_t | var | ||
) |
ctx | compilation context |
source | GOTO's source |
var | GOTO's var atom |
Compiles a goto
statement. Creates code which searches in real-time for tag named as the input and makes jump to it.
If the input is a constant word, then find taget address at compile-time.
{ atom_t target_src = CDR(source); atom_t target = CAR(target_src); //printf("target="); dumpln(target); // try to find whether the target is known at compile-time if( IS_ANY_WORD(target) && LENGTH(target)>1 && *STRING(target)==TEXT('"') ) { // We have direct GOTO - i.e. we know the target's name atom_t real_name = new_subword( target, STRING(target)+1, LENGTH(target)-1 ); atom_t var = find_local_var( real_name, ctx->parent ); DEUSE( real_name ); // test whether the target exists during // the second pass of the compilation if( ctx->generate && (!var || !IS_TAG(var)) ) { return new_error( ERROR_NOT_A_TAG, target ); } asm_goto( ctx, var ); return unbound; } // the target is not a word-constant asm_goto_prologue( ctx, target ); // pushes target's source // prepare tag atom_t y = compile_expr( ctx, target_src, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; asm_call_atom( ctx, var, 1 ); asm_pop_atom( ctx ); // pop tag asm_pop_dummy ( ctx ); // pop source asm_goto_epilogue( ctx, target ); return unbound; }
atom_t compile_iftest | ( | context_t * | ctx, |
atom_t | source, | ||
int | criteria | ||
) |
atom_t compile_for | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | FOR's source |
Compiles a for
statement.
{ atom_t name = CAR(CDR(source)); atom_t limits = CAR(CDR(CDR(source))); atom_t commands_lisp = CAR(CDR(CDR(CDR(source)))); int branch = 0; int branch2 = -1; branch = 0; if( !IS_LIST(limits) || !GET_FLAGS(limits,FLAG_WAS_LIST_CONST) ) { return new_error(ERROR_NOT_A_LIST_CONST,limits); } if( !IS_LIST(commands_lisp) || !GET_FLAGS(commands_lisp,FLAG_WAS_LIST_CONST) ) { return new_error(ERROR_NOT_A_LIST_CONST,commands_lisp); } if( IS_EMPTY(limits) || IS_EMPTY(CDR(limits)) ) { return new_error(ERROR_MISSING_FOR_LIMITS,source); } // compile initial limit atom_t y = compile_expr( ctx, limits, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; // copy this limit to the control variable if( !IS_ANY_WORD(name) || LENGTH(name)<2 || *STRING(name)!=TEXT('"') ) return new_error( ERROR_NOT_A_WORD_CONST, name ); if( !ctx->generate ) { atom_t inc; atom_t qname = new_word( STRING(name), LENGTH(name) ); *STRING(qname) = L':'; atom_t cname = new_word( STRING(name), LENGTH(name)+1 ); memmove( STRING(cname)+1, STRING(cname), LENGTH(name)*CHAR_SIZE ); *STRING(cname) = L':'; *(STRING(cname)+1) = L'^'; inc = new_list(cname,empty_list); inc = new_list(qname,inc); inc = new_list(USE(word_plus),inc); SET_FLAGS(inc,FLAG_EXPRESSION); inc = new_list(inc,empty_list); inc = new_list(USE(name),inc); inc = new_list(USE(word_make),inc); SET_FLAGS(inc,FLAG_EXPRESSION+FLAG_WAS_LIST_CONST); if( IS_EMPTY(commands_lisp) ) { //printf("~1~\n"); commands_lisp = new_list(inc,empty_list); CAR(CDR(CDR(CDR(source)))) = commands_lisp; SET_FLAGS(commands_lisp,FLAG_EXPRESSION+FLAG_WAS_LIST_CONST); } else { //printf("~2~\n"); atom_t x = commands_lisp; while( !IS_EMPTY(CDR(x)) ) x = CDR(x); CDR(x) = new_list(inc,empty_list); } //printf("===========NEW LISP==="); dumpln(commands_lisp); } atom_t real_name = new_subword( name, STRING(name)+1, LENGTH(name)-1 ); //printf("real name="); dumpln(real_name); //printf("parent="); dumpln(ctx->parent); atom_t control_var = find_var( real_name, ctx->parent ); assert( control_var ); DEUSE( real_name ); atom_t step_name = new_word( STRING(name), LENGTH(name) ); *STRING(step_name) = L'^'; atom_t step_var = find_var( step_name, ctx->parent ); assert( step_var ); DEUSE( step_name ); // new value of control var is already in stack, // duplicate it in otder to make: MAKE "<VAR> <FROM_VALUE> asm_pop_result( ctx ); asm_push_result( ctx ); asm_push_result( ctx ); asm_make_direct( ctx, control_var, source ); // compile final limit y = compile_expr( ctx, CDR(limits), COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; // compile step (if any) if( IS_NOT_EMPTY(CDR(CDR(limits))) ) { y = compile_expr( ctx, CDR(CDR(limits)), COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; } else { asm_push_atom( ctx, unbound ); } branch = asm_for_prologue( ctx, step_var, limits, &branch2 ); y = compile_block( ctx, commands_lisp, COMPILE_AS_PROC ); if( IS_ERROR(y) ) return y; asm_for_epilogue( ctx, branch, branch2 ); return unbound; }
void init_compiler | ( | outter_t | outter, |
inner_t | inner, | ||
inner_eof_t | inner_eof | ||
) |
outter | outter function to use by dump and dumpln |
inner | inner function to use for text input |
inner_eof | inner_eof function to test eof of text input |
Initializes the compiler and all other modules
{ //2011.02.09 Now output to console is always through UTF-8 (see lhogho.c) //#ifdef UNICODE_CHARS //fwide(stdout,1); //#else //fwide(stdout,-1); //#endif running_compiled_code = 0; compiling_code = 0; init_output( outter ); init_input( inner, inner_eof ); init_atoms(); init_parser(); init_vars(); init_runtime(); init_options( ); init_errors( ); //printf("testing barrization\n"); //char_t i; //printf("a->|a|\t|a|->a\n"); //for(i=0;i<128;i++) if( (i!=ENBAR(i)) || (i!=DEBAR(i)) ) // { // if(i!=ENBAR(i)) // printf("%d->%d",i,ENBAR(i)); // else printf("\t"); // // if(i!=DEBAR(i)) // printf("\t%d->%d",i,DEBAR(i)); // printf("\n"); // } }
void finit_compiler | ( | ) |
Finalizes all modules of the compiler.
{ if( OPTION_VARIABLES || OPTION_USER_VARIABLES ) { outter( TEXT("Variables:\n\0"), UNKNOWN ); dumpln( root ); //outter( TEXT("\n\0"), UNKNOWN ); } clear_all_errors(); finit_errors(); finit_runtime(); finit_vars(); finit_atoms(); finit_options(); }
int compile_from_options | ( | ) |
Compiles according to the compiler's options. In case of error dumps the error message and returns non-zero value.
{ //printf("enter compile()\n"); //printf("##### path=|%s|#######\n",getcwd (NULL,0)); //printf("enter compile()\n"); //printf("##### path=|%s|#######\n",getcwd (NULL,0)); atom_t x; atom_t sources = empty_list; // load comman-line source if( option_source_filename_chars ) { x = read_word( option_source_filename_chars ); if( IS_ERROR(x) ) { dumpln(x); return 1; } atom_t y = trim_shell_comment( x ); DEUSE( x ); sources = new_list( y, sources ); } // load embedded sources int ptr; unsigned char* code = load_file( option_compiler_filename_chars, &ptr ); // check for magic number while( (*(int*)(code+ptr-4)==MAGIC_NUMBER) || (*(int*)(code+ptr-4)==MAGIC_COMPILER_NUMBER)) { int size = *(int*)(code+ptr-8); ptr -= size+8; x = decode_word(code+ptr,size,0); atom_t y = trim_shell_comment( x ); DEUSE( x ); sources = new_list( y, sources ); } DEALLOC( code ); // if there is any source then read it and compile it if( IS_EMPTY(sources) ) { output_compiler_name( 0 ); return 0; } compiling_code = 1; FULLSOURCE(root) = ( sources ); // already used once SOURCE(root) = USE( sources ); x = compile_function( root, COMPILE_AS_PROC, COMPILE_AS_NON_MACRO ); if( IS_ERROR(x) ) { dumpln(x); //DEUSE(x); clear_all_errors(); compiling_code=0; return 1; } compiling_code = 0; // now the sources is confirmed to be compilable // now check whether an executable file must be created if( OPTION_MAKE_EXECUTABLE || OPTION_MAKE_EXECUTABLE_COMPILER ) { x = compile_to_file( ); if( IS_ERROR(x) ) { dumpln(x); DEUSE(x); return 1; } } return 0; }
Compiles current source into executable file. The name of the file is based on the name of the external source. EXE
extension is used for Windows systems. For Linux no extension is used. The generated file has read-write-execute user permisions.
If the option_make_executable_compiler is set, then the compiled file acts like a compiler in respect to its inputs.
If the option_make_executable is set, then the compiled file acts as a standalone file and does not use any of the Lhogho options.
{ FILE* infile; FILE* outfile; // compose the output name char* output_filename; chars_t output_filename_chars; { // [1] use the original source name // [2] remove trailing extension (as defined in source_extensions) // if extension is different, do not remove anything // [3] append the executable extension (as defined in EXE_EXT) output_filename = (char*)alloca( strlen(option_source_filename)+strlen(EXE_EXT)+100 ); strcpy( output_filename, option_source_filename ); char* extension = output_filename+strlen(output_filename); // remove any source extension int i; int done = 0; for( i=0; i<SRC_EXT_COUNT; i++) { if( strcasecmp(extension-strlen(SRC_EXT),SRC_EXT)==0 ) { strcpy( extension-strlen(SRC_EXT), EXE_EXT ); done = 1; break; } } // add executable extension if not done already if( !done ) { #ifdef SAFEMODE assert( strlen(EXE_EXT2)!=0 ); #endif if( strlen(EXE_EXT)==0 ) strcpy( extension, EXE_EXT2 ); else strcpy( extension, EXE_EXT ); } output_filename_chars = UNFILENAME( output_filename ); outfile = fopen( output_filename, "wb" ); if( errno ) return new_os_error( output_filename_chars ); } #define BUF_SIZE 1024 char* buffer[BUF_SIZE]; int size; // copy compiler into output file infile = fopen( option_compiler_filename, "rb" ); if( errno ) return new_os_error( option_compiler_filename_chars ); while( (size = fread( buffer, 1, BUF_SIZE, infile )) ) { if( errno ) return new_os_error( option_compiler_filename_chars ); fwrite( buffer, 1, size, outfile ); if( errno ) return new_os_error( output_filename_chars ); } fclose( infile ); if( errno ) return new_os_error( option_compiler_filename_chars ); // copy source into output file int source_size = 0; infile = fopen( option_source_filename, "rb" ); if( errno ) return new_os_error( option_source_filename_chars ); while( (size = fread( buffer, 1, BUF_SIZE, infile )) ) { if( errno ) return new_os_error( option_source_filename_chars ); source_size += size; fwrite( buffer, 1, size, outfile ); if( errno ) return new_os_error( output_filename_chars ); } fclose( infile ); if( errno ) return new_os_error( option_source_filename_chars ); // write source size fwrite( &source_size, 1, 4, outfile ); if( errno ) return new_os_error( output_filename_chars ); // write magic data size = OPTION_MAKE_EXECUTABLE?MAGIC_NUMBER:MAGIC_COMPILER_NUMBER; fwrite( &size, 1, 4, outfile ); if( errno ) return new_os_error( output_filename_chars ); fclose( outfile ); if( errno ) return new_os_error( output_filename_chars ); chmod( output_filename, S_IRWXU ); if( errno ) return new_os_error( output_filename_chars ); return empty_list; }
atom_t instruction_list | ( | atom_t | data | ) |
int run_function | ( | atom_t | function | ) |
Runs the compiled code of a function. Assumes the function is already compiled without error. If error occurs during execution the error is dumped on the output stream and its code is returned.
{ #ifdef SAFE_MODE assert( ADDRESS(function) ); #endif typedef atom_t(*user_code_t)(); // Lhogho-compiled user code user_code_t func = (user_code_t)ADDRESS(function); //int x; //printf("bin adr=%x\n",(int)func); //for( x=0; x<128; x++ ) // { // if( x % 16 ) printf(","); else printf("\n\tdb\t"); // printf("$%x", *(((unsigned char*)func)+x)); // } //printf("\n"); //printf("start executing\n"); //printf("******BEFORE******\n"); //dump_statistics(); running_compiled_code = 1; atom_t result = func(); running_compiled_code = 0; //printf("******AFTER******\n"); //dump_statistics(); //printf("result=%x\n",(int)result); //printf("ref=%d\n",REF(result)); //printf("result(ref=%d)=",REF(result)); dumpln(result); //printf("before error rootdef="); dump_atom(DEFINITIONS(root),1); printf("\n"); //printf("before error rootdef="); dump_atom(TREE(root),1); printf("\n"); if( IS_ERROR(result) ) { int exit_code = 0; if( ERRCODE(result)!= EXIT_BY_BYE && ERRCODE(result)!= EXIT_BY_THROW_TOPLEVEL && ERRCODE(result)!= EXIT_BY_THROW_SYSTEM ) { dumpln( result ); exit_code = ERRCODE(result); } DEUSE(last_error); last_error = empty_list; clear_all_errors(); return exit_code; } else { DEUSE( result ); return 0; } }
int run_source | ( | chars_t | source | ) |
Compiles source code as if it is the main program. No variables are cleared before or after the compilation. Then runs the compiled code.
{ int exit_code; compiling_code = 1; atom_t x = new_word( source, -1 ); atom_t y = trim_shell_comment( x ); DEUSE( x ); DEUSE( BODY(root) ); BODY(root) = empty_list; DEUSE( TREE(root) ); TREE(root) = empty_list; DEUSE( BINARY(root) ); BINARY(root) = empty_list; DEUSE( SOURCE(root) ); SOURCE(root) = empty_list; DEUSE( FULLSOURCE(root) ); FULLSOURCE(root) = empty_list; FULLSOURCE(root) = ( y ); // already used once SOURCE(root) = USE( y ); x = compile_function( root, COMPILE_AS_PROC, COMPILE_AS_NON_MACRO ); if( IS_ERROR(x) ) { exit_code = ERRCODE(x); DEUSE( x ); compiling_code = 0; } else { compiling_code = 0; exit_code = run_function(root); //exit_code = 0; } return exit_code; }
atom_t compile_function | ( | atom_t | func, |
int | mode, | ||
int | is_macro | ||
) |
func | function to compile |
mode | compilation mode (COMPILE_AS_ macros) |
is_macro | macro mode |
Compiles the body of a function. If the function is not parsed or a syntax tree is not generated then parse and treeify it first.
If is_macro
is false, then the epilogue of the function releases all local variables - created at compile or at runtime.
If is_macro
is true, then the epilogue of the function calls a function to process the locals. This function would typically save the locals in the parent variable.
{ #ifdef DEBUG_COMPILE printf("<COMPILE> Compile "); dump(NAME(func)); if( mode==COMPILE_AS_PROC ) printf(" as procedure\n"); if( mode==COMPILE_AS_FUNC ) printf(" as function\n"); if( mode==COMPILE_AS_UNKNOWN ) printf(" as unknown\n"); #endif need_descr2( func ); atom_t x; atom_t y; // if there is not syntax tree of the function // then parse and treeify it first if( IS_EMPTY(TREE(func)) ) { y = build_syntax_tree( func ); if( IS_ERROR(y) ) return y; } #ifdef DEBUG_COMPILE printf("<COMPILE> Syntax tree built\n"); #endif context_t ctx; ctx.size = 0; ctx.generate = NULL; ctx.parent = func; ctx.exit_addr = 0; //printf("SET0 ExAd=%d\n",ctx.exit_addr); // set offset of local variables int offset = BASE_OFFSET_LOCALS-4; // this is the start offset for( x = LOCALS(func); IS_NOT_EMPTY(x); x=CDR(x) ) if( IS_VARIABLE(CAR(x)) && OFFSET(CAR(x))==0 && IS_NORMAL(CAR(x)) ) { //printf("set offset of "); dump(NAME(CAR(x))); //printf(" to be %d\n",offset); OFFSET(CAR(x)) = offset; offset -= sizeof( atom_t ); } #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 1\n"); #endif //------------------------------ // calculate size of to-be-generated code asm_prologue( &ctx, func ); if( IS_EMPTY(TREE(func)) ) asm_empty_body( &ctx ); else { SET_FLAGS( TREE(func), FLAG_WAS_LIST_CONST ); y = compile_block( &ctx, TREE(func), mode ); if( mode==COMPILE_AS_FUNC ) { asm_output( &ctx, TREE(func), 0 ); // simulate OUTPUT } if( IS_ERROR(y) ) return y; } asm_preepilogue( &ctx ); ctx.exit_addr = ctx.size; asm_epilogue( &ctx, func, is_macro ); #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 1 done!\n"); printf("<COMPILE> Code size=%d\n\n",ctx.size); printf("<COMPILE> Pass 2\n"); #endif //----------------------------- // allocate memory for code ctx.generate = new_mem( ctx.size ); ctx.size = 0; //printf("old exit addr=%8x\n",ctx.exit_addr); ctx.exit_addr = (int)MEMORY(ctx.generate)+ctx.exit_addr; //printf("new base addr=%8x\n",(int)MEMORY(ctx.generate)); //printf("new exit addr=%8x\n",ctx.exit_addr); //--------------------------------- // generate code for the body of the function asm_prologue( &ctx, func ); if( IS_EMPTY(TREE(func)) ) asm_empty_body( &ctx ); else { y = compile_block( &ctx, TREE(func), mode ); if( mode==COMPILE_AS_FUNC ) asm_output( &ctx, TREE(func), 0 ); // simulate OUTPUT if( IS_ERROR(y) ) { DEUSE( ctx.generate ); return y; } } asm_preepilogue( &ctx ); asm_epilogue( &ctx, func, is_macro ); #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 2 done!\n"); #endif BINARY(func) = ctx.generate; ADDRESS(func) = (int)MEMORY(BINARY(func)); //dumpln( root ); // generate code for local functions for( x = LOCALS(func); IS_NOT_EMPTY(x); x=CDR(x) ) if( !IS_VARIABLE(CAR(x)) && !IS_PRIMITIVE(CAR(x)) && !IS_TAG(CAR(x)) ) { y = compile_function( CAR(x), COMPILE_AS_PROC, COMPILE_AS_NON_MACRO ); if( IS_ERROR(y) ) return y; } return empty_list; }
atom_t compile_external_function | ( | atom_t | func | ) |
func | function to compile |
Compiles the trampoline of an external function.
{ #ifdef DEBUG_COMPILE printf("<COMPILE> Re-compile external "); dump(NAME(func)); #endif #ifdef SAFE_MODE assert( IS_EXTERNAL(func) ); #endif context_t ctx; ctx.size = 0; ctx.generate = NULL; ctx.parent = func; ctx.exit_addr = 0; #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 1\n"); #endif //------------------------------ // calculate size of to-be-generated code asm_external_function( &ctx, func ); //ctx.exit_addr = ctx.size; #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 1 done!\n"); printf("<COMPILE> Code size=%d\n\n",ctx.size); printf("<COMPILE> Pass 2\n"); #endif //----------------------------- // allocate memory for code ctx.generate = new_mem( ctx.size ); ctx.size = 0; //--------------------------------- // generate code for the body of the function asm_external_function( &ctx, func ); #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 2 done!\n"); #endif DEUSE(BINARY(func)); BINARY(func) = ctx.generate; ADDRESS(func) = (int)MEMORY(BINARY(func)); return empty_list; }
atom_t compile_internal_function | ( | atom_t | func, |
int | static_link | ||
) |
func | function to compile |
static_link | static link from the current frame |
Compiles the trampoline of an internal function.
{ #ifdef DEBUG_COMPILE printf("<COMPILE> Re-compile internal "); dump(NAME(func)); #endif #ifdef SAFE_MODE assert( IS_INTERNAL(func) ); #endif context_t ctx; ctx.size = 0; ctx.generate = NULL; ctx.parent = func; ctx.exit_addr = 0; #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 1\n"); #endif //------------------------------ // calculate size of to-be-generated code asm_internal_function( &ctx, static_link, func ); //ctx.exit_addr = ctx.size; #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 1 done!\n"); printf("<COMPILE> Code size=%d\n\n",ctx.size); printf("<COMPILE> Pass 2\n"); #endif //----------------------------- // allocate memory for code ctx.generate = new_mem( ctx.size ); ctx.size = 0; //--------------------------------- // generate code for the body of the function asm_internal_function( &ctx, static_link, func ); #ifdef DEBUG_COMPILE printf("<COMPILE> Pass 2 done!\n"); #endif // The address of the function should point to the // newly created trampoline. The memory link should // contain atoms that must be freed when the function // is freed, namely: // - the original memory atom // - the new memory atom // The old memoty atom is needed, because it points // to the old code of the Lhogho function which is // actually used by the new code. BINARY(func) = new_list(ctx.generate,new_list(BINARY(func),empty_list)); ADDRESS(func) = (int)MEMORY(ctx.generate); return empty_list; }
int is_constant | ( | atom_t | lisp | ) |
lisp | statement to check |
Lisps which are expressions (i.e. they are in parentheses) are not constants. References to variables like ':a' are not constants. Everything else is considered constants.
{ //printf("is_constant "); dump_atom(lisp,1); printf("\n"); // empty list if( IS_EMPTY(lisp) ) return 1; // number? if( IS_INTEGER(lisp) ) return 1; if( IS_FLOAT(lisp) ) return 1; // most non-expressions are constants if( !IS_LIST(lisp) || !IS_EXPRESSION(lisp) ) { if( IS_ANY_WORD(lisp) && LENGTH(lisp)>1 && *STRING(lisp)==TEXT(':') ) { return 0; } return 1; } // if there are more than 1 items then it is not a constant if( IS_NOT_EMPTY(CDR(lisp)) ) return 0; // so we have one element - it could be: // constant - (2) // non-constant - (pi) atom_t elem = CAR(lisp); // (2) if( IS_INTEGER(elem) ) return 1; // (2.5) if( IS_FLOAT(elem) ) return 1; // ([2 5]) if( IS_LIST(elem) && !IS_EXPRESSION(elem) ) return 1; // (<not-word>), e.g. error, memory, var, ... if( !IS_ANY_WORD(elem) ) return 0; // ("a) if( LENGTH(elem)>1 && *STRING(elem)==TEXT('"') ) return 1; // (:a) if( LENGTH(elem)>1 && *STRING(elem)==TEXT(':') ) return 0; // (number) float64_t x; if( atom_to_float( elem, &x ) ) return 1; // not a constant return 0; }
int is_reference | ( | atom_t | lisp | ) |
lisp | statement to check |
References are words which start with colon, like ':a'.
{ //printf("is reference="); dumpln(lisp); // if a word starting with ":" then is a reference if( IS_ANY_WORD(lisp) && LENGTH(lisp)>1 && *STRING(lisp)==TEXT(':') ) { //printf("it is reference\n"); return 1; } // the only other reference is (:a) // non-expressions are not references if( !IS_EXPRESSION(lisp) ) return 0; // empty lists are not references if( IS_EMPTY(lisp) ) return 0; // if there are more than 1 items then it is not a reference if( IS_NOT_EMPTY(CDR(lisp)) ) return 0; //printf("call recursive\n"); return is_reference( CAR(lisp) ); }
atom_t compile_lisp_const | ( | context_t * | ctx, |
atom_t | lisp | ||
) |
ctx | compilation context |
lisp | constant to compile |
Compiles a single constant.
{ atom_t value = IS_LIST(lisp)&&IS_EXPRESSION(lisp)?CAR(lisp):lisp; if( ctx->generate ) { // create ABC from "ABC if the constant is a word constant //printf("before = "); dumpln(lisp); if( IS_LIST(lisp) && IS_EXPRESSION(lisp) ) lisp = CAR( lisp ); //Fix for bug #3427526 //printf("after = "); dumpln(lisp); if( IS_ANY_WORD( lisp ) && LENGTH(lisp)>0 && *STRING(lisp)==TEXT('"') ) { value = new_subword( lisp, STRING(lisp)+1, LENGTH(lisp)-1 ); ATOMS(ctx->generate) = new_list( value, ATOMS(ctx->generate) ); } } asm_push_atom( ctx, value ); return unbound; }
atom_t compile_local | ( | context_t * | ctx, |
atom_t | source, | ||
int * | processed | ||
) |
ctx | compilation context |
source | LOCAL's source |
processed | 1 if the statement is processed |
Compiles a LOCAL statement. Actually does not compile but process the LOCAL's source by removing all constant-words. Such parameters should already be processed by the treefier. If all parameters are removed, then the whole LOCAL statement is ignored. Otherwise the modified source is processed as ordinary user-defined function.
{ // remove all inputs which are constant words // because they are processed automatically atom_t a; atom_t b; for( a=source; IS_NOT_EMPTY(CDR(a)); ) // possibly infinite if atoms are broken { atom_t node = CAR(CDR(a)); if( (!IS_ANY_WORD(node)) || (LENGTH(node)==0) || (*STRING(node)!=TEXT('"')) ) { a = CDR(a); continue; } // get rid of the next element in the // list, because it is constant-word b = CDR(a); CDR(a) = CDR(b); CDR(b) = empty_list; DEUSE( b ); } // check whether everything is removed // if yes, then no need to process LOCAL any more *processed = IS_EMPTY( CDR(source) ); return unbound; }
atom_t compile_make | ( | context_t * | ctx, |
atom_t | source, | ||
int | is_name, | ||
int * | processed | ||
) |
ctx | compilation context |
source | MAKE's source |
is_name | MAKE=0, NAME=1 |
processed | 1 if the statement is processed |
Compiles a MAKE statement. The generated code depends on the parameters - whether they are constants or expressions. At the end of execution of generated code the result of MAKE should be in the stack (the result is either error atom or unbound atom).
Some MAKE statements cannot be processed - e.g. those in which the name of the variable is an expression. In such cases processed
is set to 0.
{ atom_t params = CDR(source); atom_t name; atom_t value_src; if( is_name ) { name = CAR(CDR(params)); value_src = params; } else { name = CAR(params); value_src = CDR(params); } //printf("value="); dumpln(value_src); *processed = 1; // Check the first input of MAKE. If it is a word constant // then we have immediate MAKE which is compiled directly. // Otherwise we have indirect MAKE. if( IS_ANY_WORD(name) && LENGTH(name)>1 && *STRING(name)==TEXT('"') ) { // We have direct MAKE - i.e. we know the name of the // variable which value is changed. Generate code which // will push the absolute address of the variable. atom_t real_name = new_subword( name, STRING(name)+1, LENGTH(name)-1 ); atom_t var = find_var( real_name, ctx->parent ); if( var ) { //printf("found var "); dump_atom(NAME(var),1); printf(" parent="); dumpln(NAME(PARENT(var))); //printf("--- isglobal=%d isnormal=%d isruntime=%d\n",IS_GLOBAL(var),IS_NORMAL(var),IS_RUNTIME(var)); if( !IS_VARIABLE( var ) ) { DEUSE( real_name ); return new_error( ERROR_NOT_A_VAR, name ); } // direct make + existing variable // push <value> atom_t y = compile_expr( ctx, value_src, COMPILE_AS_FUNC ); if( IS_ERROR(y) ) return y; asm_make_direct( ctx, var, source ); } else { // direct make + NON-existing variable *processed = 0; } DEUSE( real_name ); } else { // indirect make // leave handling for rt_make //printf("not found var\n"); *processed = 0; } return unbound; }
atom_t compile_output | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | OUTPUT's source |
Compiles an OUTPUT statement.
{ atom_t params = CDR(source); atom_t value_src = params; atom_t result = compile_expr( ctx, value_src, COMPILE_AS_FUNC ); asm_output( ctx, source, 1 ); // true output return result; }
atom_t compile_maybeoutput | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | MAYBEOUTPUT's source |
Compiles an MAYBEOUTPUT statement.
{ atom_t params = CDR(source); atom_t value_src = params; atom_t result = compile_expr( ctx, value_src, COMPILE_AS_UNKNOWN ); asm_push_result( ctx ); asm_output( ctx, source, 1 ); // true output return result; }
atom_t compile_stop | ( | context_t * | ctx, |
atom_t | source | ||
) |
atom_t compile_test | ( | context_t * | ctx, |
atom_t | source, | ||
int | criteria | ||
) |
ctx | compilation context |
source | IFTRUE's or IFFALSE's source |
criteria | IFTRUE=1, IFFALSE=0 |
Compiles a iftrue
or a iffalse
statement. If the value of criteria
is 1, then generated code is for iftrue
. If it is 0, then the code is for iffalse
.
{ atom_t commands = CAR(CDR(source)); //printf("condition = "); dumpln(condition_lisp); //printf("commands = "); dumpln(commands_lisp); int skip_branch = asm_test_prologue( ctx, criteria ); atom_t y = compile_block( ctx, commands, COMPILE_AS_PROC ); if( IS_ERROR(y) ) return y; asm_fix( ctx, skip_branch ); return unbound; }
atom_t compile_lisp_reference | ( | context_t * | ctx, |
atom_t | source | ||
) |
ctx | compilation context |
source | reference source |
Compiles a reference to variable's value - :a.
{ atom_t result = unbound; //printf("in===="); dumpln(source); atom_t name; if( IS_ANY_WORD(source) ) name = source; else name = CAR(source); atom_t real_name = new_subword( name, STRING(name)+1, LENGTH(name)-1 ); atom_t var = find_var( real_name, ctx->parent ); if( var && IS_NORMAL(var) ) { if( IS_VARIABLE( var ) ) { // existing variable asm_reference( ctx, var, source, 1 ); } else result = new_error( ERROR_NOT_A_VAR, name ); } else { // NON-existing variable compile_lisp_const( ctx, name ); asm_runtime_reference( ctx ); asm_pop_atom( ctx ); asm_push_result( ctx ); } DEUSE( real_name ); return result; }
int compiling_code |
char* source_extensions[] = { ".lgo", ".log", ".lg", ".logo", ".lho", ".lhogho" } |