To parse individual commands from Neptune files.
typedef struct kind_command_definition { char *text_of_command; int opcode_number; /* one of the |*_KCC| values below */ int operand_type; /* one of the |*_KCA| values below */ char *warning_if_used; } kind_command_definition;
- The structure kind_command_definition is accessed in 4/kc and here.
enumerate NO_KCA 0 /* there's no operand */
enumerate BOOLEAN_KCA /* must be |yes| or |no| */
enumerate CCM_KCA /* a constant compilation method */
enumerate TEXT_KCA /* any text (no quotation marks or other delimiters are used) */
enumerate VOCABULARY_KCA /* any single word */
enumerate NUMERIC_KCA /* any decimal number */
enumerate CONSTRUCTOR_KCA /* any valid kind number, such as "number" */
enumerate TEMPLATE_KCA /* the name of a template whose definition is given in the file */
enumerate MACRO_KCA /* the name of a macro whose definition is given in the file */
enumerate SCHEMA_KCA /* an I6 compilation schema */
enumerate apply_macro_KCC 1
enumerate invent_source_text_KCC
enumerate can_coincide_with_property_KCC
enumerate can_exchange_KCC
enumerate compatible_with_KCC
enumerate compare_function_KCC
enumerate comparison_schema_KCC
enumerate constant_compilation_method_KCC
enumerate default_value_KCC
enumerate distinguish_function_KCC
enumerate documentation_reference_KCC
enumerate understand_function_KCC
enumerate forbid_assertion_creation_KCC
enumerate printing_routine_for_debugging_KCC
enumerate say_function_KCC
enumerate index_default_value_KCC
enumerate index_maximum_value_KCC
enumerate index_minimum_value_KCC
enumerate indexed_grey_if_empty_KCC
enumerate index_priority_KCC
enumerate conforms_to_KCC
enumerate is_incompletely_defined_KCC
enumerate loop_domain_schema_KCC
enumerate modifying_adjective_KCC
enumerate long_block_size_KCC
enumerate flexible_long_block_size_KCC
enumerate plural_KCC
enumerate recognise_function_KCC
enumerate singular_KCC
enumerate specification_text_KCC
enumerate short_block_size_KCC
enumerate terms_KCC
enumerate instance_KCC
enumerate plus_schema_KCC
enumerate minus_schema_KCC
enumerate times_schema_KCC
enumerate divide_schema_KCC
enumerate remainder_schema_KCC
enumerate approximate_schema_KCC
enumerate negate_schema_KCC
enumerate root_schema_KCC
enumerate cuberoot_schema_KCC
enumerate power_schema_KCC
enumerate arithmetic_modulus_KCC
enumerate dimensionless_KCC
enumerate create_function_KCC
enumerate cast_function_KCC
enumerate copy_function_KCC
enumerate copy_short_block_function_KCC
enumerate quick_copy_function_KCC
enumerate destroy_function_KCC
enumerate make_mutable_function_KCC
enumerate hash_function_KCC
enumerate long_block_size_function_KCC
enumerate serialise_function_KCC
enumerate unserialise_function_KCC
kind_command_definition table_of_kind_commands[] = { { "can-coincide-with-property", can_coincide_with_property_KCC, BOOLEAN_KCA, NULL }, { "can-exchange", can_exchange_KCC, BOOLEAN_KCA, NULL }, { "indexed-grey-if-empty", indexed_grey_if_empty_KCC, BOOLEAN_KCA, NULL }, { "is-incompletely-defined", is_incompletely_defined_KCC, BOOLEAN_KCA, NULL }, { "multiple-block", -1, BOOLEAN_KCA, "'multiple-block: no' can be omitted; 'multiple-block: yes' should now be 'flexible-long-block-size: N' for some typical field count N" }, { "long-block-size", long_block_size_KCC, NUMERIC_KCA, NULL }, { "flexible-long-block-size", flexible_long_block_size_KCC, NUMERIC_KCA, NULL }, { "forbid-assertion-creation", forbid_assertion_creation_KCC, BOOLEAN_KCA, NULL }, { "constant-compilation-method", constant_compilation_method_KCC, CCM_KCA, NULL }, { "comparison-routine", compare_function_KCC, TEXT_KCA, "this command has been renamed 'compare-function'" }, { "compare-function", compare_function_KCC, TEXT_KCA, NULL }, { "default-value", default_value_KCC, TEXT_KCA, NULL }, { "distinguishing-routine", distinguish_function_KCC, TEXT_KCA, "this command has been renamed 'distinguish-function'" }, { "distinguish-function", distinguish_function_KCC, TEXT_KCA, NULL }, { "documentation-reference", documentation_reference_KCC, TEXT_KCA, NULL }, { "parsing-routine", understand_function_KCC, TEXT_KCA, "this command has been renamed 'understand-function'" }, { "understand-function", understand_function_KCC, TEXT_KCA, NULL }, { "printing-routine", say_function_KCC, TEXT_KCA, "this command has been renamed 'say-function'" }, { "say-function", say_function_KCC, TEXT_KCA, NULL }, { "printing-routine-for-debugging", -1, TEXT_KCA, "this command has been withdrawn" }, { "index-default-value", index_default_value_KCC, TEXT_KCA, NULL }, { "index-maximum-value", index_maximum_value_KCC, TEXT_KCA, NULL }, { "index-minimum-value", index_minimum_value_KCC, TEXT_KCA, NULL }, { "loop-domain-schema", loop_domain_schema_KCC, TEXT_KCA, NULL }, { "recognition-routine", recognise_function_KCC, TEXT_KCA, "this command has been renamed 'recognise-function'" }, { "recognise-function", recognise_function_KCC, TEXT_KCA, NULL }, { "specification-text", specification_text_KCC, TEXT_KCA, NULL }, { "create-function", create_function_KCC, TEXT_KCA, NULL }, { "cast-function", cast_function_KCC, TEXT_KCA, NULL }, { "copy-function", copy_function_KCC, TEXT_KCA, NULL }, { "copy-short-block-function", copy_short_block_function_KCC, TEXT_KCA, NULL }, { "quick-copy-function", quick_copy_function_KCC, TEXT_KCA, NULL }, { "destroy-function", destroy_function_KCC, TEXT_KCA, NULL }, { "make-mutable-function", make_mutable_function_KCC, TEXT_KCA, NULL }, { "hash-function", hash_function_KCC, TEXT_KCA, NULL }, { "long-block-size-function", long_block_size_function_KCC, TEXT_KCA, NULL }, { "serialise-function", serialise_function_KCC, TEXT_KCA, NULL }, { "unserialise-function", unserialise_function_KCC, TEXT_KCA, NULL }, { "comparison-schema", comparison_schema_KCC, SCHEMA_KCA, NULL }, { "compatible-with", compatible_with_KCC, CONSTRUCTOR_KCA, NULL }, { "conforms-to", conforms_to_KCC, CONSTRUCTOR_KCA, NULL }, { "plus-schema", plus_schema_KCC, SCHEMA_KCA, NULL }, { "minus-schema", minus_schema_KCC, SCHEMA_KCA, NULL }, { "times-schema", times_schema_KCC, SCHEMA_KCA, NULL }, { "divide-schema", divide_schema_KCC, SCHEMA_KCA, NULL }, { "remainder-schema", remainder_schema_KCC, SCHEMA_KCA, NULL }, { "approximate-schema", approximate_schema_KCC, SCHEMA_KCA, NULL }, { "negate-schema", negate_schema_KCC, SCHEMA_KCA, NULL }, { "root-schema", root_schema_KCC, SCHEMA_KCA, NULL }, { "cuberoot-schema", cuberoot_schema_KCC, SCHEMA_KCA, NULL }, { "power-schema", power_schema_KCC, SCHEMA_KCA, NULL }, { "arithmetic-modulus", arithmetic_modulus_KCC, NUMERIC_KCA, NULL }, { "dimensionless", dimensionless_KCC, BOOLEAN_KCA, NULL }, { "plural", plural_KCC, VOCABULARY_KCA, NULL }, { "singular", singular_KCC, VOCABULARY_KCA, NULL }, { "terms", terms_KCC, TEXT_KCA, NULL }, { "index-priority", index_priority_KCC, NUMERIC_KCA, NULL }, { "small-block-size", short_block_size_KCC, NUMERIC_KCA, "this command has been renamed 'short-block-size'" }, { "short-block-size", short_block_size_KCC, NUMERIC_KCA, NULL }, { "invent-source-text", invent_source_text_KCC, TEMPLATE_KCA, NULL }, { "instance", instance_KCC, TEXT_KCA, NULL }, { "apply-macro", apply_macro_KCC, MACRO_KCA, NULL }, { NULL, -1, NO_KCA, NULL } };
typedef struct single_kind_command { struct kind_command_definition *which_kind_command; int boolean_argument; /* where appropriate */ int numeric_argument; /* where appropriate */ struct text_stream *textual_argument; /* where appropriate */ int ccm_argument; /* where appropriate */ struct word_assemblage vocabulary_argument; /* where appropriate */ struct text_stream *constructor_argument; /* where appropriate */ struct kind_template_definition *template_argument; /* where appropriate */ struct kind_macro_definition *macro_argument; /* where appropriate */ struct text_file_position *origin; struct kind_constructor *defined_for; int completed; } single_kind_command;
- The structure single_kind_command is accessed in 4/nf, 4/st, 4/kc and here.
§5. Parsing single kind commands. Each command is read in as text, parsed and stored into a modest structure.
kind_constructor *constructor_described = NULL; additional_property_set *additional_property_set_described = NULL; typedef struct additional_property_set { struct text_stream *owner_name; struct linked_list *properties; /* of |additional_property| */ CLASS_DEFINITION } additional_property_set; typedef struct additional_property { int attr; struct text_stream *property_name; struct text_stream *value_text; CLASS_DEFINITION } additional_property; additional_property_set *NeptuneSyntax::new_additional_property_set(text_stream *owner_name) { additional_property_set *set = CREATE(additional_property_set); set->owner_name = Str::duplicate(owner_name); set->properties = NEW_LINKED_LIST(additional_property); return set; } single_kind_command NeptuneSyntax::parse_command(text_stream *whole_command, text_file_position *tfp) { single_kind_command stc; Initialise the STC to a blank command5.1; if (Str::eq(whole_command, I"}")) { if (StarTemplates::recording()) StarTemplates::end(whole_command, tfp); else if (NeptuneMacros::recording()) NeptuneMacros::end(tfp); else { constructor_described = NULL; additional_property_set_described = NULL; } stc.completed = TRUE; } else if (StarTemplates::recording()) { StarTemplates::record_line(whole_command, tfp); stc.completed = TRUE; } else if (Str::get_last_char(whole_command) == '{') { if ((constructor_described) || (additional_property_set_described)) { NeptuneFiles::error(whole_command, I"previous declaration not closed with '}'", tfp); constructor_described = NULL; additional_property_set_described = NULL; } match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, whole_command, U"properties of (%c+) {")) { additional_property_set_described = NeptuneSyntax::new_additional_property_set(mr.exp[0]); } else if (Regexp::match(&mr, whole_command, U"invention (%C+) {")) { StarTemplates::begin(mr.exp[0], tfp); } else if (Regexp::match(&mr, whole_command, U"macro (#%C+) {")) { NeptuneMacros::begin(mr.exp[0], tfp); } else if (Regexp::match(&mr, whole_command, U"(%C+) (%C+) (%C+) {")) { int should_know = NOT_APPLICABLE; if (Str::eq(mr.exp[0], I"new")) should_know = FALSE; else if (Str::eq(mr.exp[0], I"builtin")) should_know = TRUE; if (should_know == NOT_APPLICABLE) NeptuneFiles::error(whole_command, I"declaration must begin 'new' or 'builtin'", tfp); else { int group = -1; if (Str::eq(mr.exp[1], I"punctuation")) group = PUNCTUATION_GRP; else if (Str::eq(mr.exp[1], I"protocol")) group = PROTOCOL_GRP; else if (Str::eq(mr.exp[1], I"base")) group = BASE_CONSTRUCTOR_GRP; else if (Str::eq(mr.exp[1], I"constructor")) group = PROPER_CONSTRUCTOR_GRP; if (group < 0) NeptuneFiles::error(whole_command, I"must declare 'variable', 'protocol', 'base' or 'constructor', or 'property'", tfp); else { text_stream *name = mr.exp[2]; Create a new constructor5.2; } } } else { NeptuneFiles::error(whole_command, I"malformed declaration line", tfp); } Regexp::dispose_of(&mr); stc.completed = TRUE; } else if (Str::get_last_char(whole_command) == ':') { NeptuneFiles::error(whole_command, I"trailing colon was unexpected", tfp); stc.completed = TRUE; } else { TEMPORARY_TEXT(command) TEMPORARY_TEXT(argument) Parse line into command and argument, divided by a colon5.3; if (additional_property_set_described) Handle as an additional property setting5.4 else Handle as a kind constructor setting5.5; DISCARD_TEXT(command) DISCARD_TEXT(argument) } return stc; }
- The structure additional_property_set is private to this section.
- The structure additional_property is private to this section.
§5.1. Initialise the STC to a blank command5.1 =
stc.which_kind_command = NULL; stc.boolean_argument = NOT_APPLICABLE; stc.numeric_argument = 0; stc.textual_argument = Str::new(); stc.ccm_argument = -1; stc.vocabulary_argument = WordAssemblages::lit_0(); stc.constructor_argument = Str::new(); stc.macro_argument = NULL; stc.template_argument = NULL; stc.completed = FALSE; stc.origin = tfp; stc.defined_for = constructor_described;
- This code is used in §5.
§5.2. Create a new constructor5.2 =
int do_know = FamiliarKinds::is_known(name); if ((do_know == FALSE) && (should_know == TRUE)) NeptuneFiles::error(whole_command, I"kind command describes kind with no known name", tfp); if ((do_know == TRUE) && (should_know == FALSE)) NeptuneFiles::error(whole_command, I"kind command describes already-known kind", tfp); constructor_described = KindConstructors::new(Kinds::get_construct(K_value), name, NULL, group); #ifdef NEW_BASE_KINDS_CALLBACK if ((constructor_described != CON_KIND_VARIABLE) && (constructor_described != CON_INTERMEDIATE)) { NEW_BASE_KINDS_CALLBACK( Kinds::base_construction(constructor_described), NULL, name, EMPTY_WORDING); } #endif
- This code is used in §5.
§5.3. Spaces and tabs after the colon are skipped; so a textual argument cannot begin with those characters, but that doesn't matter for the things we need.
Parse line into command and argument, divided by a colon5.3 =
match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, whole_command, U" *(%c+?) *: *(%c+?) *")) { Str::copy(command, mr.exp[0]); Str::copy(argument, mr.exp[1]); Regexp::dispose_of(&mr); } else { NeptuneFiles::error(whole_command, I"kind command without argument", tfp); } Regexp::dispose_of(&mr);
- This code is used in §5.
§5.4. Handle as an additional property setting5.4 =
int attr = NOT_APPLICABLE; if (Str::eq(command, I"attribute")) attr = TRUE; if (Str::eq(command, I"property")) attr = FALSE; if (attr == NOT_APPLICABLE) { NeptuneFiles::error(whole_command, I"only 'attribute' and 'property' commands are allowed here", tfp); } else { additional_property *ap = CREATE(additional_property); ap->attr = attr; match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, argument, U"(%C+?) *= *(%c+)")) { if (attr) NeptuneFiles::error(whole_command, I"only 'property' commands can use '='", tfp); ap->property_name = Str::duplicate(mr.exp[0]); ap->value_text = Str::duplicate(mr.exp[1]); } else { ap->property_name = Str::duplicate(argument); ap->value_text = (attr)?I"1":I"0"; } if (Str::get_first_char(ap->property_name) == '~') { if (attr) { Str::delete_first_character(ap->property_name); ap->value_text = I"0"; } else { NeptuneFiles::error(whole_command, I"only 'attribute' commands can use '~'", tfp); } } Regexp::dispose_of(&mr); ADD_TO_LINKED_LIST(ap, additional_property, additional_property_set_described->properties); } stc.completed = TRUE;
- This code is used in §5.
§5.5. Handle as a kind constructor setting5.5 =
Identify the command being used5.5.1; switch(stc.which_kind_command->operand_type) { case BOOLEAN_KCA: Parse a boolean argument for a kind command5.5.2; break; case CCM_KCA: Parse a CCM argument for a kind command5.5.3; break; case CONSTRUCTOR_KCA: Parse a constructor-name argument for a kind command5.5.8; break; case MACRO_KCA: Parse a macro name argument for a kind command5.5.10; break; case NUMERIC_KCA: Parse a numeric argument for a kind command5.5.7; break; case TEMPLATE_KCA: Parse a template name argument for a kind command5.5.9; break; case TEXT_KCA: Parse a textual argument for a kind command5.5.4; break; case SCHEMA_KCA: Parse a schema argument for a kind command5.5.5; break; case VOCABULARY_KCA: Parse a vocabulary argument for a kind command5.5.6; break; }
- This code is used in §5.
§5.5.1. The following is clearly inefficient, but is not worth optimising. It makes about 20 string comparisons per command, and there are about 600 commands in a typical run of Inform, so the total cost is about 12,000 comparisons with quite small strings as arguments -- which is negligible for our purposes, so we neglect it.
Identify the command being used5.5.1 =
for (int i=0; table_of_kind_commands[i].text_of_command; i++) if (Str::eq_narrow_string(command, table_of_kind_commands[i].text_of_command)) stc.which_kind_command = &(table_of_kind_commands[i]); if (stc.which_kind_command == NULL) { NeptuneFiles::error(command, I"no such kind command", tfp); stc.completed = TRUE; return stc; } if (stc.which_kind_command->opcode_number == -1) { TEMPORARY_TEXT(err) WRITE_TO(err, "%s: %s", stc.which_kind_command->text_of_command, stc.which_kind_command->warning_if_used); NeptuneFiles::error(command, err, tfp); stc.completed = TRUE; DISCARD_TEXT(err) return stc; } if (stc.which_kind_command->warning_if_used) { TEMPORARY_TEXT(err) WRITE_TO(err, "%s: %s", stc.which_kind_command->text_of_command, stc.which_kind_command->warning_if_used); NeptuneFiles::warning(command, err, tfp); DISCARD_TEXT(err) }
- This code is used in §5.5.
§5.5.2. Parse a boolean argument for a kind command5.5.2 =
if (Str::eq_wide_string(argument, U"yes")) stc.boolean_argument = TRUE; else if (Str::eq_wide_string(argument, U"no")) stc.boolean_argument = FALSE; else NeptuneFiles::error(command, I"boolean kind command takes yes/no argument", tfp);
- This code is used in §5.5.
§5.5.3. Parse a CCM argument for a kind command5.5.3 =
if (Str::eq_wide_string(argument, U"none")) stc.ccm_argument = NONE_CCM; else if (Str::eq_wide_string(argument, U"literal")) stc.ccm_argument = LITERAL_CCM; else if (Str::eq_wide_string(argument, U"quantitative")) stc.ccm_argument = NAMED_CONSTANT_CCM; else if (Str::eq_wide_string(argument, U"special")) stc.ccm_argument = SPECIAL_CCM; else { NeptuneFiles::error(command, I"kind command with unknown constant-compilation-method", tfp); stc.completed = TRUE; return stc; }
- This code is used in §5.5.
§5.5.4. Parse a textual argument for a kind command5.5.4 =
Str::copy(stc.textual_argument, argument);
- This code is used in §5.5.
§5.5.5. Parse a schema argument for a kind command5.5.5 =
match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, argument, U"(%c*?)>>>(%c+)")) { Str::copy(stc.constructor_argument, mr.exp[0]); Str::copy(stc.textual_argument, mr.exp[1]); } else { Str::copy(stc.textual_argument, argument); } Regexp::dispose_of(&mr);
- This code is used in §5.5.
§5.5.6. Parse a vocabulary argument for a kind command5.5.6 =
stc.vocabulary_argument = WordAssemblages::lit_0(); feed_t id = Feeds::begin(); Feeds::feed_text(argument); wording W = Feeds::end(id); if (Wordings::length(W) >= 30) { NeptuneFiles::error(command, I"too many words in kind command", tfp); stc.completed = TRUE; return stc; } else stc.vocabulary_argument = WordAssemblages::from_wording(W);
- This code is used in §5.5.
§5.5.7. Parse a numeric argument for a kind command5.5.7 =
stc.numeric_argument = Str::atoi(argument, 0);
- This code is used in §5.5.
§5.5.8. Parse a constructor-name argument for a kind command5.5.8 =
stc.constructor_argument = Str::duplicate(argument);
- This code is used in §5.5.
§5.5.9. Parse a template name argument for a kind command5.5.9 =
stc.template_argument = StarTemplates::parse_name(argument); if (stc.template_argument == NULL) { NeptuneFiles::error(command, I"unknown template name in kind command", tfp); stc.completed = TRUE; return stc; }
- This code is used in §5.5.
§5.5.10. Parse a macro name argument for a kind command5.5.10 =
stc.macro_argument = NeptuneMacros::parse_name(argument); if (stc.macro_argument == NULL) { NeptuneFiles::error(command, I"unknown template name in kind command", tfp); stc.completed = TRUE; return stc; }
- This code is used in §5.5.