You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

627 lines
16 KiB

/*
* dpas-builtin.c - Handling for Dynamic Pascal builtins.
*
* Copyright (C) 2004 Southern Storm Software, Pty Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "dpas-internal.h"
#include <stdio.h>
#include <stdlib.h>
/*
* Functions for writing values to stdout.
*/
static void dpas_write_ln(void)
{
putc('\n', stdout);
}
static void dpas_write_int(jit_int value)
{
printf("%ld", (long)value);
}
static void dpas_write_uint(jit_uint value)
{
printf("%lu", (unsigned long)value);
}
static void dpas_write_long(jit_long value)
{
jit_constant_t val;
char *name;
val.type = jit_type_long;
val.un.long_value = value;
name = dpas_constant_name(&val);
fputs(name, stdout);
jit_free(name);
}
static void dpas_write_ulong(jit_ulong value)
{
jit_constant_t val;
char *name;
val.type = jit_type_ulong;
val.un.ulong_value = value;
name = dpas_constant_name(&val);
fputs(name, stdout);
jit_free(name);
}
static void dpas_write_nfloat(jit_nfloat value)
{
printf("%g", (double)value);
}
static void dpas_write_string(char *value)
{
if(value)
{
fputs(value, stdout);
}
else
{
fputs("(null)", stdout);
}
}
/*
* Call a native builtin function.
*/
static jit_value_t call_builtin
(jit_function_t func, const char *name, void *native_func,
jit_type_t arg1_type, jit_value_t value1,
jit_type_t arg2_type, jit_value_t value2,
jit_type_t return_type)
{
jit_type_t signature;
jit_type_t arg_types[2];
jit_value_t args[2];
int num_args = 0;
if(arg1_type)
{
args[num_args] = jit_insn_convert(func, value1, arg1_type, 0);
if(!(args[num_args]))
{
dpas_out_of_memory();
}
arg_types[num_args] = arg1_type;
++num_args;
}
if(arg2_type)
{
args[num_args] = jit_insn_convert(func, value2, arg2_type, 0);
if(!(args[num_args]))
{
dpas_out_of_memory();
}
arg_types[num_args] = arg2_type;
++num_args;
}
signature = jit_type_create_signature
(jit_abi_cdecl, return_type, arg_types, num_args, 1);
if(!signature)
{
dpas_out_of_memory();
}
value1 = jit_insn_call_native(func, name, native_func, signature,
args, num_args, JIT_CALL_NOTHROW);
if(!value1)
{
dpas_out_of_memory();
}
jit_type_free(signature);
return value1;
}
/*
* Call a native "write" function.
*/
static void call_write
(jit_function_t func, const char *name, void *native_func,
jit_type_t arg1_type, jit_value_t value1)
{
call_builtin(func, name, native_func, arg1_type,
value1, 0, 0, jit_type_void);
}
/*
* Write values to "stdout".
*/
static dpas_semvalue dpas_write_inner
(dpas_semvalue *args, int num_args, int newline)
{
jit_function_t func = dpas_current_function();
dpas_semvalue result;
jit_type_t type;
jit_type_t orig_type;
const char *name;
void *native_func;
int index;
for(index = 0; index < num_args; ++index)
{
result = dpas_lvalue_to_rvalue(args[index]);
if(!dpas_sem_is_rvalue(result))
{
dpas_error("invalid value for parameter %d", index + 1);
}
else
{
orig_type = dpas_sem_get_type(result);
type = jit_type_normalize(orig_type);
if(jit_type_is_pointer(orig_type) &&
jit_type_get_ref(orig_type) == dpas_type_char)
{
type = jit_type_void_ptr;
name = "dpas_write_string";
native_func = (void *)dpas_write_string;
}
else if(type == jit_type_sbyte ||
type == jit_type_ubyte ||
type == jit_type_short ||
type == jit_type_ushort ||
type == jit_type_int)
{
type = jit_type_int;
name = "dpas_write_int";
native_func = (void *)dpas_write_int;
}
else if(type == jit_type_uint)
{
type = jit_type_uint;
name = "dpas_write_uint";
native_func = (void *)dpas_write_uint;
}
else if(type == jit_type_long)
{
type = jit_type_long;
name = "dpas_write_long";
native_func = (void *)dpas_write_long;
}
else if(type == jit_type_ulong)
{
type = jit_type_ulong;
name = "dpas_write_ulong";
native_func = (void *)dpas_write_ulong;
}
else if(type == jit_type_float32 ||
type == jit_type_float64 ||
type == jit_type_nfloat)
{
type = jit_type_nfloat;
name = "dpas_write_nfloat";
native_func = (void *)dpas_write_nfloat;
}
else
{
dpas_error("unprintable value for parameter %d", index + 1);
continue;
}
call_write(func, name, native_func, type,
dpas_sem_get_value(result));
}
}
if(newline)
{
call_write(func, "dpas_write_nl", (void *)dpas_write_ln, 0, 0);
}
dpas_sem_set_void(result);
return result;
}
static dpas_semvalue dpas_write(dpas_semvalue *args, int num_args)
{
return dpas_write_inner(args, num_args, 0);
}
/*
* Write values to "stdout", followed by a newline.
*/
static dpas_semvalue dpas_writeln(dpas_semvalue *args, int num_args)
{
return dpas_write_inner(args, num_args, 1);
}
/*
* Flush stdout.
*/
static void dpas_flush_stdout(void)
{
fflush(stdout);
}
static dpas_semvalue dpas_flush(dpas_semvalue *args, int num_args)
{
dpas_semvalue result;
call_write(dpas_current_function(), "dpas_flush_stdout",
(void *)dpas_flush_stdout, 0, 0);
dpas_sem_set_void(result);
return result;
}
/*
* Terminate program execution with an exit status code.
*/
static void dpas_terminate_program(jit_int value)
{
exit((int)value);
}
static dpas_semvalue dpas_terminate(dpas_semvalue *args, int num_args)
{
dpas_semvalue result;
result = dpas_lvalue_to_rvalue(args[0]);
call_write(dpas_current_function(), "dpas_terminate_program",
(void *)dpas_terminate_program, jit_type_int,
dpas_sem_get_value(result));
dpas_sem_set_void(result);
return result;
}
/*
* Create a new object of a specific type and put its pointer into a variable.
*/
static dpas_semvalue dpas_new(dpas_semvalue *args, int num_args)
{
dpas_semvalue result;
jit_type_t type = dpas_sem_get_type(args[0]);
jit_nuint size;
jit_value_t value;
if(dpas_sem_is_lvalue(args[0]) && jit_type_is_pointer(type))
{
size = jit_type_get_size(jit_type_get_ref(type));
value = call_builtin
(dpas_current_function(), "jit_calloc", (void *)jit_calloc,
jit_type_sys_uint,
jit_value_create_nint_constant
(dpas_current_function(), jit_type_sys_uint, 1),
jit_type_sys_uint,
jit_value_create_nint_constant
(dpas_current_function(), jit_type_sys_uint, (jit_nint)size),
jit_type_void_ptr);
if(!value)
{
dpas_out_of_memory();
}
if(!jit_insn_store(dpas_current_function(),
dpas_sem_get_value(args[0]), value))
{
dpas_out_of_memory();
}
}
else if(dpas_sem_is_lvalue_ea(args[0]) && jit_type_is_pointer(type))
{
size = jit_type_get_size(jit_type_get_ref(type));
value = call_builtin
(dpas_current_function(), "jit_calloc", (void *)jit_calloc,
jit_type_sys_uint,
jit_value_create_nint_constant
(dpas_current_function(), jit_type_sys_uint, 1),
jit_type_sys_uint,
jit_value_create_nint_constant
(dpas_current_function(), jit_type_sys_uint, (jit_nint)size),
jit_type_void_ptr);
if(!value)
{
dpas_out_of_memory();
}
if(!jit_insn_store_relative(dpas_current_function(),
dpas_sem_get_value(args[0]), 0, value))
{
dpas_out_of_memory();
}
}
else
{
if(!dpas_sem_is_error(args[0]))
{
dpas_error("invalid l-value used with `New'");
}
}
dpas_sem_set_void(result);
return result;
}
/*
* Dispose of an object that is referenced by a pointer.
*/
static dpas_semvalue dpas_dispose(dpas_semvalue *args, int num_args)
{
dpas_semvalue result;
jit_type_t type = dpas_sem_get_type(args[0]);
result = dpas_lvalue_to_rvalue(args[0]);
if(dpas_sem_is_rvalue(result) && jit_type_is_pointer(type))
{
call_write(dpas_current_function(), "jit_free", (void *)jit_free,
jit_type_void_ptr, dpas_sem_get_value(result));
}
else if(!dpas_sem_is_error(result))
{
dpas_error("invalid argument used with `Dispose'");
}
dpas_sem_set_void(result);
return result;
}
/*
* Determine if two values/types have identical types.
*/
static dpas_semvalue dpas_same_type(dpas_semvalue *args, int num_args)
{
dpas_semvalue result;
if((!dpas_sem_is_rvalue(args[0]) && !dpas_sem_is_type(args[0])) ||
(!dpas_sem_is_rvalue(args[1]) && !dpas_sem_is_type(args[1])))
{
dpas_error("invalid operands to `SameType'");
dpas_sem_set_error(result);
}
else if(dpas_type_identical(dpas_sem_get_type(args[0]),
dpas_sem_get_type(args[1]), 0))
{
dpas_sem_set_rvalue
(result, dpas_type_boolean,
jit_value_create_nint_constant
(dpas_current_function(), dpas_type_boolean, 1));
}
else
{
dpas_sem_set_rvalue
(result, dpas_type_boolean,
jit_value_create_nint_constant
(dpas_current_function(), dpas_type_boolean, 0));
}
return result;
}
/*
* Determine if two values/types have the same basic shape.
*/
static dpas_semvalue dpas_same_shape(dpas_semvalue *args, int num_args)
{
dpas_semvalue result;
if((!dpas_sem_is_rvalue(args[0]) && !dpas_sem_is_type(args[0])) ||
(!dpas_sem_is_rvalue(args[1]) && !dpas_sem_is_type(args[1])))
{
dpas_error("invalid operands to `SameShape'");
dpas_sem_set_error(result);
}
else if(dpas_type_identical(dpas_sem_get_type(args[0]),
dpas_sem_get_type(args[1]), 1))
{
dpas_sem_set_rvalue
(result, dpas_type_boolean,
jit_value_create_nint_constant
(dpas_current_function(), dpas_type_boolean, 1));
}
else
{
dpas_sem_set_rvalue
(result, dpas_type_boolean,
jit_value_create_nint_constant
(dpas_current_function(), dpas_type_boolean, 0));
}
return result;
}
/*
* Mathematical functions.
*/
#define dpas_math_unary(name,func) \
static dpas_semvalue dpas_##name(dpas_semvalue *args, int num_args) \
{ \
dpas_semvalue result; \
jit_value_t value; \
if(!dpas_sem_is_rvalue(args[0])) \
{ \
dpas_error("invalid operand to unary `" #name "'"); \
dpas_sem_set_error(result); \
} \
else \
{ \
value = func(dpas_current_function(), \
dpas_sem_get_value(dpas_lvalue_to_rvalue(args[0]))); \
if(!value) \
{ \
dpas_out_of_memory(); \
} \
dpas_sem_set_rvalue(result, jit_value_get_type(value), value); \
} \
return result; \
}
#define dpas_math_binary(name,func) \
static dpas_semvalue dpas_##name(dpas_semvalue *args, int num_args) \
{ \
dpas_semvalue result; \
jit_value_t value; \
if(!dpas_sem_is_rvalue(args[0]) || !dpas_sem_is_rvalue(args[0])) \
{ \
dpas_error("invalid operands to binary `" #name "'"); \
dpas_sem_set_error(result); \
} \
else \
{ \
value = func(dpas_current_function(), \
dpas_sem_get_value(dpas_lvalue_to_rvalue(args[0])), \
dpas_sem_get_value(dpas_lvalue_to_rvalue(args[1]))); \
if(!value) \
{ \
dpas_out_of_memory(); \
} \
dpas_sem_set_rvalue(result, jit_value_get_type(value), value); \
} \
return result; \
}
#define dpas_math_test(name,func) \
static dpas_semvalue dpas_##name(dpas_semvalue *args, int num_args) \
{ \
dpas_semvalue result; \
jit_value_t value; \
if(!dpas_sem_is_rvalue(args[0])) \
{ \
dpas_error("invalid operand to unary `" #name "'"); \
dpas_sem_set_error(result); \
} \
else \
{ \
value = func(dpas_current_function(), \
dpas_sem_get_value(dpas_lvalue_to_rvalue(args[0]))); \
if(!value) \
{ \
dpas_out_of_memory(); \
} \
dpas_sem_set_rvalue(result, dpas_type_boolean, value); \
} \
return result; \
}
dpas_math_unary(acos, jit_insn_acos)
dpas_math_unary(asin, jit_insn_asin)
dpas_math_unary(atan, jit_insn_atan)
dpas_math_binary(atan2, jit_insn_atan2)
dpas_math_unary(ceil, jit_insn_ceil)
dpas_math_unary(cos, jit_insn_cos)
dpas_math_unary(cosh, jit_insn_cosh)
dpas_math_unary(exp, jit_insn_exp)
dpas_math_unary(floor, jit_insn_floor)
dpas_math_unary(log, jit_insn_log)
dpas_math_unary(log10, jit_insn_log10)
dpas_math_unary(rint, jit_insn_rint)
dpas_math_unary(round, jit_insn_round)
dpas_math_unary(sin, jit_insn_sin)
dpas_math_unary(sinh, jit_insn_sinh)
dpas_math_unary(sqrt, jit_insn_sqrt)
dpas_math_unary(tan, jit_insn_tan)
dpas_math_unary(tanh, jit_insn_tanh)
dpas_math_unary(abs, jit_insn_abs)
dpas_math_binary(min, jit_insn_min)
dpas_math_binary(max, jit_insn_max)
dpas_math_unary(sign, jit_insn_sign)
dpas_math_test(isnan, jit_insn_is_nan)
dpas_math_unary(isinf, jit_insn_is_inf)
dpas_math_test(finite, jit_insn_is_finite)
/*
* Builtins that we currently recognize.
*/
#define DPAS_BUILTIN_WRITE 1
#define DPAS_BUILTIN_WRITELN 2
#define DPAS_BUILTIN_FLUSH 3
#define DPAS_BUILTIN_TERMINATE 4
#define DPAS_BUILTIN_NEW 5
#define DPAS_BUILTIN_DISPOSE 6
#define DPAS_BUILTIN_SAMETYPE 7
#define DPAS_BUILTIN_SAMESHAPE 8
#define DPAS_BUILTIN_ACOS 9
#define DPAS_BUILTIN_ASIN 10
#define DPAS_BUILTIN_ATAN 11
#define DPAS_BUILTIN_ATAN2 12
#define DPAS_BUILTIN_CEIL 13
#define DPAS_BUILTIN_COS 14
#define DPAS_BUILTIN_COSH 15
#define DPAS_BUILTIN_EXP 16
#define DPAS_BUILTIN_FLOOR 17
#define DPAS_BUILTIN_LOG 18
#define DPAS_BUILTIN_LOG10 19
#define DPAS_BUILTIN_RINT 20
#define DPAS_BUILTIN_ROUND 21
#define DPAS_BUILTIN_SIN 22
#define DPAS_BUILTIN_SINH 23
#define DPAS_BUILTIN_SQRT 24
#define DPAS_BUILTIN_TAN 25
#define DPAS_BUILTIN_TANH 26
#define DPAS_BUILTIN_ABS 27
#define DPAS_BUILTIN_MIN 28
#define DPAS_BUILTIN_MAX 29
#define DPAS_BUILTIN_SIGN 30
#define DPAS_BUILTIN_ISNAN 31
#define DPAS_BUILTIN_ISINF 32
#define DPAS_BUILTIN_FINITE 33
/*
* Table that defines the builtins.
*/
typedef struct
{
const char *name;
int identifier;
dpas_semvalue (*func)(dpas_semvalue *args, int num_args);
int num_args; /* -1 for a vararg function */
} dpas_builtin;
static dpas_builtin const builtins[] = {
{"Write", DPAS_BUILTIN_WRITE, dpas_write, -1},
{"WriteLn", DPAS_BUILTIN_WRITELN, dpas_writeln, -1},
{"Flush", DPAS_BUILTIN_FLUSH, dpas_flush, 0},
{"Terminate", DPAS_BUILTIN_TERMINATE, dpas_terminate, 1},
{"New", DPAS_BUILTIN_NEW, dpas_new, 1},
{"Dispose", DPAS_BUILTIN_DISPOSE, dpas_dispose, 1},
{"SameType", DPAS_BUILTIN_SAMETYPE, dpas_same_type, 2},
{"SameShape", DPAS_BUILTIN_SAMESHAPE, dpas_same_shape, 2},
{"Acos", DPAS_BUILTIN_ACOS, dpas_acos, 1},
{"Asin", DPAS_BUILTIN_ASIN, dpas_asin, 1},
{"Atan", DPAS_BUILTIN_ATAN, dpas_atan, 1},
{"Atan2", DPAS_BUILTIN_ATAN2, dpas_atan2, 2},
{"Ceil", DPAS_BUILTIN_CEIL, dpas_ceil, 1},
{"Cos", DPAS_BUILTIN_COS, dpas_cos, 1},
{"Cosh", DPAS_BUILTIN_COSH, dpas_cosh, 1},
{"Exp", DPAS_BUILTIN_EXP, dpas_exp, 1},
{"Floor", DPAS_BUILTIN_FLOOR, dpas_floor, 1},
{"Log", DPAS_BUILTIN_LOG, dpas_log, 1},
{"Log10", DPAS_BUILTIN_LOG10, dpas_log10, 1},
{"Rint", DPAS_BUILTIN_RINT, dpas_rint, 1},
{"Round", DPAS_BUILTIN_ROUND, dpas_round, 1},
{"Sin", DPAS_BUILTIN_SIN, dpas_sin, 1},
{"Sinh", DPAS_BUILTIN_SINH, dpas_sinh, 1},
{"Sqrt", DPAS_BUILTIN_SQRT, dpas_sqrt, 1},
{"Tan", DPAS_BUILTIN_TAN, dpas_tan, 1},
{"Tanh", DPAS_BUILTIN_TANH, dpas_tanh, 1},
{"Abs", DPAS_BUILTIN_ABS, dpas_abs, 1},
{"Min", DPAS_BUILTIN_MIN, dpas_min, 2},
{"Max", DPAS_BUILTIN_MAX, dpas_max, 2},
{"Sign", DPAS_BUILTIN_SIGN, dpas_sign, 1},
{"IsNaN", DPAS_BUILTIN_ISNAN, dpas_isnan, 1},
{"IsInf", DPAS_BUILTIN_ISINF, dpas_isinf, 1},
{"Finite", DPAS_BUILTIN_FINITE, dpas_finite, 1},
};
#define num_builtins (sizeof(builtins) / sizeof(dpas_builtin))
int dpas_is_builtin(const char *name)
{
int index;
for(index = 0; index < num_builtins; ++index)
{
if(!jit_stricmp(name, builtins[index].name))
{
return builtins[index].identifier;
}
}
return 0;
}
dpas_semvalue dpas_expand_builtin
(int identifier, dpas_semvalue *args, int num_args)
{
int index;
dpas_semvalue value;
for(index = 0; index < num_builtins; ++index)
{
if(builtins[index].identifier == identifier)
{
if(builtins[index].num_args != -1 &&
builtins[index].num_args != num_args)
{
dpas_error("incorrect number of arguments to `%s' builtin",
builtins[index].name);
dpas_sem_set_error(value);
return value;
}
return (*(builtins[index].func))(args, num_args);
}
}
dpas_sem_set_error(value);
return value;
}