mirror of
https://github.com/ganelson/inform.git
synced 2024-07-02 23:14:57 +03:00
291 lines
8.8 KiB
OpenEdge ABL
291 lines
8.8 KiB
OpenEdge ABL
[Compatibility::] Compatibility.
|
|
|
|
To manage compatibility lists: what can be compiled to what format.
|
|
|
|
@
|
|
|
|
=
|
|
typedef struct compatibility_specification {
|
|
struct text_stream *parsed_from;
|
|
int default_allows;
|
|
struct linked_list *exceptions; /* of |target_vm| */
|
|
MEMORY_MANAGEMENT
|
|
} compatibility_specification;
|
|
|
|
compatibility_specification *Compatibility::all(void) {
|
|
compatibility_specification *C = CREATE(compatibility_specification);
|
|
C->parsed_from = NULL;
|
|
C->default_allows = TRUE;
|
|
C->exceptions = NEW_LINKED_LIST(target_vm);
|
|
return C;
|
|
}
|
|
|
|
int Compatibility::universal(compatibility_specification *C) {
|
|
if (C == NULL) return FALSE;
|
|
if (LinkedLists::len(C->exceptions) > 0) return FALSE;
|
|
if (C->default_allows == FALSE) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
void Compatibility::write(OUTPUT_STREAM, compatibility_specification *C) {
|
|
if (C == NULL) { WRITE("for none"); return; }
|
|
int x = LinkedLists::len(C->exceptions);
|
|
if (x == 0) {
|
|
if (C->default_allows) WRITE("for all");
|
|
else WRITE("for none");
|
|
} else {
|
|
if (C->default_allows) WRITE("not ");
|
|
WRITE("for ");
|
|
int n = 0;
|
|
target_vm *VM;
|
|
LOOP_OVER_LINKED_LIST(VM, target_vm, C->exceptions) {
|
|
n++;
|
|
if ((n > 1) && (n < x)) WRITE(", ");
|
|
if ((n > 1) && (n == x)) WRITE(" or ");
|
|
TargetVMs::write(OUT, VM);
|
|
}
|
|
}
|
|
}
|
|
|
|
compatibility_specification *Compatibility::from_text(text_stream *text) {
|
|
compatibility_specification *C = Compatibility::all();
|
|
int incorrect = FALSE;
|
|
if (Str::len(text) == 0) return C;
|
|
TEMPORARY_TEXT(parse);
|
|
WRITE_TO(parse, "%S", text);
|
|
C->parsed_from = Str::duplicate(parse);
|
|
Str::trim_white_space(parse);
|
|
if ((Str::get_first_char(parse) == '(') && (Str::get_last_char(parse) == ')')) {
|
|
Str::delete_first_character(parse);
|
|
Str::delete_last_character(parse);
|
|
Str::trim_white_space(parse);
|
|
}
|
|
LOOP_THROUGH_TEXT(pos, parse)
|
|
Str::put(pos, Characters::tolower(Str::get(pos)));
|
|
|
|
C->default_allows = FALSE;
|
|
match_results mr = Regexp::create_mr();
|
|
int negated = FALSE;
|
|
if (Regexp::match(&mr, parse, L"not (%c+)")) {
|
|
Str::clear(parse);
|
|
WRITE_TO(parse, "%S", mr.exp[0]);
|
|
Str::trim_white_space(parse);
|
|
C->default_allows = TRUE;
|
|
negated = TRUE;
|
|
}
|
|
|
|
if (Regexp::match(&mr, parse, L"for (%c+)")) {
|
|
Str::clear(parse);
|
|
WRITE_TO(parse, "%S", mr.exp[0]);
|
|
Str::trim_white_space(parse);
|
|
}
|
|
|
|
if (Regexp::match(&mr, parse, L"(%c+) only")) {
|
|
Str::clear(parse);
|
|
WRITE_TO(parse, "%S", mr.exp[0]);
|
|
Str::trim_white_space(parse);
|
|
if (negated) incorrect = TRUE; /* "not for 32d only" */
|
|
}
|
|
|
|
if (Str::eq(parse, I"all")) {
|
|
if (negated) incorrect = TRUE; /* "not for all" */
|
|
C->default_allows = TRUE;
|
|
} else if (Str::eq(parse, I"none")) {
|
|
if (negated) incorrect = TRUE; /* "not for none" */
|
|
C->default_allows = FALSE;
|
|
} else if (Compatibility::clause(C, parse) == FALSE)
|
|
incorrect = TRUE;
|
|
|
|
DISCARD_TEXT(parse);
|
|
Regexp::dispose_of(&mr);
|
|
if (incorrect) C = NULL;
|
|
return C;
|
|
}
|
|
|
|
typedef struct compat_parser_state {
|
|
compatibility_specification *C;
|
|
text_stream *current_family;
|
|
int version_allowed;
|
|
int version_required;
|
|
int family_used;
|
|
} compat_parser_state;
|
|
|
|
int Compatibility::clause(compatibility_specification *C, text_stream *text) {
|
|
int correct = TRUE;
|
|
match_results mr = Regexp::create_mr();
|
|
|
|
compat_parser_state cps;
|
|
cps.C = C; cps.version_allowed = FALSE; cps.version_required = FALSE;
|
|
cps.current_family = NULL; cps.family_used = FALSE;
|
|
|
|
while (Regexp::match(&mr, text, L"(%C+) (%c+)")) {
|
|
int comma = FALSE;
|
|
if (Str::get_last_char(mr.exp[0]) == ',') {
|
|
comma = TRUE;
|
|
Str::delete_last_character(mr.exp[0]);
|
|
Str::trim_white_space(mr.exp[0]);
|
|
}
|
|
int with = NOT_APPLICABLE;
|
|
match_results mr2 = Regexp::create_mr();
|
|
if (Regexp::match(&mr2, mr.exp[1], L"with debugging,* *(%c*)")) {
|
|
Str::clear(mr.exp[1]);
|
|
Str::copy(mr.exp[1], mr2.exp[0]);
|
|
with = TRUE;
|
|
} else if (Regexp::match(&mr2, mr.exp[1], L"without debugging,* *(%c*)")) {
|
|
Str::clear(mr.exp[1]);
|
|
Str::copy(mr.exp[1], mr2.exp[0]);
|
|
with = FALSE;
|
|
}
|
|
Regexp::dispose_of(&mr2);
|
|
correct = (correct && Compatibility::word(&cps, mr.exp[0], with));
|
|
if (comma) correct = (correct && Compatibility::word(&cps, I"or", with));
|
|
Str::clear(text); Str::copy(text, mr.exp[1]);
|
|
}
|
|
if (Str::len(text) > 0)
|
|
correct = (correct && Compatibility::word(&cps, text, NOT_APPLICABLE));
|
|
if ((correct) && (cps.family_used == FALSE) && (Str::len(cps.current_family) > 0)) {
|
|
target_vm *VM;
|
|
LOOP_OVER(VM, target_vm)
|
|
if (Str::eq_insensitive(cps.current_family, VM->family_name))
|
|
Compatibility::add(C, VM);
|
|
}
|
|
|
|
Regexp::dispose_of(&mr);
|
|
return correct;
|
|
}
|
|
|
|
int Compatibility::word(compat_parser_state *cps, text_stream *word, int with) {
|
|
if (cps->version_allowed) {
|
|
semantic_version_number V = VersionNumbers::from_text(word);
|
|
if (VersionNumbers::is_null(V)) {
|
|
if (cps->version_required) return FALSE;
|
|
} else {
|
|
if (Str::len(cps->current_family) == 0) return FALSE;
|
|
cps->family_used = TRUE;
|
|
target_vm *VM;
|
|
int seen = FALSE;
|
|
LOOP_OVER(VM, target_vm)
|
|
if (Str::eq_insensitive(VM->family_name, cps->current_family)) {
|
|
seen = TRUE;
|
|
if ((VersionNumbers::eq(VM->version, V)) &&
|
|
((with == NOT_APPLICABLE) || (TargetVMs::debug_enabled(VM) == with)))
|
|
Compatibility::add(cps->C, VM);
|
|
}
|
|
cps->version_required = FALSE;
|
|
return seen;
|
|
}
|
|
}
|
|
|
|
if (Str::eq_insensitive(word, I"or")) {
|
|
if (with != NOT_APPLICABLE) return FALSE;
|
|
return TRUE;
|
|
}
|
|
if ((Str::eq_insensitive(word, I"version")) ||
|
|
(Str::eq_insensitive(word, I"versions"))) {
|
|
if (with != NOT_APPLICABLE) return FALSE;
|
|
cps->version_required = TRUE;
|
|
cps->version_allowed = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
cps->version_required = FALSE;
|
|
cps->version_allowed = FALSE;
|
|
|
|
int bits = NOT_APPLICABLE;
|
|
if (Str::eq_insensitive(word, I"16-bit")) bits = TRUE;
|
|
if (Str::eq_insensitive(word, I"32-bit")) bits = FALSE;
|
|
if (bits != NOT_APPLICABLE) {
|
|
target_vm *VM;
|
|
LOOP_OVER(VM, target_vm)
|
|
if (TargetVMs::is_16_bit(VM) == bits)
|
|
if ((with == NOT_APPLICABLE) || (TargetVMs::debug_enabled(VM) == with))
|
|
Compatibility::add(cps->C, VM);
|
|
cps->current_family = NULL;
|
|
cps->family_used = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
if (with != NOT_APPLICABLE) {
|
|
int seen = FALSE;
|
|
target_vm *VM;
|
|
LOOP_OVER(VM, target_vm)
|
|
if (Str::eq_insensitive(VM->family_name, word)) {
|
|
seen = TRUE;
|
|
if (TargetVMs::debug_enabled(VM) == with)
|
|
Compatibility::add(cps->C, VM);
|
|
}
|
|
cps->current_family = NULL;
|
|
cps->family_used = FALSE;
|
|
return seen;
|
|
}
|
|
|
|
target_vm *VM;
|
|
LOOP_OVER(VM, target_vm)
|
|
if (Str::eq_insensitive(VM->family_name, word)) {
|
|
cps->current_family = VM->family_name;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int Compatibility::add(compatibility_specification *C, target_vm *VM) {
|
|
int already_there = FALSE;
|
|
target_vm *X;
|
|
LOOP_OVER_LINKED_LIST(X, target_vm, C->exceptions)
|
|
if (VM == X)
|
|
already_there = TRUE;
|
|
if (already_there == FALSE)
|
|
ADD_TO_LINKED_LIST(VM, target_vm, C->exceptions);
|
|
return already_there;
|
|
}
|
|
|
|
int Compatibility::with(compatibility_specification *C, target_vm *VM) {
|
|
if (C == NULL) return FALSE;
|
|
int decision = C->default_allows;
|
|
target_vm *X;
|
|
LOOP_OVER_LINKED_LIST(X, target_vm, C->exceptions)
|
|
if (VM == X)
|
|
decision = decision?FALSE:TRUE;
|
|
return decision;
|
|
}
|
|
|
|
void Compatibility::test(OUTPUT_STREAM) {
|
|
Compatibility::test_one(OUT, I"for all");
|
|
Compatibility::test_one(OUT, I"all");
|
|
Compatibility::test_one(OUT, I"not for all");
|
|
Compatibility::test_one(OUT, I"not all");
|
|
Compatibility::test_one(OUT, I"for none");
|
|
Compatibility::test_one(OUT, I"none");
|
|
Compatibility::test_one(OUT, I"not for none");
|
|
Compatibility::test_one(OUT, I"not none");
|
|
Compatibility::test_one(OUT, I"for 16-bit with debugging");
|
|
Compatibility::test_one(OUT, I"not for 32-bit");
|
|
Compatibility::test_one(OUT, I"for 16-bit with debugging or 32-bit with debugging");
|
|
Compatibility::test_one(OUT, I"not for 32-bit or 16-bit");
|
|
Compatibility::test_one(OUT, I"for 16-bit with debugging, 32-bit with debugging or 32-bit");
|
|
Compatibility::test_one(OUT, I"not for 16-bit with debugging, 32-bit with debugging or 32-bit");
|
|
Compatibility::test_one(OUT, I"for glulx");
|
|
Compatibility::test_one(OUT, I"for glulx or z-machine version 8");
|
|
Compatibility::test_one(OUT, I"for glulx without debugging");
|
|
Compatibility::test_one(OUT, I"for z-machine version 8");
|
|
Compatibility::test_one(OUT, I"for z-machine version 5 with debugging");
|
|
Compatibility::test_one(OUT, I"for z-machine version 8, or Glulx without debugging");
|
|
Compatibility::test_one(OUT, I"for z-machine version 5 or 8");
|
|
}
|
|
|
|
void Compatibility::test_one(OUTPUT_STREAM, text_stream *test) {
|
|
WRITE("'%S': ", test);
|
|
compatibility_specification *C = Compatibility::from_text(test);
|
|
if (C == NULL) { WRITE("not a valid compatibility specification\n\n"); return; }
|
|
Compatibility::write(OUT, C);
|
|
WRITE(":\n"); INDENT;
|
|
target_vm *VM;
|
|
LOOP_OVER(VM, target_vm) {
|
|
if (Compatibility::with(C, VM)) {
|
|
TargetVMs::write(OUT, VM);
|
|
WRITE("\n");
|
|
}
|
|
}
|
|
OUTDENT; WRITE("\n");
|
|
}
|