|
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 |