Basic support for languages to recognise structure and function declarations.

§1. For each typedef struct we find, we will make one of these:

typedef struct language_type {
    struct text_stream *structure_name;
    int tangled;  whether the structure definition has been tangled out
    struct source_line *structure_header_at;  opening line of typedef
    struct source_line *typedef_ends;  closing line, where } appears
    struct linked_list *incorporates;  of language_type
    struct linked_list *elements;  of structure_element
    struct language_type *next_cst_alphabetically;
} language_type;


language_type *first_cst_alphabetically = NULL;

language_type *Functions::new_struct(web *W, text_stream *name, source_line *L) {
    language_type *str = CREATE(language_type);
    Initialise the language type structure2.1;
    Analyser::mark_reserved_word_at_line(L, str->structure_name, RESERVED_COLOUR);
    Add this to the lists for its web and its paragraph2.2;
    Insertion-sort this into the alphabetical list of all structures found2.3;
    return str;

§2.1. Initialise the language type structure2.1 =

    str->structure_name = Str::duplicate(name);
    str->structure_header_at = L;
    str->tangled = FALSE;
    str->typedef_ends = NULL;
    str->incorporates = NEW_LINKED_LIST(language_type);
    str->elements = NEW_LINKED_LIST(structure_element);

§2.2. Add this to the lists for its web and its paragraph2.2 =

    Tags::add_by_name(L->owning_paragraph, I"Structures");
    ADD_TO_LINKED_LIST(str, language_type, W->language_types);
    ADD_TO_LINKED_LIST(str, language_type, L->owning_paragraph->structures);

§2.3. Insertion-sort this into the alphabetical list of all structures found2.3 =

    str->next_cst_alphabetically = NULL;
    if (first_cst_alphabetically == NULL) first_cst_alphabetically = str;
    else {
        int placed = FALSE;
        language_type *last = NULL;
        for (language_type *seq = first_cst_alphabetically; seq;
            seq = seq->next_cst_alphabetically) {
            if (Str::cmp(str->structure_name, seq->structure_name) < 0) {
                if (seq == first_cst_alphabetically) {
                    str->next_cst_alphabetically = first_cst_alphabetically;
                    first_cst_alphabetically = str;
                } else {
                    last->next_cst_alphabetically = str;
                    str->next_cst_alphabetically = seq;
                placed = TRUE;
            last = seq;
        if (placed == FALSE) last->next_cst_alphabetically = str;

§3. A language can also create an instance of structure_element to record the existence of the element val, and add it to the linked list of elements of the structure being defined.

In InC, only, certain element names used often in Inform's source code are given mildly special treatment. This doesn't amount to much. allow_sharing has no effect on tangling, so it doesn't change the program. It simply affects the reports in the woven code about where structures are used.

typedef struct structure_element {
    struct text_stream *element_name;
    struct source_line *element_created_at;
    int allow_sharing;
} structure_element;


structure_element *Functions::new_element(language_type *str, text_stream *elname,
    source_line *L) {
    Analyser::mark_reserved_word_at_line(L, elname, ELEMENT_COLOUR);
    structure_element *elt = CREATE(structure_element);
    elt->element_name = Str::duplicate(elname);
    elt->allow_sharing = FALSE;
    elt->element_created_at = L;
    if (LanguageMethods::share_element(L->owning_section->sect_language, elname))
        elt->allow_sharing = TRUE;
    ADD_TO_LINKED_LIST(elt, structure_element, str->elements);
    return elt;


language_type *Functions::find_structure(web *W, text_stream *name) {
    language_type *str;
    LOOP_OVER_LINKED_LIST(str, language_type, W->language_types)
        if (Str::eq(name, str->structure_name))
            return str;
    return NULL;

§6. Functions. Each function definition found results in one of these structures being made:

typedef struct language_function {
    struct text_stream *function_name;  e.g., "cultivate"
    struct text_stream *function_type;  e.g., "tree *"
    struct text_stream *function_arguments;  e.g., "int rainfall)": note )
    struct source_line *function_header_at;  where the first line of the header begins
    int within_namespace;  written using InC namespace dividers
    int called_from_other_sections;
    int call_freely;
    int usage_described;
    int no_conditionals;
    struct source_line *within_conditionals[MAX_CONDITIONAL_COMPILATION_STACK];
} language_function;


language_function *Functions::new_function(text_stream *fname, source_line *L) {
    Check the function is not a duplicate definition within the same paragraph7.2;
    hash_table_entry *hte =
        Analyser::mark_reserved_word_at_line(L, fname, FUNCTION_COLOUR);
    language_function *fn = CREATE(language_function);
    hte->as_function = fn;
    Initialise the function structure7.1;
    Add the function to its paragraph and line7.3;
    if (L->owning_section->sect_language->supports_namespaces)
        Check that the function has its namespace correctly declared7.4;
    return fn;

§7.1. Note that we take a snapshot of the conditional compilation stack as part of the function structure. We'll need it when predeclaring the function.

Initialise the function structure7.1 =

    fn->function_name = Str::duplicate(fname);
    fn->function_arguments = Str::new();
    fn->function_type = Str::new();
    fn->within_namespace = FALSE;
    fn->called_from_other_sections = FALSE;
    fn->call_freely = FALSE;
    fn->function_header_at = L;
    fn->usage_described = FALSE;
    if ((Str::eq_wide_string(fname, U"main")) &&
        fn->usage_described = TRUE;
    fn->no_conditionals = 0;

§7.2. The following would become inefficient if there were enormous numbers of functions defined in the same paragraph, but for now the overhead of creating a dictionary with hash-lookup seems greater than the plausible saving of time. The point of this check is to handle situations where the code in the web is offering alternative definitions of the same function, within some form of conditional compilation preprocessing — if this, define f as this; otherwise, define f as that — which can otherwise be read as two declarations of f, leading to spurious extra text at the weaving stage.

Check the function is not a duplicate definition within the same paragraph7.2 =

    paragraph *P = L->owning_paragraph;
    language_function *fn;
    LOOP_OVER_LINKED_LIST(fn, language_function, P->functions)
        if (Str::eq(fname, fn->function_name))
            return fn;

§7.3. Add the function to its paragraph and line7.3 =

    paragraph *P = L->owning_paragraph;
    if (P) ADD_TO_LINKED_LIST(fn, language_function, P->functions);
    L->function_defined = fn;

§7.4. Check that the function has its namespace correctly declared7.4 =

    text_stream *declared_namespace = NULL;
    text_stream *ambient_namespace = L->owning_section->sect_namespace;
    match_results mr = Regexp::create_mr();
    if (Regexp::match(&mr, fname, U"(%c+::)%c*")) {
        declared_namespace = mr.exp[0];
        fn->within_namespace = TRUE;
    } else if ((Str::eq_wide_string(fname, U"main")) &&
        (Str::eq_wide_string(ambient_namespace, U"Main::")))
        declared_namespace = I"Main::";
    if ((Str::ne(declared_namespace, ambient_namespace)) &&
        (L->owning_paragraph->placed_very_early == FALSE)) {
        if (Str::len(declared_namespace) == 0)
            WRITE_TO(err_mess, "Function '%S' should have namespace prefix '%S'",
                fname, ambient_namespace);
        else if (Str::len(ambient_namespace) == 0)
            WRITE_TO(err_mess, "Function '%S' declared in a section with no namespace",
            WRITE_TO(err_mess, "Function '%S' declared in a section with the wrong namespace '%S'",
                fname, ambient_namespace);
        Main::error_in_web(err_mess, L);

§8. "Elsewhere" here means "in a paragraph of code other than the one in which the function's definition appears".

int Functions::used_elsewhere(language_function *fn) {
    paragraph *P = fn->function_header_at->owning_paragraph;
    hash_table_entry *hte =
            fn->function_name, FALSE);
    hash_table_entry_usage *hteu = NULL;
    LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages)
        if ((P != hteu->usage_recorded_at) &&
            (P->under_section == hteu->usage_recorded_at->under_section))
            return TRUE;
    LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages)
        if (P->under_section != hteu->usage_recorded_at->under_section)
            return TRUE;
    return FALSE;

§9. Cataloguing. This implements the additional information in the -structures and -functions forms of section catalogue.

void Functions::catalogue(section *S, int functions_too) {
    language_type *str;
    LOOP_OVER(str, language_type)
        if (str->structure_header_at->owning_section == S)
            PRINT(" %S ", str->structure_name);
    if (functions_too) {
        language_function *fn;
        LOOP_OVER(fn, language_function)
            if (fn->function_header_at->owning_section == S)
                PRINT("\n                     %S", fn->function_name);