A command-line interface for Inter functions which are not part of the normal operation of the Inform compiler.


§1. Settings variables. The following will be set at the command line.

pathname *path_to_inter = NULL;

pathname *kit_to_build = NULL;
pathname *domain_path = NULL;
linked_list *inter_file_list = NULL;  of filename
filename *output_file = NULL;
dictionary *pipeline_vars = NULL;
filename *pipeline_as_file = NULL;
text_stream *pipeline_as_text = NULL;
pathname *internal_path = NULL;
text_stream *output_format = NULL;  for any -o output
int tracing = FALSE;

void Main::add_pipeline_variable(text_stream *name, text_stream *value) {
    Str::copy(Dictionaries::create_text(pipeline_vars, name), value);
}
void Main::add_pipeline_variable_from_filename(text_stream *name, filename *F) {
    TEMPORARY_TEXT(fullname)
    WRITE_TO(fullname, "%f", F);
    Main::add_pipeline_variable(name, fullname);
    DISCARD_TEXT(fullname)
}

§2. Main routine. When Inter is called at the command line, it begins at main, like all C programs.

Inter can do three different things: build a kit, run a pipeline of code generation stages, and verify/transcode files of Inter code. In fact, though, that's really only two different things, because kit-building is also done with a pipeline.

int main(int argc, char **argv) {
    Start up the modules2.1;
    Begin with an empty file list and variables dictionary2.2;
    Read the command line2.6;
    if (kit_to_build) Select the build-kit pipeline2.3;
    Run the pipeline2.4;
    Shut down the modules2.5;
    if (Errors::have_occurred()) return 1;
    return 0;
}

§2.1. Start up the modules2.1 =

    Foundation::start(argc, argv);  must be started first
    ArchModule::start();
    BytecodeModule::start();
    BuildingModule::start();
    PipelineModule::start();
    FinalModule::start();
    IndexModule::start();

§2.2. Begin with an empty file list and variables dictionary2.2 =

    inter_file_list = NEW_LINKED_LIST(filename);
    pipeline_vars = ParsingPipelines::basic_dictionary(I"output.i6");
    internal_path = Pathnames::from_text(I"inform7/Internal");

§2.3. Select the build-kit pipeline2.3 =

    inter_architecture *A = PipelineModule::get_architecture();
    if (A == NULL) Errors::fatal("no -architecture given");

    pathname *path_to_pipelines = Pathnames::down(path_to_inter, I"Pipelines");
    pipeline_as_file = Filenames::in(path_to_pipelines, I"build-kit.interpipeline");
    pipeline_as_text = NULL;

    Main::add_pipeline_variable(I"*kit",
        Pathnames::directory_name(kit_to_build));
    Main::add_pipeline_variable_from_filename(I"*out",
        Architectures::canonical_binary(kit_to_build, A));

§2.4. Run the pipeline2.4 =

    if ((pipeline_as_file) && (pipeline_as_text))
        Errors::fatal("-pipeline-text and -pipeline-file are mutually exclusive");
    if (Str::len(output_format) == 0) output_format = I"Text";
    target_vm *VM = TargetVMs::find(output_format);
    if (VM == NULL) Errors::fatal("unrecognised compilation -format");
    inter_architecture *A = PipelineModule::get_architecture();
    if (A == NULL)
        PipelineModule::set_architecture(
            Architectures::to_codename(TargetVMs::get_architecture(VM)));

    inter_tree *I = InterTree::new();
    if (LinkedLists::len(inter_file_list) > 0) {
        if (LinkedLists::len(inter_file_list) > 1)
            Errors::fatal("only one file of Inter can be supplied");
        filename *F;
        LOOP_OVER_LINKED_LIST(F, filename, inter_file_list)
            Main::add_pipeline_variable_from_filename(I"*in", F);
    }
    if (output_file) {
        Main::add_pipeline_variable_from_filename(I"*out", output_file);
    }
    inter_pipeline *SS = NULL;
    Compile the pipeline2.4.1;
    if (SS) {
        linked_list *req_list = NEW_LINKED_LIST(attachment_instruction);
        RunningPipelines::run(domain_path, SS, I, kit_to_build, req_list, VM, tracing);
    }

§2.4.1. Compile the pipeline2.4.1 =

    if (pipeline_as_file) {
        SS = ParsingPipelines::from_file(pipeline_as_file, pipeline_vars, NULL);
        if (SS == NULL) Errors::fatal("pipeline could not be parsed");
    } else if (pipeline_as_text) {
        SS = ParsingPipelines::from_text(pipeline_as_text, pipeline_vars);
        if (SS == NULL) Errors::fatal("pipeline could not be parsed");
    } else if (output_file) {
        SS = ParsingPipelines::from_text(I"read <- *in, generate -> *out", pipeline_vars);
        if (SS == NULL) Errors::fatal("pipeline could not be parsed");
    } else if (LinkedLists::len(inter_file_list) > 0) {
        SS = ParsingPipelines::from_text(I"read <- *in", pipeline_vars);
        if (SS == NULL) Errors::fatal("pipeline could not be parsed");
    }

§2.5. Shut down the modules2.5 =

    BytecodeModule::end();
    BuildingModule::end();
    PipelineModule::end();
    FinalModule::end();
    ArchModule::end();
    IndexModule::end();
    Foundation::end();  must be ended last

