Lhogho
0.0.027
|
Data Structures | |
struct | ctxnode |
Defines | |
#define | COMPILE_AS_FUNC 0 |
compile and check result to be non-unbound | |
#define | COMPILE_AS_PROC 1 |
compile and check result to be unbound | |
#define | COMPILE_AS_UNKNOWN 2 |
compile and accept any result | |
#define | COMPILE_AS_NON_MACRO 0 |
The COMPILE_AS_*_MACRO are independent on the other COMPILE_AS_* symbols. | |
#define | COMPILE_AS_MACRO 1 |
compile a function as a macro | |
#define | MAGIC_NUMBER 1980069381 |
code for standard standalone executables | |
#define | MAGIC_COMPILER_NUMBER 1761700097 |
code for custom Lhogho compilers | |
Typedefs | |
typedef struct ctxnode | context_t |
structure holding compilation context | |
Functions | |
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 | 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 | run_source (chars_t source) |
compiles and runs source code | |
int | run_function (atom_t function) |
runs the compiled code of a function | |
Variables | |
int | running_compiled_code |
indicate whether generated code is currently running | |
int | compiling_code |
indicate whether source code is currently compiling |
#define COMPILE_AS_FUNC 0 |
#define COMPILE_AS_PROC 1 |
#define COMPILE_AS_UNKNOWN 2 |
#define COMPILE_AS_NON_MACRO 0 |
compile a function as a function
#define COMPILE_AS_MACRO 1 |
#define MAGIC_NUMBER 1980069381 |
#define MAGIC_COMPILER_NUMBER 1761700097 |
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 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 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; }
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 compiling_code |