To parse individual commands from Neptune files.
§1. The command set. Each different operation is defined with a block like so:
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.
§2. The operands have different types, and the possibilities are given here:
enum NO_KCA from 0 there's no operand enum BOOLEAN_KCA must be yes or no enum CCM_KCA a constant compilation method enum TEXT_KCA any text (no quotation marks or other delimiters are used) enum VOCABULARY_KCA any single word enum NUMERIC_KCA any decimal number enum CONSTRUCTOR_KCA any valid kind number, such as "number" enum TEMPLATE_KCA the name of a template whose definition is given in the file enum MACRO_KCA the name of a macro whose definition is given in the file enum SCHEMA_KCA an I6 compilation schema
§3. And, to cut to the chase, here is the complete table of commands:
enum apply_macro_KCC from 1 enum invent_source_text_KCC enum can_coincide_with_property_KCC enum can_exchange_KCC enum compatible_with_KCC enum compare_function_KCC enum comparison_schema_KCC enum constant_compilation_method_KCC enum default_value_KCC enum distinguish_function_KCC enum documentation_reference_KCC enum understand_function_KCC enum forbid_assertion_creation_KCC enum printing_routine_for_debugging_KCC enum say_function_KCC enum index_default_value_KCC enum index_maximum_value_KCC enum index_minimum_value_KCC enum indexed_grey_if_empty_KCC enum index_priority_KCC enum conforms_to_KCC enum is_incompletely_defined_KCC enum loop_domain_schema_KCC enum modifying_adjective_KCC enum long_block_size_KCC enum flexible_long_block_size_KCC enum plural_KCC enum recognise_function_KCC enum singular_KCC enum specification_text_KCC enum short_block_size_KCC enum terms_KCC enum instance_KCC enum plus_schema_KCC enum minus_schema_KCC enum times_schema_KCC enum divide_schema_KCC enum remainder_schema_KCC enum approximate_schema_KCC enum negate_schema_KCC enum root_schema_KCC enum cuberoot_schema_KCC enum power_schema_KCC enum arithmetic_modulus_KCC enum dimensionless_KCC enum create_function_KCC enum cast_function_KCC enum copy_function_KCC enum copy_short_block_function_KCC enum quick_copy_function_KCC enum destroy_function_KCC enum make_mutable_function_KCC enum hash_function_KCC enum long_block_size_function_KCC enum serialise_function_KCC enum 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 } };
§4. When processing a command, we parse it into one of the following structures:
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.