§2.6. Command line.

define PROGRAM_NAME "inter"
enum PIPELINE_CLSW
enum PIPELINE_FILE_CLSW
enum PIPELINE_VARIABLE_CLSW
enum DOMAIN_CLSW
enum ARCHITECTURE_CLSW
enum BUILD_KIT_CLSW
enum INTERNAL_CLSW
enum CONSTRUCTS_CLSW
enum ANNOTATIONS_CLSW
enum PRIMITIVES_CLSW
enum FORMAT_CLSW
enum O_CLSW
enum TRACE_CLSW

Read the command line2.6 =

    CommandLine::declare_heading(
        U"[[Purpose]]\n\n"
        U"usage: inter file1 file2 ... [options]\n");

    CommandLine::declare_switch(O_CLSW, U"o", 2,
        U"code-generate to file X");
    CommandLine::declare_textual_switch(FORMAT_CLSW, U"format", 1,
        U"code-generate -o output to format X (default is Text)");
    CommandLine::declare_switch(PIPELINE_CLSW, U"pipeline-text", 2,
        U"specify pipeline textually, with X being a comma-separated list of stages");
    CommandLine::declare_switch(PIPELINE_FILE_CLSW, U"pipeline-file", 2,
        U"specify pipeline as file X");
    CommandLine::declare_switch(PIPELINE_VARIABLE_CLSW, U"variable", 2,
        U"set pipeline variable X (in form name=value)");
    CommandLine::declare_switch(DOMAIN_CLSW, U"domain", 2,
        U"specify folder to read/write inter files from/to");
    CommandLine::declare_switch(INTERNAL_CLSW, U"internal", 2,
        U"specify folder of internal Inform resources");
    CommandLine::declare_switch(ARCHITECTURE_CLSW, U"architecture", 2,
        U"generate Inter with architecture X");
    CommandLine::declare_switch(BUILD_KIT_CLSW, U"build-kit", 2,
        U"build Inter kit X for the current architecture");
    CommandLine::declare_switch(CONSTRUCTS_CLSW, U"constructs", 1,
        U"print out table of all constructs in the Inter language");
    CommandLine::declare_switch(ANNOTATIONS_CLSW, U"annotations", 1,
        U"print out table of all symbol annotations in the Inter language");
    CommandLine::declare_switch(PRIMITIVES_CLSW, U"primitives", 1,
        U"print out table of all primitive invocations in the Inter language");
    CommandLine::declare_boolean_switch(TRACE_CLSW, U"trace", 1,
        U"print out all pipeline steps as they are followed", FALSE);

    CommandLine::read(argc, argv, NULL, &Main::respond, &Main::add_file);

    path_to_inter = Pathnames::installation_path("INTER_PATH", I"inter");

§3.

void Main::respond(int id, int val, text_stream *arg, void *state) {
    switch (id) {
        case O_CLSW: output_file = Filenames::from_text(arg); break;
        case FORMAT_CLSW: output_format = Str::duplicate(arg); break;
        case PIPELINE_CLSW: pipeline_as_text = Str::duplicate(arg); break;
        case PIPELINE_FILE_CLSW: pipeline_as_file = Filenames::from_text(arg); break;
        case PIPELINE_VARIABLE_CLSW: Add a pipeline variable to the dictionary3.1; break;
        case DOMAIN_CLSW: domain_path = Pathnames::from_text(arg); break;
        case BUILD_KIT_CLSW: kit_to_build = Pathnames::from_text(arg); break;
        case INTERNAL_CLSW: internal_path = Pathnames::from_text(arg); break;
        case ARCHITECTURE_CLSW:
            if (PipelineModule::set_architecture(arg) == FALSE)
                Errors::fatal("no such -architecture");
            break;
        case CONSTRUCTS_CLSW:  InterInstruction::show_constructs(STDOUT); break;
        case ANNOTATIONS_CLSW: SymbolAnnotation::show_annotations(STDOUT); break;
        case PRIMITIVES_CLSW:  Primitives::show_primitives(STDOUT); break;
        case TRACE_CLSW: tracing = (val)?TRUE:FALSE; break;
    }
}

§3.1. Add a pipeline variable to the dictionary3.1 =

    match_results mr = Regexp::create_mr();
    if (Regexp::match(&mr, arg, U"(%c+)=(%c+)")) {
        if (Str::get_first_char(arg) != '*') {
            Errors::fatal("-variable names must begin with '*'");
        } else {
            Main::add_pipeline_variable(mr.exp[0], mr.exp[1]);
        }
    } else {
        Errors::fatal("-variable should take the form 'name=value'");
    }
    Regexp::dispose_of(&mr);

§4.

void Main::add_file(int id, text_stream *arg, void *state) {
    filename *F = Filenames::from_text(arg);
    ADD_TO_LINKED_LIST(F, filename, inter_file_list);
}

§5. The modules included in inter make use of the Inform 7 module kinds, but when we are using inter on its own, kinds have no meaning for us. We are required to create a kind type, in order for kinds to compile; but no instances of this kind will ever in fact exist. K_value is a global constant meaning "any kind at all", and that also must exist.

typedef void kind;
kind *K_value = NULL;

§6. This is where the html module can find CSS files and similar resources:

define INSTALLED_FILES_HTML_CALLBACK Main::internal_path
pathname *Main::internal_path(void) {
    return internal_path;
}