451 lines
11 KiB
C
451 lines
11 KiB
C
#include "scope.h"
|
|
|
|
#include <bshell/format.h>
|
|
#include <bshell/runtime/cmdcall.h>
|
|
#include <bshell/runtime/pipeline.h>
|
|
#include <bshell/runtime/runtime.h>
|
|
#include <bshell/runtime/scope.h>
|
|
#include <bshell/status.h>
|
|
#include <fx/collections/array.h>
|
|
#include <fx/collections/hashtable.h>
|
|
#include <fx/string.h>
|
|
#include <stdio.h>
|
|
|
|
static enum bshell_status eval_instruction(
|
|
struct bshell_runtime *rt,
|
|
struct bshell_runtime_scope *scope,
|
|
bshell_instruction instr)
|
|
{
|
|
fx_value x, y, z;
|
|
enum bshell_opcode opcode;
|
|
fx_status status;
|
|
enum bshell_status bstatus = BSHELL_SUCCESS;
|
|
uint32_t arg;
|
|
fx_type_id type;
|
|
fx_value *pool_value = NULL;
|
|
bshell_variable *var = NULL;
|
|
const char *s = NULL;
|
|
bshell_instruction_decode(instr, &opcode, &arg);
|
|
|
|
switch (opcode) {
|
|
case BSHELL_OPCODE_LDC_INT:
|
|
x = FX_INT(arg);
|
|
bshell_runtime_scope_push_value(scope, &x);
|
|
fx_value_unset(&x);
|
|
break;
|
|
case BSHELL_OPCODE_LDC_FP:
|
|
pool_value = bshell_scriptblock_get_pool_value(
|
|
scope->s_block,
|
|
arg,
|
|
FX_TYPE_DOUBLE);
|
|
if (pool_value) {
|
|
bshell_runtime_scope_push_value(scope, pool_value);
|
|
} else {
|
|
fprintf(stderr, "RUNTIME: invalid ldc.fp operand\n");
|
|
return BSHELL_ERR_BAD_SYNTAX;
|
|
}
|
|
break;
|
|
case BSHELL_OPCODE_LDC_STR:
|
|
pool_value = bshell_scriptblock_get_pool_value(
|
|
scope->s_block,
|
|
arg,
|
|
FX_TYPE_STRING);
|
|
if (!pool_value) {
|
|
pool_value = bshell_scriptblock_get_pool_value(
|
|
scope->s_block,
|
|
arg,
|
|
FX_TYPE_CSTR);
|
|
}
|
|
if (pool_value) {
|
|
bshell_runtime_scope_push_value(scope, pool_value);
|
|
} else {
|
|
fprintf(stderr, "RUNTIME: invalid ldc.str operand\n");
|
|
return BSHELL_ERR_BAD_SYNTAX;
|
|
}
|
|
break;
|
|
case BSHELL_OPCODE_ADD:
|
|
x = bshell_runtime_scope_pop_value(scope);
|
|
y = bshell_runtime_scope_pop_value(scope);
|
|
type = fx_value_get_common_type(&x, &y);
|
|
if (!type) {
|
|
fprintf(stderr,
|
|
"RUNTIME: cannot apply operator to operands of "
|
|
"incompatible type\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
status = fx_value_change_type(&y, &y, type);
|
|
if (!FX_OK(status)) {
|
|
fprintf(stderr,
|
|
"RUNTIME: cannot convert operand to the "
|
|
"necessary type\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
status = fx_value_change_type(&x, &x, type);
|
|
if (!FX_OK(status)) {
|
|
fprintf(stderr,
|
|
"RUNTIME: cannot convert operand to the "
|
|
"necessary type\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
status = fx_value_add(&x, &y, &z);
|
|
if (!FX_OK(status)) {
|
|
fprintf(stderr,
|
|
"RUNTIME: operands do not support specified "
|
|
"operator\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
bshell_runtime_scope_push_value(scope, &z);
|
|
fx_value_unset(&z);
|
|
break;
|
|
case BSHELL_OPCODE_SUB:
|
|
x = bshell_runtime_scope_pop_value(scope);
|
|
y = bshell_runtime_scope_pop_value(scope);
|
|
type = fx_value_get_common_type(&x, &y);
|
|
if (!type) {
|
|
fprintf(stderr,
|
|
"RUNTIME: cannot apply operator to operands of "
|
|
"incompatible type\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
status = fx_value_subtract(&x, &y, &z);
|
|
if (!FX_OK(status)) {
|
|
fprintf(stderr,
|
|
"RUNTIME: operands do not support specified "
|
|
"operator\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
bshell_runtime_scope_push_value(scope, &z);
|
|
fx_value_unset(&z);
|
|
break;
|
|
case BSHELL_OPCODE_MUL:
|
|
x = bshell_runtime_scope_pop_value(scope);
|
|
y = bshell_runtime_scope_pop_value(scope);
|
|
type = fx_value_get_common_type(&x, &y);
|
|
if (!type) {
|
|
fprintf(stderr,
|
|
"RUNTIME: cannot apply operator to operands of "
|
|
"incompatible type\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
status = fx_value_multiply(&x, &y, &z);
|
|
if (!FX_OK(status)) {
|
|
fprintf(stderr,
|
|
"RUNTIME: operands do not support specified "
|
|
"operator\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
bshell_runtime_scope_push_value(scope, &z);
|
|
fx_value_unset(&z);
|
|
break;
|
|
case BSHELL_OPCODE_DIV:
|
|
x = bshell_runtime_scope_pop_value(scope);
|
|
y = bshell_runtime_scope_pop_value(scope);
|
|
status = fx_value_change_type(&x, &x, FX_TYPE_DOUBLE);
|
|
if (!FX_OK(status)) {
|
|
fprintf(stderr,
|
|
"RUNTIME: operands do not support specified "
|
|
"operator\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
status = fx_value_change_type(&y, &y, FX_TYPE_DOUBLE);
|
|
if (!FX_OK(status)) {
|
|
fprintf(stderr,
|
|
"RUNTIME: operands do not support specified "
|
|
"operator\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
status = fx_value_divide(&x, &y, &z);
|
|
if (!FX_OK(status)) {
|
|
fprintf(stderr,
|
|
"RUNTIME: operands do not support specified "
|
|
"operator\n");
|
|
return BSHELL_ERR_NOT_SUPPORTED;
|
|
}
|
|
bshell_runtime_scope_push_value(scope, &z);
|
|
fx_value_unset(&z);
|
|
break;
|
|
case BSHELL_OPCODE_LDLOCAL:
|
|
pool_value = bshell_scriptblock_get_pool_value(
|
|
scope->s_block,
|
|
arg,
|
|
FX_TYPE_STRING);
|
|
if (!pool_value) {
|
|
fprintf(stderr, "RUNTIME: invalid ldlocal operand\n");
|
|
return BSHELL_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
fx_value_get_cstr(pool_value, &s);
|
|
var = bshell_runtime_find_var(rt, s);
|
|
if (var) {
|
|
bshell_runtime_scope_push_value(
|
|
scope,
|
|
bshell_variable_get_value(var));
|
|
} else {
|
|
bshell_runtime_scope_push_value(scope, &FX_VALUE_EMPTY);
|
|
}
|
|
|
|
break;
|
|
case BSHELL_OPCODE_LDBLOCK:
|
|
pool_value = bshell_scriptblock_get_pool_value(
|
|
scope->s_block,
|
|
arg,
|
|
BSHELL_TYPE_SCRIPTBLOCK);
|
|
if (!pool_value) {
|
|
fprintf(stderr, "RUNTIME: invalid ldblock operand\n");
|
|
return BSHELL_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
bshell_runtime_scope_push_value(scope, pool_value);
|
|
|
|
break;
|
|
case BSHELL_OPCODE_LDPROP: {
|
|
/* container */
|
|
x = bshell_runtime_scope_pop_value(scope);
|
|
/* property */
|
|
y = bshell_runtime_scope_pop_value(scope);
|
|
|
|
const fx_type *ty = fx_type_get_by_id(x.v_type);
|
|
if (!ty) {
|
|
fx_value_unset(&x);
|
|
fx_value_unset(&y);
|
|
bshell_runtime_scope_push_value(scope, &FX_VALUE_EMPTY);
|
|
break;
|
|
}
|
|
|
|
fx_stringstream *strm = fx_stringstream_create();
|
|
fx_value_to_string(&y, strm, NULL);
|
|
fx_value_unset(&y);
|
|
|
|
const fx_property *prop
|
|
= fx_type_get_property(ty, fx_stringstream_get_cstr(strm));
|
|
fx_stringstream_unref(strm);
|
|
|
|
if (!prop) {
|
|
fx_value_unset(&x);
|
|
bshell_runtime_scope_push_value(scope, &FX_VALUE_EMPTY);
|
|
break;
|
|
}
|
|
|
|
fx_property_get_value(prop, &x, &z);
|
|
fx_value_unset(&x);
|
|
bshell_runtime_scope_push_value(scope, &z);
|
|
fx_value_unset(&z);
|
|
|
|
break;
|
|
}
|
|
case BSHELL_OPCODE_STLOCAL:
|
|
x = bshell_runtime_scope_pop_value(scope);
|
|
pool_value = bshell_scriptblock_get_pool_value(
|
|
scope->s_block,
|
|
arg,
|
|
FX_TYPE_STRING);
|
|
if (!pool_value) {
|
|
fprintf(stderr, "RUNTIME: invalid stlocal operand\n");
|
|
return BSHELL_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
fx_value_get_cstr(pool_value, &s);
|
|
var = bshell_runtime_find_var(rt, s);
|
|
if (!var) {
|
|
var = bshell_runtime_scope_define_variable(
|
|
bshell_runtime_get_current_scope(rt),
|
|
s);
|
|
}
|
|
|
|
bshell_variable_set_value(var, x);
|
|
bshell_runtime_scope_push_value(scope, &x);
|
|
fx_value_unset(&x);
|
|
break;
|
|
case BSHELL_OPCODE_MK_STR: {
|
|
fx_string *result = fx_string_create();
|
|
fx_stringstream *strm = fx_stringstream_create();
|
|
|
|
while (arg > 0) {
|
|
x = bshell_runtime_scope_pop_value(scope);
|
|
fx_stringstream_reset(strm);
|
|
fx_value_to_string(&x, strm, NULL);
|
|
fx_string_prepend_cstr(
|
|
result,
|
|
fx_stringstream_get_cstr(strm));
|
|
fx_value_unset(&x);
|
|
arg--;
|
|
}
|
|
|
|
fx_stringstream_unref(strm);
|
|
y = FX_VALUE_OBJECT(result);
|
|
bshell_runtime_scope_push_value(scope, &y);
|
|
fx_value_unset(&y);
|
|
break;
|
|
}
|
|
case BSHELL_OPCODE_MK_HTAB: {
|
|
fx_hashtable *result = fx_hashtable_create();
|
|
|
|
while (arg > 0) {
|
|
fx_value key = bshell_runtime_scope_pop_value(scope);
|
|
fx_value value = bshell_runtime_scope_pop_value(scope);
|
|
fx_hashtable_put(result, &key, &value);
|
|
fx_value_unset(&key);
|
|
fx_value_unset(&value);
|
|
arg--;
|
|
}
|
|
|
|
y = FX_VALUE_OBJECT(result);
|
|
bshell_runtime_scope_push_value(scope, &y);
|
|
fx_value_unset(&y);
|
|
break;
|
|
}
|
|
case BSHELL_OPCODE_MK_ARRAY: {
|
|
fx_array *result = fx_array_create();
|
|
|
|
while (arg > 0) {
|
|
fx_value value = bshell_runtime_scope_pop_value(scope);
|
|
fx_array_push_front(result, value);
|
|
fx_value_unset(&value);
|
|
arg--;
|
|
}
|
|
|
|
y = FX_VALUE_OBJECT(result);
|
|
bshell_runtime_scope_push_value(scope, &y);
|
|
fx_value_unset(&y);
|
|
break;
|
|
}
|
|
case BSHELL_OPCODE_LDCMD: {
|
|
bshell_cmdcall *result = bshell_cmdcall_create();
|
|
|
|
while (arg > 0) {
|
|
fx_value cmd_arg
|
|
= bshell_runtime_scope_pop_value(scope);
|
|
bshell_cmdcall_push_arg(result, cmd_arg);
|
|
fx_value_unset(&cmd_arg);
|
|
arg--;
|
|
}
|
|
|
|
bstatus = bshell_cmdcall_resolve(result, rt);
|
|
if (bstatus != BSHELL_SUCCESS) {
|
|
bshell_cmdcall_unref(result);
|
|
break;
|
|
}
|
|
|
|
y = FX_VALUE_OBJECT(result);
|
|
bshell_runtime_scope_push_value(scope, &y);
|
|
fx_value_unset(&y);
|
|
break;
|
|
}
|
|
case BSHELL_OPCODE_PEXEC: {
|
|
bshell_pipeline *pipeline = bshell_pipeline_create();
|
|
bool single = (arg == 1);
|
|
while (arg > 0) {
|
|
fx_value cmd = bshell_runtime_scope_pop_value(scope);
|
|
bshell_cmdcall *cmdcall = NULL;
|
|
if (fx_value_is_type(&cmd, BSHELL_TYPE_CMDCALL)) {
|
|
fx_value_get_object(&cmd, &cmdcall);
|
|
bshell_pipeline_add_cmdcall(pipeline, cmdcall);
|
|
} else {
|
|
bshell_pipeline_add_input_value(
|
|
pipeline,
|
|
cmd,
|
|
true);
|
|
}
|
|
|
|
fx_value_unset(&cmd);
|
|
arg--;
|
|
}
|
|
|
|
int end_of_data = 0;
|
|
fx_value record = FX_VALUE_EMPTY;
|
|
bshell_runtime_begin_output(rt);
|
|
while (1) {
|
|
bstatus = bshell_pipeline_pump_record(
|
|
pipeline,
|
|
rt,
|
|
&record,
|
|
&end_of_data);
|
|
if (bstatus != BSHELL_SUCCESS || end_of_data) {
|
|
break;
|
|
}
|
|
|
|
if (!record.v_type) {
|
|
continue;
|
|
}
|
|
|
|
bshell_runtime_output_value(rt, &record);
|
|
fx_value_unset(&record);
|
|
}
|
|
|
|
bshell_runtime_end_output(rt);
|
|
bshell_pipeline_unref(pipeline);
|
|
break;
|
|
}
|
|
default:
|
|
fprintf(stderr,
|
|
"RUNTIME: encountered unknown opcode %02x\n",
|
|
opcode);
|
|
return BSHELL_ERR_BAD_FORMAT;
|
|
}
|
|
|
|
scope->s_ip++;
|
|
return bstatus;
|
|
}
|
|
|
|
static fx_value runtime_eval(struct bshell_runtime *rt)
|
|
{
|
|
enum bshell_status status = BSHELL_SUCCESS;
|
|
while (status == BSHELL_SUCCESS) {
|
|
struct bshell_runtime_scope *scope = runtime_get_scope(rt);
|
|
if (scope->s_ip >= scope->s_nr_instr) {
|
|
break;
|
|
}
|
|
|
|
bshell_instruction instr = scope->s_instr[scope->s_ip];
|
|
status = eval_instruction(rt, scope, instr);
|
|
}
|
|
|
|
struct bshell_runtime_scope *scope = runtime_get_scope(rt);
|
|
return bshell_runtime_scope_pop_value(scope);
|
|
}
|
|
|
|
fx_value bshell_runtime_eval_global(
|
|
struct bshell_runtime *rt,
|
|
bshell_scriptblock *block)
|
|
{
|
|
enum bshell_status status = BSHELL_SUCCESS;
|
|
bshell_runtime_scope_set_block(rt->rt_global, block);
|
|
fx_value result = runtime_eval(rt);
|
|
bshell_runtime_scope_set_block(rt->rt_global, NULL);
|
|
return result;
|
|
}
|
|
|
|
fx_value bshell_runtime_eval_script(
|
|
struct bshell_runtime *rt,
|
|
bshell_scriptblock *block)
|
|
{
|
|
runtime_push_scope(rt, RUNTIME_SCOPE_SCRIPT, block);
|
|
fx_value result = runtime_eval(rt);
|
|
runtime_pop_scope(rt);
|
|
return result;
|
|
}
|
|
|
|
fx_value bshell_runtime_eval(struct bshell_runtime *rt)
|
|
{
|
|
return runtime_eval(rt);
|
|
}
|
|
|
|
enum bshell_status bshell_runtime_push_scope(
|
|
struct bshell_runtime *rt,
|
|
bshell_scriptblock *block)
|
|
{
|
|
struct bshell_runtime_scope *scope
|
|
= runtime_push_scope(rt, RUNTIME_SCOPE_SCRIPT, block);
|
|
return scope ? BSHELL_SUCCESS : BSHELL_ERR_NO_MEMORY;
|
|
}
|
|
|
|
enum bshell_status bshell_runtime_pop_scope(struct bshell_runtime *rt)
|
|
{
|
|
runtime_pop_scope(rt);
|
|
return BSHELL_SUCCESS;
|
|
}
|