/*
* Copyright (c) 2010 Jiri Svoboda
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file Builtin symbol binding.
*
* 'Builtin' symbols are implemented outside of the language itself.
* Here we refer to entities residing within the interpreted universe
* as 'internal', while anything implemented outside this universe
* as 'external'. This module facilitates declaration of builtin
* symbols and the binding of these symbols to their external
* implementation.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "builtin/bi_boxed.h"
#include "builtin/bi_error.h"
#include "builtin/bi_char.h"
#include "builtin/bi_console.h"
#include "builtin/bi_int.h"
#include "builtin/bi_task.h"
#include "builtin/bi_textfile.h"
#include "builtin/bi_string.h"
#include "input.h"
#include "intmap.h"
#include "lex.h"
#include "list.h"
#include "mytypes.h"
#include "os/os.h"
#include "parse.h"
#include "rdata.h"
#include "run.h"
#include "stree.h"
#include "strtab.h"
#include "symbol.h"
#include "builtin.h"
static builtin_t *builtin_new(void);
/** Declare builtin symbols in the program.
*
* Declares symbols that will be hooked to builtin interpreter procedures.
*
* @param program Program in which to declare builtin symbols.
*/
void builtin_declare(stree_program_t *program)
{
builtin_t *bi;
bi = builtin_new();
bi->program = program;
program->builtin = bi;
/*
* Declare grandfather class.
*/
builtin_code_snippet(bi,
"class Object is\n"
"end\n");
bi->gf_class = builtin_find_lvl0(bi, "Object");
/*
* Declare other builtin classes/functions.
*/
bi_error_declare(bi);
bi_char_declare(bi);
bi_console_declare(bi);
bi_int_declare(bi);
bi_task_declare(bi);
bi_textfile_declare(bi);
bi_string_declare(bi);
}
/** Bind internal interpreter references to symbols in the program.
*
* This is performed in separate phase for several reasons. First,
* symbol lookups do not work until ancestry is processed. Second,
* this gives a chance to process the library first and thus bind
* to symbols defined there.
*/
void builtin_bind(builtin_t *bi)
{
bi_boxed_bind(bi);
bi_error_bind(bi);
bi_char_bind(bi);
bi_console_bind(bi);
bi_int_bind(bi);
bi_task_bind(bi);
bi_textfile_bind(bi);
bi_string_bind(bi);
}
/** Get grandfather class.
*
* Grandfather class is the class from which all other classes are
* (directly or indirectly) derived.
*
* @param builtin Builtin context (corresponsds to program).
* @return Grandfather class (CSI).
*/
stree_csi_t *builtin_get_gf_class(builtin_t *builtin)
{
if (builtin->gf_class == NULL)
return NULL;
return symbol_to_csi(builtin->gf_class);
}
/** Allocate new builtin context object.
*
* @return Builtin context object.
*/
static builtin_t *builtin_new(void)
{
builtin_t *builtin;
builtin = calloc(1, sizeof(builtin_t));
if (builtin == NULL) {
printf("Memory allocation failed.\n");
exit(1);
}
return builtin;
}
/** Parse a declaration code snippet.
*
* Parses a piece of code from a string at the module level. This can be
* used to declare builtin symbols easily and without need for an external
* file.
*/
void builtin_code_snippet(builtin_t *bi, const char *snippet)
{
input_t *input;
lex_t lex;
parse_t parse;
input_new_string(&input, snippet);
lex_init(&lex, input);
parse_init(&parse, bi->program, &lex);
parse_module(&parse);
}
/** Simplifed search for a global symbol.
*
* The specified symbol must exist.
*
* @param bi Builtin context object.
* @param sym_name Name of symbol to find.
* @return Symbol.
*/
stree_symbol_t *builtin_find_lvl0(builtin_t *bi, const char *sym_name)
{
stree_symbol_t *sym;
stree_ident_t *ident;
ident = stree_ident_new();
ident->sid = strtab_get_sid(sym_name);
sym = symbol_lookup_in_csi(bi->program, NULL, ident);
assert(sym != NULL);
return sym;
}
/** Simplifed search for a level 1 symbol.
*
* The specified symbol must exist.
*
* @param bi Builtin context object.
* @param csi_name CSI in which to look for symbol.
* @param sym_name Name of symbol to find.
* @return Symbol.
*/
stree_symbol_t *builtin_find_lvl1(builtin_t *bi, const char *csi_name,
const char *sym_name)
{
stree_symbol_t *csi_sym;
stree_csi_t *csi;
stree_symbol_t *mbr_sym;
stree_ident_t *ident;
ident = stree_ident_new();
ident->sid = strtab_get_sid(csi_name);
csi_sym = symbol_lookup_in_csi(bi->program, NULL, ident);
assert(csi_sym != NULL);
csi = symbol_to_csi(csi_sym);
assert(csi != NULL);
ident->sid = strtab_get_sid(sym_name);
mbr_sym = symbol_lookup_in_csi(bi->program, csi, ident);
assert(mbr_sym != NULL);
return mbr_sym;
}
/** Bind level 1 member function to external implementation.
*
* Binds a member function (of a global class) to external implementation.
* The specified CSI and member function must exist.
*
* @param bi Builtin context object.
* @param csi_name CSI which contains the function.
* @param sym_name Function name.
* @param bproc Pointer to C function implementation.
*/
void builtin_fun_bind(builtin_t *bi, const char *csi_name,
const char *sym_name, builtin_proc_t bproc)
{
stree_symbol_t *fun_sym;
stree_fun_t *fun;
fun_sym = builtin_find_lvl1(bi, csi_name, sym_name);
assert(fun_sym != NULL);
fun = symbol_to_fun(fun_sym);
assert(fun != NULL);
fun->proc->bi_handler = bproc;
}
/** Execute a builtin procedure.
*
* Executes a procedure that has an external implementation.
*
* @param run Runner object.
* @param proc Procedure that has an external implementation.
*/
void builtin_run_proc(run_t *run, stree_proc_t *proc)
{
stree_symbol_t *fun_sym;
builtin_proc_t bproc;
#ifdef DEBUG_RUN_TRACE
printf("Run builtin procedure.\n");
#endif
fun_sym = proc->outer_symbol;
bproc = proc->bi_handler;
if (bproc == NULL) {
printf("Error: Unrecognized builtin function '");
symbol_print_fqn(fun_sym);
printf("'.\n");
exit(1);
}
/* Run the builtin procedure handler. */
(*bproc)(run);
}
/** Get pointer to member var of current object.
*
* Returns the var node that corresponds to a member of the currently
* active object with the given name. This member must exist.
*
* @param run Runner object.
* @param mbr_name Name of member to find.
* @return Var node of the member.
*/
rdata_var_t *builtin_get_self_mbr_var(run_t *run, const char *mbr_name)
{
run_proc_ar_t *proc_ar;
rdata_object_t *object;
sid_t mbr_name_sid;
rdata_var_t *mbr_var;
proc_ar = run_get_current_proc_ar(run);
assert(proc_ar->obj->vc == vc_object);
object = proc_ar->obj->u.object_v;
mbr_name_sid = strtab_get_sid(mbr_name);
mbr_var = intmap_get(&object->fields, mbr_name_sid);
assert(mbr_var != NULL);
return mbr_var;
}
/** Return string value from builtin procedure.
*
* Makes it easy for a builtin procedure to return value of type @c string.
*
* @param run Runner object
* @param str String value. Must be allocated from heap and its
* ownership is hereby given up.
*/
void builtin_return_string(run_t *run, const char *astr)
{
rdata_string_t *rstring;
rdata_var_t *rvar;
rdata_value_t *rval;
rdata_item_t *ritem;
run_proc_ar_t *proc_ar;
#ifdef DEBUG_RUN_TRACE
printf("Return string '%s' from builtin function.\n", astr);
#endif
rstring = rdata_string_new();
rstring->value = astr;
rvar = rdata_var_new(vc_string);
rvar->u.string_v = rstring;
rval = rdata_value_new();
rval->var = rvar;
ritem = rdata_item_new(ic_value);
ritem->u.value = rval;
proc_ar = run_get_current_proc_ar(run);
proc_ar->retval = ritem;
}
/** Declare a static builtin function in @a csi.
*
* Declare a builtin function member of CSI @a csi. Deprecated in favor
* of builtin_code_snippet().
*
* @param csi CSI in which to declare function.
* @param name Name of member function to declare.
* @return Symbol of newly declared function.
*/
stree_symbol_t *builtin_declare_fun(stree_csi_t *csi, const char *name)
{
stree_ident_t *ident;
stree_fun_t *fun;
stree_fun_sig_t *sig;
stree_csimbr_t *csimbr;
stree_symbol_t *fun_sym;
stree_symbol_attr_t *sym_attr;
ident = stree_ident_new();
ident->sid = strtab_get_sid(name);
fun = stree_fun_new();
fun->name = ident;
fun->proc = stree_proc_new();
fun->proc->body = NULL;
sig = stree_fun_sig_new();
fun->sig = sig;
list_init(&fun->sig->args);
csimbr = stree_csimbr_new(csimbr_fun);
csimbr->u.fun = fun;
fun_sym = stree_symbol_new(sc_fun);
fun_sym->u.fun = fun;
fun_sym->outer_csi = csi;
sym_attr = stree_symbol_attr_new(sac_static);
list_append(&fun_sym->attr, sym_attr);
fun->symbol = fun_sym;
fun->proc->outer_symbol = fun_sym;
list_append(&csi->members, csimbr);
return fun_sym;
}
/** Add one formal parameter to function.
*
* Used to incrementally construct formal parameter list of a builtin
* function. Deprecated in favor of builtin_code_snippet(). Does not
* support type checking.
*
* @param fun_sym Symbol of function to add parameters to.
* @param name Name of parameter to add.
*/
void builtin_fun_add_arg(stree_symbol_t *fun_sym, const char *name)
{
stree_proc_arg_t *proc_arg;
stree_fun_t *fun;
fun = symbol_to_fun(fun_sym);
assert(fun != NULL);
proc_arg = stree_proc_arg_new();
proc_arg->name = stree_ident_new();
proc_arg->name->sid = strtab_get_sid(name);
proc_arg->type = NULL; /* XXX */
list_append(&fun->sig->args, proc_arg);
}