1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-03 07:24:58 +03:00
inform7/inter/building-module/Chapter 2/Emitting Inter Schemas.w
2021-05-16 11:37:28 +01:00

552 lines
20 KiB
OpenEdge ABL

[EmitInterSchemas::] Emitting Inter Schemas.
@h Compilation.
=
void EmitInterSchemas::emit(inter_tree *I, value_holster *VH, inter_schema *sch, void *opaque_state,
inter_symbols_table *first_call, inter_symbols_table *second_call,
void (*inline_command_handler)(value_holster *VH, inter_schema_token *t, void *opaque_state, int prim_cat),
void (*i7_source_handler)(value_holster *VH, text_stream *S, void *opaque_state, int prim_cat)) {
int prim_cat = CODE_PRIM_CAT;
if (VH->vhmode_wanted == INTER_VAL_VHMODE) {
prim_cat = VAL_PRIM_CAT;
} else if (VH->vhmode_wanted != INTER_VOID_VHMODE) {
internal_error("can only emit schemas in INTER_VAL_VHMODE or INTER_VOID_VHMODE");
}
if (sch->mid_case) { Produce::to_last_level(I, 4); }
int again = TRUE;
while (again) {
again = FALSE;
for (inter_schema_node *isn = sch->node_tree; isn; isn=isn->next_node)
if (EmitInterSchemas::process_conditionals(I, isn, first_call, second_call))
again = TRUE;
}
for (inter_schema_node *isn = sch->node_tree; isn; isn=isn->next_node)
EmitInterSchemas::emit_inner(I, isn, VH, sch, opaque_state, prim_cat,
first_call, second_call, inline_command_handler, i7_source_handler);
}
@ =
int EmitInterSchemas::process_conditionals(inter_tree *I, inter_schema_node *isn,
inter_symbols_table *first_call, inter_symbols_table *second_call) {
if (isn == NULL) return FALSE;
if (isn->blocked_by_conditional) return FALSE;
if (isn->isn_type == DIRECTIVE_ISNT) @<Directive@>;
for (isn=isn->child_node; isn; isn=isn->next_node)
if (EmitInterSchemas::process_conditionals(I, isn, first_call, second_call))
return TRUE;
return FALSE;
}
@<Directive@> =
if ((isn->dir_clarifier == IFDEF_I6RW) ||
(isn->dir_clarifier == IFNDEF_I6RW) ||
(isn->dir_clarifier == IFTRUE_I6RW) ||
(isn->dir_clarifier == IFFALSE_I6RW)) {
LOGIF(SCHEMA_COMPILATION, "Conditional directive in schema!\n");
inter_schema_node *ifnot_node = NULL, *endif_node = NULL;
inter_schema_node *at = isn->next_node;
while (at) {
if (at->blocked_by_conditional == FALSE) {
if (at->dir_clarifier == IFDEF_I6RW) { isn = at; ifnot_node = NULL; }
if (at->dir_clarifier == IFNDEF_I6RW) { isn = at; ifnot_node = NULL; }
if (at->dir_clarifier == IFTRUE_I6RW) { isn = at; ifnot_node = NULL; }
if (at->dir_clarifier == IFFALSE_I6RW) { isn = at; ifnot_node = NULL; }
if (at->dir_clarifier == IFNOT_I6RW) ifnot_node = at;
if (at->dir_clarifier == ENDIF_I6RW) { endif_node = at; break; }
}
at = at->next_node;
}
if (endif_node == NULL) internal_error("no matching #endif");
text_stream *symbol_to_check = NULL;
text_stream *value_to_check = NULL;
inter_ti operation_to_check = 0;
if ((isn->dir_clarifier == IFDEF_I6RW) ||
(isn->dir_clarifier == IFNDEF_I6RW)) {
symbol_to_check = isn->child_node->expression_tokens->material;
} else {
inter_schema_node *to_eval = isn->child_node;
while ((to_eval) && (to_eval->isn_type == SUBEXPRESSION_ISNT))
to_eval = to_eval->child_node;
if ((to_eval == NULL) || (to_eval->child_node->expression_tokens == NULL))
internal_error("bad iftrue");
symbol_to_check = to_eval->child_node->expression_tokens->material;
operation_to_check = to_eval->isn_clarifier;
value_to_check = to_eval->child_node->next_node->expression_tokens->material;
}
LOGIF(SCHEMA_COMPILATION, "Means checking %S\n", symbol_to_check);
if (value_to_check) LOGIF(SCHEMA_COMPILATION, "Against %S\n", value_to_check);
int val = -1, def = FALSE;
if (Str::eq(symbol_to_check, I"#version_number")) { val = 8; def = TRUE; }
else if (Str::eq(symbol_to_check, I"STRICT_MODE")) { def = TRUE; }
else {
inter_symbol *symb = EmitInterSchemas::find_identifier_text(I, symbol_to_check,
Inter::Packages::scope(Packaging::incarnate(Site::veneer_request(I))),
second_call);
while ((symb) && (symb->equated_to)) symb = symb->equated_to;
LOGIF(SCHEMA_COMPILATION, "Symb is $3\n", symb);
if (Inter::Symbols::is_defined(symb)) {
def = TRUE;
val = Inter::Symbols::evaluate_to_int(symb);
}
}
LOGIF(SCHEMA_COMPILATION, "Defined: %d, value: %d\n", def, val);
int decision = TRUE;
if ((isn->dir_clarifier == IFNDEF_I6RW)
|| (isn->dir_clarifier == IFDEF_I6RW)) decision = def;
else {
int h = Str::atoi(value_to_check, 0);
LOGIF(SCHEMA_COMPILATION, "Want value %d\n", h);
if (operation_to_check == EQ_BIP) decision = (val == h)?TRUE:FALSE;
if (operation_to_check == NE_BIP) decision = (val != h)?TRUE:FALSE;
if (operation_to_check == GE_BIP) decision = (val >= h)?TRUE:FALSE;
if (operation_to_check == GT_BIP) decision = (val > h)?TRUE:FALSE;
if (operation_to_check == LE_BIP) decision = (val <= h)?TRUE:FALSE;
if (operation_to_check == LT_BIP) decision = (val < h)?TRUE:FALSE;
}
if (isn->dir_clarifier == IFNDEF_I6RW) decision = decision?FALSE:TRUE;
if (isn->dir_clarifier == IFFALSE_I6RW) decision = decision?FALSE:TRUE;
isn->blocked_by_conditional = TRUE;
endif_node->blocked_by_conditional = TRUE;
if (ifnot_node) ifnot_node->blocked_by_conditional = TRUE;
if (decision) {
inter_schema_node *at = ifnot_node;
while ((at) && (at != endif_node)) {
at->blocked_by_conditional = TRUE;
at = at->next_node;
}
} else {
inter_schema_node *at = isn;
while ((at) && (at != endif_node) && (at != ifnot_node)) {
at->blocked_by_conditional = TRUE;
at = at->next_node;
}
}
if (Log::aspect_switched_on(SCHEMA_COMPILATION_DA)) {
LOG("--- Resulting in: ---\n");
for (inter_schema_node *at = isn; at; at = at->next_node)
InterSchemas::log_just(at, 0);
LOG("------\n");
}
return TRUE;
}
@ =
void EmitInterSchemas::emit_inner(inter_tree *I, inter_schema_node *isn, value_holster *VH,
inter_schema *sch, void *opaque_state, int prim_cat, inter_symbols_table *first_call, inter_symbols_table *second_call,
void (*inline_command_handler)(value_holster *VH, inter_schema_token *t, void *opaque_state, int prim_cat),
void (*i7_source_handler)(value_holster *VH, text_stream *S, void *opaque_state, int prim_cat)) {
if (isn == NULL) return;
if (isn->blocked_by_conditional) return;
switch (isn->isn_type) {
case LABEL_ISNT: @<Label@>; break;
case CODE_ISNT: @<Code block@>; break;
case EVAL_ISNT: @<Eval block@>; break;
case EXPRESSION_ISNT: @<Expression@>; break;
case SUBEXPRESSION_ISNT: @<Subexpression@>; break;
case STATEMENT_ISNT: @<Statement@>; break;
case OPERATION_ISNT: @<Operation@>; break;
case ASSEMBLY_ISNT: @<Assembly@>; break;
case CALL_ISNT: @<Call@>; break;
case MESSAGE_ISNT: @<Message@>; break;
case CALLMESSAGE_ISNT: @<Call-message@>; break;
case DIRECTIVE_ISNT: @<Non-conditional directive@>; break;
default: internal_error("unknown schema node type");
}
}
@<Label@> =
if (prim_cat != CODE_PRIM_CAT) internal_error("label outside code");
TEMPORARY_TEXT(L)
WRITE_TO(L, ".");
for (inter_schema_node *at = isn->child_node; at; at=at->next_node) {
for (inter_schema_token *t = at->expression_tokens; t; t=t->next) {
if (t->ist_type == IDENTIFIER_ISTT)
WRITE_TO(L, "%S", t->material);
else if ((t->ist_type == INLINE_ISTT) && (t->inline_command == label_ISINC)) {
#ifdef CORE_MODULE
JumpLabels::write(L, t->operand);
#endif
#ifndef CORE_MODULE
internal_error("label namespaces are unavailable in assimilation mode");
#endif
} else if ((t->ist_type == INLINE_ISTT) &&
((t->inline_command == counter_up_ISINC) || (t->inline_command == counter_down_ISINC))) {
value_holster VN = Holsters::new(INTER_DATA_VHMODE);
if (inline_command_handler)
(*inline_command_handler)(&VN, t, opaque_state, VAL_PRIM_CAT);
} else internal_error("bad label stuff");
}
}
Produce::place_label(I, Produce::reserve_label(I, L));
DISCARD_TEXT(L)
@<Code block@> =
if (prim_cat != CODE_PRIM_CAT) internal_error("code block in expression");
if (isn->unopened == FALSE) {
Produce::code(I);
Produce::down(I);
}
for (inter_schema_node *at = isn->child_node; at; at=at->next_node)
EmitInterSchemas::emit_inner(I, at,
VH, sch, opaque_state, CODE_PRIM_CAT, first_call, second_call,
inline_command_handler, i7_source_handler);
if (isn->unclosed == FALSE) {
Produce::up(I);
}
if (isn->unopened) Produce::to_last_level(I, 0);
@<Assembly@> =
if (prim_cat != CODE_PRIM_CAT) internal_error("assembly in expression");
inter_schema_node *at = isn->child_node;
if (at) {
text_stream *opcode_text = NULL;
if (at->isn_type == EXPRESSION_ISNT) {
inter_schema_token *tok = at->expression_tokens;
if ((tok->ist_type == OPCODE_ISTT) && (tok->next == NULL))
opcode_text = tok->material;
}
if (opcode_text == NULL) internal_error("assembly malformed");
Produce::inv_assembly(I, opcode_text);
Produce::down(I);
for (at = at->next_node; at; at=at->next_node)
EmitInterSchemas::emit_inner(I, at, VH, sch, opaque_state,
VAL_PRIM_CAT, first_call, second_call,
inline_command_handler, i7_source_handler);
Produce::up(I);
}
@<Call@> =
if (isn->child_node) {
inter_schema_node *at = isn->child_node;
inter_symbol *to_call = NULL;
if (at->isn_type == EXPRESSION_ISNT) {
inter_schema_token *tok = at->expression_tokens;
if ((tok->ist_type == IDENTIFIER_ISTT) && (tok->next == NULL)) {
to_call = EmitInterSchemas::find_identifier(I, tok, first_call, second_call);
if (Inter::Symbols::is_local(to_call)) to_call = NULL;
}
}
if (to_call) {
Produce::inv_call(I, to_call);
at = at->next_node;
} else {
int argc = 0;
for (inter_schema_node *n = isn->child_node; n; n=n->next_node) {
if ((n->expression_tokens) && (n->expression_tokens->inline_command == combine_ISINC)) argc++;
argc++;
}
switch (argc) {
case 1: Produce::inv_primitive(I, INDIRECT0_BIP); break;
case 2: Produce::inv_primitive(I, INDIRECT1_BIP); break;
case 3: Produce::inv_primitive(I, INDIRECT2_BIP); break;
case 4: Produce::inv_primitive(I, INDIRECT3_BIP); break;
case 5: Produce::inv_primitive(I, INDIRECT4_BIP); break;
default: internal_error("too many args for indirect call"); break;
}
}
Produce::down(I);
for (; at; at=at->next_node)
EmitInterSchemas::emit_inner(I, at,
VH, sch, opaque_state, VAL_PRIM_CAT,
first_call, second_call, inline_command_handler, i7_source_handler);
Produce::up(I);
}
@<Message@> =
if (isn->child_node) {
inter_schema_node *at = isn->child_node;
int argc = 0;
for (inter_schema_node *n = isn->child_node; n; n=n->next_node) argc++;
switch (argc) {
case 2: Produce::inv_primitive(I, MESSAGE0_BIP); break;
case 3: Produce::inv_primitive(I, MESSAGE1_BIP); break;
case 4: Produce::inv_primitive(I, MESSAGE2_BIP); break;
case 5: Produce::inv_primitive(I, MESSAGE3_BIP); break;
default: internal_error("too many args for message"); break;
}
Produce::down(I);
for (; at; at=at->next_node)
EmitInterSchemas::emit_inner(I, at,
VH, sch, opaque_state, VAL_PRIM_CAT,
first_call, second_call, inline_command_handler, i7_source_handler);
Produce::up(I);
}
@<Call-message@> =
if (isn->child_node) {
inter_schema_node *at = isn->child_node;
int argc = 0;
for (inter_schema_node *n = isn->child_node; n; n=n->next_node) argc++;
switch (argc) {
case 1: Produce::inv_primitive(I, CALLMESSAGE0_BIP); break;
case 2: Produce::inv_primitive(I, CALLMESSAGE1_BIP); break;
case 3: Produce::inv_primitive(I, CALLMESSAGE2_BIP); break;
case 4: Produce::inv_primitive(I, CALLMESSAGE3_BIP); break;
default: internal_error("too many args for call-message"); break;
}
Produce::down(I);
for (; at; at=at->next_node)
EmitInterSchemas::emit_inner(I, at,
VH, sch, opaque_state, VAL_PRIM_CAT,
first_call, second_call, inline_command_handler, i7_source_handler);
Produce::up(I);
}
@<Eval block@> =
if ((prim_cat != CODE_PRIM_CAT) && (prim_cat != VAL_PRIM_CAT))
internal_error("eval block outside evaluation context");
if (isn->child_node == NULL) Produce::val(I, K_value, LITERAL_IVAL, 1);
else {
int d = 0;
for (inter_schema_node *at = isn->child_node; at; at=at->next_node) {
if (at->next_node) {
d++;
Produce::inv_primitive(I, SEQUENTIAL_BIP);
Produce::down(I);
}
EmitInterSchemas::emit_inner(I, at,
VH, sch, opaque_state, VAL_PRIM_CAT,
first_call, second_call, inline_command_handler, i7_source_handler);
}
while (d > 0) { Produce::up(I); d--; }
}
@<Operation@> =
if (prim_cat == REF_PRIM_CAT) { Produce::reference(I); Produce::down(I); }
Produce::inv_primitive(I, isn->isn_clarifier);
Produce::down(I);
int pc = VAL_PRIM_CAT;
if (InterSchemas::first_operand_ref(isn->isn_clarifier)) pc = REF_PRIM_CAT;
EmitInterSchemas::emit_inner(I, isn->child_node,
VH, sch, opaque_state, pc, first_call, second_call,
inline_command_handler, i7_source_handler);
if (InterSchemas::arity(isn->isn_clarifier) == 2)
EmitInterSchemas::emit_inner(I, isn->child_node->next_node,
VH, sch, opaque_state, VAL_PRIM_CAT,
first_call, second_call,
inline_command_handler, i7_source_handler);
Produce::up(I);
if (prim_cat == REF_PRIM_CAT) { Produce::up(I); }
@<Subexpression@> =
int d = 0;
for (inter_schema_node *at = isn->child_node; at; at=at->next_node) {
if (at->next_node) {
d++;
Produce::inv_primitive(I, SEQUENTIAL_BIP);
Produce::down(I);
}
EmitInterSchemas::emit_inner(I, at,
VH, sch, opaque_state, prim_cat, first_call, second_call,
inline_command_handler, i7_source_handler);
}
while (d > 0) { Produce::up(I); d--; }
@<Statement@> =
if (prim_cat != CODE_PRIM_CAT) internal_error("statement in expression");
if (isn->isn_clarifier == CASE_BIP) Produce::to_last_level(I, 2);
Produce::inv_primitive(I, isn->isn_clarifier);
int arity = InterSchemas::ip_arity(isn->isn_clarifier);
if (arity > 0) {
Produce::down(I);
if (isn->isn_clarifier == OBJECTLOOP_BIP)
@<Add the objectloop range tokens@>;
inter_schema_node *at = isn->child_node;
inter_schema_node *last = NULL;
int actual_arity = 0;
for (int i = 0; ((at) && (i<arity)); i++) {
actual_arity++;
EmitInterSchemas::emit_inner(I, at, VH, sch, opaque_state,
InterSchemas::ip_prim_cat(isn->isn_clarifier, i),
first_call, second_call, inline_command_handler, i7_source_handler);
last = at;
at = at->next_node;
}
if (!((last) && (last->unclosed))) {
Produce::up(I);
}
}
@<Add the objectloop range tokens@> =
inter_schema_node *oc_node = isn->child_node;
while ((oc_node) &&
((oc_node->isn_type != OPERATION_ISNT) ||
(oc_node->isn_clarifier != OFCLASS_BIP)))
oc_node = oc_node->child_node;
if (oc_node) {
inter_schema_node *var_node = oc_node->child_node;
inter_schema_node *cl_node = var_node?(var_node->next_node):NULL;
if ((var_node) && (cl_node)) {
EmitInterSchemas::emit_inner(I, var_node, VH, sch, opaque_state, REF_PRIM_CAT,
first_call, second_call, inline_command_handler, i7_source_handler);
EmitInterSchemas::emit_inner(I, cl_node, VH, sch, opaque_state, VAL_PRIM_CAT,
first_call, second_call, inline_command_handler, i7_source_handler);
} else internal_error("malformed OC node");
} else {
inter_schema_node *var_node = isn->child_node;
while ((var_node) && (var_node->isn_type != EXPRESSION_ISNT))
var_node = var_node->child_node;
if (var_node) {
EmitInterSchemas::emit_inner(I, var_node, VH, sch, opaque_state, REF_PRIM_CAT,
first_call, second_call, inline_command_handler, i7_source_handler);
#ifdef CORE_MODULE
Produce::val_iname(I, K_value, RTKindDeclarations::iname(K_object));
#endif
#ifndef CORE_MODULE
Produce::val_symbol(I, K_value, Site::veneer_symbol(I, OBJECT_VSYMB));
#endif
} else internal_error("objectloop without visible variable");
}
@<Expression@> =
int cat_me = FALSE, lab_me = FALSE, print_ret_me = FALSE;
int tc = 0; for (inter_schema_token *t = isn->expression_tokens; t; t=t->next) tc++;
if ((tc > 1) && (prim_cat == VAL_PRIM_CAT)) cat_me = TRUE;
if ((tc == 1) && (prim_cat == CODE_PRIM_CAT) && (isn->expression_tokens->ist_type == DQUOTED_ISTT))
print_ret_me = TRUE;
if ((tc == 1) && (prim_cat == LAB_PRIM_CAT)) lab_me = TRUE;
if (cat_me) { Produce::evaluation(I); Produce::down(I); }
if (prim_cat == REF_PRIM_CAT) { Produce::reference(I); Produce::down(I); }
for (inter_schema_token *t = isn->expression_tokens; t; t=t->next) {
switch (t->ist_type) {
case IDENTIFIER_ISTT: {
if (lab_me)
Produce::lab(I, Produce::reserve_label(I, t->material));
else {
#ifdef CORE_MODULE
local_variable *lvar = LocalVariables::by_identifier(t->material);
if (lvar) {
inter_symbol *lvar_s = LocalVariables::declare(lvar);
Produce::val_symbol(I, K_value, lvar_s);
} else {
Produce::val_symbol(I, K_value, EmitInterSchemas::find_identifier(I, t, first_call, second_call));
}
#endif
#ifndef CORE_MODULE
Produce::val_symbol(I, K_value, EmitInterSchemas::find_identifier(I, t, first_call, second_call));
#endif
}
break;
}
case ASM_ARROW_ISTT:
Produce::val_symbol(I, K_value, Site::veneer_symbol(I, ASM_ARROW_VSYMB));
break;
case ASM_SP_ISTT:
Produce::val_symbol(I, K_value, Site::veneer_symbol(I, ASM_SP_VSYMB));
break;
case ASM_NEGATED_LABEL_ISTT:
if (Str::eq(t->material, I"rtrue"))
Produce::val_symbol(I, K_value, Site::veneer_symbol(I, ASM_NEG_RTRUE_VSYMB));
else if (Str::eq(t->material, I"rfalse"))
Produce::val_symbol(I, K_value, Site::veneer_symbol(I, ASM_NEG_RFALSE_VSYMB));
else {
Produce::val_symbol(I, K_value, Site::veneer_symbol(I, ASM_NEG_VSYMB));
Produce::lab(I, Produce::reserve_label(I, t->material));
}
break;
case ASM_LABEL_ISTT:
if (Str::eq(t->material, I"rtrue"))
Produce::val_symbol(I, K_value, Site::veneer_symbol(I, ASM_RTRUE_VSYMB));
else if (Str::eq(t->material, I"rfalse"))
Produce::val_symbol(I, K_value, Site::veneer_symbol(I, ASM_RFALSE_VSYMB));
else Produce::lab(I, Produce::reserve_label(I, t->material));
break;
case NUMBER_ISTT:
case BIN_NUMBER_ISTT:
case HEX_NUMBER_ISTT: {
inter_ti v1 = 0, v2 = 0;
if (t->constant_number >= 0) { v1 = LITERAL_IVAL; v2 = (inter_ti) t->constant_number; }
else if (Inter::Types::read_int_in_I6_notation(t->material, &v1, &v2) == FALSE)
internal_error("bad number");
Produce::val(I, K_value, v1, v2);
break;
}
case REAL_NUMBER_ISTT:
Produce::val_real_from_text(I, t->material);
break;
case DQUOTED_ISTT:
if (print_ret_me) {
Produce::inv_primitive(I, PRINTRET_BIP);
Produce::down(I);
}
Produce::val_text(I, t->material);
if (print_ret_me) {
Produce::up(I);
}
break;
case SQUOTED_ISTT:
if (Str::len(t->material) == 1) {
Produce::val_char(I, Str::get_at(t->material, 0));
} else {
Produce::val_dword(I, t->material);
}
break;
case I7_ISTT:
if (i7_source_handler)
(*i7_source_handler)(VH, t->material, opaque_state, prim_cat);
break;
case INLINE_ISTT:
if (inline_command_handler)
(*inline_command_handler)(VH, t, opaque_state, prim_cat);
break;
default:
internal_error("bad expression token");
}
}
if (cat_me) { Produce::up(I); }
if (prim_cat == REF_PRIM_CAT) { Produce::up(I); }
@<Non-conditional directive@> =
internal_error("unknown directive");
@ =
inter_symbol *EmitInterSchemas::find_identifier(inter_tree *I, inter_schema_token *t, inter_symbols_table *first_call, inter_symbols_table *second_call) {
if (t->as_quoted) return InterNames::to_symbol(t->as_quoted);
return EmitInterSchemas::find_identifier_text(I, t->material, first_call, second_call);
}
inter_symbol *EmitInterSchemas::find_identifier_text(inter_tree *I, text_stream *name, inter_symbols_table *first_call, inter_symbols_table *second_call) {
if (Str::get_at(name, 0) == 0x00A7) {
TEMPORARY_TEXT(SR)
Str::copy(SR, name);
Str::delete_first_character(SR);
Str::delete_last_character(SR);
inter_symbol *S = InterSymbolsTables::url_name_to_symbol(I, NULL, SR);
DISCARD_TEXT(SR)
if (S) return S;
}
if (first_call) {
inter_symbol *S = Produce::seek_symbol(first_call, name);
if (S) return S;
}
if (second_call) {
inter_symbol *S = Produce::seek_symbol(second_call, name);
if (S) return S;
}
inter_symbol *S = Veneer::find(I, name, Produce::kind_to_symbol(NULL));
if (S) return S;
S = Produce::seek_symbol(Produce::connectors_scope(I), name);
if (S) return S;
S = Produce::seek_symbol(Produce::main_scope(I), name);
if (S) return S;
S = InterNames::to_symbol(Produce::find_by_name(I, name));
if (S) return S;
LOG("Defeated on %S\n", name);
internal_error("unable to find identifier");
return NULL;
}