Lhogho  0.0.027
Data Structures | Defines | Typedefs | Functions | Variables
compiler.h File Reference

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 Documentation

#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

Typedef Documentation

typedef struct ctxnode context_t

Function Documentation

void init_compiler ( outter_t  outter,
inner_t  inner,
inner_eof_t  inner_eof 
)
Parameters:
outteroutter function to use by dump and dumpln
innerinner function to use for text input
inner_eofinner_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");
  //  }
}

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();
}
Returns:
0 if there was no error

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 
)
Parameters:
funcfunction to compile
modecompilation mode (COMPILE_AS_ macros)
is_macromacro mode
Returns:
empty list or error atom

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;
}
Parameters:
funcfunction to compile
Returns:
empty list or error atom

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 
)
Parameters:
funcfunction to compile
static_linkstatic link from the current frame
Returns:
empty list or error atom

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)
Returns:
0 or exit_code

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)
Returns:
exit code (0 if no error)

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

Variable Documentation


[ HOME | INDEX | ATOMS | VARS | REFERENCE ]
Lhogho Developer's Documentation
Tue Feb 7 2012