diff --git a/Chapter 1/Basics.w b/Chapter 1/Basics.w index da3fe8b..4aed58a 100755 --- a/Chapter 1/Basics.w +++ b/Chapter 1/Basics.w @@ -30,7 +30,8 @@ see //foundation: A Brief Guide to Foundation//. @e macro_usage_CLASS @e makefile_macro_CLASS @e makefile_macro_parameter_CLASS -@e makefile_macro_playback_CLASS +@e makefile_variable_CLASS +@e makefile_variable_set_CLASS @e nonterminal_variable_CLASS @e para_macro_CLASS @e paragraph_CLASS @@ -123,7 +124,8 @@ DECLARE_CLASS(macro_usage) DECLARE_CLASS(macro) DECLARE_CLASS(makefile_macro) DECLARE_CLASS(makefile_macro_parameter) -DECLARE_CLASS(makefile_macro_playback) +DECLARE_CLASS(makefile_variable) +DECLARE_CLASS(makefile_variable_set) DECLARE_CLASS(nonterminal_variable) DECLARE_CLASS(para_macro) DECLARE_CLASS(paragraph_tagging) diff --git a/Chapter 1/Program Control.w b/Chapter 1/Program Control.w index 834f198..0469b32 100644 --- a/Chapter 1/Program Control.w +++ b/Chapter 1/Program Control.w @@ -153,7 +153,7 @@ void Main::follow_instructions(inweb_instructions *ins) { @ But otherwise we do something with the given web: @ = - Reader::print_web_statistics(W); + if (ins->inweb_mode != ANALYSE_MODE) Reader::print_web_statistics(W); if (ins->inweb_mode == ANALYSE_MODE) @; if (ins->inweb_mode == TANGLE_MODE) @; if (ins->inweb_mode == WEAVE_MODE) @; diff --git a/Chapter 6/Makefiles.w b/Chapter 6/Makefiles.w index 5804eac..cc92b77 100644 --- a/Chapter 6/Makefiles.w +++ b/Chapter 6/Makefiles.w @@ -2,450 +2,232 @@ Constructing a suitable makefile for a simple inweb project. -@ This section offers just one function, which constructs a makefile by -following a "prototype". +@h Introduction. +At some point, the material in this section will probably be spun out into an +an independent tool called "inmake". It's a simple utility for constructing makefiles, +but has gradually become less simple over time, as is the way of these things. -@d MAX_MAKEFILE_MACRO_PARAMETERS 8 -@d MAX_MAKEFILE_MACRO_LINES 128 +The idea is simple enough: the user writes a "makescript", which is really a +makefile but with the possibility of using some higher-level features, and we +translate that it into an actual makefile (which is usually longer and less +easy to read). + +@h State. + +@d MAX_MAKEFILE_REPEAT_DEPTH 8 = -typedef struct makefile_macro { - struct text_stream *identifier; - struct makefile_macro_parameter *parameters[MAX_MAKEFILE_MACRO_PARAMETERS]; - int no_parameters; - struct text_stream *lines[MAX_MAKEFILE_MACRO_LINES]; - int no_lines; - CLASS_DEFINITION -} makefile_macro; - -typedef struct makefile_macro_parameter { - struct text_stream *name; - struct text_stream *definition_token; - int optional; - CLASS_DEFINITION -} makefile_macro_parameter; - -typedef struct makefile_macro_playback { - struct makefile_macro *which; - struct text_stream *parameter_values[MAX_MAKEFILE_MACRO_PARAMETERS]; - int line_position; - struct makefile_macro_playback *prior_to_this; - struct text_stream *text_to_follow; - CLASS_DEFINITION -} makefile_macro_playback; +typedef struct makefile_repeat { + int repeat_scope; /* during a repeat, either |MAKEFILE_TOOL_MOM| or |MAKEFILE_MODULE_MOM| */ + struct text_stream *repeat_tag; + struct text_stream *repeat_with; + struct text_stream *repeat_in; + int repeat_is_block; + struct text_stream *repeat_saved_dest; +} makefile_repeat; typedef struct makefile_state { struct web *for_web; - struct text_stream to_makefile; - struct text_stream *repeat_block; /* a "repeatblock" body being scanned */ + struct text_stream *dest; struct makefile_macro *defining; /* a "define" body being scanned */ - struct makefile_macro_playback *playing_back; - int inside_block; /* scanning a "repeatblock" into that text? */ + int repeat_sp; + int shadow_sp; + struct makefile_repeat repeat_data[MAX_MAKEFILE_REPEAT_DEPTH]; + int suppress_newline; /* at the end of this line */ int last_line_was_blank; /* used to suppress runs of multiple blank lines */ - int allow_commands; /* permit the prototype to use special commands */ - int repeat_scope; /* during a repeat, either |MAKEFILE_TOOL_MOM| or |MAKEFILE_MODULE_MOM| */ - struct text_stream *repeat_tag; struct dictionary *tools_dictionary; struct dictionary *webs_dictionary; struct dictionary *modules_dictionary; struct module_search *search_path; + struct makefile_variable_set *global_variables; + struct makefile_variable_set *stack_frame; + struct linked_list *known_macros; /* of |makefile_macro| */ } makefile_state; void Makefiles::write(web *W, filename *prototype, filename *F, module_search *I) { + struct text_stream makefile; + if (STREAM_OPEN_TO_FILE(&makefile, F, ISO_ENC) == FALSE) + Errors::fatal_with_file("unable to write tangled file", F); + WRITE_TO(STDOUT, "(Read script from %f)\n", prototype); + text_stream *OUT = &makefile; + makefile_state MS; + MS.dest = OUT; MS.for_web = W; + MS.suppress_newline = FALSE; MS.last_line_was_blank = TRUE; - MS.repeat_block = Str::new(); MS.defining = NULL; - MS.playing_back = NULL; - MS.inside_block = FALSE; - MS.allow_commands = TRUE; + MS.repeat_sp = 0; + MS.shadow_sp = 0; MS.tools_dictionary = Dictionaries::new(16, FALSE); MS.webs_dictionary = Dictionaries::new(16, FALSE); MS.modules_dictionary = Dictionaries::new(16, FALSE); MS.search_path = I; - MS.repeat_scope = -1; - MS.repeat_tag = NULL; - text_stream *OUT = &(MS.to_makefile); - if (STREAM_OPEN_TO_FILE(OUT, F, ISO_ENC) == FALSE) - Errors::fatal_with_file("unable to write tangled file", F); + MS.global_variables = Makefiles::new_variable_set(NULL); + MS.stack_frame = MS.global_variables; + MS.known_macros = Makefiles::list_of_reserved_macros(); WRITE("# This makefile was automatically written by inweb -makefile\n"); WRITE("# and is not intended for human editing\n\n"); TextFiles::read(prototype, FALSE, "can't open prototype file", TRUE, Makefiles::scan_makefile_line, NULL, &MS); STREAM_CLOSE(OUT); - WRITE_TO(STDOUT, "Wrote makefile '%f' from script '%f'\n", F, prototype); } -@ = +@h Scanner. + += void Makefiles::scan_makefile_line(text_stream *line, text_file_position *tfp, void *X) { makefile_state *MS = (makefile_state *) X; - text_stream *OUT = &(MS->to_makefile); - match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, line, L" *#%c*")) { Regexp::dispose_of(&mr); return; } // Skip comment lines - if (MS->allow_commands) { - if (Regexp::match(&mr, line, L" *{define: *(%C+) (%c*)} *")) @; - if (Regexp::match(&mr, line, L" *{end-define} *")) @; - if (MS->defining) @; - if (Regexp::match(&mr, line, L" *{repeat-tools-block:(%C*)} *")) - @; - if (Regexp::match(&mr, line, L" *{repeat-webs-block:(%C*)} *")) - @; - if (Regexp::match(&mr, line, L" *{repeat-modules-block:(%C*)} *")) - @; - if (Regexp::match(&mr, line, L" *{end-block} *")) @; - if (MS->inside_block) @; - - if (Regexp::match(&mr, line, L"(%c*){repeat-tools-span}(%c*?){end-span}(%c*)")) - @; - if (Regexp::match(&mr, line, L"(%c*){repeat-webs-span}(%c*?){end-span}(%c*)")) - @; - if (Regexp::match(&mr, line, L"(%c*){repeat-modules-span}(%c*?){end-span}(%c*)")) - @; - - if (Regexp::match(&mr, line, L" *{identity-settings} *")) @; - if (Regexp::match(&mr, line, L" *{platform-settings} *")) @; - - if (Regexp::match(&mr, line, L" *{tool} *(%C+) (%C+) (%c+) (%C+) *")) @; - if (Regexp::match(&mr, line, L" *{web} *(%C+) (%C+) (%c+) (%C+) *")) @; - if (Regexp::match(&mr, line, L" *{module} *(%C+) (%C+) (%c+) (%C+) *")) @; - - if (Regexp::match(&mr, line, L"(%c*?) *{dependent-files} *")) @; - if (Regexp::match(&mr, line, L"(%c*?) *{dependent-files-for-tool-alone} *(%C+)")) - @; - if (Regexp::match(&mr, line, L"(%c*?) *{dependent-files-for-tool-and-modules} *(%C+)")) - @; - if (Regexp::match(&mr, line, L"(%c*?) *{dependent-files-for-module} *(%C+)")) - @; - - if (Regexp::match(&mr, line, L"(%c*?) *{(%C+) *(%c+?)} *(%c*?)")) @; - } + if (Regexp::match(&mr, line, L" *{define: *(%C+) (%c*)} *")) @; + if (Regexp::match(&mr, line, L" *{end-define} *")) @; + if (MS->defining) @; Regexp::dispose_of(&mr); - @; + Makefiles::expand(line, tfp, MS); + + if (MS->suppress_newline == FALSE) { + text_stream *OUT = MS->dest; + if (Str::len(line) == 0) { + if (MS->last_line_was_blank == FALSE) WRITE("\n"); + MS->last_line_was_blank = TRUE; + } else { + MS->last_line_was_blank = FALSE; + WRITE("\n"); + } + } + MS->suppress_newline = FALSE; } @ = - if (MS->defining) Errors::in_text_file("nested definitions are not allowed", tfp); + if (MS->defining) + Errors::in_text_file("nested definitions are not allowed", tfp); text_stream *name = mr.exp[0]; text_stream *parameter_specification = mr.exp[1]; - makefile_macro *new_macro = CREATE(makefile_macro); - new_macro->identifier = Str::duplicate(name); - new_macro->no_parameters = 0; - new_macro->no_lines = 0; - - match_results mr2 = Regexp::create_mr(); - while (Regexp::match(&mr2, parameter_specification, L" *(%C+): *(%C+) *(%c*)")) { - if (new_macro->no_parameters >= MAX_MAKEFILE_MACRO_PARAMETERS) { - Errors::in_text_file("too many parameters in this definition", tfp); - break; - } - makefile_macro_parameter *new_parameter = CREATE(makefile_macro_parameter); - new_parameter->name = Str::duplicate(mr2.exp[0]); - new_parameter->definition_token = Str::duplicate(mr2.exp[1]); - new_parameter->optional = FALSE; - if (Str::get_first_char(new_parameter->name) == '?') { - new_parameter->optional = TRUE; - Str::delete_first_character(new_parameter->name); - } - new_macro->parameters[new_macro->no_parameters++] = new_parameter; - Str::clear(parameter_specification); - Str::copy(parameter_specification, mr2.exp[2]); - } - Regexp::dispose_of(&mr2); - if (Str::is_whitespace(parameter_specification) == FALSE) - Errors::in_text_file("parameter list for this definition is malformed", tfp); - - MS->defining = new_macro; + MS->defining = Makefiles::new_macro(MS->known_macros, name, parameter_specification, tfp); Regexp::dispose_of(&mr); return; @ = - if (MS->defining->no_lines >= MAX_MAKEFILE_MACRO_LINES) { - Errors::in_text_file("too many lines in this definition", tfp); - } else { - MS->defining->lines[MS->defining->no_lines++] = Str::duplicate(line); - } + Makefiles::add_line_to_macro(MS->defining, line, tfp); Regexp::dispose_of(&mr); return; @ = - if (MS->defining == NULL) Errors::in_text_file("{end-define} without {define: ...}", tfp); + if (MS->defining == NULL) + Errors::in_text_file("{end-define} without {define: ...}", tfp); MS->defining = NULL; Regexp::dispose_of(&mr); return; +@ = +void Makefiles::expand(text_stream *text, text_file_position *tfp, makefile_state *MS) { + TEMPORARY_TEXT(before_matter) + TEMPORARY_TEXT(braced_matter) + TEMPORARY_TEXT(after_matter) + int bl = 0, after_times = FALSE; + for (int i = 0; i < Str::len(text); i++) { + wchar_t c = Str::get_at(text, i); + if (after_times) PUT_TO(after_matter, c); + else if (c == '{') { + bl++; + if (bl > 1) PUT_TO(braced_matter, c); + } else if (c == '}') { + bl--; + if (bl == 0) after_times = TRUE; + else PUT_TO(braced_matter, c); + } else { + if (bl < 0) Errors::in_text_file("too many '}'s", tfp); + if (bl == 0) PUT_TO(before_matter, c); + else PUT_TO(braced_matter, c); + } + } + if (bl > 0) Errors::in_text_file("too many '{'s", tfp); + if (after_times) { + @; + } else { + WRITE_TO(MS->dest, "%S", text); + } + DISCARD_TEXT(before_matter) + DISCARD_TEXT(braced_matter) + DISCARD_TEXT(after_matter) +} + @ = - text_stream *before_matter = mr.exp[0]; - text_stream *identifier = mr.exp[1]; - text_stream *parameter_settings = mr.exp[2]; - text_stream *after_matter = mr.exp[3]; - - makefile_macro_playback *playback = CREATE(makefile_macro_playback); - playback->which = NULL; - makefile_macro *mm; - LOOP_OVER(mm, makefile_macro) - if (Str::eq(mm->identifier, identifier)) - playback->which = mm; - if (playback->which == NULL) { - Errors::in_text_file("unknown macro or command in braces", tfp); - Regexp::dispose_of(&mr); - return; + text_stream *identifier = braced_matter; + text_stream *parameter_settings = NULL; + match_results mr = Regexp::create_mr(); + if (Regexp::match(&mr, identifier, L"(%C+) (%c*)")) { + identifier = mr.exp[0]; + parameter_settings = mr.exp[1]; } - - for (int i=0; iparameter_values[i] = NULL; - - match_results mr2 = Regexp::create_mr(); - while (Regexp::match(&mr2, parameter_settings, L" *(%C+): *(%C+) *(%c*)")) { - text_stream *setting = mr2.exp[0]; - text_stream *value = mr2.exp[1]; - text_stream *remainder = mr2.exp[2]; - int found = FALSE; - for (int i=0; iwhich->no_parameters; i++) - if (Str::eq(setting, playback->which->parameters[i]->name)) { - found = TRUE; - playback->parameter_values[i] = Str::duplicate(value); - } - if (found == FALSE) Errors::in_text_file("unknown parameter in this macro", tfp); - Str::clear(parameter_settings); - Str::copy(parameter_settings, remainder); + if (Str::eq(identifier, I"repeat")) { + if (Str::is_whitespace(after_matter)) identifier = I"repeat-block"; + else identifier = I"repeat-span"; + } + if (Str::eq(identifier, I"end-repeat")) { + if ((MS->repeat_sp > 0) && (MS->repeat_data[MS->repeat_sp-1].repeat_is_block)) + identifier = I"end-block"; + else + identifier = I"end-span"; } - Regexp::dispose_of(&mr2); - if (Str::is_whitespace(parameter_settings) == FALSE) - Errors::in_text_file("parameter list for this macro is malformed", tfp); - - for (int i=0; iwhich->no_parameters; i++) - if (playback->parameter_values[i] == NULL) - if (playback->which->parameters[i]->optional == FALSE) - Errors::in_text_file("compulsory macro parameter not given", tfp); - playback->line_position = 0; - playback->prior_to_this = MS->playing_back; - playback->text_to_follow = NULL; - if (Str::is_whitespace(after_matter) == FALSE) - playback->text_to_follow = Str::duplicate(after_matter); - - MS->playing_back = playback; - WRITE("%S", before_matter); - for (int i=0; iwhich->no_lines; i++) { - TEMPORARY_TEXT(line) - text_stream *from = playback->which->lines[i]; - for (int j=0; jwhich->no_parameters; i++) - if (Str::eq(token, playback->which->parameters[i]->definition_token)) { - found = TRUE; - WRITE_TO(line, "%S", playback->parameter_values[i]); - } - if (found == FALSE) closed = FALSE; - } - DISCARD_TEXT(token) - if (closed == FALSE) { j = old_j; PUT_TO(line, '{'); } + if (Makefiles::acceptable_variable_name(identifier)) { + Makefiles::expand(before_matter, tfp, MS); + if (MS->repeat_sp > 0) { + WRITE_TO(MS->dest, "{%S}", identifier); + } else { + makefile_variable *var = Makefiles::find_variable_in(identifier, MS->stack_frame); + if (var) { + WRITE_TO(MS->dest, "%S", var->value); } else { - PUT_TO(line, Str::get_at(from, j)); + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown variable '%S'", identifier); + Errors::in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) } } - Makefiles::scan_makefile_line(line, tfp, (void *) MS); - DISCARD_TEXT(line) - } - MS->playing_back = playback->prior_to_this; - if (Str::is_whitespace(after_matter) == FALSE) - WRITE("%S\n", after_matter); - Regexp::dispose_of(&mr); - return; - -@ = - int marker = MAKEFILE_TOOL_MOM; - @; - -@ = - int marker = MAKEFILE_WEB_MOM; - @; - -@ = - int marker = MAKEFILE_MODULE_MOM; - @; - -@ = - if (MS->inside_block) Errors::in_text_file("nested repeat blocks are not allowed", tfp); - MS->inside_block = TRUE; - MS->repeat_scope = marker; - MS->repeat_tag = Str::duplicate(mr.exp[0]); - Str::clear(MS->repeat_block); - Regexp::dispose_of(&mr); - return; - -@ = - WRITE_TO(MS->repeat_block, "%S\n", line); - return; - -@ = - if (MS->inside_block == FALSE) - Errors::in_text_file("{endblock} without {repeatblock}", tfp); - MS->inside_block = FALSE; - Makefiles::repeat(OUT, NULL, TRUE, MS->repeat_block, TRUE, NULL, tfp, MS, MS->repeat_scope, MS->repeat_tag); - Str::clear(MS->repeat_block); - Regexp::dispose_of(&mr); - return; - -@ = - int marker = MAKEFILE_TOOL_MOM; - @; - -@ = - int marker = MAKEFILE_WEB_MOM; - @; - -@ = - int marker = MAKEFILE_MODULE_MOM; - @; - -@ = - WRITE("%S", mr.exp[0]); - Makefiles::repeat(OUT, I" ", FALSE, mr.exp[1], FALSE, NULL, tfp, MS, marker, I"all"); - WRITE("%S\n", mr.exp[2]); - MS->last_line_was_blank = FALSE; - Regexp::dispose_of(&mr); - return; - -@ = - filename *prototype = Filenames::in(path_to_inweb, I"platform-settings.mk"); - text_stream *INWEBPLATFORM = Str::new(); - TextFiles::read(prototype, FALSE, "can't open platform settings file", - TRUE, Makefiles::seek_INWEBPLATFORM, NULL, INWEBPLATFORM); - if (Str::len(INWEBPLATFORM) == 0) { - Errors::in_text_file( - "found platform settings file, but it does not set INWEBPLATFORM", tfp); + Makefiles::expand(after_matter, tfp, MS); } else { - pathname *P = Pathnames::down(path_to_inweb, I"Materials"); - P = Pathnames::down(P, I"platforms"); - WRITE_TO(INWEBPLATFORM, ".mkscript"); - filename *F = Filenames::in(P, INWEBPLATFORM); - TextFiles::read(F, FALSE, "can't open platform definitions file", - TRUE, Makefiles::scan_makefile_line, NULL, MS); - WRITE_TO(STDOUT, "(Read definitions from %f)\n", F); + makefile_macro *mm = Makefiles::find_macro(MS->known_macros, identifier); + if (mm == NULL) { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown macro '%S'", identifier); + Errors::in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } else { + if (mm->suppress_whitespace_when_expanding) { + while (Characters::is_whitespace(Str::get_last_char(before_matter))) + Str::delete_last_character(before_matter); + while (Characters::is_whitespace(Str::get_first_char(after_matter))) + Str::delete_first_character(after_matter); + } + Makefiles::expand(before_matter, tfp, MS); + int divert_if_repeating = TRUE; + if ((mm) && + ((mm->reserved_macro_meaning == REPEAT_BLOCK_RMM) || + (mm->reserved_macro_meaning == REPEAT_SPAN_RMM))) { + MS->shadow_sp++; + } + if ((mm) && + ((mm->reserved_macro_meaning == END_BLOCK_RMM) || + (mm->reserved_macro_meaning == END_SPAN_RMM))) { + MS->shadow_sp--; + if (MS->shadow_sp == 0) divert_if_repeating = FALSE; + } + + if ((divert_if_repeating) && (MS->repeat_sp > 0)) { + WRITE_TO(MS->dest, "{%S}", braced_matter); + } else { + Makefiles::expand_macro(MS, mm, parameter_settings, tfp); + if (mm->suppress_newline_after_expanding) MS->suppress_newline = TRUE; + } + Makefiles::expand(after_matter, tfp, MS); + } } Regexp::dispose_of(&mr); - return; - -@ = - WRITE("INWEB = "); Makefiles::pathname_slashed(OUT, path_to_inweb); WRITE("/Tangled/inweb\n"); - pathname *path_to_intest = Pathnames::down(Pathnames::up(path_to_inweb), I"intest"); - WRITE("INTEST = "); Makefiles::pathname_slashed(OUT, path_to_intest); WRITE("/Tangled/intest\n"); - if (MS->for_web) { - WRITE("MYNAME = %S\n", Pathnames::directory_name(MS->for_web->md->path_to_web)); - WRITE("ME = "); Makefiles::pathname_slashed(OUT, MS->for_web->md->path_to_web); - WRITE("\n"); - MS->last_line_was_blank = FALSE; - } - Regexp::dispose_of(&mr); - return; - -@ = - int marker = MAKEFILE_TOOL_MOM; - dictionary *D = MS->tools_dictionary; - @; - -@ = - int marker = MAKEFILE_WEB_MOM; - dictionary *D = MS->webs_dictionary; - @; - -@ = - int marker = MAKEFILE_MODULE_MOM; - dictionary *D = MS->modules_dictionary; - @; - -@ = - WRITE("%SLEAF = %S\n", mr.exp[0], mr.exp[1]); - WRITE("%SWEB = %S\n", mr.exp[0], mr.exp[2]); - WRITE("%SMAKER = $(%SWEB)/%S.mk\n", mr.exp[0], mr.exp[0], mr.exp[1]); - WRITE("%SX = $(%SWEB)/Tangled/%S\n", mr.exp[0], mr.exp[0], mr.exp[1]); - MS->last_line_was_blank = FALSE; - web_md *Wm = Reader::load_web_md(Pathnames::from_text(mr.exp[2]), NULL, MS->search_path, TRUE); - Wm->as_module->module_name = Str::duplicate(mr.exp[0]); - Wm->as_module->module_tag = Str::duplicate(mr.exp[3]); - Wm->as_module->origin_marker = marker; - Dictionaries::create(D, mr.exp[0]); - Dictionaries::write_value(D, mr.exp[0], Wm); - Regexp::dispose_of(&mr); - return; - -@ = - WRITE("%S", mr.exp[0]); - Makefiles::pattern(OUT, MS->for_web->md->sections_md, MS->for_web->md->contents_filename); - WRITE("\n"); - MS->last_line_was_blank = FALSE; - Regexp::dispose_of(&mr); - return; - -@ = - WRITE("%S", mr.exp[0]); - if (Dictionaries::find(MS->tools_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries::read_value(MS->tools_dictionary, mr.exp[1]); - Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename); - } else if (Dictionaries::find(MS->webs_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries::read_value(MS->webs_dictionary, mr.exp[1]); - Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename); - } else { - PRINT("Tool %S\n", mr.exp[0]); - Errors::in_text_file("unknown tool to find dependencies for", tfp); - } - WRITE("\n"); - MS->last_line_was_blank = FALSE; - Regexp::dispose_of(&mr); - return; - -@ = - WRITE("%S", mr.exp[0]); - if (Dictionaries::find(MS->tools_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries::read_value(MS->tools_dictionary, mr.exp[1]); - Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename); - } else if (Dictionaries::find(MS->webs_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries::read_value(MS->webs_dictionary, mr.exp[1]); - Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename); - } else { - PRINT("Tool %S\n", mr.exp[0]); - Errors::in_text_file("unknown tool to find dependencies for", tfp); - } - WRITE("\n"); - MS->last_line_was_blank = FALSE; - Regexp::dispose_of(&mr); - return; - -@ = - WRITE("%S", mr.exp[0]); - if (Dictionaries::find(MS->modules_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries::read_value(MS->modules_dictionary, mr.exp[1]); - Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename); - } else { - Errors::in_text_file("unknown module to find dependencies for", tfp); - WRITE_TO(STDERR, "-- module name: %S\n", mr.exp[1]); - } - WRITE("\n"); - MS->last_line_was_blank = FALSE; - Regexp::dispose_of(&mr); - return; - -@ = - if (Str::len(line) == 0) { - if (MS->last_line_was_blank == FALSE) WRITE("\n"); - MS->last_line_was_blank = TRUE; - } else { - MS->last_line_was_blank = FALSE; - WRITE("%S\n", line); - } @ = void Makefiles::pathname_slashed(OUTPUT_STREAM, pathname *P) { @@ -498,38 +280,57 @@ void Makefiles::pattern(OUTPUT_STREAM, linked_list *L, filename *F) { @ And finally, the following handles repetitions both of blocks and of spans: = -void Makefiles::repeat(OUTPUT_STREAM, text_stream *prefix, int every_time, text_stream *matter, - int as_lines, text_stream *suffix, text_file_position *tfp, makefile_state *MS, int over, text_stream *tag) { - module *M; - int c = 0; - LOOP_OVER(M, module) { - if ((M->origin_marker == over) && - ((Str::eq(tag, I"all")) || (Str::eq(tag, M->module_tag)))) { - if ((prefix) && ((c++ > 0) || (every_time))) WRITE("%S", prefix); - if (matter) { - TEMPORARY_TEXT(line) - LOOP_THROUGH_TEXT(pos, matter) { - if (Str::get(pos) == '\n') { - if (as_lines) { - Makefiles::scan_makefile_line(line, tfp, (void *) MS); - Str::clear(line); - } - } else { - if (Str::get(pos) == '@') { - WRITE_TO(line, "%S", M->module_name); - } else { - PUT_TO(line, Str::get(pos)); - } - } - } - if (!as_lines) WRITE("%S", line); - DISCARD_TEXT(line) +void Makefiles::repeat(text_stream *matter, + int as_lines, text_file_position *tfp, makefile_repeat *rep, makefile_state *MS) { + int over = rep->repeat_scope; + text_stream *tag = rep->repeat_tag; + MS->stack_frame = Makefiles::new_variable_set(MS->stack_frame); + text_stream *loop_var_name = I"NAME"; + if (Str::len(rep->repeat_with) > 0) loop_var_name = rep->repeat_with; + makefile_variable *loop_var = Makefiles::ensure_variable(loop_var_name, MS->stack_frame); + if (Str::len(rep->repeat_in) > 0) { + match_results mr = Regexp::create_mr(); + while (Regexp::match(&mr, rep->repeat_in, L"(%c*?),(%c*)")) { + text_stream *value = mr.exp[0]; + Str::trim_white_space(value); + @; + Str::clear(rep->repeat_in); + Str::copy(rep->repeat_in, mr.exp[1]); + } + Regexp::dispose_of(&mr); + text_stream *value = rep->repeat_in; + Str::trim_white_space(value); + @; + } else { + module *M; + LOOP_OVER(M, module) { + if ((M->origin_marker == over) && + ((Str::eq(tag, I"all")) || (Str::eq(tag, M->module_tag)))) { + text_stream *value = M->module_name; + @; } - if (suffix) WRITE("%S", suffix); } } + MS->stack_frame = MS->stack_frame->outer; } +@ = + loop_var->value = value; + if (as_lines) { + TEMPORARY_TEXT(line) + LOOP_THROUGH_TEXT(pos, matter) { + if (Str::get(pos) == '\n') { + Makefiles::scan_makefile_line(line, tfp, (void *) MS); + Str::clear(line); + } else { + PUT_TO(line, Str::get(pos)); + } + } + DISCARD_TEXT(line) + } else { + Makefiles::expand(matter, tfp, MS); + } + @ This is used to scan the platform settings file for a definition line in the shape INWEBPLATFORM = PLATFORM, in order to find out what PLATFORM the make file will be used on. @@ -541,3 +342,502 @@ void Makefiles::seek_INWEBPLATFORM(text_stream *line, text_file_position *tfp, v if (Regexp::match(&mr, line, L" *INWEBPLATFORM = (%C+) *")) WRITE("%S", mr.exp[0]); Regexp::dispose_of(&mr); } + +@h Variables. + += +typedef struct makefile_variable { + struct text_stream *name; + struct text_stream *value; + CLASS_DEFINITION +} makefile_variable; + +typedef struct makefile_variable_set { + struct linked_list *variables; /* of |makefile_variable| */ + struct makefile_variable_set *outer; + CLASS_DEFINITION +} makefile_variable_set; + +makefile_variable_set *Makefiles::new_variable_set(makefile_variable_set *outer) { + makefile_variable_set *set = CREATE(makefile_variable_set); + set->variables = NEW_LINKED_LIST(makefile_variable); + set->outer = outer; + return set; +} + +makefile_variable *Makefiles::find_variable_in_one(text_stream *name, makefile_variable_set *set) { + if (set == NULL) return NULL; + makefile_variable *var; + LOOP_OVER_LINKED_LIST(var, makefile_variable, set->variables) + if (Str::eq(name, var->name)) + return var; + return NULL; +} + +makefile_variable *Makefiles::find_variable_in(text_stream *name, makefile_variable_set *set) { + while (set) { + makefile_variable *var = Makefiles::find_variable_in_one(name, set); + if (var) return var; + set = set->outer; + } + return NULL; +} + +makefile_variable *Makefiles::ensure_variable(text_stream *name, makefile_variable_set *in_set) { + if (in_set == NULL) internal_error("variable without set"); + makefile_variable *var = Makefiles::find_variable_in_one(name, in_set); + if (var == NULL) { + var = CREATE(makefile_variable); + var->name = Str::duplicate(name); + var->value = I""; + ADD_TO_LINKED_LIST(var, makefile_variable, in_set->variables); + } + return var; +} + +int Makefiles::acceptable_variable_name(text_stream *name) { + LOOP_THROUGH_TEXT(pos, name) { + wchar_t c = Str::get(pos); + if ((c >= '0') && (c <= '9')) continue; + if ((c >= 'A') && (c <= 'Z')) continue; + if (c == '_') continue; + return FALSE; + } + return TRUE; +} + +@h Macros. +A typical macro definition looks like this: += (text) +{define: link to: TO from: FROM ?options: OPTS} + clang $(CCOPTS) -g -o {TO} {FROM} {OPTS} +{end-define} += +And here is a usage of it: += (text) + {link from: frog.o to: frog.c} += +This doesn't specify "options: ...", but doesn't have to, because that's optional -- +note the question mark in the macro declaration. But it does specify "from: ..." +and "to: ...", which are compulsory. Parameters are always named, as this example +suggests, and can be given in any order so long as all the non-optional ones are +present. + +This usage results in the following line in the final makefile: += (text) + clang $(CCOPTS) -g -o frog.c frog.o += +Note the difference between |$(CCOPTS)|, which is a make variable, and the braced +tokens |{TO}|, |{FROM}| and |{OPTS}|, which are makescript variables. In makescripts, +the only material treated as special is material in braces |{...}|. + +@ The above definition has three parameters, one optional, but only one line. There +are (for now, anyway) hard but harmlessly large limits on the number of these: + +@d MAX_MAKEFILE_MACRO_PARAMETERS 8 +@d MAX_MAKEFILE_MACRO_LINES 128 + += +typedef struct makefile_macro { + struct text_stream *identifier; + struct makefile_macro_parameter *parameters[MAX_MAKEFILE_MACRO_PARAMETERS]; + int no_parameters; + struct text_stream *lines[MAX_MAKEFILE_MACRO_LINES]; + int no_lines; + int reserved_macro_meaning; + int suppress_newline_after_expanding; + int suppress_whitespace_when_expanding; + CLASS_DEFINITION +} makefile_macro; + +typedef struct makefile_macro_parameter { + struct text_stream *name; + struct text_stream *definition_token; + int optional; + CLASS_DEFINITION +} makefile_macro_parameter; + +@ New macro declaration lines are processed here, and added to a list |L| of +valid macros: + += +makefile_macro *Makefiles::new_macro(linked_list *L, text_stream *name, + text_stream *parameter_specification, text_file_position *tfp) { + if (Makefiles::find_macro(L, name)) + Errors::in_text_file("a macro with this name already exists", tfp); + + makefile_macro *new_macro = CREATE(makefile_macro); + new_macro->identifier = Str::duplicate(name); + new_macro->no_parameters = 0; + new_macro->no_lines = 0; + new_macro->reserved_macro_meaning = UNRESERVED_RMM; + new_macro->suppress_newline_after_expanding = TRUE; + new_macro->suppress_whitespace_when_expanding = TRUE; + + match_results mr2 = Regexp::create_mr(); + while (Regexp::match(&mr2, parameter_specification, L" *(%C+): *(%C+) *(%c*)")) { + text_stream *par_name = mr2.exp[0]; + text_stream *token_name = mr2.exp[1]; + Str::clear(parameter_specification); + Str::copy(parameter_specification, mr2.exp[2]); + if (new_macro->no_parameters >= MAX_MAKEFILE_MACRO_PARAMETERS) { + Errors::in_text_file("too many parameters in this definition", tfp); + } else { + @; + } + } + Regexp::dispose_of(&mr2); + if (Str::is_whitespace(parameter_specification) == FALSE) + Errors::in_text_file("parameter list for this definition is malformed", tfp); + ADD_TO_LINKED_LIST(new_macro, makefile_macro, L); + return new_macro; +} + +@ = + makefile_macro_parameter *new_parameter = CREATE(makefile_macro_parameter); + new_parameter->name = Str::duplicate(par_name); + new_parameter->definition_token = Str::duplicate(token_name); + new_parameter->optional = FALSE; + if (Str::get_first_char(new_parameter->name) == '?') { + new_parameter->optional = TRUE; + Str::delete_first_character(new_parameter->name); + } + new_macro->parameters[new_macro->no_parameters++] = new_parameter; + +@ We can then add lines to the definition: + += +void Makefiles::add_line_to_macro(makefile_macro *mm, text_stream *line, text_file_position *tfp) { + if (mm->no_lines >= MAX_MAKEFILE_MACRO_LINES) { + Errors::in_text_file("too many lines in this definition", tfp); + } else { + mm->lines[mm->no_lines++] = Str::duplicate(line); + } +} + +@ A few macros are "reserved", that is, have built-in meanings and are not +declared by any makescript but by us. (These have no lines, only parameters.) + +@e UNRESERVED_RMM from 0 +@e PLATFORM_SETTINGS_RMM +@e IDENTITY_SETTINGS_RMM +@e COMPONENT_RMM +@e DEPENDENT_FILES_RMM +@e REPEAT_BLOCK_RMM +@e END_BLOCK_RMM +@e REPEAT_SPAN_RMM +@e END_SPAN_RMM +@e SET_RMM + += +linked_list *Makefiles::list_of_reserved_macros(void) { + linked_list *L = NEW_LINKED_LIST(makefile_macro); + Makefiles::reserve_macro(L, I"platform-settings", NULL, PLATFORM_SETTINGS_RMM); + Makefiles::reserve_macro(L, I"identity-settings", NULL, IDENTITY_SETTINGS_RMM); + Makefiles::reserve_macro(L, I"component", + I"symbol: SYMBOL webname: WEBNAME path: PATH set: SET category: CATEGORY", + COMPONENT_RMM); + Makefiles::reserve_macro(L, I"dependent-files", + I"?tool: TOOL ?module: MODULES ?tool-and-modules: BOTH", + DEPENDENT_FILES_RMM); + Makefiles::reserve_macro(L, I"repeat-block", + I"?over: CATEGORY ?set: SET ?with: WITH ?in: IN", REPEAT_BLOCK_RMM); + Makefiles::reserve_macro(L, I"end-block", NULL, END_BLOCK_RMM); + Makefiles::reserve_span_macro(L, I"repeat-span", + I"?over: CATEGORY ?set: SET ?with: WITH ?in: IN", REPEAT_SPAN_RMM); + Makefiles::reserve_span_macro(L, I"end-span", NULL, END_SPAN_RMM); + Makefiles::reserve_span_macro(L, I"set", + I"name: NAME value: VALUE", SET_RMM); + return L; +} + +void Makefiles::reserve_macro(linked_list *L, text_stream *name, + text_stream *parameter_specification, int rmm) { + makefile_macro *reserved = Makefiles::new_macro(L, name, + Str::duplicate(parameter_specification), NULL); + reserved->reserved_macro_meaning = rmm; +} + +void Makefiles::reserve_span_macro(linked_list *L, text_stream *name, + text_stream *parameter_specification, int rmm) { + makefile_macro *reserved = Makefiles::new_macro(L, name, + Str::duplicate(parameter_specification), NULL); + reserved->reserved_macro_meaning = rmm; + reserved->suppress_newline_after_expanding = FALSE; + reserved->suppress_whitespace_when_expanding = FALSE; +} + +@ Finding a macro in a list. (We could use a dictionary for efficiency, but really, +it's unlikely there are ever more than a few macros.) + += +makefile_macro *Makefiles::find_macro(linked_list *L, text_stream *name) { + makefile_macro *mm; + LOOP_OVER_LINKED_LIST(mm, makefile_macro, L) + if (Str::eq(mm->identifier, name)) + return mm; + return NULL; +} + +@ Expanding a macro is the main event, then: + += +void Makefiles::expand_macro(makefile_state *MS, makefile_macro *mm, + text_stream *parameter_settings, text_file_position *tfp) { + text_stream *OUT = MS->dest; + text_stream *parameter_values[MAX_MAKEFILE_MACRO_PARAMETERS]; + for (int i=0; ino_parameters; i++) + if (Str::eq(setting, mm->parameters[i]->name)) { + found = TRUE; + parameter_values[i] = Str::new(); + text_stream *saved = MS->dest; + MS->dest = parameter_values[i]; + Makefiles::expand(value, tfp, MS); + MS->dest = saved; + } + if (found == FALSE) { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown parameter '%S'", setting); + Errors::in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + Str::clear(parameter_settings); + Str::copy(parameter_settings, remainder); + Regexp::dispose_of(&mr3); + } + Regexp::dispose_of(&mr2); + if (Str::is_whitespace(parameter_settings) == FALSE) + Errors::in_text_file("parameter list is malformed", tfp); + + for (int i=0; ino_parameters; i++) + if (parameter_values[i] == NULL) + if (mm->parameters[i]->optional == FALSE) { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "compulsory parameter '%S' not given", mm->parameters[i]->name); + Errors::in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + + switch (mm->reserved_macro_meaning) { + case UNRESERVED_RMM: @; break; + case PLATFORM_SETTINGS_RMM: @; break; + case IDENTITY_SETTINGS_RMM: @; break; + case COMPONENT_RMM: @; break; + case DEPENDENT_FILES_RMM: @; break; + case REPEAT_BLOCK_RMM: @; break; + case END_BLOCK_RMM: @; break; + case REPEAT_SPAN_RMM: @; break; + case END_SPAN_RMM: @; break; + case SET_RMM: @; break; + default: internal_error("unimplemented reserved macro"); + } +} + +@ = + MS->stack_frame = Makefiles::new_variable_set(MS->stack_frame); + for (int i=0; ino_parameters; i++) { + makefile_variable *var = + Makefiles::ensure_variable(mm->parameters[i]->definition_token, MS->stack_frame); + var->value = parameter_values[i]; + } + for (int i=0; ino_lines; i++) + Makefiles::scan_makefile_line(mm->lines[i], tfp, (void *) MS); + MS->stack_frame = MS->stack_frame->outer; + +@ = + filename *prototype = Filenames::in(path_to_inweb, I"platform-settings.mk"); + text_stream *INWEBPLATFORM = Str::new(); + TextFiles::read(prototype, FALSE, "can't open platform settings file", + TRUE, Makefiles::seek_INWEBPLATFORM, NULL, INWEBPLATFORM); + if (Str::len(INWEBPLATFORM) == 0) { + Errors::in_text_file( + "found platform settings file, but it does not set INWEBPLATFORM", tfp); + } else { + pathname *P = Pathnames::down(path_to_inweb, I"Materials"); + P = Pathnames::down(P, I"platforms"); + WRITE_TO(INWEBPLATFORM, ".mkscript"); + filename *F = Filenames::in(P, INWEBPLATFORM); + TextFiles::read(F, FALSE, "can't open platform definitions file", + TRUE, Makefiles::scan_makefile_line, NULL, MS); + WRITE_TO(STDOUT, "(Read definitions file '%S' from ", INWEBPLATFORM); + Pathnames::to_text_relative(STDOUT, path_to_inweb, P); + WRITE_TO(STDOUT, ")\n"); + } + +@ = + WRITE("INWEB = "); Makefiles::pathname_slashed(OUT, path_to_inweb); WRITE("/Tangled/inweb\n"); + pathname *path_to_intest = Pathnames::down(Pathnames::up(path_to_inweb), I"intest"); + WRITE("INTEST = "); Makefiles::pathname_slashed(OUT, path_to_intest); WRITE("/Tangled/intest\n"); + if (MS->for_web) { + WRITE("MYNAME = %S\n", Pathnames::directory_name(MS->for_web->md->path_to_web)); + WRITE("ME = "); Makefiles::pathname_slashed(OUT, MS->for_web->md->path_to_web); + WRITE("\n"); + MS->last_line_was_blank = FALSE; + } + +@ = + text_stream *symbol = parameter_values[0]; + text_stream *webname = parameter_values[1]; + text_stream *path = parameter_values[2]; + text_stream *set = parameter_values[3]; + text_stream *category = parameter_values[4]; + + int marker = -1; + dictionary *D = NULL; + if (Str::eq(category, I"tool")) { + marker = MAKEFILE_TOOL_MOM; + D = MS->tools_dictionary; + } else if (Str::eq(category, I"web")) { + marker = MAKEFILE_WEB_MOM; + D = MS->webs_dictionary; + } else if (Str::eq(category, I"module")) { + marker = MAKEFILE_MODULE_MOM; + D = MS->modules_dictionary; + } else { + Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp); + } + if (D) { + WRITE("%SLEAF = %S\n", symbol, webname); + WRITE("%SWEB = %S\n", symbol, path); + WRITE("%SMAKER = $(%SWEB)/%S.mk\n", symbol, symbol, webname); + WRITE("%SX = $(%SWEB)/Tangled/%S\n", symbol, symbol, webname); + MS->last_line_was_blank = FALSE; + web_md *Wm = Reader::load_web_md(Pathnames::from_text(path), NULL, MS->search_path, TRUE); + Wm->as_module->module_name = Str::duplicate(symbol); + Wm->as_module->module_tag = Str::duplicate(set); + Wm->as_module->origin_marker = marker; + Dictionaries::create(D, symbol); + Dictionaries::write_value(D, symbol, Wm); + } + +@ = + text_stream *tool = parameter_values[0]; + text_stream *modules = parameter_values[1]; + text_stream *both = parameter_values[2]; + if (Str::len(tool) > 0) { + if (Dictionaries::find(MS->tools_dictionary, tool)) { + web_md *Wm = Dictionaries::read_value(MS->tools_dictionary, tool); + Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename); + } else if (Dictionaries::find(MS->webs_dictionary, tool)) { + web_md *Wm = Dictionaries::read_value(MS->webs_dictionary, tool); + Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename); + } else { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown tool '%S' to find dependencies for", tool); + Errors::in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + } else if (Str::len(modules) > 0) { + if (Dictionaries::find(MS->modules_dictionary, modules)) { + web_md *Wm = Dictionaries::read_value(MS->modules_dictionary, modules); + Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename); + } else { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown module '%S' to find dependencies for", modules); + Errors::in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + } else if (Str::len(both) > 0) { + if (Dictionaries::find(MS->tools_dictionary, both)) { + web_md *Wm = Dictionaries::read_value(MS->tools_dictionary, both); + Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename); + } else if (Dictionaries::find(MS->webs_dictionary, both)) { + web_md *Wm = Dictionaries::read_value(MS->webs_dictionary, both); + Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename); + } else { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown tool '%S' to find dependencies for", both); + Errors::in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + } else { + Makefiles::pattern(OUT, MS->for_web->md->sections_md, MS->for_web->md->contents_filename); + } + WRITE("\n"); + MS->last_line_was_blank = FALSE; + +@ = + if (MS->repeat_sp >= MAX_MAKEFILE_REPEAT_DEPTH) { + Errors::in_text_file("repetition too deep", tfp); + } else { + text_stream *category = parameter_values[0]; + text_stream *set = parameter_values[1]; + text_stream *with = parameter_values[2]; + text_stream *in = parameter_values[3]; + if (Str::len(set) == 0) set = I"all"; + if (Str::eq(category, I"tool")) { + int marker = MAKEFILE_TOOL_MOM; + @; + } else if (Str::eq(category, I"web")) { + int marker = MAKEFILE_WEB_MOM; + @; + } else if (Str::eq(category, I"module")) { + int marker = MAKEFILE_MODULE_MOM; + @; + } else if (Str::len(category) > 0) { + Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp); + } else { + if ((Str::len(with) == 0) || (Str::len(in) == 0)) + Errors::in_text_file("should give both with: VAR and in: LIST", tfp); + @; + } + } + +@ = + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = marker; + rep->repeat_tag = Str::duplicate(set); + rep->repeat_with = NULL; + rep->repeat_in = NULL; + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str::new(); + +@ = + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = -1; + rep->repeat_tag = NULL; + rep->repeat_with = Str::duplicate(with); + rep->repeat_in = Str::duplicate(in); + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str::new(); + +@ = + MS->shadow_sp = 0; + if (MS->repeat_sp == 0) Errors::in_text_file("end without repeat", tfp); + else { + makefile_repeat *rep = &(MS->repeat_data[--(MS->repeat_sp)]); + int as_lines = TRUE; + if (mm->reserved_macro_meaning == END_SPAN_RMM) as_lines = FALSE; + text_stream *matter = MS->dest; + MS->dest = rep->repeat_saved_dest; + Makefiles::repeat(matter, as_lines, tfp, rep, MS); + } + +@ = + text_stream *name = parameter_values[0]; + text_stream *value = parameter_values[1]; + + if (Makefiles::acceptable_variable_name(name) == FALSE) + Errors::in_text_file("improper variable name", tfp); + + makefile_variable *var = Makefiles::ensure_variable(name, MS->stack_frame); + var->value = Str::duplicate(value); diff --git a/Figures/scan.txt b/Figures/scan.txt index 1ef49fa..d2d0cfe 100644 --- a/Figures/scan.txt +++ b/Figures/scan.txt @@ -1,4 +1,3 @@ -web "The Twin Primes Conjecture": 1 section : 3 paragraphs : 55 lines Scan of source lines for '0' 0000001 SECTION_HEADING..... Main. 0000002 COMMENT_BODY........ diff --git a/Tangled/inweb.c b/Tangled/inweb.c index ef81bc1..80c3a57 100644 --- a/Tangled/inweb.c +++ b/Tangled/inweb.c @@ -807,73 +807,74 @@ struct Win32_Mutex { INIT_ONCE init; CRITICAL_SECTION crit; }; #define macro_usage_CLASS 55 #define makefile_macro_CLASS 56 #define makefile_macro_parameter_CLASS 57 -#define makefile_macro_playback_CLASS 58 -#define nonterminal_variable_CLASS 59 -#define para_macro_CLASS 60 -#define paragraph_CLASS 61 -#define paragraph_tagging_CLASS 62 -#define preform_nonterminal_CLASS 63 -#define programming_language_CLASS 64 -#define reserved_word_CLASS 65 -#define section_CLASS 66 -#define source_line_CLASS 67 -#define structure_element_CLASS 68 -#define tangle_target_CLASS 69 -#define tex_results_CLASS 70 -#define text_literal_CLASS 71 -#define theme_tag_CLASS 72 -#define weave_format_CLASS 73 -#define weave_pattern_CLASS 74 -#define weave_plugin_CLASS 75 -#define weave_order_CLASS 76 -#define web_CLASS 77 -#define writeme_asset_CLASS 78 -#define weave_document_node_CLASS 79 -#define weave_head_node_CLASS 80 -#define weave_body_node_CLASS 81 -#define weave_tail_node_CLASS 82 -#define weave_section_header_node_CLASS 83 -#define weave_section_footer_node_CLASS 84 -#define weave_chapter_header_node_CLASS 85 -#define weave_chapter_footer_node_CLASS 86 -#define weave_verbatim_node_CLASS 87 -#define weave_section_purpose_node_CLASS 88 -#define weave_subheading_node_CLASS 89 -#define weave_bar_node_CLASS 90 -#define weave_linebreak_node_CLASS 91 -#define weave_pagebreak_node_CLASS 92 -#define weave_paragraph_heading_node_CLASS 93 -#define weave_endnote_node_CLASS 94 -#define weave_material_node_CLASS 95 -#define weave_figure_node_CLASS 96 -#define weave_extract_node_CLASS 97 -#define weave_audio_node_CLASS 98 -#define weave_download_node_CLASS 99 -#define weave_video_node_CLASS 100 -#define weave_embed_node_CLASS 101 -#define weave_pmac_node_CLASS 102 -#define weave_vskip_node_CLASS 103 -#define weave_chapter_node_CLASS 104 -#define weave_section_node_CLASS 105 -#define weave_code_line_node_CLASS 106 -#define weave_function_usage_node_CLASS 107 -#define weave_commentary_node_CLASS 108 -#define weave_carousel_slide_node_CLASS 109 -#define weave_toc_node_CLASS 110 -#define weave_toc_line_node_CLASS 111 -#define weave_chapter_title_page_node_CLASS 112 -#define weave_defn_node_CLASS 113 -#define weave_source_code_node_CLASS 114 -#define weave_url_node_CLASS 115 -#define weave_footnote_cue_node_CLASS 116 -#define weave_begin_footnote_text_node_CLASS 117 -#define weave_display_line_node_CLASS 118 -#define weave_function_defn_node_CLASS 119 -#define weave_item_node_CLASS 120 -#define weave_grammar_index_node_CLASS 121 -#define weave_inline_node_CLASS 122 -#define weave_locale_node_CLASS 123 -#define weave_maths_node_CLASS 124 +#define makefile_variable_CLASS 58 +#define makefile_variable_set_CLASS 59 +#define nonterminal_variable_CLASS 60 +#define para_macro_CLASS 61 +#define paragraph_CLASS 62 +#define paragraph_tagging_CLASS 63 +#define preform_nonterminal_CLASS 64 +#define programming_language_CLASS 65 +#define reserved_word_CLASS 66 +#define section_CLASS 67 +#define source_line_CLASS 68 +#define structure_element_CLASS 69 +#define tangle_target_CLASS 70 +#define tex_results_CLASS 71 +#define text_literal_CLASS 72 +#define theme_tag_CLASS 73 +#define weave_format_CLASS 74 +#define weave_pattern_CLASS 75 +#define weave_plugin_CLASS 76 +#define weave_order_CLASS 77 +#define web_CLASS 78 +#define writeme_asset_CLASS 79 +#define weave_document_node_CLASS 80 +#define weave_head_node_CLASS 81 +#define weave_body_node_CLASS 82 +#define weave_tail_node_CLASS 83 +#define weave_section_header_node_CLASS 84 +#define weave_section_footer_node_CLASS 85 +#define weave_chapter_header_node_CLASS 86 +#define weave_chapter_footer_node_CLASS 87 +#define weave_verbatim_node_CLASS 88 +#define weave_section_purpose_node_CLASS 89 +#define weave_subheading_node_CLASS 90 +#define weave_bar_node_CLASS 91 +#define weave_linebreak_node_CLASS 92 +#define weave_pagebreak_node_CLASS 93 +#define weave_paragraph_heading_node_CLASS 94 +#define weave_endnote_node_CLASS 95 +#define weave_material_node_CLASS 96 +#define weave_figure_node_CLASS 97 +#define weave_extract_node_CLASS 98 +#define weave_audio_node_CLASS 99 +#define weave_download_node_CLASS 100 +#define weave_video_node_CLASS 101 +#define weave_embed_node_CLASS 102 +#define weave_pmac_node_CLASS 103 +#define weave_vskip_node_CLASS 104 +#define weave_chapter_node_CLASS 105 +#define weave_section_node_CLASS 106 +#define weave_code_line_node_CLASS 107 +#define weave_function_usage_node_CLASS 108 +#define weave_commentary_node_CLASS 109 +#define weave_carousel_slide_node_CLASS 110 +#define weave_toc_node_CLASS 111 +#define weave_toc_line_node_CLASS 112 +#define weave_chapter_title_page_node_CLASS 113 +#define weave_defn_node_CLASS 114 +#define weave_source_code_node_CLASS 115 +#define weave_url_node_CLASS 116 +#define weave_footnote_cue_node_CLASS 117 +#define weave_begin_footnote_text_node_CLASS 118 +#define weave_display_line_node_CLASS 119 +#define weave_function_defn_node_CLASS 120 +#define weave_item_node_CLASS 121 +#define weave_grammar_index_node_CLASS 122 +#define weave_inline_node_CLASS 123 +#define weave_locale_node_CLASS 124 +#define weave_maths_node_CLASS 125 #define NO_MODE 0 #define ANALYSE_MODE 1 #define TANGLE_MODE 2 @@ -1088,11 +1089,22 @@ struct Win32_Mutex { INIT_ONCE init; CRITICAL_SECTION crit; }; #define POST_PROCESS_REPORT_POS_MTID 41 #define POST_PROCESS_SUBSTITUTE_POS_MTID 42 #define PDFTEX_TEX_FORM 1 +#define MAX_MAKEFILE_REPEAT_DEPTH 8 #define MAX_MAKEFILE_MACRO_PARAMETERS 8 #define MAX_MAKEFILE_MACRO_LINES 128 +#define UNRESERVED_RMM 0 +#define PLATFORM_SETTINGS_RMM 1 +#define IDENTITY_SETTINGS_RMM 2 +#define COMPONENT_RMM 3 +#define DEPENDENT_FILES_RMM 4 +#define REPEAT_BLOCK_RMM 5 +#define END_BLOCK_RMM 6 +#define REPEAT_SPAN_RMM 7 +#define END_SPAN_RMM 8 +#define SET_RMM 9 #define NO_DEFINED_CLSW_VALUES 38 #define NO_DEFINED_DA_VALUES 4 -#define NO_DEFINED_CLASS_VALUES 125 +#define NO_DEFINED_CLASS_VALUES 126 #define NO_DEFINED_MREASON_VALUES 5 #define NO_DEFINED_LOCALE_VALUES 2 #define NO_DEFINED_MTID_VALUES 42 @@ -1110,6 +1122,7 @@ struct Win32_Mutex { INIT_ONCE init; CRITICAL_SECTION crit; }; #define NO_DEFINED_SECTIONCAT_VALUES 3 #define NO_DEFINED_MATERIAL_VALUES 6 #define NO_DEFINED_FORM_VALUES 1 +#define NO_DEFINED_RMM_VALUES 10 #line 41 "inweb/foundation-module/Chapter 2/Debugging Log.w" typedef struct debugging_aspect { struct text_stream *hyphenated_name; /* e.g., "memory-usage" */ @@ -2340,48 +2353,64 @@ typedef struct tex_results { struct filename *PDF_filename; CLASS_DEFINITION } tex_results; -#line 12 "inweb/Chapter 6/Makefiles.w" +#line 20 "inweb/Chapter 6/Makefiles.w" +typedef struct makefile_repeat { + int repeat_scope; /* during a repeat, either |MAKEFILE_TOOL_MOM| or |MAKEFILE_MODULE_MOM| */ + struct text_stream *repeat_tag; + struct text_stream *repeat_with; + struct text_stream *repeat_in; + int repeat_is_block; + struct text_stream *repeat_saved_dest; +} makefile_repeat; +#line 29 "inweb/Chapter 6/Makefiles.w" +typedef struct makefile_state { + struct web *for_web; + struct text_stream *dest; + struct makefile_macro *defining; /* a "define" body being scanned */ + int repeat_sp; + int shadow_sp; + struct makefile_repeat repeat_data[MAX_MAKEFILE_REPEAT_DEPTH]; + int suppress_newline; /* at the end of this line */ + int last_line_was_blank; /* used to suppress runs of multiple blank lines */ + struct dictionary *tools_dictionary; + struct dictionary *webs_dictionary; + struct dictionary *modules_dictionary; + struct module_search *search_path; + struct makefile_variable_set *global_variables; + struct makefile_variable_set *stack_frame; + struct linked_list *known_macros; /* of |makefile_macro| */ +} makefile_state; +#line 349 "inweb/Chapter 6/Makefiles.w" +typedef struct makefile_variable { + struct text_stream *name; + struct text_stream *value; + CLASS_DEFINITION +} makefile_variable; +#line 355 "inweb/Chapter 6/Makefiles.w" +typedef struct makefile_variable_set { + struct linked_list *variables; /* of |makefile_variable| */ + struct makefile_variable_set *outer; + CLASS_DEFINITION +} makefile_variable_set; +#line 441 "inweb/Chapter 6/Makefiles.w" typedef struct makefile_macro { struct text_stream *identifier; struct makefile_macro_parameter *parameters[MAX_MAKEFILE_MACRO_PARAMETERS]; int no_parameters; struct text_stream *lines[MAX_MAKEFILE_MACRO_LINES]; int no_lines; + int reserved_macro_meaning; + int suppress_newline_after_expanding; + int suppress_whitespace_when_expanding; CLASS_DEFINITION } makefile_macro; -#line 21 "inweb/Chapter 6/Makefiles.w" +#line 453 "inweb/Chapter 6/Makefiles.w" typedef struct makefile_macro_parameter { struct text_stream *name; struct text_stream *definition_token; int optional; CLASS_DEFINITION } makefile_macro_parameter; -#line 28 "inweb/Chapter 6/Makefiles.w" -typedef struct makefile_macro_playback { - struct makefile_macro *which; - struct text_stream *parameter_values[MAX_MAKEFILE_MACRO_PARAMETERS]; - int line_position; - struct makefile_macro_playback *prior_to_this; - struct text_stream *text_to_follow; - CLASS_DEFINITION -} makefile_macro_playback; -#line 37 "inweb/Chapter 6/Makefiles.w" -typedef struct makefile_state { - struct web *for_web; - struct text_stream to_makefile; - struct text_stream *repeat_block; /* a "repeatblock" body being scanned */ - struct makefile_macro *defining; /* a "define" body being scanned */ - struct makefile_macro_playback *playing_back; - int inside_block; /* scanning a "repeatblock" into that text? */ - int last_line_was_blank; /* used to suppress runs of multiple blank lines */ - int allow_commands; /* permit the prototype to use special commands */ - int repeat_scope; /* during a repeat, either |MAKEFILE_TOOL_MOM| or |MAKEFILE_MODULE_MOM| */ - struct text_stream *repeat_tag; - struct dictionary *tools_dictionary; - struct dictionary *webs_dictionary; - struct dictionary *modules_dictionary; - struct module_search *search_path; -} makefile_state; #line 9 "inweb/Chapter 6/Git Support.w" typedef struct gitignore_state { struct web *for_web; @@ -4506,18 +4535,44 @@ int TeXUtilities__substitute_post_processing_data(text_stream *to, weave_order void TeXUtilities__remove_math_mode(OUTPUT_STREAM, text_stream *text) ; #line 133 "inweb/Chapter 5/TeX Utilities.w" void TeXUtilities__remove_math_mode_range(OUTPUT_STREAM, text_stream *text, int from, int to) ; -#line 54 "inweb/Chapter 6/Makefiles.w" +#line 47 "inweb/Chapter 6/Makefiles.w" void Makefiles__write(web *W, filename *prototype, filename *F, module_search *I) ; -#line 81 "inweb/Chapter 6/Makefiles.w" +#line 79 "inweb/Chapter 6/Makefiles.w" void Makefiles__scan_makefile_line(text_stream *line, text_file_position *tfp, void *X) ; -#line 451 "inweb/Chapter 6/Makefiles.w" +#line 126 "inweb/Chapter 6/Makefiles.w" +void Makefiles__expand(text_stream *text, text_file_position *tfp, makefile_state *MS) ; +#line 233 "inweb/Chapter 6/Makefiles.w" void Makefiles__pathname_slashed(OUTPUT_STREAM, pathname *P) ; -#line 462 "inweb/Chapter 6/Makefiles.w" +#line 244 "inweb/Chapter 6/Makefiles.w" void Makefiles__pattern(OUTPUT_STREAM, linked_list *L, filename *F) ; -#line 501 "inweb/Chapter 6/Makefiles.w" -void Makefiles__repeat(OUTPUT_STREAM, text_stream *prefix, int every_time, text_stream *matter, int as_lines, text_stream *suffix, text_file_position *tfp, makefile_state *MS, int over, text_stream *tag) ; -#line 538 "inweb/Chapter 6/Makefiles.w" +#line 283 "inweb/Chapter 6/Makefiles.w" +void Makefiles__repeat(text_stream *matter, int as_lines, text_file_position *tfp, makefile_repeat *rep, makefile_state *MS) ; +#line 339 "inweb/Chapter 6/Makefiles.w" void Makefiles__seek_INWEBPLATFORM(text_stream *line, text_file_position *tfp, void *X) ; +#line 361 "inweb/Chapter 6/Makefiles.w" +makefile_variable_set * Makefiles__new_variable_set(makefile_variable_set *outer) ; +#line 368 "inweb/Chapter 6/Makefiles.w" +makefile_variable * Makefiles__find_variable_in_one(text_stream *name, makefile_variable_set *set) ; +#line 377 "inweb/Chapter 6/Makefiles.w" +makefile_variable * Makefiles__find_variable_in(text_stream *name, makefile_variable_set *set) ; +#line 386 "inweb/Chapter 6/Makefiles.w" +makefile_variable * Makefiles__ensure_variable(text_stream *name, makefile_variable_set *in_set) ; +#line 398 "inweb/Chapter 6/Makefiles.w" +int Makefiles__acceptable_variable_name(text_stream *name) ; +#line 464 "inweb/Chapter 6/Makefiles.w" +makefile_macro * Makefiles__new_macro(linked_list *L, text_stream *name, text_stream *parameter_specification, text_file_position *tfp) ; +#line 510 "inweb/Chapter 6/Makefiles.w" +void Makefiles__add_line_to_macro(makefile_macro *mm, text_stream *line, text_file_position *tfp) ; +#line 533 "inweb/Chapter 6/Makefiles.w" +linked_list * Makefiles__list_of_reserved_macros(void) ; +#line 554 "inweb/Chapter 6/Makefiles.w" +void Makefiles__reserve_macro(linked_list *L, text_stream *name, text_stream *parameter_specification, int rmm) ; +#line 561 "inweb/Chapter 6/Makefiles.w" +void Makefiles__reserve_span_macro(linked_list *L, text_stream *name, text_stream *parameter_specification, int rmm) ; +#line 574 "inweb/Chapter 6/Makefiles.w" +makefile_macro * Makefiles__find_macro(linked_list *L, text_stream *name) ; +#line 585 "inweb/Chapter 6/Makefiles.w" +void Makefiles__expand_macro(makefile_state *MS, makefile_macro *mm, text_stream *parameter_settings, text_file_position *tfp) ; #line 15 "inweb/Chapter 6/Git Support.w" void Git__write_gitignore(web *W, filename *prototype, filename *F) ; #line 31 "inweb/Chapter 6/Git Support.w" @@ -5252,6 +5307,33 @@ text_stream *TL_IS_657 = NULL; text_stream *TL_IS_658 = NULL; text_stream *TL_IS_659 = NULL; text_stream *TL_IS_660 = NULL; +text_stream *TL_IS_661 = NULL; +text_stream *TL_IS_662 = NULL; +text_stream *TL_IS_663 = NULL; +text_stream *TL_IS_664 = NULL; +text_stream *TL_IS_665 = NULL; +text_stream *TL_IS_666 = NULL; +text_stream *TL_IS_667 = NULL; +text_stream *TL_IS_668 = NULL; +text_stream *TL_IS_669 = NULL; +text_stream *TL_IS_670 = NULL; +text_stream *TL_IS_671 = NULL; +text_stream *TL_IS_672 = NULL; +text_stream *TL_IS_673 = NULL; +text_stream *TL_IS_674 = NULL; +text_stream *TL_IS_675 = NULL; +text_stream *TL_IS_676 = NULL; +text_stream *TL_IS_677 = NULL; +text_stream *TL_IS_678 = NULL; +text_stream *TL_IS_679 = NULL; +text_stream *TL_IS_680 = NULL; +text_stream *TL_IS_681 = NULL; +text_stream *TL_IS_682 = NULL; +text_stream *TL_IS_683 = NULL; +text_stream *TL_IS_684 = NULL; +text_stream *TL_IS_685 = NULL; +text_stream *TL_IS_686 = NULL; +text_stream *TL_IS_687 = NULL; void register_tangled_text_literals(void); #line 57 "inweb/foundation-module/Chapter 2/Streams.w" #define WRITE(args...) Writers__printf(OUT, args) @@ -8663,11 +8745,11 @@ int CommandLine__read_pair_p(text_stream *opt, text_stream *opt_val, int N, ; innocuous = TRUE; break; case VERSION_CLSW: { PRINT("inweb"); - char *svn = "7-alpha.1+1A90"; + char *svn = "7-alpha.1+1A91"; if (svn[0]) PRINT(" version %s", svn); char *vname = "Escape to Danger"; if (vname[0]) PRINT(" '%s'", vname); - char *d = "19 April 2022"; + char *d = "22 April 2022"; if (d[0]) PRINT(" (%s)", d); PRINT("\n"); innocuous = TRUE; break; @@ -16473,7 +16555,7 @@ void SimpleTangler__tangle_L3(OUTPUT_STREAM, text_stream *text, if (Input_File) fclose(Input_File); } -#line 105 "inweb/Chapter 1/Basics.w" +#line 106 "inweb/Chapter 1/Basics.w" DECLARE_CLASS_ALLOCATED_IN_ARRAYS(source_line, 1000) DECLARE_CLASS(asset_rule) DECLARE_CLASS(breadcrumb_request) @@ -16495,7 +16577,8 @@ DECLARE_CLASS(macro_usage) DECLARE_CLASS(macro) DECLARE_CLASS(makefile_macro) DECLARE_CLASS(makefile_macro_parameter) -DECLARE_CLASS(makefile_macro_playback) +DECLARE_CLASS(makefile_variable) +DECLARE_CLASS(makefile_variable_set) DECLARE_CLASS(nonterminal_variable) DECLARE_CLASS(para_macro) DECLARE_CLASS(paragraph_tagging) @@ -16681,7 +16764,7 @@ void Main__follow_instructions(inweb_instructions *ins) { else if (ins->inweb_mode != NO_MODE) { #line 156 "inweb/Chapter 1/Program Control.w" - Reader__print_web_statistics(W); + if (ins->inweb_mode != ANALYSE_MODE) Reader__print_web_statistics(W); if (ins->inweb_mode == ANALYSE_MODE) { #line 164 "inweb/Chapter 1/Program Control.w" @@ -30448,599 +30531,210 @@ void TeXUtilities__remove_math_mode_range(OUTPUT_STREAM, text_stream *text, int } } -#line 20 "inweb/Chapter 6/Makefiles.w" +#line 28 "inweb/Chapter 6/Makefiles.w" -#line 27 "inweb/Chapter 6/Makefiles.w" - -#line 36 "inweb/Chapter 6/Makefiles.w" - -#line 53 "inweb/Chapter 6/Makefiles.w" +#line 46 "inweb/Chapter 6/Makefiles.w" void Makefiles__write(web *W, filename *prototype, filename *F, module_search *I) { + struct text_stream makefile; + if (STREAM_OPEN_TO_FILE(&makefile, F, ISO_ENC) == FALSE) + Errors__fatal_with_file("unable to write tangled file", F); + WRITE_TO(STDOUT, "(Read script from %f)\n", prototype); + text_stream *OUT = &makefile; + makefile_state MS; + MS.dest = OUT; MS.for_web = W; + MS.suppress_newline = FALSE; MS.last_line_was_blank = TRUE; - MS.repeat_block = Str__new(); MS.defining = NULL; - MS.playing_back = NULL; - MS.inside_block = FALSE; - MS.allow_commands = TRUE; + MS.repeat_sp = 0; + MS.shadow_sp = 0; MS.tools_dictionary = Dictionaries__new(16, FALSE); MS.webs_dictionary = Dictionaries__new(16, FALSE); MS.modules_dictionary = Dictionaries__new(16, FALSE); MS.search_path = I; - MS.repeat_scope = -1; - MS.repeat_tag = NULL; - text_stream *OUT = &(MS.to_makefile); - if (STREAM_OPEN_TO_FILE(OUT, F, ISO_ENC) == FALSE) - Errors__fatal_with_file("unable to write tangled file", F); + MS.global_variables = Makefiles__new_variable_set(NULL); + MS.stack_frame = MS.global_variables; + MS.known_macros = Makefiles__list_of_reserved_macros(); WRITE("# This makefile was automatically written by inweb -makefile\n"); WRITE("# and is not intended for human editing\n\n"); TextFiles__read(prototype, FALSE, "can't open prototype file", TRUE, Makefiles__scan_makefile_line, NULL, &MS); STREAM_CLOSE(OUT); - WRITE_TO(STDOUT, "Wrote makefile '%f' from script '%f'\n", F, prototype); } -#line 81 "inweb/Chapter 6/Makefiles.w" +#line 79 "inweb/Chapter 6/Makefiles.w" void Makefiles__scan_makefile_line(text_stream *line, text_file_position *tfp, void *X) { makefile_state *MS = (makefile_state *) X; - text_stream *OUT = &(MS->to_makefile); - match_results mr = Regexp__create_mr(); if (Regexp__match(&mr, line, L" *#%c*")) { Regexp__dispose_of(&mr); return; } // Skip comment lines - if (MS->allow_commands) { - if (Regexp__match(&mr, line, L" *{define: *(%C+) (%c*)} *")) + + if (Regexp__match(&mr, line, L" *{define: *(%C+) (%c*)} *")) { -#line 131 "inweb/Chapter 6/Makefiles.w" - if (MS->defining) Errors__in_text_file("nested definitions are not allowed", tfp); +#line 105 "inweb/Chapter 6/Makefiles.w" + if (MS->defining) + Errors__in_text_file("nested definitions are not allowed", tfp); text_stream *name = mr.exp[0]; text_stream *parameter_specification = mr.exp[1]; - makefile_macro *new_macro = CREATE(makefile_macro); - new_macro->identifier = Str__duplicate(name); - new_macro->no_parameters = 0; - new_macro->no_lines = 0; - - match_results mr2 = Regexp__create_mr(); - while (Regexp__match(&mr2, parameter_specification, L" *(%C+): *(%C+) *(%c*)")) { - if (new_macro->no_parameters >= MAX_MAKEFILE_MACRO_PARAMETERS) { - Errors__in_text_file("too many parameters in this definition", tfp); - break; - } - makefile_macro_parameter *new_parameter = CREATE(makefile_macro_parameter); - new_parameter->name = Str__duplicate(mr2.exp[0]); - new_parameter->definition_token = Str__duplicate(mr2.exp[1]); - new_parameter->optional = FALSE; - if (Str__get_first_char(new_parameter->name) == '?') { - new_parameter->optional = TRUE; - Str__delete_first_character(new_parameter->name); - } - new_macro->parameters[new_macro->no_parameters++] = new_parameter; - Str__clear(parameter_specification); - Str__copy(parameter_specification, mr2.exp[2]); - } - Regexp__dispose_of(&mr2); - if (Str__is_whitespace(parameter_specification) == FALSE) - Errors__in_text_file("parameter list for this definition is malformed", tfp); - - MS->defining = new_macro; + MS->defining = Makefiles__new_macro(MS->known_macros, name, parameter_specification, tfp); Regexp__dispose_of(&mr); return; } -#line 88 "inweb/Chapter 6/Makefiles.w" +#line 84 "inweb/Chapter 6/Makefiles.w" ; - if (Regexp__match(&mr, line, L" *{end-define} *")) + if (Regexp__match(&mr, line, L" *{end-define} *")) { -#line 175 "inweb/Chapter 6/Makefiles.w" - if (MS->defining == NULL) Errors__in_text_file("{end-define} without {define: ...}", tfp); +#line 119 "inweb/Chapter 6/Makefiles.w" + if (MS->defining == NULL) + Errors__in_text_file("{end-define} without {define: ...}", tfp); MS->defining = NULL; Regexp__dispose_of(&mr); return; } -#line 89 "inweb/Chapter 6/Makefiles.w" +#line 85 "inweb/Chapter 6/Makefiles.w" ; - if (MS->defining) + if (MS->defining) { -#line 166 "inweb/Chapter 6/Makefiles.w" - if (MS->defining->no_lines >= MAX_MAKEFILE_MACRO_LINES) { - Errors__in_text_file("too many lines in this definition", tfp); - } else { - MS->defining->lines[MS->defining->no_lines++] = Str__duplicate(line); +#line 114 "inweb/Chapter 6/Makefiles.w" + Makefiles__add_line_to_macro(MS->defining, line, tfp); + Regexp__dispose_of(&mr); + return; + +} +#line 86 "inweb/Chapter 6/Makefiles.w" +; + Regexp__dispose_of(&mr); + + Makefiles__expand(line, tfp, MS); + + if (MS->suppress_newline == FALSE) { + text_stream *OUT = MS->dest; + if (Str__len(line) == 0) { + if (MS->last_line_was_blank == FALSE) WRITE("\n"); + MS->last_line_was_blank = TRUE; + } else { + MS->last_line_was_blank = FALSE; + WRITE("\n"); + } } - Regexp__dispose_of(&mr); - return; - + MS->suppress_newline = FALSE; } -#line 90 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L" *{repeat-tools-block:(%C*)} *")) - -{ -#line 269 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_TOOL_MOM; - -{ -#line 281 "inweb/Chapter 6/Makefiles.w" - if (MS->inside_block) Errors__in_text_file("nested repeat blocks are not allowed", tfp); - MS->inside_block = TRUE; - MS->repeat_scope = marker; - MS->repeat_tag = Str__duplicate(mr.exp[0]); - Str__clear(MS->repeat_block); - Regexp__dispose_of(&mr); - return; - -} -#line 270 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 93 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L" *{repeat-webs-block:(%C*)} *")) - -{ -#line 273 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_WEB_MOM; - -{ -#line 281 "inweb/Chapter 6/Makefiles.w" - if (MS->inside_block) Errors__in_text_file("nested repeat blocks are not allowed", tfp); - MS->inside_block = TRUE; - MS->repeat_scope = marker; - MS->repeat_tag = Str__duplicate(mr.exp[0]); - Str__clear(MS->repeat_block); - Regexp__dispose_of(&mr); - return; - -} -#line 274 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 95 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L" *{repeat-modules-block:(%C*)} *")) - -{ -#line 277 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_MODULE_MOM; - -{ -#line 281 "inweb/Chapter 6/Makefiles.w" - if (MS->inside_block) Errors__in_text_file("nested repeat blocks are not allowed", tfp); - MS->inside_block = TRUE; - MS->repeat_scope = marker; - MS->repeat_tag = Str__duplicate(mr.exp[0]); - Str__clear(MS->repeat_block); - Regexp__dispose_of(&mr); - return; - -} -#line 278 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 97 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L" *{end-block} *")) -{ -#line 294 "inweb/Chapter 6/Makefiles.w" - if (MS->inside_block == FALSE) - Errors__in_text_file("{endblock} without {repeatblock}", tfp); - MS->inside_block = FALSE; - Makefiles__repeat(OUT, NULL, TRUE, MS->repeat_block, TRUE, NULL, tfp, MS, MS->repeat_scope, MS->repeat_tag); - Str__clear(MS->repeat_block); - Regexp__dispose_of(&mr); - return; - -} -#line 98 "inweb/Chapter 6/Makefiles.w" -; - if (MS->inside_block) -{ -#line 290 "inweb/Chapter 6/Makefiles.w" - WRITE_TO(MS->repeat_block, "%S\n", line); - return; - -} -#line 99 "inweb/Chapter 6/Makefiles.w" -; - - if (Regexp__match(&mr, line, L"(%c*){repeat-tools-span}(%c*?){end-span}(%c*)")) - -{ -#line 303 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_TOOL_MOM; - -{ -#line 315 "inweb/Chapter 6/Makefiles.w" - WRITE("%S", mr.exp[0]); - Makefiles__repeat(OUT, TL_IS_628, FALSE, mr.exp[1], FALSE, NULL, tfp, MS, marker, TL_IS_629); - WRITE("%S\n", mr.exp[2]); - MS->last_line_was_blank = FALSE; - Regexp__dispose_of(&mr); - return; - -} -#line 304 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 102 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L"(%c*){repeat-webs-span}(%c*?){end-span}(%c*)")) - -{ -#line 307 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_WEB_MOM; - -{ -#line 315 "inweb/Chapter 6/Makefiles.w" - WRITE("%S", mr.exp[0]); - Makefiles__repeat(OUT, TL_IS_628, FALSE, mr.exp[1], FALSE, NULL, tfp, MS, marker, TL_IS_629); - WRITE("%S\n", mr.exp[2]); - MS->last_line_was_blank = FALSE; - Regexp__dispose_of(&mr); - return; - -} -#line 308 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 104 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L"(%c*){repeat-modules-span}(%c*?){end-span}(%c*)")) - -{ -#line 311 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_MODULE_MOM; - -{ -#line 315 "inweb/Chapter 6/Makefiles.w" - WRITE("%S", mr.exp[0]); - Makefiles__repeat(OUT, TL_IS_628, FALSE, mr.exp[1], FALSE, NULL, tfp, MS, marker, TL_IS_629); - WRITE("%S\n", mr.exp[2]); - MS->last_line_was_blank = FALSE; - Regexp__dispose_of(&mr); - return; - -} -#line 312 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 106 "inweb/Chapter 6/Makefiles.w" -; - - if (Regexp__match(&mr, line, L" *{identity-settings} *")) -{ -#line 343 "inweb/Chapter 6/Makefiles.w" - WRITE("INWEB = "); Makefiles__pathname_slashed(OUT, path_to_inweb); WRITE("/Tangled/inweb\n"); - pathname *path_to_intest = Pathnames__down(Pathnames__up(path_to_inweb), TL_IS_633); - WRITE("INTEST = "); Makefiles__pathname_slashed(OUT, path_to_intest); WRITE("/Tangled/intest\n"); - if (MS->for_web) { - WRITE("MYNAME = %S\n", Pathnames__directory_name(MS->for_web->md->path_to_web)); - WRITE("ME = "); Makefiles__pathname_slashed(OUT, MS->for_web->md->path_to_web); - WRITE("\n"); - MS->last_line_was_blank = FALSE; +#line 126 "inweb/Chapter 6/Makefiles.w" +void Makefiles__expand(text_stream *text, text_file_position *tfp, makefile_state *MS) { + TEMPORARY_TEXT(before_matter) + TEMPORARY_TEXT(braced_matter) + TEMPORARY_TEXT(after_matter) + int bl = 0, after_times = FALSE; + for (int i = 0; i < Str__len(text); i++) { + wchar_t c = Str__get_at(text, i); + if (after_times) PUT_TO(after_matter, c); + else if (c == '{') { + bl++; + if (bl > 1) PUT_TO(braced_matter, c); + } else if (c == '}') { + bl--; + if (bl == 0) after_times = TRUE; + else PUT_TO(braced_matter, c); + } else { + if (bl < 0) Errors__in_text_file("too many '}'s", tfp); + if (bl == 0) PUT_TO(before_matter, c); + else PUT_TO(braced_matter, c); + } } - Regexp__dispose_of(&mr); - return; - -} -#line 108 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L" *{platform-settings} *")) + if (bl > 0) Errors__in_text_file("too many '{'s", tfp); + if (after_times) { + { -#line 323 "inweb/Chapter 6/Makefiles.w" - filename *prototype = Filenames__in(path_to_inweb, TL_IS_630); - text_stream *INWEBPLATFORM = Str__new(); - TextFiles__read(prototype, FALSE, "can't open platform settings file", - TRUE, Makefiles__seek_INWEBPLATFORM, NULL, INWEBPLATFORM); - if (Str__len(INWEBPLATFORM) == 0) { - Errors__in_text_file( - "found platform settings file, but it does not set INWEBPLATFORM", tfp); - } else { - pathname *P = Pathnames__down(path_to_inweb, TL_IS_631); - P = Pathnames__down(P, TL_IS_632); - WRITE_TO(INWEBPLATFORM, ".mkscript"); - filename *F = Filenames__in(P, INWEBPLATFORM); - TextFiles__read(F, FALSE, "can't open platform definitions file", - TRUE, Makefiles__scan_makefile_line, NULL, MS); - WRITE_TO(STDOUT, "(Read definitions from %f)\n", F); +#line 159 "inweb/Chapter 6/Makefiles.w" + text_stream *identifier = braced_matter; + text_stream *parameter_settings = NULL; + match_results mr = Regexp__create_mr(); + if (Regexp__match(&mr, identifier, L"(%C+) (%c*)")) { + identifier = mr.exp[0]; + parameter_settings = mr.exp[1]; } - Regexp__dispose_of(&mr); - return; - -} -#line 109 "inweb/Chapter 6/Makefiles.w" -; - - if (Regexp__match(&mr, line, L" *{tool} *(%C+) (%C+) (%c+) (%C+) *")) -{ -#line 356 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_TOOL_MOM; - dictionary *D = MS->tools_dictionary; - -{ -#line 371 "inweb/Chapter 6/Makefiles.w" - WRITE("%SLEAF = %S\n", mr.exp[0], mr.exp[1]); - WRITE("%SWEB = %S\n", mr.exp[0], mr.exp[2]); - WRITE("%SMAKER = $(%SWEB)/%S.mk\n", mr.exp[0], mr.exp[0], mr.exp[1]); - WRITE("%SX = $(%SWEB)/Tangled/%S\n", mr.exp[0], mr.exp[0], mr.exp[1]); - MS->last_line_was_blank = FALSE; - web_md *Wm = Reader__load_web_md(Pathnames__from_text(mr.exp[2]), NULL, MS->search_path, TRUE); - Wm->as_module->module_name = Str__duplicate(mr.exp[0]); - Wm->as_module->module_tag = Str__duplicate(mr.exp[3]); - Wm->as_module->origin_marker = marker; - Dictionaries__create(D, mr.exp[0]); - Dictionaries__write_value(D, mr.exp[0], Wm); - Regexp__dispose_of(&mr); - return; - -} -#line 358 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 111 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L" *{web} *(%C+) (%C+) (%c+) (%C+) *")) -{ -#line 361 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_WEB_MOM; - dictionary *D = MS->webs_dictionary; - -{ -#line 371 "inweb/Chapter 6/Makefiles.w" - WRITE("%SLEAF = %S\n", mr.exp[0], mr.exp[1]); - WRITE("%SWEB = %S\n", mr.exp[0], mr.exp[2]); - WRITE("%SMAKER = $(%SWEB)/%S.mk\n", mr.exp[0], mr.exp[0], mr.exp[1]); - WRITE("%SX = $(%SWEB)/Tangled/%S\n", mr.exp[0], mr.exp[0], mr.exp[1]); - MS->last_line_was_blank = FALSE; - web_md *Wm = Reader__load_web_md(Pathnames__from_text(mr.exp[2]), NULL, MS->search_path, TRUE); - Wm->as_module->module_name = Str__duplicate(mr.exp[0]); - Wm->as_module->module_tag = Str__duplicate(mr.exp[3]); - Wm->as_module->origin_marker = marker; - Dictionaries__create(D, mr.exp[0]); - Dictionaries__write_value(D, mr.exp[0], Wm); - Regexp__dispose_of(&mr); - return; - -} -#line 363 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 112 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L" *{module} *(%C+) (%C+) (%c+) (%C+) *")) -{ -#line 366 "inweb/Chapter 6/Makefiles.w" - int marker = MAKEFILE_MODULE_MOM; - dictionary *D = MS->modules_dictionary; - -{ -#line 371 "inweb/Chapter 6/Makefiles.w" - WRITE("%SLEAF = %S\n", mr.exp[0], mr.exp[1]); - WRITE("%SWEB = %S\n", mr.exp[0], mr.exp[2]); - WRITE("%SMAKER = $(%SWEB)/%S.mk\n", mr.exp[0], mr.exp[0], mr.exp[1]); - WRITE("%SX = $(%SWEB)/Tangled/%S\n", mr.exp[0], mr.exp[0], mr.exp[1]); - MS->last_line_was_blank = FALSE; - web_md *Wm = Reader__load_web_md(Pathnames__from_text(mr.exp[2]), NULL, MS->search_path, TRUE); - Wm->as_module->module_name = Str__duplicate(mr.exp[0]); - Wm->as_module->module_tag = Str__duplicate(mr.exp[3]); - Wm->as_module->origin_marker = marker; - Dictionaries__create(D, mr.exp[0]); - Dictionaries__write_value(D, mr.exp[0], Wm); - Regexp__dispose_of(&mr); - return; - -} -#line 368 "inweb/Chapter 6/Makefiles.w" -; - -} -#line 113 "inweb/Chapter 6/Makefiles.w" -; - - if (Regexp__match(&mr, line, L"(%c*?) *{dependent-files} *")) -{ -#line 386 "inweb/Chapter 6/Makefiles.w" - WRITE("%S", mr.exp[0]); - Makefiles__pattern(OUT, MS->for_web->md->sections_md, MS->for_web->md->contents_filename); - WRITE("\n"); - MS->last_line_was_blank = FALSE; - Regexp__dispose_of(&mr); - return; - -} -#line 115 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L"(%c*?) *{dependent-files-for-tool-alone} *(%C+)")) - -{ -#line 411 "inweb/Chapter 6/Makefiles.w" - WRITE("%S", mr.exp[0]); - if (Dictionaries__find(MS->tools_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries__read_value(MS->tools_dictionary, mr.exp[1]); - Makefiles__pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename); - } else if (Dictionaries__find(MS->webs_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries__read_value(MS->webs_dictionary, mr.exp[1]); - Makefiles__pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename); - } else { - PRINT("Tool %S\n", mr.exp[0]); - Errors__in_text_file("unknown tool to find dependencies for", tfp); + if (Str__eq(identifier, TL_IS_628)) { + if (Str__is_whitespace(after_matter)) identifier = TL_IS_629; + else identifier = TL_IS_630; } - WRITE("\n"); - MS->last_line_was_blank = FALSE; - Regexp__dispose_of(&mr); - return; - -} -#line 117 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L"(%c*?) *{dependent-files-for-tool-and-modules} *(%C+)")) - -{ -#line 394 "inweb/Chapter 6/Makefiles.w" - WRITE("%S", mr.exp[0]); - if (Dictionaries__find(MS->tools_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries__read_value(MS->tools_dictionary, mr.exp[1]); - Makefiles__pattern(OUT, Wm->sections_md, Wm->contents_filename); - } else if (Dictionaries__find(MS->webs_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries__read_value(MS->webs_dictionary, mr.exp[1]); - Makefiles__pattern(OUT, Wm->sections_md, Wm->contents_filename); - } else { - PRINT("Tool %S\n", mr.exp[0]); - Errors__in_text_file("unknown tool to find dependencies for", tfp); - } - WRITE("\n"); - MS->last_line_was_blank = FALSE; - Regexp__dispose_of(&mr); - return; - -} -#line 119 "inweb/Chapter 6/Makefiles.w" -; - if (Regexp__match(&mr, line, L"(%c*?) *{dependent-files-for-module} *(%C+)")) - -{ -#line 428 "inweb/Chapter 6/Makefiles.w" - WRITE("%S", mr.exp[0]); - if (Dictionaries__find(MS->modules_dictionary, mr.exp[1])) { - web_md *Wm = Dictionaries__read_value(MS->modules_dictionary, mr.exp[1]); - Makefiles__pattern(OUT, Wm->sections_md, Wm->contents_filename); - } else { - Errors__in_text_file("unknown module to find dependencies for", tfp); - WRITE_TO(STDERR, "-- module name: %S\n", mr.exp[1]); - } - WRITE("\n"); - MS->last_line_was_blank = FALSE; - Regexp__dispose_of(&mr); - return; - -} -#line 121 "inweb/Chapter 6/Makefiles.w" -; - - if (Regexp__match(&mr, line, L"(%c*?) *{(%C+) *(%c+?)} *(%c*?)")) -{ -#line 181 "inweb/Chapter 6/Makefiles.w" - text_stream *before_matter = mr.exp[0]; - text_stream *identifier = mr.exp[1]; - text_stream *parameter_settings = mr.exp[2]; - text_stream *after_matter = mr.exp[3]; - - makefile_macro_playback *playback = CREATE(makefile_macro_playback); - playback->which = NULL; - makefile_macro *mm; - LOOP_OVER(mm, makefile_macro) - if (Str__eq(mm->identifier, identifier)) - playback->which = mm; - if (playback->which == NULL) { - Errors__in_text_file("unknown macro or command in braces", tfp); - Regexp__dispose_of(&mr); - return; + if (Str__eq(identifier, TL_IS_631)) { + if ((MS->repeat_sp > 0) && (MS->repeat_data[MS->repeat_sp-1].repeat_is_block)) + identifier = TL_IS_632; + else + identifier = TL_IS_633; } - for (int i=0; iparameter_values[i] = NULL; - - match_results mr2 = Regexp__create_mr(); - while (Regexp__match(&mr2, parameter_settings, L" *(%C+): *(%C+) *(%c*)")) { - text_stream *setting = mr2.exp[0]; - text_stream *value = mr2.exp[1]; - text_stream *remainder = mr2.exp[2]; - int found = FALSE; - for (int i=0; iwhich->no_parameters; i++) - if (Str__eq(setting, playback->which->parameters[i]->name)) { - found = TRUE; - playback->parameter_values[i] = Str__duplicate(value); - } - if (found == FALSE) Errors__in_text_file("unknown parameter in this macro", tfp); - Str__clear(parameter_settings); - Str__copy(parameter_settings, remainder); - } - Regexp__dispose_of(&mr2); - if (Str__is_whitespace(parameter_settings) == FALSE) - Errors__in_text_file("parameter list for this macro is malformed", tfp); - - for (int i=0; iwhich->no_parameters; i++) - if (playback->parameter_values[i] == NULL) - if (playback->which->parameters[i]->optional == FALSE) - Errors__in_text_file("compulsory macro parameter not given", tfp); - - playback->line_position = 0; - playback->prior_to_this = MS->playing_back; - playback->text_to_follow = NULL; - if (Str__is_whitespace(after_matter) == FALSE) - playback->text_to_follow = Str__duplicate(after_matter); - - MS->playing_back = playback; - WRITE("%S", before_matter); - for (int i=0; iwhich->no_lines; i++) { - TEMPORARY_TEXT(line) - text_stream *from = playback->which->lines[i]; - for (int j=0; jwhich->no_parameters; i++) - if (Str__eq(token, playback->which->parameters[i]->definition_token)) { - found = TRUE; - WRITE_TO(line, "%S", playback->parameter_values[i]); - } - if (found == FALSE) closed = FALSE; - } - DISCARD_TEXT(token) - if (closed == FALSE) { j = old_j; PUT_TO(line, '{'); } + if (Makefiles__acceptable_variable_name(identifier)) { + Makefiles__expand(before_matter, tfp, MS); + if (MS->repeat_sp > 0) { + WRITE_TO(MS->dest, "{%S}", identifier); + } else { + makefile_variable *var = Makefiles__find_variable_in(identifier, MS->stack_frame); + if (var) { + WRITE_TO(MS->dest, "%S", var->value); } else { - PUT_TO(line, Str__get_at(from, j)); + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown variable '%S'", identifier); + Errors__in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) } } - Makefiles__scan_makefile_line(line, tfp, (void *) MS); - DISCARD_TEXT(line) - } - MS->playing_back = playback->prior_to_this; - if (Str__is_whitespace(after_matter) == FALSE) - WRITE("%S\n", after_matter); - Regexp__dispose_of(&mr); - return; - -} -#line 123 "inweb/Chapter 6/Makefiles.w" -; - } - Regexp__dispose_of(&mr); - - -{ -#line 442 "inweb/Chapter 6/Makefiles.w" - if (Str__len(line) == 0) { - if (MS->last_line_was_blank == FALSE) WRITE("\n"); - MS->last_line_was_blank = TRUE; + Makefiles__expand(after_matter, tfp, MS); } else { - MS->last_line_was_blank = FALSE; - WRITE("%S\n", line); + makefile_macro *mm = Makefiles__find_macro(MS->known_macros, identifier); + if (mm == NULL) { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown macro '%S'", identifier); + Errors__in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } else { + if (mm->suppress_whitespace_when_expanding) { + while (Characters__is_whitespace(Str__get_last_char(before_matter))) + Str__delete_last_character(before_matter); + while (Characters__is_whitespace(Str__get_first_char(after_matter))) + Str__delete_first_character(after_matter); + } + Makefiles__expand(before_matter, tfp, MS); + int divert_if_repeating = TRUE; + if ((mm) && + ((mm->reserved_macro_meaning == REPEAT_BLOCK_RMM) || + (mm->reserved_macro_meaning == REPEAT_SPAN_RMM))) { + MS->shadow_sp++; + } + if ((mm) && + ((mm->reserved_macro_meaning == END_BLOCK_RMM) || + (mm->reserved_macro_meaning == END_SPAN_RMM))) { + MS->shadow_sp--; + if (MS->shadow_sp == 0) divert_if_repeating = FALSE; + } + + if ((divert_if_repeating) && (MS->repeat_sp > 0)) { + WRITE_TO(MS->dest, "{%S}", braced_matter); + } else { + Makefiles__expand_macro(MS, mm, parameter_settings, tfp); + if (mm->suppress_newline_after_expanding) MS->suppress_newline = TRUE; + } + Makefiles__expand(after_matter, tfp, MS); + } } + Regexp__dispose_of(&mr); } -#line 127 "inweb/Chapter 6/Makefiles.w" +#line 149 "inweb/Chapter 6/Makefiles.w" ; + } else { + WRITE_TO(MS->dest, "%S", text); + } + DISCARD_TEXT(before_matter) + DISCARD_TEXT(braced_matter) + DISCARD_TEXT(after_matter) } -#line 451 "inweb/Chapter 6/Makefiles.w" +#line 233 "inweb/Chapter 6/Makefiles.w" void Makefiles__pathname_slashed(OUTPUT_STREAM, pathname *P) { TEMPORARY_TEXT(PT) WRITE_TO(PT, "%p", P); @@ -31056,7 +30750,7 @@ void Makefiles__pattern(OUTPUT_STREAM, linked_list *L, filename *F) { dictionary *patterns_done = Dictionaries__new(16, TRUE); if (F) { -#line 473 "inweb/Chapter 6/Makefiles.w" +#line 255 "inweb/Chapter 6/Makefiles.w" pathname *P = Filenames__up(F); TEMPORARY_TEXT(leaf_pattern) WRITE_TO(leaf_pattern, "%S", Pathnames__directory_name(P)); @@ -31083,14 +30777,14 @@ void Makefiles__pattern(OUTPUT_STREAM, linked_list *L, filename *F) { DISCARD_TEXT(tester) } -#line 464 "inweb/Chapter 6/Makefiles.w" +#line 246 "inweb/Chapter 6/Makefiles.w" ; section_md *Sm; LOOP_OVER_LINKED_LIST(Sm, section_md, L) { filename *F = Sm->source_file_for_section; { -#line 473 "inweb/Chapter 6/Makefiles.w" +#line 255 "inweb/Chapter 6/Makefiles.w" pathname *P = Filenames__up(F); TEMPORARY_TEXT(leaf_pattern) WRITE_TO(leaf_pattern, "%S", Pathnames__directory_name(P)); @@ -31117,45 +30811,110 @@ void Makefiles__pattern(OUTPUT_STREAM, linked_list *L, filename *F) { DISCARD_TEXT(tester) } -#line 468 "inweb/Chapter 6/Makefiles.w" +#line 250 "inweb/Chapter 6/Makefiles.w" ; } } -#line 501 "inweb/Chapter 6/Makefiles.w" -void Makefiles__repeat(OUTPUT_STREAM, text_stream *prefix, int every_time, text_stream *matter, - int as_lines, text_stream *suffix, text_file_position *tfp, makefile_state *MS, int over, text_stream *tag) { - module *M; - int c = 0; - LOOP_OVER(M, module) { - if ((M->origin_marker == over) && - ((Str__eq(tag, TL_IS_634)) || (Str__eq(tag, M->module_tag)))) { - if ((prefix) && ((c++ > 0) || (every_time))) WRITE("%S", prefix); - if (matter) { - TEMPORARY_TEXT(line) - LOOP_THROUGH_TEXT(pos, matter) { - if (Str__get(pos) == '\n') { - if (as_lines) { - Makefiles__scan_makefile_line(line, tfp, (void *) MS); - Str__clear(line); - } - } else { - if (Str__get(pos) == '@') { - WRITE_TO(line, "%S", M->module_name); - } else { - PUT_TO(line, Str__get(pos)); - } - } - } - if (!as_lines) WRITE("%S", line); - DISCARD_TEXT(line) +#line 283 "inweb/Chapter 6/Makefiles.w" +void Makefiles__repeat(text_stream *matter, + int as_lines, text_file_position *tfp, makefile_repeat *rep, makefile_state *MS) { + int over = rep->repeat_scope; + text_stream *tag = rep->repeat_tag; + MS->stack_frame = Makefiles__new_variable_set(MS->stack_frame); + text_stream *loop_var_name = TL_IS_634; + if (Str__len(rep->repeat_with) > 0) loop_var_name = rep->repeat_with; + makefile_variable *loop_var = Makefiles__ensure_variable(loop_var_name, MS->stack_frame); + if (Str__len(rep->repeat_in) > 0) { + match_results mr = Regexp__create_mr(); + while (Regexp__match(&mr, rep->repeat_in, L"(%c*?),(%c*)")) { + text_stream *value = mr.exp[0]; + Str__trim_white_space(value); + +{ +#line 318 "inweb/Chapter 6/Makefiles.w" + loop_var->value = value; + if (as_lines) { + TEMPORARY_TEXT(line) + LOOP_THROUGH_TEXT(pos, matter) { + if (Str__get(pos) == '\n') { + Makefiles__scan_makefile_line(line, tfp, (void *) MS); + Str__clear(line); + } else { + PUT_TO(line, Str__get(pos)); + } + } + DISCARD_TEXT(line) + } else { + Makefiles__expand(matter, tfp, MS); + } + +} +#line 296 "inweb/Chapter 6/Makefiles.w" +; + Str__clear(rep->repeat_in); + Str__copy(rep->repeat_in, mr.exp[1]); + } + Regexp__dispose_of(&mr); + text_stream *value = rep->repeat_in; + Str__trim_white_space(value); + +{ +#line 318 "inweb/Chapter 6/Makefiles.w" + loop_var->value = value; + if (as_lines) { + TEMPORARY_TEXT(line) + LOOP_THROUGH_TEXT(pos, matter) { + if (Str__get(pos) == '\n') { + Makefiles__scan_makefile_line(line, tfp, (void *) MS); + Str__clear(line); + } else { + PUT_TO(line, Str__get(pos)); + } + } + DISCARD_TEXT(line) + } else { + Makefiles__expand(matter, tfp, MS); + } + +} +#line 303 "inweb/Chapter 6/Makefiles.w" +; + } else { + module *M; + LOOP_OVER(M, module) { + if ((M->origin_marker == over) && + ((Str__eq(tag, TL_IS_635)) || (Str__eq(tag, M->module_tag)))) { + text_stream *value = M->module_name; + +{ +#line 318 "inweb/Chapter 6/Makefiles.w" + loop_var->value = value; + if (as_lines) { + TEMPORARY_TEXT(line) + LOOP_THROUGH_TEXT(pos, matter) { + if (Str__get(pos) == '\n') { + Makefiles__scan_makefile_line(line, tfp, (void *) MS); + Str__clear(line); + } else { + PUT_TO(line, Str__get(pos)); + } + } + DISCARD_TEXT(line) + } else { + Makefiles__expand(matter, tfp, MS); + } + +} +#line 310 "inweb/Chapter 6/Makefiles.w" +; } - if (suffix) WRITE("%S", suffix); } } + MS->stack_frame = MS->stack_frame->outer; } -#line 538 "inweb/Chapter 6/Makefiles.w" +#line 339 "inweb/Chapter 6/Makefiles.w" void Makefiles__seek_INWEBPLATFORM(text_stream *line, text_file_position *tfp, void *X) { text_stream *OUT = (text_stream *) X; match_results mr = Regexp__create_mr(); @@ -31163,6 +30922,612 @@ void Makefiles__seek_INWEBPLATFORM(text_stream *line, text_file_position *tfp, v Regexp__dispose_of(&mr); } +#line 354 "inweb/Chapter 6/Makefiles.w" + +#line 360 "inweb/Chapter 6/Makefiles.w" + +makefile_variable_set *Makefiles__new_variable_set(makefile_variable_set *outer) { + makefile_variable_set *set = CREATE(makefile_variable_set); + set->variables = NEW_LINKED_LIST(makefile_variable); + set->outer = outer; + return set; +} + +makefile_variable *Makefiles__find_variable_in_one(text_stream *name, makefile_variable_set *set) { + if (set == NULL) return NULL; + makefile_variable *var; + LOOP_OVER_LINKED_LIST(var, makefile_variable, set->variables) + if (Str__eq(name, var->name)) + return var; + return NULL; +} + +makefile_variable *Makefiles__find_variable_in(text_stream *name, makefile_variable_set *set) { + while (set) { + makefile_variable *var = Makefiles__find_variable_in_one(name, set); + if (var) return var; + set = set->outer; + } + return NULL; +} + +makefile_variable *Makefiles__ensure_variable(text_stream *name, makefile_variable_set *in_set) { + if (in_set == NULL) internal_error("variable without set"); + makefile_variable *var = Makefiles__find_variable_in_one(name, in_set); + if (var == NULL) { + var = CREATE(makefile_variable); + var->name = Str__duplicate(name); + var->value = TL_IS_636; + ADD_TO_LINKED_LIST(var, makefile_variable, in_set->variables); + } + return var; +} + +int Makefiles__acceptable_variable_name(text_stream *name) { + LOOP_THROUGH_TEXT(pos, name) { + wchar_t c = Str__get(pos); + if ((c >= '0') && (c <= '9')) continue; + if ((c >= 'A') && (c <= 'Z')) continue; + if (c == '_') continue; + return FALSE; + } + return TRUE; +} + +#line 452 "inweb/Chapter 6/Makefiles.w" + +#line 459 "inweb/Chapter 6/Makefiles.w" + +#line 464 "inweb/Chapter 6/Makefiles.w" +makefile_macro *Makefiles__new_macro(linked_list *L, text_stream *name, + text_stream *parameter_specification, text_file_position *tfp) { + if (Makefiles__find_macro(L, name)) + Errors__in_text_file("a macro with this name already exists", tfp); + + makefile_macro *new_macro = CREATE(makefile_macro); + new_macro->identifier = Str__duplicate(name); + new_macro->no_parameters = 0; + new_macro->no_lines = 0; + new_macro->reserved_macro_meaning = UNRESERVED_RMM; + new_macro->suppress_newline_after_expanding = TRUE; + new_macro->suppress_whitespace_when_expanding = TRUE; + + match_results mr2 = Regexp__create_mr(); + while (Regexp__match(&mr2, parameter_specification, L" *(%C+): *(%C+) *(%c*)")) { + text_stream *par_name = mr2.exp[0]; + text_stream *token_name = mr2.exp[1]; + Str__clear(parameter_specification); + Str__copy(parameter_specification, mr2.exp[2]); + if (new_macro->no_parameters >= MAX_MAKEFILE_MACRO_PARAMETERS) { + Errors__in_text_file("too many parameters in this definition", tfp); + } else { + +{ +#line 497 "inweb/Chapter 6/Makefiles.w" + makefile_macro_parameter *new_parameter = CREATE(makefile_macro_parameter); + new_parameter->name = Str__duplicate(par_name); + new_parameter->definition_token = Str__duplicate(token_name); + new_parameter->optional = FALSE; + if (Str__get_first_char(new_parameter->name) == '?') { + new_parameter->optional = TRUE; + Str__delete_first_character(new_parameter->name); + } + new_macro->parameters[new_macro->no_parameters++] = new_parameter; + +} +#line 486 "inweb/Chapter 6/Makefiles.w" +; + } + } + Regexp__dispose_of(&mr2); + if (Str__is_whitespace(parameter_specification) == FALSE) + Errors__in_text_file("parameter list for this definition is malformed", tfp); + ADD_TO_LINKED_LIST(new_macro, makefile_macro, L); + return new_macro; +} + +#line 510 "inweb/Chapter 6/Makefiles.w" +void Makefiles__add_line_to_macro(makefile_macro *mm, text_stream *line, text_file_position *tfp) { + if (mm->no_lines >= MAX_MAKEFILE_MACRO_LINES) { + Errors__in_text_file("too many lines in this definition", tfp); + } else { + mm->lines[mm->no_lines++] = Str__duplicate(line); + } +} + +#line 531 "inweb/Chapter 6/Makefiles.w" + +#line 533 "inweb/Chapter 6/Makefiles.w" +linked_list *Makefiles__list_of_reserved_macros(void) { + linked_list *L = NEW_LINKED_LIST(makefile_macro); + Makefiles__reserve_macro(L, TL_IS_637, NULL, PLATFORM_SETTINGS_RMM); + Makefiles__reserve_macro(L, TL_IS_638, NULL, IDENTITY_SETTINGS_RMM); + Makefiles__reserve_macro(L, TL_IS_639, + TL_IS_640, + COMPONENT_RMM); + Makefiles__reserve_macro(L, TL_IS_641, + TL_IS_642, + DEPENDENT_FILES_RMM); + Makefiles__reserve_macro(L, TL_IS_643, + TL_IS_644, REPEAT_BLOCK_RMM); + Makefiles__reserve_macro(L, TL_IS_645, NULL, END_BLOCK_RMM); + Makefiles__reserve_span_macro(L, TL_IS_646, + TL_IS_647, REPEAT_SPAN_RMM); + Makefiles__reserve_span_macro(L, TL_IS_648, NULL, END_SPAN_RMM); + Makefiles__reserve_span_macro(L, TL_IS_649, + TL_IS_650, SET_RMM); + return L; +} + +void Makefiles__reserve_macro(linked_list *L, text_stream *name, + text_stream *parameter_specification, int rmm) { + makefile_macro *reserved = Makefiles__new_macro(L, name, + Str__duplicate(parameter_specification), NULL); + reserved->reserved_macro_meaning = rmm; +} + +void Makefiles__reserve_span_macro(linked_list *L, text_stream *name, + text_stream *parameter_specification, int rmm) { + makefile_macro *reserved = Makefiles__new_macro(L, name, + Str__duplicate(parameter_specification), NULL); + reserved->reserved_macro_meaning = rmm; + reserved->suppress_newline_after_expanding = FALSE; + reserved->suppress_whitespace_when_expanding = FALSE; +} + +#line 574 "inweb/Chapter 6/Makefiles.w" +makefile_macro *Makefiles__find_macro(linked_list *L, text_stream *name) { + makefile_macro *mm; + LOOP_OVER_LINKED_LIST(mm, makefile_macro, L) + if (Str__eq(mm->identifier, name)) + return mm; + return NULL; +} + +#line 585 "inweb/Chapter 6/Makefiles.w" +void Makefiles__expand_macro(makefile_state *MS, makefile_macro *mm, + text_stream *parameter_settings, text_file_position *tfp) { + text_stream *OUT = MS->dest; + text_stream *parameter_values[MAX_MAKEFILE_MACRO_PARAMETERS]; + for (int i=0; ino_parameters; i++) + if (Str__eq(setting, mm->parameters[i]->name)) { + found = TRUE; + parameter_values[i] = Str__new(); + text_stream *saved = MS->dest; + MS->dest = parameter_values[i]; + Makefiles__expand(value, tfp, MS); + MS->dest = saved; + } + if (found == FALSE) { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown parameter '%S'", setting); + Errors__in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + Str__clear(parameter_settings); + Str__copy(parameter_settings, remainder); + Regexp__dispose_of(&mr3); + } + Regexp__dispose_of(&mr2); + if (Str__is_whitespace(parameter_settings) == FALSE) + Errors__in_text_file("parameter list is malformed", tfp); + + for (int i=0; ino_parameters; i++) + if (parameter_values[i] == NULL) + if (mm->parameters[i]->optional == FALSE) { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "compulsory parameter '%S' not given", mm->parameters[i]->name); + Errors__in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + + switch (mm->reserved_macro_meaning) { + case UNRESERVED_RMM: +{ +#line 650 "inweb/Chapter 6/Makefiles.w" + MS->stack_frame = Makefiles__new_variable_set(MS->stack_frame); + for (int i=0; ino_parameters; i++) { + makefile_variable *var = + Makefiles__ensure_variable(mm->parameters[i]->definition_token, MS->stack_frame); + var->value = parameter_values[i]; + } + for (int i=0; ino_lines; i++) + Makefiles__scan_makefile_line(mm->lines[i], tfp, (void *) MS); + MS->stack_frame = MS->stack_frame->outer; + +} +#line 635 "inweb/Chapter 6/Makefiles.w" +; break; + case PLATFORM_SETTINGS_RMM: +{ +#line 661 "inweb/Chapter 6/Makefiles.w" + filename *prototype = Filenames__in(path_to_inweb, TL_IS_651); + text_stream *INWEBPLATFORM = Str__new(); + TextFiles__read(prototype, FALSE, "can't open platform settings file", + TRUE, Makefiles__seek_INWEBPLATFORM, NULL, INWEBPLATFORM); + if (Str__len(INWEBPLATFORM) == 0) { + Errors__in_text_file( + "found platform settings file, but it does not set INWEBPLATFORM", tfp); + } else { + pathname *P = Pathnames__down(path_to_inweb, TL_IS_652); + P = Pathnames__down(P, TL_IS_653); + WRITE_TO(INWEBPLATFORM, ".mkscript"); + filename *F = Filenames__in(P, INWEBPLATFORM); + TextFiles__read(F, FALSE, "can't open platform definitions file", + TRUE, Makefiles__scan_makefile_line, NULL, MS); + WRITE_TO(STDOUT, "(Read definitions file '%S' from ", INWEBPLATFORM); + Pathnames__to_text_relative(STDOUT, path_to_inweb, P); + WRITE_TO(STDOUT, ")\n"); + } + +} +#line 636 "inweb/Chapter 6/Makefiles.w" +; break; + case IDENTITY_SETTINGS_RMM: +{ +#line 681 "inweb/Chapter 6/Makefiles.w" + WRITE("INWEB = "); Makefiles__pathname_slashed(OUT, path_to_inweb); WRITE("/Tangled/inweb\n"); + pathname *path_to_intest = Pathnames__down(Pathnames__up(path_to_inweb), TL_IS_654); + WRITE("INTEST = "); Makefiles__pathname_slashed(OUT, path_to_intest); WRITE("/Tangled/intest\n"); + if (MS->for_web) { + WRITE("MYNAME = %S\n", Pathnames__directory_name(MS->for_web->md->path_to_web)); + WRITE("ME = "); Makefiles__pathname_slashed(OUT, MS->for_web->md->path_to_web); + WRITE("\n"); + MS->last_line_was_blank = FALSE; + } + +} +#line 637 "inweb/Chapter 6/Makefiles.w" +; break; + case COMPONENT_RMM: +{ +#line 692 "inweb/Chapter 6/Makefiles.w" + text_stream *symbol = parameter_values[0]; + text_stream *webname = parameter_values[1]; + text_stream *path = parameter_values[2]; + text_stream *set = parameter_values[3]; + text_stream *category = parameter_values[4]; + + int marker = -1; + dictionary *D = NULL; + if (Str__eq(category, TL_IS_655)) { + marker = MAKEFILE_TOOL_MOM; + D = MS->tools_dictionary; + } else if (Str__eq(category, TL_IS_656)) { + marker = MAKEFILE_WEB_MOM; + D = MS->webs_dictionary; + } else if (Str__eq(category, TL_IS_657)) { + marker = MAKEFILE_MODULE_MOM; + D = MS->modules_dictionary; + } else { + Errors__in_text_file("category should be 'tool', 'module' or 'web'", tfp); + } + if (D) { + WRITE("%SLEAF = %S\n", symbol, webname); + WRITE("%SWEB = %S\n", symbol, path); + WRITE("%SMAKER = $(%SWEB)/%S.mk\n", symbol, symbol, webname); + WRITE("%SX = $(%SWEB)/Tangled/%S\n", symbol, symbol, webname); + MS->last_line_was_blank = FALSE; + web_md *Wm = Reader__load_web_md(Pathnames__from_text(path), NULL, MS->search_path, TRUE); + Wm->as_module->module_name = Str__duplicate(symbol); + Wm->as_module->module_tag = Str__duplicate(set); + Wm->as_module->origin_marker = marker; + Dictionaries__create(D, symbol); + Dictionaries__write_value(D, symbol, Wm); + } + +} +#line 638 "inweb/Chapter 6/Makefiles.w" +; break; + case DEPENDENT_FILES_RMM: +{ +#line 727 "inweb/Chapter 6/Makefiles.w" + text_stream *tool = parameter_values[0]; + text_stream *modules = parameter_values[1]; + text_stream *both = parameter_values[2]; + if (Str__len(tool) > 0) { + if (Dictionaries__find(MS->tools_dictionary, tool)) { + web_md *Wm = Dictionaries__read_value(MS->tools_dictionary, tool); + Makefiles__pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename); + } else if (Dictionaries__find(MS->webs_dictionary, tool)) { + web_md *Wm = Dictionaries__read_value(MS->webs_dictionary, tool); + Makefiles__pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename); + } else { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown tool '%S' to find dependencies for", tool); + Errors__in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + } else if (Str__len(modules) > 0) { + if (Dictionaries__find(MS->modules_dictionary, modules)) { + web_md *Wm = Dictionaries__read_value(MS->modules_dictionary, modules); + Makefiles__pattern(OUT, Wm->sections_md, Wm->contents_filename); + } else { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown module '%S' to find dependencies for", modules); + Errors__in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + } else if (Str__len(both) > 0) { + if (Dictionaries__find(MS->tools_dictionary, both)) { + web_md *Wm = Dictionaries__read_value(MS->tools_dictionary, both); + Makefiles__pattern(OUT, Wm->sections_md, Wm->contents_filename); + } else if (Dictionaries__find(MS->webs_dictionary, both)) { + web_md *Wm = Dictionaries__read_value(MS->webs_dictionary, both); + Makefiles__pattern(OUT, Wm->sections_md, Wm->contents_filename); + } else { + TEMPORARY_TEXT(erm) + WRITE_TO(erm, "unknown tool '%S' to find dependencies for", both); + Errors__in_text_file_S(erm, tfp); + DISCARD_TEXT(erm) + } + } else { + Makefiles__pattern(OUT, MS->for_web->md->sections_md, MS->for_web->md->contents_filename); + } + WRITE("\n"); + MS->last_line_was_blank = FALSE; + +} +#line 639 "inweb/Chapter 6/Makefiles.w" +; break; + case REPEAT_BLOCK_RMM: +{ +#line 773 "inweb/Chapter 6/Makefiles.w" + if (MS->repeat_sp >= MAX_MAKEFILE_REPEAT_DEPTH) { + Errors__in_text_file("repetition too deep", tfp); + } else { + text_stream *category = parameter_values[0]; + text_stream *set = parameter_values[1]; + text_stream *with = parameter_values[2]; + text_stream *in = parameter_values[3]; + if (Str__len(set) == 0) set = TL_IS_658; + if (Str__eq(category, TL_IS_659)) { + int marker = MAKEFILE_TOOL_MOM; + +{ +#line 800 "inweb/Chapter 6/Makefiles.w" + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = marker; + rep->repeat_tag = Str__duplicate(set); + rep->repeat_with = NULL; + rep->repeat_in = NULL; + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str__new(); + +} +#line 783 "inweb/Chapter 6/Makefiles.w" +; + } else if (Str__eq(category, TL_IS_660)) { + int marker = MAKEFILE_WEB_MOM; + +{ +#line 800 "inweb/Chapter 6/Makefiles.w" + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = marker; + rep->repeat_tag = Str__duplicate(set); + rep->repeat_with = NULL; + rep->repeat_in = NULL; + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str__new(); + +} +#line 786 "inweb/Chapter 6/Makefiles.w" +; + } else if (Str__eq(category, TL_IS_661)) { + int marker = MAKEFILE_MODULE_MOM; + +{ +#line 800 "inweb/Chapter 6/Makefiles.w" + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = marker; + rep->repeat_tag = Str__duplicate(set); + rep->repeat_with = NULL; + rep->repeat_in = NULL; + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str__new(); + +} +#line 789 "inweb/Chapter 6/Makefiles.w" +; + } else if (Str__len(category) > 0) { + Errors__in_text_file("category should be 'tool', 'module' or 'web'", tfp); + } else { + if ((Str__len(with) == 0) || (Str__len(in) == 0)) + Errors__in_text_file("should give both with: VAR and in: LIST", tfp); + +{ +#line 812 "inweb/Chapter 6/Makefiles.w" + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = -1; + rep->repeat_tag = NULL; + rep->repeat_with = Str__duplicate(with); + rep->repeat_in = Str__duplicate(in); + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str__new(); + +} +#line 795 "inweb/Chapter 6/Makefiles.w" +; + } + } + +} +#line 640 "inweb/Chapter 6/Makefiles.w" +; break; + case END_BLOCK_RMM: +{ +#line 824 "inweb/Chapter 6/Makefiles.w" + MS->shadow_sp = 0; + if (MS->repeat_sp == 0) Errors__in_text_file("end without repeat", tfp); + else { + makefile_repeat *rep = &(MS->repeat_data[--(MS->repeat_sp)]); + int as_lines = TRUE; + if (mm->reserved_macro_meaning == END_SPAN_RMM) as_lines = FALSE; + text_stream *matter = MS->dest; + MS->dest = rep->repeat_saved_dest; + Makefiles__repeat(matter, as_lines, tfp, rep, MS); + } + +} +#line 641 "inweb/Chapter 6/Makefiles.w" +; break; + case REPEAT_SPAN_RMM: +{ +#line 773 "inweb/Chapter 6/Makefiles.w" + if (MS->repeat_sp >= MAX_MAKEFILE_REPEAT_DEPTH) { + Errors__in_text_file("repetition too deep", tfp); + } else { + text_stream *category = parameter_values[0]; + text_stream *set = parameter_values[1]; + text_stream *with = parameter_values[2]; + text_stream *in = parameter_values[3]; + if (Str__len(set) == 0) set = TL_IS_658; + if (Str__eq(category, TL_IS_659)) { + int marker = MAKEFILE_TOOL_MOM; + +{ +#line 800 "inweb/Chapter 6/Makefiles.w" + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = marker; + rep->repeat_tag = Str__duplicate(set); + rep->repeat_with = NULL; + rep->repeat_in = NULL; + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str__new(); + +} +#line 783 "inweb/Chapter 6/Makefiles.w" +; + } else if (Str__eq(category, TL_IS_660)) { + int marker = MAKEFILE_WEB_MOM; + +{ +#line 800 "inweb/Chapter 6/Makefiles.w" + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = marker; + rep->repeat_tag = Str__duplicate(set); + rep->repeat_with = NULL; + rep->repeat_in = NULL; + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str__new(); + +} +#line 786 "inweb/Chapter 6/Makefiles.w" +; + } else if (Str__eq(category, TL_IS_661)) { + int marker = MAKEFILE_MODULE_MOM; + +{ +#line 800 "inweb/Chapter 6/Makefiles.w" + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = marker; + rep->repeat_tag = Str__duplicate(set); + rep->repeat_with = NULL; + rep->repeat_in = NULL; + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str__new(); + +} +#line 789 "inweb/Chapter 6/Makefiles.w" +; + } else if (Str__len(category) > 0) { + Errors__in_text_file("category should be 'tool', 'module' or 'web'", tfp); + } else { + if ((Str__len(with) == 0) || (Str__len(in) == 0)) + Errors__in_text_file("should give both with: VAR and in: LIST", tfp); + +{ +#line 812 "inweb/Chapter 6/Makefiles.w" + makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]); + MS->shadow_sp = 1; + rep->repeat_scope = -1; + rep->repeat_tag = NULL; + rep->repeat_with = Str__duplicate(with); + rep->repeat_in = Str__duplicate(in); + rep->repeat_is_block = TRUE; + if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE; + rep->repeat_saved_dest = MS->dest; + MS->dest = Str__new(); + +} +#line 795 "inweb/Chapter 6/Makefiles.w" +; + } + } + +} +#line 642 "inweb/Chapter 6/Makefiles.w" +; break; + case END_SPAN_RMM: +{ +#line 824 "inweb/Chapter 6/Makefiles.w" + MS->shadow_sp = 0; + if (MS->repeat_sp == 0) Errors__in_text_file("end without repeat", tfp); + else { + makefile_repeat *rep = &(MS->repeat_data[--(MS->repeat_sp)]); + int as_lines = TRUE; + if (mm->reserved_macro_meaning == END_SPAN_RMM) as_lines = FALSE; + text_stream *matter = MS->dest; + MS->dest = rep->repeat_saved_dest; + Makefiles__repeat(matter, as_lines, tfp, rep, MS); + } + +} +#line 643 "inweb/Chapter 6/Makefiles.w" +; break; + case SET_RMM: +{ +#line 836 "inweb/Chapter 6/Makefiles.w" + text_stream *name = parameter_values[0]; + text_stream *value = parameter_values[1]; + + if (Makefiles__acceptable_variable_name(name) == FALSE) + Errors__in_text_file("improper variable name", tfp); + + makefile_variable *var = Makefiles__ensure_variable(name, MS->stack_frame); + var->value = Str__duplicate(value); + +} +#line 644 "inweb/Chapter 6/Makefiles.w" +; break; + default: internal_error("unimplemented reserved macro"); + } +} + #line 14 "inweb/Chapter 6/Git Support.w" void Git__write_gitignore(web *W, filename *prototype, filename *F) { @@ -31191,7 +31556,7 @@ void Git__copy_gitignore_line(text_stream *line, text_file_position *tfp, void * { #line 44 "inweb/Chapter 6/Git Support.w" filename *prototype = - Filenames__in(path_to_inweb_materials, TL_IS_635); + Filenames__in(path_to_inweb_materials, TL_IS_662); TextFiles__read(prototype, FALSE, "can't open make settings file", TRUE, Git__copy_gitignore_line, NULL, MS); Regexp__dispose_of(&mr); @@ -31226,7 +31591,7 @@ void Ctags__write(web *W, filename *F) { P = Filenames__up(F); } else { P = W->md->path_to_web; - F = Filenames__in(P, TL_IS_636); + F = Filenames__in(P, TL_IS_663); } text_stream *OUT = &ctags_file; if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE) @@ -31238,7 +31603,7 @@ void Ctags__write(web *W, filename *F) { WRITE("!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/\n"); WRITE("!_TAG_PROGRAM_AUTHOR\tGraham Nelson\t/graham.nelson@mod-langs.ox.ac.uk/\n"); WRITE("!_TAG_PROGRAM_NAME\tinweb\t//\n"); - WRITE("!_TAG_PROGRAM_VERSION\t7-alpha.1+1A90\t/built 19 April 2022/\n"); + WRITE("!_TAG_PROGRAM_VERSION\t7-alpha.1+1A91\t/built 22 April 2022/\n"); } #line 47 "inweb/Chapter 6/Ctags Support.w" @@ -31332,11 +31697,11 @@ void Readme__write(filename *from, filename *to) { write_state ws; ws.current_definition = NULL; ws.known_macros = NEW_LINKED_LIST(macro); - macro *V = Readme__new_macro(TL_IS_637, NULL, NULL); + macro *V = Readme__new_macro(TL_IS_664, NULL, NULL); ADD_TO_LINKED_LIST(V, macro, ws.known_macros); - macro *P = Readme__new_macro(TL_IS_638, NULL, NULL); + macro *P = Readme__new_macro(TL_IS_665, NULL, NULL); ADD_TO_LINKED_LIST(P, macro, ws.known_macros); - macro *A = Readme__new_macro(TL_IS_639, NULL, NULL); + macro *A = Readme__new_macro(TL_IS_666, NULL, NULL); ADD_TO_LINKED_LIST(A, macro, ws.known_macros); ws.stack_frame = NULL; text_stream file_to; @@ -31372,7 +31737,7 @@ void Readme__write_helper(text_stream *text, text_file_position *tfp, void *stat } } else { Readme__expand_material(ws, OUT, text, tfp); - Readme__expand_material(ws, OUT, TL_IS_640, tfp); + Readme__expand_material(ws, OUT, TL_IS_667, tfp); } Regexp__dispose_of(&mr); } @@ -31503,7 +31868,7 @@ void Readme__expand_at(write_state *ws, text_stream *OUT, text_stream *macro_nam #line 204 "inweb/Chapter 6/Readme Writeme.w" void Readme__expand_macro(write_state *ws, text_stream *OUT, macro *M, text_file_position *tfp) { - if (Str__eq(M->name, TL_IS_641)) + if (Str__eq(M->name, TL_IS_668)) { #line 215 "inweb/Chapter 6/Readme Writeme.w" if (ws->stack_frame->no_pars != 1) @@ -31511,14 +31876,14 @@ void Readme__expand_macro(write_state *ws, text_stream *OUT, macro *M, text_file else { TEMPORARY_TEXT(program) Readme__expand_material(ws, program, ws->stack_frame->pars[0], tfp); - Readme__write_var(OUT, program, TL_IS_644); + Readme__write_var(OUT, program, TL_IS_671); DISCARD_TEXT(program) } } #line 205 "inweb/Chapter 6/Readme Writeme.w" - else if (Str__eq(M->name, TL_IS_642)) + else if (Str__eq(M->name, TL_IS_669)) { #line 225 "inweb/Chapter 6/Readme Writeme.w" if (ws->stack_frame->no_pars != 1) @@ -31526,14 +31891,14 @@ void Readme__expand_macro(write_state *ws, text_stream *OUT, macro *M, text_file else { TEMPORARY_TEXT(program) Readme__expand_material(ws, program, ws->stack_frame->pars[0], tfp); - Readme__write_var(OUT, program, TL_IS_645); + Readme__write_var(OUT, program, TL_IS_672); DISCARD_TEXT(program) } } #line 206 "inweb/Chapter 6/Readme Writeme.w" - else if (Str__eq(M->name, TL_IS_643)) + else if (Str__eq(M->name, TL_IS_670)) { #line 235 "inweb/Chapter 6/Readme Writeme.w" if (ws->stack_frame->no_pars != 2) @@ -31562,8 +31927,8 @@ void Readme__expand_macro(write_state *ws, text_stream *OUT, macro *M, text_file void Readme__write_var(text_stream *OUT, text_stream *program, text_stream *datum) { writeme_asset *A = Readme__find_asset(program); if (A->if_web) WRITE("%S", Bibliographic__get_datum(A->if_web, datum)); - else if (Str__eq(datum, TL_IS_646)) WRITE("%S", A->date); - else if (Str__eq(datum, TL_IS_647)) WRITE("%S", A->version); + else if (Str__eq(datum, TL_IS_673)) WRITE("%S", A->date); + else if (Str__eq(datum, TL_IS_674)) WRITE("%S", A->version); } #line 273 "inweb/Chapter 6/Readme Writeme.w" @@ -31594,7 +31959,7 @@ writeme_asset *Readme__find_asset(text_stream *program) { A->if_web = WebMetadata__get_without_modules(Pathnames__from_text(program), NULL); } else { filename *I6_vn = Filenames__in( - Pathnames__down(Pathnames__from_text(program), TL_IS_648), TL_IS_649); + Pathnames__down(Pathnames__from_text(program), TL_IS_675), TL_IS_676); if (TextFiles__exists(I6_vn)) { #line 310 "inweb/Chapter 6/Readme Writeme.w" @@ -31604,7 +31969,7 @@ writeme_asset *Readme__find_asset(text_stream *program) { } #line 295 "inweb/Chapter 6/Readme Writeme.w" ; - filename *template_vn = Filenames__in(Pathnames__from_text(program), TL_IS_650); + filename *template_vn = Filenames__in(Pathnames__from_text(program), TL_IS_677); if (TextFiles__exists(template_vn)) { #line 314 "inweb/Chapter 6/Readme Writeme.w" @@ -31614,7 +31979,7 @@ writeme_asset *Readme__find_asset(text_stream *program) { } #line 297 "inweb/Chapter 6/Readme Writeme.w" ; - filename *rmt_vn = Filenames__in(Pathnames__from_text(program), TL_IS_651); + filename *rmt_vn = Filenames__in(Pathnames__from_text(program), TL_IS_678); if (TextFiles__exists(rmt_vn)) { #line 318 "inweb/Chapter 6/Readme Writeme.w" @@ -31624,7 +31989,7 @@ writeme_asset *Readme__find_asset(text_stream *program) { } #line 299 "inweb/Chapter 6/Readme Writeme.w" ; - rmt_vn = Filenames__in(Pathnames__from_text(program), TL_IS_652); + rmt_vn = Filenames__in(Pathnames__from_text(program), TL_IS_679); if (TextFiles__exists(rmt_vn)) { #line 318 "inweb/Chapter 6/Readme Writeme.w" @@ -31699,7 +32064,7 @@ void Readme__readme_harvester(text_stream *text, text_file_position *tfp, void * void Colonies__load(filename *F) { colony *C = CREATE(colony); C->members = NEW_LINKED_LIST(colony_member); - C->home = TL_IS_653; + C->home = TL_IS_680; C->assets_path = NULL; C->patterns_path = NULL; colony_reader_state crs; @@ -31723,8 +32088,8 @@ void Colonies__read_line(text_stream *line, text_file_position *tfp, void *v_crs match_results mr = Regexp__create_mr(); if (Regexp__match(&mr, line, L"(%c*?): \"*(%C+)\" at \"(%c*)\" in \"(%c*)\"")) { colony_member *CM = CREATE(colony_member); - if (Str__eq(mr.exp[0], TL_IS_654)) CM->web_rather_than_module = TRUE; - else if (Str__eq(mr.exp[0], TL_IS_655)) CM->web_rather_than_module = FALSE; + if (Str__eq(mr.exp[0], TL_IS_681)) CM->web_rather_than_module = TRUE; + else if (Str__eq(mr.exp[0], TL_IS_682)) CM->web_rather_than_module = FALSE; else { CM->web_rather_than_module = FALSE; Errors__in_text_file("text before ':' must be 'web' or 'module'", tfp); @@ -31732,7 +32097,7 @@ void Colonies__read_line(text_stream *line, text_file_position *tfp, void *v_crs CM->name = Str__duplicate(mr.exp[1]); CM->path = Str__duplicate(mr.exp[2]); CM->home_leaf = Str__new(); - if (Str__suffix_eq(CM->path, TL_IS_656, 6)) { + if (Str__suffix_eq(CM->path, TL_IS_683, 6)) { filename *F = Filenames__from_text(CM->path); Filenames__write_unextended_leafname(CM->home_leaf, F); WRITE_TO(CM->home_leaf, ".html"); @@ -31872,7 +32237,7 @@ module *Colonies__as_module(colony_member *CM, source_line *L, web_md *Wm) { #line 254 "inweb/Chapter 6/Colonies.w" filename *F = NULL; pathname *P = NULL; - if (Str__suffix_eq(CM->path, TL_IS_657, 6)) + if (Str__suffix_eq(CM->path, TL_IS_684, 6)) F = Filenames__from_text(CM->path); else P = Pathnames__from_text(CM->path); @@ -31899,7 +32264,7 @@ text_stream *Colonies__home(void) { colony *C; LOOP_OVER(C, colony) return C->home; - return TL_IS_658; + return TL_IS_685; } pathname *Colonies__assets_path(void) { @@ -32091,7 +32456,7 @@ int Colonies__resolve_reference_in_weave_inner(text_stream *url, text_stream *ti return FALSE; } if (N > 1) { - Main__error_in_web(TL_IS_659, L); + Main__error_in_web(TL_IS_686, L); WebModules__named_reference(&found_M, &found_Sm, &bare_module_name, title, search_M, text, TRUE, FALSE); return FALSE; @@ -32173,7 +32538,7 @@ void Colonies__paragraph_URL(OUTPUT_STREAM, paragraph *P, filename *from) { if (P == NULL) internal_error("no para"); section *to_S = P->under_section; module *to_M = to_S->md->owning_module; - if (Str__ne(to_M->module_name, TL_IS_660)) { + if (Str__ne(to_M->module_name, TL_IS_687)) { colony_member *to_C = Colonies__find(to_M->module_name); if (to_C) { pathname *from_path = Filenames__up(from); @@ -32830,37 +33195,64 @@ void register_tangled_text_literals(void) { TL_IS_625 = Str__literal(L"t"); TL_IS_626 = Str__literal(L"exists"); TL_IS_627 = Str__literal(L"forall"); - TL_IS_628 = Str__literal(L" "); - TL_IS_629 = Str__literal(L"all"); - TL_IS_630 = Str__literal(L"platform-settings.mk"); - TL_IS_631 = Str__literal(L"Materials"); - TL_IS_632 = Str__literal(L"platforms"); - TL_IS_633 = Str__literal(L"intest"); - TL_IS_634 = Str__literal(L"all"); - TL_IS_635 = Str__literal(L"gitignorescript.txt"); - TL_IS_636 = Str__literal(L"tags"); - TL_IS_637 = Str__literal(L"version"); - TL_IS_638 = Str__literal(L"purpose"); - TL_IS_639 = Str__literal(L"var"); - TL_IS_640 = Str__literal(L"\n"); - TL_IS_641 = Str__literal(L"version"); - TL_IS_642 = Str__literal(L"purpose"); - TL_IS_643 = Str__literal(L"var"); - TL_IS_644 = Str__literal(L"Version Number"); - TL_IS_645 = Str__literal(L"Purpose"); - TL_IS_646 = Str__literal(L"Build Date"); - TL_IS_647 = Str__literal(L"Version Number"); - TL_IS_648 = Str__literal(L"inform6"); - TL_IS_649 = Str__literal(L"header.h"); - TL_IS_650 = Str__literal(L"(manifest).txt"); - TL_IS_651 = Str__literal(L"README.txt"); - TL_IS_652 = Str__literal(L"README.md"); - TL_IS_653 = Str__literal(L"docs"); - TL_IS_654 = Str__literal(L"web"); - TL_IS_655 = Str__literal(L"module"); - TL_IS_656 = Str__literal(L".inweb"); - TL_IS_657 = Str__literal(L".inweb"); - TL_IS_658 = Str__literal(L"docs"); - TL_IS_659 = Str__literal(L"Multiple cross-references might be meant here"); - TL_IS_660 = Str__literal(L"(main)"); + TL_IS_628 = Str__literal(L"repeat"); + TL_IS_629 = Str__literal(L"repeat-block"); + TL_IS_630 = Str__literal(L"repeat-span"); + TL_IS_631 = Str__literal(L"end-repeat"); + TL_IS_632 = Str__literal(L"end-block"); + TL_IS_633 = Str__literal(L"end-span"); + TL_IS_634 = Str__literal(L"NAME"); + TL_IS_635 = Str__literal(L"all"); + TL_IS_636 = Str__literal(L""); + TL_IS_637 = Str__literal(L"platform-settings"); + TL_IS_638 = Str__literal(L"identity-settings"); + TL_IS_639 = Str__literal(L"component"); + TL_IS_640 = Str__literal(L"symbol: SYMBOL webname: WEBNAME path: PATH set: SET category: CATEGORY"); + TL_IS_641 = Str__literal(L"dependent-files"); + TL_IS_642 = Str__literal(L"?tool: TOOL ?module: MODULES ?tool-and-modules: BOTH"); + TL_IS_643 = Str__literal(L"repeat-block"); + TL_IS_644 = Str__literal(L"?over: CATEGORY ?set: SET ?with: WITH ?in: IN"); + TL_IS_645 = Str__literal(L"end-block"); + TL_IS_646 = Str__literal(L"repeat-span"); + TL_IS_647 = Str__literal(L"?over: CATEGORY ?set: SET ?with: WITH ?in: IN"); + TL_IS_648 = Str__literal(L"end-span"); + TL_IS_649 = Str__literal(L"set"); + TL_IS_650 = Str__literal(L"name: NAME value: VALUE"); + TL_IS_651 = Str__literal(L"platform-settings.mk"); + TL_IS_652 = Str__literal(L"Materials"); + TL_IS_653 = Str__literal(L"platforms"); + TL_IS_654 = Str__literal(L"intest"); + TL_IS_655 = Str__literal(L"tool"); + TL_IS_656 = Str__literal(L"web"); + TL_IS_657 = Str__literal(L"module"); + TL_IS_658 = Str__literal(L"all"); + TL_IS_659 = Str__literal(L"tool"); + TL_IS_660 = Str__literal(L"web"); + TL_IS_661 = Str__literal(L"module"); + TL_IS_662 = Str__literal(L"gitignorescript.txt"); + TL_IS_663 = Str__literal(L"tags"); + TL_IS_664 = Str__literal(L"version"); + TL_IS_665 = Str__literal(L"purpose"); + TL_IS_666 = Str__literal(L"var"); + TL_IS_667 = Str__literal(L"\n"); + TL_IS_668 = Str__literal(L"version"); + TL_IS_669 = Str__literal(L"purpose"); + TL_IS_670 = Str__literal(L"var"); + TL_IS_671 = Str__literal(L"Version Number"); + TL_IS_672 = Str__literal(L"Purpose"); + TL_IS_673 = Str__literal(L"Build Date"); + TL_IS_674 = Str__literal(L"Version Number"); + TL_IS_675 = Str__literal(L"inform6"); + TL_IS_676 = Str__literal(L"header.h"); + TL_IS_677 = Str__literal(L"(manifest).txt"); + TL_IS_678 = Str__literal(L"README.txt"); + TL_IS_679 = Str__literal(L"README.md"); + TL_IS_680 = Str__literal(L"docs"); + TL_IS_681 = Str__literal(L"web"); + TL_IS_682 = Str__literal(L"module"); + TL_IS_683 = Str__literal(L".inweb"); + TL_IS_684 = Str__literal(L".inweb"); + TL_IS_685 = Str__literal(L"docs"); + TL_IS_686 = Str__literal(L"Multiple cross-references might be meant here"); + TL_IS_687 = Str__literal(L"(main)"); } diff --git a/docs/goldbach/goldbach.pdf b/docs/goldbach/goldbach.pdf index 35aa57a..f783c21 100644 Binary files a/docs/goldbach/goldbach.pdf and b/docs/goldbach/goldbach.pdf differ diff --git a/docs/inweb/1-bsc.html b/docs/inweb/1-bsc.html index dc9c501..f78e3dd 100644 --- a/docs/inweb/1-bsc.html +++ b/docs/inweb/1-bsc.html @@ -71,7 +71,8 @@ see A Brief Guide t enum macro_usage_CLASS enum makefile_macro_CLASS enum makefile_macro_parameter_CLASS -enum makefile_macro_playback_CLASS +enum makefile_variable_CLASS +enum makefile_variable_set_CLASS enum nonterminal_variable_CLASS enum para_macro_CLASS enum paragraph_CLASS @@ -164,7 +165,8 @@ see A Brief Guide t DECLARE_CLASS(macro) DECLARE_CLASS(makefile_macro) DECLARE_CLASS(makefile_macro_parameter) -DECLARE_CLASS(makefile_macro_playback) +DECLARE_CLASS(makefile_variable) +DECLARE_CLASS(makefile_variable_set) DECLARE_CLASS(nonterminal_variable) DECLARE_CLASS(para_macro) DECLARE_CLASS(paragraph_tagging) diff --git a/docs/inweb/1-pc.html b/docs/inweb/1-pc.html index b8e7950..7372dbf 100644 --- a/docs/inweb/1-pc.html +++ b/docs/inweb/1-pc.html @@ -186,7 +186,7 @@ program: some input, some thinking, a choice of three forms of output. if ((ins->writeme_setting) && (ins->prototype_setting == NULL)) ins->prototype_setting = Filenames::from_text(I"READMEscript.txt"); if (ins->makefile_setting) - Makefiles::write(W, ins->prototype_setting, ins->makefile_setting, + Makefiles::write(W, ins->prototype_setting, ins->makefile_setting, WebModules::make_search_path(ins->import_setting)); else if (ins->gitignore_setting) Git::write_gitignore(W, ins->prototype_setting, ins->gitignore_setting); @@ -235,7 +235,7 @@ program: some input, some thinking, a choice of three forms of output.

-    Reader::print_web_statistics(W);
+    if (ins->inweb_mode != ANALYSE_MODE) Reader::print_web_statistics(W);
     if (ins->inweb_mode == ANALYSE_MODE) Analyse the web7.4.1;
     if (ins->inweb_mode == TANGLE_MODE) Tangle the web7.4.2;
     if (ins->inweb_mode == WEAVE_MODE) Weave the web7.4.3;
diff --git a/docs/inweb/2-tr.html b/docs/inweb/2-tr.html
index 2b366ed..aae2aa2 100644
--- a/docs/inweb/2-tr.html
+++ b/docs/inweb/2-tr.html
@@ -152,7 +152,7 @@ each of which has a list of s
 

-web_md *Reader::load_web_md(pathname *P, filename *alt_F, module_search *I,
+web_md *Reader::load_web_md(pathname *P, filename *alt_F, module_search *I,
     int including_modules) {
     return WebMetadata::get(P, alt_F, default_inweb_syntax, I, verbose_mode,
         including_modules, path_to_inweb);
diff --git a/docs/inweb/3-ta.html b/docs/inweb/3-ta.html
index abac89b..8dc0ec2 100644
--- a/docs/inweb/3-ta.html
+++ b/docs/inweb/3-ta.html
@@ -489,7 +489,7 @@ folder: failing that, we fall back on a default script belonging to Inweb.
     DISCARD_TEXT(leafname)
     if (!(TextFiles::exists(prototype)))
         prototype = Filenames::in(path_to_inweb_materials, I"default.mkscript");
-    Makefiles::write(W, prototype, F, I);
+    Makefiles::write(W, prototype, F, I);
 }
 
 void Analyser::write_gitignore(web *W, filename *F) {
diff --git a/docs/inweb/6-mkf.html b/docs/inweb/6-mkf.html
index 51e9a84..7f98b4a 100644
--- a/docs/inweb/6-mkf.html
+++ b/docs/inweb/6-mkf.html
@@ -50,559 +50,259 @@ function togglePopup(material_id) {
     
 

Constructing a suitable makefile for a simple inweb project.

-

§1. This section offers just one function, which constructs a makefile by -following a "prototype". +


+ +

§1. Introduction. At some point, the material in this section will probably be spun out into an +an independent tool called "inmake". It's a simple utility for constructing makefiles, +but has gradually become less simple over time, as is the way of these things.

-
define MAX_MAKEFILE_MACRO_PARAMETERS 8
-define MAX_MAKEFILE_MACRO_LINES 128
+

The idea is simple enough: the user writes a "makescript", which is really a +makefile but with the possibility of using some higher-level features, and we +translate that it into an actual makefile (which is usually longer and less +easy to read). +

+ +

§2. State.

+ +
define MAX_MAKEFILE_REPEAT_DEPTH 8
 
-typedef struct makefile_macro {
-    struct text_stream *identifier;
-    struct makefile_macro_parameter *parameters[MAX_MAKEFILE_MACRO_PARAMETERS];
-    int no_parameters;
-    struct text_stream *lines[MAX_MAKEFILE_MACRO_LINES];
-    int no_lines;
-    CLASS_DEFINITION
-} makefile_macro;
-
-typedef struct makefile_macro_parameter {
-    struct text_stream *name;
-    struct text_stream *definition_token;
-    int optional;
-    CLASS_DEFINITION
-} makefile_macro_parameter;
-
-typedef struct makefile_macro_playback {
-    struct makefile_macro *which;
-    struct text_stream *parameter_values[MAX_MAKEFILE_MACRO_PARAMETERS];
-    int line_position;
-    struct makefile_macro_playback *prior_to_this;
-    struct text_stream *text_to_follow;
-    CLASS_DEFINITION
-} makefile_macro_playback;
+typedef struct makefile_repeat {
+    int repeat_scope;  during a repeat, either MAKEFILE_TOOL_MOM or MAKEFILE_MODULE_MOM
+    struct text_stream *repeat_tag;
+    struct text_stream *repeat_with;
+    struct text_stream *repeat_in;
+    int repeat_is_block;
+    struct text_stream *repeat_saved_dest;
+} makefile_repeat;
 
 typedef struct makefile_state {
     struct web *for_web;
-    struct text_stream to_makefile;
-    struct text_stream *repeat_block;  a "repeatblock" body being scanned
+    struct text_stream *dest;
     struct makefile_macro *defining;  a "define" body being scanned
-    struct makefile_macro_playback *playing_back;
-    int inside_block;  scanning a "repeatblock" into that text?
+    int repeat_sp;
+    int shadow_sp;
+    struct makefile_repeat repeat_data[MAX_MAKEFILE_REPEAT_DEPTH];
+    int suppress_newline;  at the end of this line
     int last_line_was_blank;  used to suppress runs of multiple blank lines
-    int allow_commands;  permit the prototype to use special commands
-    int repeat_scope;  during a repeat, either MAKEFILE_TOOL_MOM or MAKEFILE_MODULE_MOM
-    struct text_stream *repeat_tag;
     struct dictionary *tools_dictionary;
     struct dictionary *webs_dictionary;
     struct dictionary *modules_dictionary;
     struct module_search *search_path;
+    struct makefile_variable_set *global_variables;
+    struct makefile_variable_set *stack_frame;
+    struct linked_list *known_macros;  of makefile_macro
 } makefile_state;
 
 void Makefiles::write(web *W, filename *prototype, filename *F, module_search *I) {
+    struct text_stream makefile;
+    if (STREAM_OPEN_TO_FILE(&makefile, F, ISO_ENC) == FALSE)
+        Errors::fatal_with_file("unable to write tangled file", F);
+    WRITE_TO(STDOUT, "(Read script from %f)\n", prototype);
+    text_stream *OUT = &makefile;
+
     makefile_state MS;
+    MS.dest = OUT;
     MS.for_web = W;
+    MS.suppress_newline = FALSE;
     MS.last_line_was_blank = TRUE;
-    MS.repeat_block = Str::new();
     MS.defining = NULL;
-    MS.playing_back = NULL;
-    MS.inside_block = FALSE;
-    MS.allow_commands = TRUE;
+    MS.repeat_sp = 0;
+    MS.shadow_sp = 0;
     MS.tools_dictionary = Dictionaries::new(16, FALSE);
     MS.webs_dictionary = Dictionaries::new(16, FALSE);
     MS.modules_dictionary = Dictionaries::new(16, FALSE);
     MS.search_path = I;
-    MS.repeat_scope = -1;
-    MS.repeat_tag = NULL;
-    text_stream *OUT = &(MS.to_makefile);
-    if (STREAM_OPEN_TO_FILE(OUT, F, ISO_ENC) == FALSE)
-        Errors::fatal_with_file("unable to write tangled file", F);
+    MS.global_variables = Makefiles::new_variable_set(NULL);
+    MS.stack_frame = MS.global_variables;
+    MS.known_macros = Makefiles::list_of_reserved_macros();
     WRITE("# This makefile was automatically written by inweb -makefile\n");
     WRITE("# and is not intended for human editing\n\n");
     TextFiles::read(prototype, FALSE, "can't open prototype file",
-        TRUE, Makefiles::scan_makefile_line, NULL, &MS);
+        TRUE, Makefiles::scan_makefile_line, NULL, &MS);
     STREAM_CLOSE(OUT);
-    WRITE_TO(STDOUT, "Wrote makefile '%f' from script '%f'\n", F, prototype);
 }
 
-
  • The structure makefile_macro is private to this section.
  • The structure makefile_macro_parameter is accessed in 2/trs, 5/wt, 6/cs, 6/rw, 6/cln and here.
  • The structure makefile_macro_playback is accessed in 4/tf and here.
  • The structure makefile_state is accessed in 3/tc, 4/pl, 6/gs and here.
-

§2.

+
  • The structure makefile_repeat is private to this section.
  • The structure makefile_state is accessed in 3/tc, 4/pl, 6/gs, 6/rw and here.
+

§3. Scanner.

-void Makefiles::scan_makefile_line(text_stream *line, text_file_position *tfp, void *X) {
+void Makefiles::scan_makefile_line(text_stream *line, text_file_position *tfp, void *X) {
     makefile_state *MS = (makefile_state *) X;
-    text_stream *OUT = &(MS->to_makefile);
-
     match_results mr = Regexp::create_mr();
     if (Regexp::match(&mr, line, L" *#%c*")) { Regexp::dispose_of(&mr); return; }  Skip comment lines
-    if (MS->allow_commands) {
-        if (Regexp::match(&mr, line, L" *{define: *(%C+) (%c*)} *")) Begin a definition2.1;
-        if (Regexp::match(&mr, line, L" *{end-define} *")) End a definition2.3;
-        if (MS->defining) Continue a definition2.2;
 
-        if (Regexp::match(&mr, line, L" *{repeat-tools-block:(%C*)} *"))
-            Begin a repeat tool block2.5;
-        if (Regexp::match(&mr, line, L" *{repeat-webs-block:(%C*)} *"))
-            Begin a repeat web block2.6;
-        if (Regexp::match(&mr, line, L" *{repeat-modules-block:(%C*)} *"))
-            Begin a repeat module block2.7;
-        if (Regexp::match(&mr, line, L" *{end-block} *")) End a repeat block2.9;
-        if (MS->inside_block) Deal with a line in a repeat block2.8;
-
-        if (Regexp::match(&mr, line, L"(%c*){repeat-tools-span}(%c*?){end-span}(%c*)"))
-            Deal with a repeat span2.10;
-        if (Regexp::match(&mr, line, L"(%c*){repeat-webs-span}(%c*?){end-span}(%c*)"))
-            Deal with a repeat web span2.11;
-        if (Regexp::match(&mr, line, L"(%c*){repeat-modules-span}(%c*?){end-span}(%c*)"))
-            Deal with a repeat module span2.12;
-
-        if (Regexp::match(&mr, line, L" *{identity-settings} *")) Expand identity-settings2.14;
-        if (Regexp::match(&mr, line, L" *{platform-settings} *")) Expand platform-settings2.13;
-
-        if (Regexp::match(&mr, line, L" *{tool} *(%C+) (%C+) (%c+) (%C+) *")) Declare a tool2.15;
-        if (Regexp::match(&mr, line, L" *{web} *(%C+) (%C+) (%c+) (%C+) *")) Declare a web2.16;
-        if (Regexp::match(&mr, line, L" *{module} *(%C+) (%C+) (%c+) (%C+) *")) Declare a module2.17;
-
-        if (Regexp::match(&mr, line, L"(%c*?) *{dependent-files} *")) Expand dependent-files2.18;
-        if (Regexp::match(&mr, line, L"(%c*?) *{dependent-files-for-tool-alone} *(%C+)"))
-            Expand dependent-files-for-tool-alone2.20;
-        if (Regexp::match(&mr, line, L"(%c*?) *{dependent-files-for-tool-and-modules} *(%C+)"))
-            Expand dependent-files-for-tool2.19;
-        if (Regexp::match(&mr, line, L"(%c*?) *{dependent-files-for-module} *(%C+)"))
-            Expand dependent-files-for-module2.21;
-
-        if (Regexp::match(&mr, line, L"(%c*?) *{(%C+) *(%c+?)} *(%c*?)")) Expand a macro2.4;
-    }
+    if (Regexp::match(&mr, line, L" *{define: *(%C+) (%c*)} *")) Begin a definition3.1;
+    if (Regexp::match(&mr, line, L" *{end-define} *")) End a definition3.3;
+    if (MS->defining) Continue a definition3.2;
     Regexp::dispose_of(&mr);
 
-    And otherwise copy the line straight through2.22;
+    Makefiles::expand(line, tfp, MS);
+
+    if (MS->suppress_newline == FALSE) {
+        text_stream *OUT = MS->dest;
+        if (Str::len(line) == 0) {
+            if (MS->last_line_was_blank == FALSE) WRITE("\n");
+            MS->last_line_was_blank = TRUE;
+        } else {
+            MS->last_line_was_blank = FALSE;
+            WRITE("\n");
+        }
+    }
+    MS->suppress_newline = FALSE;
 }
 
-

§2.1. Begin a definition2.1 = +

§3.1. Begin a definition3.1 =

-    if (MS->defining) Errors::in_text_file("nested definitions are not allowed", tfp);
+    if (MS->defining)
+        Errors::in_text_file("nested definitions are not allowed", tfp);
     text_stream *name = mr.exp[0];
     text_stream *parameter_specification = mr.exp[1];
-    makefile_macro *new_macro = CREATE(makefile_macro);
-    new_macro->identifier = Str::duplicate(name);
-    new_macro->no_parameters = 0;
-    new_macro->no_lines = 0;
-
-    match_results mr2 = Regexp::create_mr();
-    while (Regexp::match(&mr2, parameter_specification, L" *(%C+): *(%C+) *(%c*)")) {
-        if (new_macro->no_parameters >= MAX_MAKEFILE_MACRO_PARAMETERS) {
-            Errors::in_text_file("too many parameters in this definition", tfp);
-            break;
-        }
-        makefile_macro_parameter *new_parameter = CREATE(makefile_macro_parameter);
-        new_parameter->name = Str::duplicate(mr2.exp[0]);
-        new_parameter->definition_token = Str::duplicate(mr2.exp[1]);
-        new_parameter->optional = FALSE;
-        if (Str::get_first_char(new_parameter->name) == '?') {
-            new_parameter->optional = TRUE;
-            Str::delete_first_character(new_parameter->name);
-        }
-        new_macro->parameters[new_macro->no_parameters++] = new_parameter;
-        Str::clear(parameter_specification);
-        Str::copy(parameter_specification, mr2.exp[2]);
-    }
-    Regexp::dispose_of(&mr2);
-    if (Str::is_whitespace(parameter_specification) == FALSE)
-        Errors::in_text_file("parameter list for this definition is malformed", tfp);
-
-    MS->defining = new_macro;
+    MS->defining = Makefiles::new_macro(MS->known_macros, name, parameter_specification, tfp);
     Regexp::dispose_of(&mr);
     return;
 
-
  • This code is used in §2.
-

§2.2. Continue a definition2.2 = +

  • This code is used in §3.
+

§3.2. Continue a definition3.2 =

-    if (MS->defining->no_lines >= MAX_MAKEFILE_MACRO_LINES) {
-        Errors::in_text_file("too many lines in this definition", tfp);
-    } else {
-        MS->defining->lines[MS->defining->no_lines++] = Str::duplicate(line);
-    }
+    Makefiles::add_line_to_macro(MS->defining, line, tfp);
     Regexp::dispose_of(&mr);
     return;
 
-
  • This code is used in §2.
-

§2.3. End a definition2.3 = +

  • This code is used in §3.
+

§3.3. End a definition3.3 =

-    if (MS->defining == NULL) Errors::in_text_file("{end-define} without {define: ...}", tfp);
+    if (MS->defining == NULL)
+        Errors::in_text_file("{end-define} without {define: ...}", tfp);
     MS->defining = NULL;
     Regexp::dispose_of(&mr);
     return;
 
-
  • This code is used in §2.
-

§2.4. Expand a macro2.4 = +

  • This code is used in §3.
+

§4.

+ +
+void Makefiles::expand(text_stream *text, text_file_position *tfp, makefile_state *MS) {
+    TEMPORARY_TEXT(before_matter)
+    TEMPORARY_TEXT(braced_matter)
+    TEMPORARY_TEXT(after_matter)
+    int bl = 0, after_times = FALSE;
+    for (int i = 0; i < Str::len(text); i++) {
+        wchar_t c = Str::get_at(text, i);
+        if (after_times) PUT_TO(after_matter, c);
+        else if (c == '{') {
+            bl++;
+            if (bl > 1) PUT_TO(braced_matter, c);
+        } else if (c == '}') {
+            bl--;
+            if (bl == 0) after_times = TRUE;
+            else PUT_TO(braced_matter, c);
+        } else {
+            if (bl < 0) Errors::in_text_file("too many '}'s", tfp);
+            if (bl == 0) PUT_TO(before_matter, c);
+            else PUT_TO(braced_matter, c);
+        }
+    }
+    if (bl > 0) Errors::in_text_file("too many '{'s", tfp);
+    if (after_times) {
+        Expand a macro4.1;
+    } else {
+        WRITE_TO(MS->dest, "%S", text);
+    }
+    DISCARD_TEXT(before_matter)
+    DISCARD_TEXT(braced_matter)
+    DISCARD_TEXT(after_matter)
+}
+
+

§4.1. Expand a macro4.1 =

-    text_stream *before_matter = mr.exp[0];
-    text_stream *identifier = mr.exp[1];
-    text_stream *parameter_settings = mr.exp[2];
-    text_stream *after_matter = mr.exp[3];
-
-    makefile_macro_playback *playback = CREATE(makefile_macro_playback);
-    playback->which = NULL;
-    makefile_macro *mm;
-    LOOP_OVER(mm, makefile_macro)
-        if (Str::eq(mm->identifier, identifier))
-            playback->which = mm;
-    if (playback->which == NULL) {
-        Errors::in_text_file("unknown macro or command in braces", tfp);
-        Regexp::dispose_of(&mr);
-        return;
+    text_stream *identifier = braced_matter;
+    text_stream *parameter_settings = NULL;
+    match_results mr = Regexp::create_mr();
+    if (Regexp::match(&mr, identifier, L"(%C+) (%c*)")) {
+        identifier = mr.exp[0];
+        parameter_settings = mr.exp[1];
+    }
+    if (Str::eq(identifier, I"repeat")) {
+        if (Str::is_whitespace(after_matter)) identifier = I"repeat-block";
+        else identifier = I"repeat-span";
+    }
+    if (Str::eq(identifier, I"end-repeat")) {
+        if ((MS->repeat_sp > 0) && (MS->repeat_data[MS->repeat_sp-1].repeat_is_block))
+            identifier = I"end-block";
+        else
+            identifier = I"end-span";
     }
 
-    for (int i=0; i<MAX_MAKEFILE_MACRO_PARAMETERS; i++)
-        playback->parameter_values[i] = NULL;
-
-    match_results mr2 = Regexp::create_mr();
-    while (Regexp::match(&mr2, parameter_settings, L" *(%C+): *(%C+) *(%c*)")) {
-        text_stream *setting = mr2.exp[0];
-        text_stream *value = mr2.exp[1];
-        text_stream *remainder = mr2.exp[2];
-        int found = FALSE;
-        for (int i=0; i<playback->which->no_parameters; i++)
-            if (Str::eq(setting, playback->which->parameters[i]->name)) {
-                found = TRUE;
-                playback->parameter_values[i] = Str::duplicate(value);
-            }
-        if (found == FALSE) Errors::in_text_file("unknown parameter in this macro", tfp);
-        Str::clear(parameter_settings);
-        Str::copy(parameter_settings, remainder);
-    }
-    Regexp::dispose_of(&mr2);
-    if (Str::is_whitespace(parameter_settings) == FALSE)
-        Errors::in_text_file("parameter list for this macro is malformed", tfp);
-
-    for (int i=0; i<playback->which->no_parameters; i++)
-        if (playback->parameter_values[i] == NULL)
-            if (playback->which->parameters[i]->optional == FALSE)
-                Errors::in_text_file("compulsory macro parameter not given", tfp);
-
-    playback->line_position = 0;
-    playback->prior_to_this = MS->playing_back;
-    playback->text_to_follow = NULL;
-    if (Str::is_whitespace(after_matter) == FALSE)
-        playback->text_to_follow = Str::duplicate(after_matter);
-
-    MS->playing_back = playback;
-    WRITE("%S", before_matter);
-    for (int i=0; i<playback->which->no_lines; i++) {
-        TEMPORARY_TEXT(line)
-        text_stream *from = playback->which->lines[i];
-        for (int j=0; j<Str::len(from); j++) {
-            if (Str::get_at(from, j) == '{') {
-                int closed = FALSE, old_j = j;
-                TEMPORARY_TEXT(token)
-                for (j++; j<Str::len(from); j++) {
-                    if (Str::get_at(from, j) == '}') { closed = TRUE; break; }
-                    PUT_TO(token, Str::get_at(from, j));
-                }
-                if (closed) {
-                    int found = FALSE;
-                    for (int i=0; i<playback->which->no_parameters; i++)
-                        if (Str::eq(token, playback->which->parameters[i]->definition_token)) {
-                            found = TRUE;
-                            WRITE_TO(line, "%S", playback->parameter_values[i]);
-                        }
-                    if (found == FALSE) closed = FALSE;
-                }
-                DISCARD_TEXT(token)
-                if (closed == FALSE) { j = old_j; PUT_TO(line, '{'); }
+    if (Makefiles::acceptable_variable_name(identifier)) {
+        Makefiles::expand(before_matter, tfp, MS);
+        if (MS->repeat_sp > 0) {
+            WRITE_TO(MS->dest, "{%S}", identifier);
+        } else {
+            makefile_variable *var = Makefiles::find_variable_in(identifier, MS->stack_frame);
+            if (var) {
+                WRITE_TO(MS->dest, "%S", var->value);
             } else {
-                PUT_TO(line, Str::get_at(from, j));
+                TEMPORARY_TEXT(erm)
+                WRITE_TO(erm, "unknown variable '%S'", identifier);
+                Errors::in_text_file_S(erm, tfp);
+                DISCARD_TEXT(erm)
             }
         }
-        Makefiles::scan_makefile_line(line, tfp, (void *) MS);
-        DISCARD_TEXT(line)
-    }
-    MS->playing_back = playback->prior_to_this;
-    if (Str::is_whitespace(after_matter) == FALSE)
-        WRITE("%S\n", after_matter);
-    Regexp::dispose_of(&mr);
-    return;
-
-
  • This code is used in §2.
-

§2.5. Begin a repeat tool block2.5 = -

- -
-    int marker = MAKEFILE_TOOL_MOM;
-    Begin a repeat block2.5.1;
-
-
  • This code is used in §2.
-

§2.6. Begin a repeat web block2.6 = -

- -
-    int marker = MAKEFILE_WEB_MOM;
-    Begin a repeat block2.5.1;
-
-
  • This code is used in §2.
-

§2.7. Begin a repeat module block2.7 = -

- -
-    int marker = MAKEFILE_MODULE_MOM;
-    Begin a repeat block2.5.1;
-
-
  • This code is used in §2.
-

§2.5.1. Begin a repeat block2.5.1 = -

- -
-    if (MS->inside_block) Errors::in_text_file("nested repeat blocks are not allowed", tfp);
-    MS->inside_block = TRUE;
-    MS->repeat_scope = marker;
-    MS->repeat_tag = Str::duplicate(mr.exp[0]);
-    Str::clear(MS->repeat_block);
-    Regexp::dispose_of(&mr);
-    return;
-
- -

§2.8. Deal with a line in a repeat block2.8 = -

- -
-    WRITE_TO(MS->repeat_block, "%S\n", line);
-    return;
-
-
  • This code is used in §2.
-

§2.9. End a repeat block2.9 = -

- -
-    if (MS->inside_block == FALSE)
-        Errors::in_text_file("{endblock} without {repeatblock}", tfp);
-    MS->inside_block = FALSE;
-    Makefiles::repeat(OUT, NULL, TRUE, MS->repeat_block, TRUE, NULL, tfp, MS, MS->repeat_scope, MS->repeat_tag);
-    Str::clear(MS->repeat_block);
-    Regexp::dispose_of(&mr);
-    return;
-
-
  • This code is used in §2.
-

§2.10. Deal with a repeat span2.10 = -

- -
-    int marker = MAKEFILE_TOOL_MOM;
-    Begin a repeat span2.10.1;
-
-
  • This code is used in §2.
-

§2.11. Deal with a repeat web span2.11 = -

- -
-    int marker = MAKEFILE_WEB_MOM;
-    Begin a repeat span2.10.1;
-
-
  • This code is used in §2.
-

§2.12. Deal with a repeat module span2.12 = -

- -
-    int marker = MAKEFILE_MODULE_MOM;
-    Begin a repeat span2.10.1;
-
-
  • This code is used in §2.
-

§2.10.1. Begin a repeat span2.10.1 = -

- -
-    WRITE("%S", mr.exp[0]);
-    Makefiles::repeat(OUT, I" ", FALSE, mr.exp[1], FALSE, NULL, tfp, MS, marker, I"all");
-    WRITE("%S\n", mr.exp[2]);
-    MS->last_line_was_blank = FALSE;
-    Regexp::dispose_of(&mr);
-    return;
-
- -

§2.13. Expand platform-settings2.13 = -

- -
-    filename *prototype = Filenames::in(path_to_inweb, I"platform-settings.mk");
-    text_stream *INWEBPLATFORM = Str::new();
-    TextFiles::read(prototype, FALSE, "can't open platform settings file",
-        TRUE, Makefiles::seek_INWEBPLATFORM, NULL, INWEBPLATFORM);
-    if (Str::len(INWEBPLATFORM) == 0) {
-        Errors::in_text_file(
-            "found platform settings file, but it does not set INWEBPLATFORM", tfp);
+        Makefiles::expand(after_matter, tfp, MS);
     } else {
-        pathname *P = Pathnames::down(path_to_inweb, I"Materials");
-        P = Pathnames::down(P, I"platforms");
-        WRITE_TO(INWEBPLATFORM, ".mkscript");
-        filename *F = Filenames::in(P, INWEBPLATFORM);
-        TextFiles::read(F, FALSE, "can't open platform definitions file",
-            TRUE, Makefiles::scan_makefile_line, NULL, MS);
-        WRITE_TO(STDOUT, "(Read definitions from %f)\n", F);
+        makefile_macro *mm = Makefiles::find_macro(MS->known_macros, identifier);
+        if (mm == NULL) {
+            TEMPORARY_TEXT(erm)
+            WRITE_TO(erm, "unknown macro '%S'", identifier);
+            Errors::in_text_file_S(erm, tfp);
+            DISCARD_TEXT(erm)
+        } else {
+            if (mm->suppress_whitespace_when_expanding) {
+                while (Characters::is_whitespace(Str::get_last_char(before_matter)))
+                    Str::delete_last_character(before_matter);
+                while (Characters::is_whitespace(Str::get_first_char(after_matter)))
+                    Str::delete_first_character(after_matter);
+            }
+            Makefiles::expand(before_matter, tfp, MS);
+            int divert_if_repeating = TRUE;
+            if ((mm) &&
+                ((mm->reserved_macro_meaning == REPEAT_BLOCK_RMM) ||
+                    (mm->reserved_macro_meaning == REPEAT_SPAN_RMM))) {
+                MS->shadow_sp++;
+            }
+            if ((mm) &&
+                ((mm->reserved_macro_meaning == END_BLOCK_RMM) ||
+                    (mm->reserved_macro_meaning == END_SPAN_RMM))) {
+                MS->shadow_sp--;
+                if (MS->shadow_sp == 0) divert_if_repeating = FALSE;
+            }
+
+            if ((divert_if_repeating) && (MS->repeat_sp > 0)) {
+                WRITE_TO(MS->dest, "{%S}", braced_matter);
+            } else {
+                Makefiles::expand_macro(MS, mm, parameter_settings, tfp);
+                if (mm->suppress_newline_after_expanding) MS->suppress_newline = TRUE;
+            }
+            Makefiles::expand(after_matter, tfp, MS);
+        }
     }
     Regexp::dispose_of(&mr);
-    return;
 
-
  • This code is used in §2.
-

§2.14. Expand identity-settings2.14 = -

+
  • This code is used in §4.
+

§5.

-    WRITE("INWEB = "); Makefiles::pathname_slashed(OUT, path_to_inweb); WRITE("/Tangled/inweb\n");
-    pathname *path_to_intest = Pathnames::down(Pathnames::up(path_to_inweb), I"intest");
-    WRITE("INTEST = "); Makefiles::pathname_slashed(OUT, path_to_intest); WRITE("/Tangled/intest\n");
-    if (MS->for_web) {
-        WRITE("MYNAME = %S\n", Pathnames::directory_name(MS->for_web->md->path_to_web));
-        WRITE("ME = "); Makefiles::pathname_slashed(OUT, MS->for_web->md->path_to_web);
-        WRITE("\n");
-        MS->last_line_was_blank = FALSE;
-    }
-    Regexp::dispose_of(&mr);
-    return;
-
-
  • This code is used in §2.
-

§2.15. Declare a tool2.15 = -

- -
-    int marker = MAKEFILE_TOOL_MOM;
-    dictionary *D = MS->tools_dictionary;
-    Declare something2.15.1;
-
-
  • This code is used in §2.
-

§2.16. Declare a web2.16 = -

- -
-    int marker = MAKEFILE_WEB_MOM;
-    dictionary *D = MS->webs_dictionary;
-    Declare something2.15.1;
-
-
  • This code is used in §2.
-

§2.17. Declare a module2.17 = -

- -
-    int marker = MAKEFILE_MODULE_MOM;
-    dictionary *D = MS->modules_dictionary;
-    Declare something2.15.1;
-
-
  • This code is used in §2.
-

§2.15.1. Declare something2.15.1 = -

- -
-    WRITE("%SLEAF = %S\n", mr.exp[0], mr.exp[1]);
-    WRITE("%SWEB = %S\n", mr.exp[0], mr.exp[2]);
-    WRITE("%SMAKER = $(%SWEB)/%S.mk\n", mr.exp[0], mr.exp[0], mr.exp[1]);
-    WRITE("%SX = $(%SWEB)/Tangled/%S\n", mr.exp[0], mr.exp[0], mr.exp[1]);
-    MS->last_line_was_blank = FALSE;
-    web_md *Wm = Reader::load_web_md(Pathnames::from_text(mr.exp[2]), NULL, MS->search_path, TRUE);
-    Wm->as_module->module_name = Str::duplicate(mr.exp[0]);
-    Wm->as_module->module_tag = Str::duplicate(mr.exp[3]);
-    Wm->as_module->origin_marker = marker;
-    Dictionaries::create(D, mr.exp[0]);
-    Dictionaries::write_value(D, mr.exp[0], Wm);
-    Regexp::dispose_of(&mr);
-    return;
-
- -

§2.18. Expand dependent-files2.18 = -

- -
-    WRITE("%S", mr.exp[0]);
-    Makefiles::pattern(OUT, MS->for_web->md->sections_md, MS->for_web->md->contents_filename);
-    WRITE("\n");
-    MS->last_line_was_blank = FALSE;
-    Regexp::dispose_of(&mr);
-    return;
-
-
  • This code is used in §2.
-

§2.19. Expand dependent-files-for-tool2.19 = -

- -
-    WRITE("%S", mr.exp[0]);
-    if (Dictionaries::find(MS->tools_dictionary, mr.exp[1])) {
-        web_md *Wm = Dictionaries::read_value(MS->tools_dictionary, mr.exp[1]);
-        Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
-    } else if (Dictionaries::find(MS->webs_dictionary, mr.exp[1])) {
-        web_md *Wm = Dictionaries::read_value(MS->webs_dictionary, mr.exp[1]);
-        Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
-    } else {
-        PRINT("Tool %S\n", mr.exp[0]);
-        Errors::in_text_file("unknown tool to find dependencies for", tfp);
-    }
-    WRITE("\n");
-    MS->last_line_was_blank = FALSE;
-    Regexp::dispose_of(&mr);
-    return;
-
-
  • This code is used in §2.
-

§2.20. Expand dependent-files-for-tool-alone2.20 = -

- -
-    WRITE("%S", mr.exp[0]);
-    if (Dictionaries::find(MS->tools_dictionary, mr.exp[1])) {
-        web_md *Wm = Dictionaries::read_value(MS->tools_dictionary, mr.exp[1]);
-        Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename);
-    } else if (Dictionaries::find(MS->webs_dictionary, mr.exp[1])) {
-        web_md *Wm = Dictionaries::read_value(MS->webs_dictionary, mr.exp[1]);
-        Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename);
-    } else {
-        PRINT("Tool %S\n", mr.exp[0]);
-        Errors::in_text_file("unknown tool to find dependencies for", tfp);
-    }
-    WRITE("\n");
-    MS->last_line_was_blank = FALSE;
-    Regexp::dispose_of(&mr);
-    return;
-
-
  • This code is used in §2.
-

§2.21. Expand dependent-files-for-module2.21 = -

- -
-    WRITE("%S", mr.exp[0]);
-    if (Dictionaries::find(MS->modules_dictionary, mr.exp[1])) {
-        web_md *Wm = Dictionaries::read_value(MS->modules_dictionary, mr.exp[1]);
-        Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
-    } else {
-        Errors::in_text_file("unknown module to find dependencies for", tfp);
-        WRITE_TO(STDERR, "-- module name: %S\n", mr.exp[1]);
-    }
-    WRITE("\n");
-    MS->last_line_was_blank = FALSE;
-    Regexp::dispose_of(&mr);
-    return;
-
-
  • This code is used in §2.
-

§2.22. And otherwise copy the line straight through2.22 = -

- -
-    if (Str::len(line) == 0) {
-        if (MS->last_line_was_blank == FALSE) WRITE("\n");
-        MS->last_line_was_blank = TRUE;
-    } else {
-        MS->last_line_was_blank = FALSE;
-        WRITE("%S\n", line);
-    }
-
-
  • This code is used in §2.
-

§3.

- -
-void Makefiles::pathname_slashed(OUTPUT_STREAM, pathname *P) {
+void Makefiles::pathname_slashed(OUTPUT_STREAM, pathname *P) {
     TEMPORARY_TEXT(PT)
     WRITE_TO(PT, "%p", P);
     LOOP_THROUGH_TEXT(pos, PT) {
@@ -613,17 +313,17 @@ following a "prototype".
     DISCARD_TEXT(PT)
 }
 
-void Makefiles::pattern(OUTPUT_STREAM, linked_list *L, filename *F) {
+void Makefiles::pattern(OUTPUT_STREAM, linked_list *L, filename *F) {
     dictionary *patterns_done = Dictionaries::new(16, TRUE);
-    if (F) Add pattern for file F, if not already given3.1;
+    if (F) Add pattern for file F, if not already given5.1;
     section_md *Sm;
     LOOP_OVER_LINKED_LIST(Sm, section_md, L) {
         filename *F = Sm->source_file_for_section;
-        Add pattern for file F, if not already given3.1;
+        Add pattern for file F, if not already given5.1;
     }
 }
 
-

§3.1. Add pattern for file F, if not already given3.1 = +

§5.1. Add pattern for file F, if not already given5.1 =

@@ -652,56 +352,637 @@ following a "prototype".
     }
     DISCARD_TEXT(tester)
 
-
  • This code is used in §3 (twice).
-

§4. And finally, the following handles repetitions both of blocks and of spans: +

  • This code is used in §5 (twice).
+

§6. And finally, the following handles repetitions both of blocks and of spans:

-void Makefiles::repeat(OUTPUT_STREAM, text_stream *prefix, int every_time, text_stream *matter,
-    int as_lines, text_stream *suffix, text_file_position *tfp, makefile_state *MS, int over, text_stream *tag) {
-    module *M;
-    int c = 0;
-    LOOP_OVER(M, module) {
-        if ((M->origin_marker == over) &&
-            ((Str::eq(tag, I"all")) || (Str::eq(tag, M->module_tag)))) {
-            if ((prefix) && ((c++ > 0) || (every_time))) WRITE("%S", prefix);
-            if (matter) {
-                TEMPORARY_TEXT(line)
-                LOOP_THROUGH_TEXT(pos, matter) {
-                    if (Str::get(pos) == '\n') {
-                        if (as_lines) {
-                            Makefiles::scan_makefile_line(line, tfp, (void *) MS);
-                            Str::clear(line);
-                        }
-                    } else {
-                        if (Str::get(pos) == '@') {
-                            WRITE_TO(line, "%S", M->module_name);
-                        } else {
-                            PUT_TO(line, Str::get(pos));
-                        }
-                    }
-                }
-                if (!as_lines) WRITE("%S", line);
-                DISCARD_TEXT(line)
+void Makefiles::repeat(text_stream *matter,
+    int as_lines, text_file_position *tfp, makefile_repeat *rep, makefile_state *MS) {
+    int over = rep->repeat_scope;
+    text_stream *tag = rep->repeat_tag;
+    MS->stack_frame = Makefiles::new_variable_set(MS->stack_frame);
+    text_stream *loop_var_name = I"NAME";
+    if (Str::len(rep->repeat_with) > 0) loop_var_name = rep->repeat_with;
+    makefile_variable *loop_var = Makefiles::ensure_variable(loop_var_name, MS->stack_frame);
+    if (Str::len(rep->repeat_in) > 0) {
+        match_results mr = Regexp::create_mr();
+        while (Regexp::match(&mr, rep->repeat_in, L"(%c*?),(%c*)")) {
+            text_stream *value = mr.exp[0];
+            Str::trim_white_space(value);
+            Iterate with this value6.1;
+            Str::clear(rep->repeat_in);
+            Str::copy(rep->repeat_in, mr.exp[1]);
+        }
+        Regexp::dispose_of(&mr);
+        text_stream *value = rep->repeat_in;
+        Str::trim_white_space(value);
+        Iterate with this value6.1;
+    } else {
+        module *M;
+        LOOP_OVER(M, module) {
+            if ((M->origin_marker == over) &&
+                ((Str::eq(tag, I"all")) || (Str::eq(tag, M->module_tag)))) {
+                text_stream *value = M->module_name;
+                Iterate with this value6.1;
             }
-            if (suffix) WRITE("%S", suffix);
         }
     }
+    MS->stack_frame = MS->stack_frame->outer;
 }
 
-

§5. This is used to scan the platform settings file for a definition line in the +

§6.1. Iterate with this value6.1 = +

+ +
+    loop_var->value = value;
+    if (as_lines) {
+        TEMPORARY_TEXT(line)
+        LOOP_THROUGH_TEXT(pos, matter) {
+            if (Str::get(pos) == '\n') {
+                Makefiles::scan_makefile_line(line, tfp, (void *) MS);
+                Str::clear(line);
+            } else {
+                PUT_TO(line, Str::get(pos));
+            }
+        }
+        DISCARD_TEXT(line)
+    } else {
+        Makefiles::expand(matter, tfp, MS);
+    }
+
+
  • This code is used in §6 (three times).
+

§7. This is used to scan the platform settings file for a definition line in the shape INWEBPLATFORM = PLATFORM, in order to find out what PLATFORM the make file will be used on.

-void Makefiles::seek_INWEBPLATFORM(text_stream *line, text_file_position *tfp, void *X) {
+void Makefiles::seek_INWEBPLATFORM(text_stream *line, text_file_position *tfp, void *X) {
     text_stream *OUT = (text_stream *) X;
     match_results mr = Regexp::create_mr();
     if (Regexp::match(&mr, line, L" *INWEBPLATFORM = (%C+) *")) WRITE("%S", mr.exp[0]);
     Regexp::dispose_of(&mr);
 }
 
+

§8. Variables.

+ +
+typedef struct makefile_variable {
+    struct text_stream *name;
+    struct text_stream *value;
+    CLASS_DEFINITION
+} makefile_variable;
+
+typedef struct makefile_variable_set {
+    struct linked_list *variables;  of makefile_variable
+    struct makefile_variable_set *outer;
+    CLASS_DEFINITION
+} makefile_variable_set;
+
+makefile_variable_set *Makefiles::new_variable_set(makefile_variable_set *outer) {
+    makefile_variable_set *set = CREATE(makefile_variable_set);
+    set->variables = NEW_LINKED_LIST(makefile_variable);
+    set->outer = outer;
+    return set;
+}
+
+makefile_variable *Makefiles::find_variable_in_one(text_stream *name, makefile_variable_set *set) {
+    if (set == NULL) return NULL;
+    makefile_variable *var;
+    LOOP_OVER_LINKED_LIST(var, makefile_variable, set->variables)
+        if (Str::eq(name, var->name))
+            return var;
+    return NULL;
+}
+
+makefile_variable *Makefiles::find_variable_in(text_stream *name, makefile_variable_set *set) {
+    while (set) {
+        makefile_variable *var = Makefiles::find_variable_in_one(name, set);
+        if (var) return var;
+        set = set->outer;
+    }
+    return NULL;
+}
+
+makefile_variable *Makefiles::ensure_variable(text_stream *name, makefile_variable_set *in_set) {
+    if (in_set == NULL) internal_error("variable without set");
+    makefile_variable *var = Makefiles::find_variable_in_one(name, in_set);
+    if (var == NULL) {
+        var = CREATE(makefile_variable);
+        var->name = Str::duplicate(name);
+        var->value = I"";
+        ADD_TO_LINKED_LIST(var, makefile_variable, in_set->variables);
+    }
+    return var;
+}
+
+int Makefiles::acceptable_variable_name(text_stream *name) {
+    LOOP_THROUGH_TEXT(pos, name) {
+        wchar_t c = Str::get(pos);
+        if ((c >= '0') && (c <= '9')) continue;
+        if ((c >= 'A') && (c <= 'Z')) continue;
+        if (c == '_') continue;
+        return FALSE;
+    }
+    return TRUE;
+}
+
+
  • The structure makefile_variable is accessed in 2/dct, 2/trs, 5/ee, 8/bdfw, 5/wt, 6/cs, 6/rw, 6/cln and here.
  • The structure makefile_variable_set is private to this section.
+

§9. Macros. A typical macro definition looks like this: +

+ +
+{define: link to: TO from: FROM ?options: OPTS}
+    clang $(CCOPTS) -g -o {TO} {FROM} {OPTS}
+{end-define}
+
+

And here is a usage of it: +

+ +
+    {link from: frog.o to: frog.c}
+
+

This doesn't specify "options: ...", but doesn't have to, because that's optional — +note the question mark in the macro declaration. But it does specify "from: ..." +and "to: ...", which are compulsory. Parameters are always named, as this example +suggests, and can be given in any order so long as all the non-optional ones are +present. +

+ +

This usage results in the following line in the final makefile: +

+ +
+    clang $(CCOPTS) -g -o frog.c frog.o
+
+

Note the difference between $(CCOPTS), which is a make variable, and the braced +tokens {TO}, {FROM} and {OPTS}, which are makescript variables. In makescripts, +the only material treated as special is material in braces {...}. +

+ +

§10. The above definition has three parameters, one optional, but only one line. There +are (for now, anyway) hard but harmlessly large limits on the number of these: +

+ +
define MAX_MAKEFILE_MACRO_PARAMETERS 8
+define MAX_MAKEFILE_MACRO_LINES 128
+
+
+typedef struct makefile_macro {
+    struct text_stream *identifier;
+    struct makefile_macro_parameter *parameters[MAX_MAKEFILE_MACRO_PARAMETERS];
+    int no_parameters;
+    struct text_stream *lines[MAX_MAKEFILE_MACRO_LINES];
+    int no_lines;
+    int reserved_macro_meaning;
+    int suppress_newline_after_expanding;
+    int suppress_whitespace_when_expanding;
+    CLASS_DEFINITION
+} makefile_macro;
+
+typedef struct makefile_macro_parameter {
+    struct text_stream *name;
+    struct text_stream *definition_token;
+    int optional;
+    CLASS_DEFINITION
+} makefile_macro_parameter;
+
+
  • The structure makefile_macro is private to this section.
  • The structure makefile_macro_parameter is accessed in 2/trs, 5/wt, 6/cs, 6/rw, 6/cln and here.
+

§11. New macro declaration lines are processed here, and added to a list L of +valid macros: +

+ +
+makefile_macro *Makefiles::new_macro(linked_list *L, text_stream *name,
+    text_stream *parameter_specification, text_file_position *tfp) {
+    if (Makefiles::find_macro(L, name))
+        Errors::in_text_file("a macro with this name already exists", tfp);
+
+    makefile_macro *new_macro = CREATE(makefile_macro);
+    new_macro->identifier = Str::duplicate(name);
+    new_macro->no_parameters = 0;
+    new_macro->no_lines = 0;
+    new_macro->reserved_macro_meaning = UNRESERVED_RMM;
+    new_macro->suppress_newline_after_expanding = TRUE;
+    new_macro->suppress_whitespace_when_expanding = TRUE;
+
+    match_results mr2 = Regexp::create_mr();
+    while (Regexp::match(&mr2, parameter_specification, L" *(%C+): *(%C+) *(%c*)")) {
+        text_stream *par_name = mr2.exp[0];
+        text_stream *token_name = mr2.exp[1];
+        Str::clear(parameter_specification);
+        Str::copy(parameter_specification, mr2.exp[2]);
+        if (new_macro->no_parameters >= MAX_MAKEFILE_MACRO_PARAMETERS) {
+            Errors::in_text_file("too many parameters in this definition", tfp);
+        } else {
+            Add parameter to macro11.1;
+        }
+    }
+    Regexp::dispose_of(&mr2);
+    if (Str::is_whitespace(parameter_specification) == FALSE)
+        Errors::in_text_file("parameter list for this definition is malformed", tfp);
+    ADD_TO_LINKED_LIST(new_macro, makefile_macro, L);
+    return new_macro;
+}
+
+

§11.1. Add parameter to macro11.1 = +

+ +
+    makefile_macro_parameter *new_parameter = CREATE(makefile_macro_parameter);
+    new_parameter->name = Str::duplicate(par_name);
+    new_parameter->definition_token = Str::duplicate(token_name);
+    new_parameter->optional = FALSE;
+    if (Str::get_first_char(new_parameter->name) == '?') {
+        new_parameter->optional = TRUE;
+        Str::delete_first_character(new_parameter->name);
+    }
+    new_macro->parameters[new_macro->no_parameters++] = new_parameter;
+
+
  • This code is used in §11.
+

§12. We can then add lines to the definition: +

+ +
+void Makefiles::add_line_to_macro(makefile_macro *mm, text_stream *line, text_file_position *tfp) {
+    if (mm->no_lines >= MAX_MAKEFILE_MACRO_LINES) {
+        Errors::in_text_file("too many lines in this definition", tfp);
+    } else {
+        mm->lines[mm->no_lines++] = Str::duplicate(line);
+    }
+}
+
+

§13. A few macros are "reserved", that is, have built-in meanings and are not +declared by any makescript but by us. (These have no lines, only parameters.) +

+ +
enum UNRESERVED_RMM from 0
+enum PLATFORM_SETTINGS_RMM
+enum IDENTITY_SETTINGS_RMM
+enum COMPONENT_RMM
+enum DEPENDENT_FILES_RMM
+enum REPEAT_BLOCK_RMM
+enum END_BLOCK_RMM
+enum REPEAT_SPAN_RMM
+enum END_SPAN_RMM
+enum SET_RMM
+
+
+linked_list *Makefiles::list_of_reserved_macros(void) {
+    linked_list *L = NEW_LINKED_LIST(makefile_macro);
+    Makefiles::reserve_macro(L, I"platform-settings", NULL, PLATFORM_SETTINGS_RMM);
+    Makefiles::reserve_macro(L, I"identity-settings", NULL, IDENTITY_SETTINGS_RMM);
+    Makefiles::reserve_macro(L, I"component",
+        I"symbol: SYMBOL webname: WEBNAME path: PATH set: SET category: CATEGORY",
+        COMPONENT_RMM);
+    Makefiles::reserve_macro(L, I"dependent-files",
+        I"?tool: TOOL ?module: MODULES ?tool-and-modules: BOTH",
+        DEPENDENT_FILES_RMM);
+    Makefiles::reserve_macro(L, I"repeat-block",
+        I"?over: CATEGORY ?set: SET ?with: WITH ?in: IN", REPEAT_BLOCK_RMM);
+    Makefiles::reserve_macro(L, I"end-block", NULL, END_BLOCK_RMM);
+    Makefiles::reserve_span_macro(L, I"repeat-span",
+        I"?over: CATEGORY ?set: SET ?with: WITH ?in: IN", REPEAT_SPAN_RMM);
+    Makefiles::reserve_span_macro(L, I"end-span", NULL, END_SPAN_RMM);
+    Makefiles::reserve_span_macro(L, I"set",
+        I"name: NAME value: VALUE", SET_RMM);
+    return L;
+}
+
+void Makefiles::reserve_macro(linked_list *L, text_stream *name,
+    text_stream *parameter_specification, int rmm) {
+    makefile_macro *reserved = Makefiles::new_macro(L, name,
+        Str::duplicate(parameter_specification), NULL);
+    reserved->reserved_macro_meaning = rmm;
+}
+
+void Makefiles::reserve_span_macro(linked_list *L, text_stream *name,
+    text_stream *parameter_specification, int rmm) {
+    makefile_macro *reserved = Makefiles::new_macro(L, name,
+        Str::duplicate(parameter_specification), NULL);
+    reserved->reserved_macro_meaning = rmm;
+    reserved->suppress_newline_after_expanding = FALSE;
+    reserved->suppress_whitespace_when_expanding = FALSE;
+}
+
+

§14. Finding a macro in a list. (We could use a dictionary for efficiency, but really, +it's unlikely there are ever more than a few macros.) +

+ +
+makefile_macro *Makefiles::find_macro(linked_list *L, text_stream *name) {
+    makefile_macro *mm;
+    LOOP_OVER_LINKED_LIST(mm, makefile_macro, L)
+        if (Str::eq(mm->identifier, name))
+            return mm;
+    return NULL;
+}
+
+

§15. Expanding a macro is the main event, then: +

+ +
+void Makefiles::expand_macro(makefile_state *MS, makefile_macro *mm,
+    text_stream *parameter_settings, text_file_position *tfp) {
+    text_stream *OUT = MS->dest;
+    text_stream *parameter_values[MAX_MAKEFILE_MACRO_PARAMETERS];
+    for (int i=0; i<MAX_MAKEFILE_MACRO_PARAMETERS; i++) parameter_values[i] = NULL;
+
+    match_results mr2 = Regexp::create_mr();
+    while (Regexp::match(&mr2, parameter_settings, L" *(%C+): *(%c*)")) {
+        text_stream *setting = mr2.exp[0];
+        text_stream *value = mr2.exp[1];
+        text_stream *remainder = NULL;
+        match_results mr3 = Regexp::create_mr();
+        if (Regexp::match(&mr3, value, L"(%c+?) *(%C+: *%c*)")) {
+            value = mr3.exp[0];
+            remainder = mr3.exp[1];
+        }
+        int found = FALSE;
+        for (int i=0; i<mm->no_parameters; i++)
+            if (Str::eq(setting, mm->parameters[i]->name)) {
+                found = TRUE;
+                parameter_values[i] = Str::new();
+                text_stream *saved = MS->dest;
+                MS->dest = parameter_values[i];
+                Makefiles::expand(value, tfp, MS);
+                MS->dest = saved;
+            }
+        if (found == FALSE) {
+            TEMPORARY_TEXT(erm)
+            WRITE_TO(erm, "unknown parameter '%S'", setting);
+            Errors::in_text_file_S(erm, tfp);
+            DISCARD_TEXT(erm)
+        }
+        Str::clear(parameter_settings);
+        Str::copy(parameter_settings, remainder);
+        Regexp::dispose_of(&mr3);
+    }
+    Regexp::dispose_of(&mr2);
+    if (Str::is_whitespace(parameter_settings) == FALSE)
+        Errors::in_text_file("parameter list is malformed", tfp);
+
+    for (int i=0; i<mm->no_parameters; i++)
+        if (parameter_values[i] == NULL)
+            if (mm->parameters[i]->optional == FALSE) {
+                TEMPORARY_TEXT(erm)
+                WRITE_TO(erm, "compulsory parameter '%S' not given", mm->parameters[i]->name);
+                Errors::in_text_file_S(erm, tfp);
+                DISCARD_TEXT(erm)
+            }
+
+    switch (mm->reserved_macro_meaning) {
+        case UNRESERVED_RMM: Expand a textual definition15.1; break;
+        case PLATFORM_SETTINGS_RMM: Expand platform settings15.2; break;
+        case IDENTITY_SETTINGS_RMM: Expand identity settings15.3; break;
+        case COMPONENT_RMM: Expand component declaration15.4; break;
+        case DEPENDENT_FILES_RMM: Expand dependent-files15.5; break;
+        case REPEAT_BLOCK_RMM: Expand repeat-block15.6; break;
+        case END_BLOCK_RMM: Expand end-block15.7; break;
+        case REPEAT_SPAN_RMM: Expand repeat-block15.6; break;
+        case END_SPAN_RMM: Expand end-block15.7; break;
+        case SET_RMM: Expand set15.8; break;
+        default: internal_error("unimplemented reserved macro");
+    }
+}
+
+

§15.1. Expand a textual definition15.1 = +

+ +
+    MS->stack_frame = Makefiles::new_variable_set(MS->stack_frame);
+    for (int i=0; i<mm->no_parameters; i++) {
+        makefile_variable *var =
+            Makefiles::ensure_variable(mm->parameters[i]->definition_token, MS->stack_frame);
+        var->value = parameter_values[i];
+    }
+    for (int i=0; i<mm->no_lines; i++)
+        Makefiles::scan_makefile_line(mm->lines[i], tfp, (void *) MS);
+    MS->stack_frame = MS->stack_frame->outer;
+
+
  • This code is used in §15.
+

§15.2. Expand platform settings15.2 = +

+ +
+    filename *prototype = Filenames::in(path_to_inweb, I"platform-settings.mk");
+    text_stream *INWEBPLATFORM = Str::new();
+    TextFiles::read(prototype, FALSE, "can't open platform settings file",
+        TRUE, Makefiles::seek_INWEBPLATFORM, NULL, INWEBPLATFORM);
+    if (Str::len(INWEBPLATFORM) == 0) {
+        Errors::in_text_file(
+            "found platform settings file, but it does not set INWEBPLATFORM", tfp);
+    } else {
+        pathname *P = Pathnames::down(path_to_inweb, I"Materials");
+        P = Pathnames::down(P, I"platforms");
+        WRITE_TO(INWEBPLATFORM, ".mkscript");
+        filename *F = Filenames::in(P, INWEBPLATFORM);
+        TextFiles::read(F, FALSE, "can't open platform definitions file",
+            TRUE, Makefiles::scan_makefile_line, NULL, MS);
+        WRITE_TO(STDOUT, "(Read definitions file '%S' from ", INWEBPLATFORM);
+        Pathnames::to_text_relative(STDOUT, path_to_inweb, P);
+        WRITE_TO(STDOUT, ")\n");
+    }
+
+
  • This code is used in §15.
+

§15.3. Expand identity settings15.3 = +

+ +
+    WRITE("INWEB = "); Makefiles::pathname_slashed(OUT, path_to_inweb); WRITE("/Tangled/inweb\n");
+    pathname *path_to_intest = Pathnames::down(Pathnames::up(path_to_inweb), I"intest");
+    WRITE("INTEST = "); Makefiles::pathname_slashed(OUT, path_to_intest); WRITE("/Tangled/intest\n");
+    if (MS->for_web) {
+        WRITE("MYNAME = %S\n", Pathnames::directory_name(MS->for_web->md->path_to_web));
+        WRITE("ME = "); Makefiles::pathname_slashed(OUT, MS->for_web->md->path_to_web);
+        WRITE("\n");
+        MS->last_line_was_blank = FALSE;
+    }
+
+
  • This code is used in §15.
+

§15.4. Expand component declaration15.4 = +

+ +
+    text_stream *symbol = parameter_values[0];
+    text_stream *webname = parameter_values[1];
+    text_stream *path = parameter_values[2];
+    text_stream *set = parameter_values[3];
+    text_stream *category = parameter_values[4];
+
+    int marker = -1;
+    dictionary *D = NULL;
+    if (Str::eq(category, I"tool")) {
+        marker = MAKEFILE_TOOL_MOM;
+        D = MS->tools_dictionary;
+    } else if (Str::eq(category, I"web")) {
+        marker = MAKEFILE_WEB_MOM;
+        D = MS->webs_dictionary;
+    } else if (Str::eq(category, I"module")) {
+        marker = MAKEFILE_MODULE_MOM;
+        D = MS->modules_dictionary;
+    } else {
+        Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp);
+    }
+    if (D) {
+        WRITE("%SLEAF = %S\n", symbol, webname);
+        WRITE("%SWEB = %S\n", symbol, path);
+        WRITE("%SMAKER = $(%SWEB)/%S.mk\n", symbol, symbol, webname);
+        WRITE("%SX = $(%SWEB)/Tangled/%S\n", symbol, symbol, webname);
+        MS->last_line_was_blank = FALSE;
+        web_md *Wm = Reader::load_web_md(Pathnames::from_text(path), NULL, MS->search_path, TRUE);
+        Wm->as_module->module_name = Str::duplicate(symbol);
+        Wm->as_module->module_tag = Str::duplicate(set);
+        Wm->as_module->origin_marker = marker;
+        Dictionaries::create(D, symbol);
+        Dictionaries::write_value(D, symbol, Wm);
+    }
+
+
  • This code is used in §15.
+

§15.5. Expand dependent-files15.5 = +

+ +
+    text_stream *tool = parameter_values[0];
+    text_stream *modules = parameter_values[1];
+    text_stream *both = parameter_values[2];
+    if (Str::len(tool) > 0) {
+        if (Dictionaries::find(MS->tools_dictionary, tool)) {
+            web_md *Wm = Dictionaries::read_value(MS->tools_dictionary, tool);
+            Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename);
+        } else if (Dictionaries::find(MS->webs_dictionary, tool)) {
+            web_md *Wm = Dictionaries::read_value(MS->webs_dictionary, tool);
+            Makefiles::pattern(OUT, Wm->as_module->sections_md, Wm->contents_filename);
+        } else {
+            TEMPORARY_TEXT(erm)
+            WRITE_TO(erm, "unknown tool '%S' to find dependencies for", tool);
+            Errors::in_text_file_S(erm, tfp);
+            DISCARD_TEXT(erm)
+        }
+    } else if (Str::len(modules) > 0) {
+        if (Dictionaries::find(MS->modules_dictionary, modules)) {
+            web_md *Wm = Dictionaries::read_value(MS->modules_dictionary, modules);
+            Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
+        } else {
+            TEMPORARY_TEXT(erm)
+            WRITE_TO(erm, "unknown module '%S' to find dependencies for", modules);
+            Errors::in_text_file_S(erm, tfp);
+            DISCARD_TEXT(erm)
+        }
+    } else if (Str::len(both) > 0) {
+        if (Dictionaries::find(MS->tools_dictionary, both)) {
+            web_md *Wm = Dictionaries::read_value(MS->tools_dictionary, both);
+            Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
+        } else if (Dictionaries::find(MS->webs_dictionary, both)) {
+            web_md *Wm = Dictionaries::read_value(MS->webs_dictionary, both);
+            Makefiles::pattern(OUT, Wm->sections_md, Wm->contents_filename);
+        } else {
+            TEMPORARY_TEXT(erm)
+            WRITE_TO(erm, "unknown tool '%S' to find dependencies for", both);
+            Errors::in_text_file_S(erm, tfp);
+            DISCARD_TEXT(erm)
+        }
+    } else {
+        Makefiles::pattern(OUT, MS->for_web->md->sections_md, MS->for_web->md->contents_filename);
+    }
+    WRITE("\n");
+    MS->last_line_was_blank = FALSE;
+
+
  • This code is used in §15.
+

§15.6. Expand repeat-block15.6 = +

+ +
+    if (MS->repeat_sp >= MAX_MAKEFILE_REPEAT_DEPTH) {
+        Errors::in_text_file("repetition too deep", tfp);
+    } else {
+        text_stream *category = parameter_values[0];
+        text_stream *set = parameter_values[1];
+        text_stream *with = parameter_values[2];
+        text_stream *in = parameter_values[3];
+        if (Str::len(set) == 0) set = I"all";
+        if (Str::eq(category, I"tool")) {
+            int marker = MAKEFILE_TOOL_MOM;
+            Begin a repeat block15.6.1;
+        } else if (Str::eq(category, I"web")) {
+            int marker = MAKEFILE_WEB_MOM;
+            Begin a repeat block15.6.1;
+        } else if (Str::eq(category, I"module")) {
+            int marker = MAKEFILE_MODULE_MOM;
+            Begin a repeat block15.6.1;
+        } else if (Str::len(category) > 0) {
+            Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp);
+        } else {
+            if ((Str::len(with) == 0) || (Str::len(in) == 0))
+                Errors::in_text_file("should give both with: VAR and in: LIST", tfp);
+            Begin a repeat with15.6.2;
+        }
+    }
+
+
  • This code is used in §15 (twice).
+

§15.6.1. Begin a repeat block15.6.1 = +

+ +
+    makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]);
+    MS->shadow_sp = 1;
+    rep->repeat_scope = marker;
+    rep->repeat_tag = Str::duplicate(set);
+    rep->repeat_with = NULL;
+    rep->repeat_in = NULL;
+    rep->repeat_is_block = TRUE;
+    if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE;
+    rep->repeat_saved_dest = MS->dest;
+    MS->dest = Str::new();
+
+
  • This code is used in §15.6 (three times).
+

§15.6.2. Begin a repeat with15.6.2 = +

+ +
+    makefile_repeat *rep = &(MS->repeat_data[MS->repeat_sp++]);
+    MS->shadow_sp = 1;
+    rep->repeat_scope = -1;
+    rep->repeat_tag = NULL;
+    rep->repeat_with = Str::duplicate(with);
+    rep->repeat_in = Str::duplicate(in);
+    rep->repeat_is_block = TRUE;
+    if (mm->reserved_macro_meaning == REPEAT_SPAN_RMM) rep->repeat_is_block = FALSE;
+    rep->repeat_saved_dest = MS->dest;
+    MS->dest = Str::new();
+
+ +

§15.7. Expand end-block15.7 = +

+ +
+    MS->shadow_sp = 0;
+    if (MS->repeat_sp == 0) Errors::in_text_file("end without repeat", tfp);
+    else {
+        makefile_repeat *rep = &(MS->repeat_data[--(MS->repeat_sp)]);
+        int as_lines = TRUE;
+        if (mm->reserved_macro_meaning == END_SPAN_RMM) as_lines = FALSE;
+        text_stream *matter = MS->dest;
+        MS->dest = rep->repeat_saved_dest;
+        Makefiles::repeat(matter, as_lines, tfp, rep, MS);
+    }
+
+
  • This code is used in §15 (twice).
+

§15.8. Expand set15.8 = +

+ +
+    text_stream *name = parameter_values[0];
+    text_stream *value = parameter_values[1];
+
+    if (Makefiles::acceptable_variable_name(name) == FALSE)
+        Errors::in_text_file("improper variable name", tfp);
+
+    makefile_variable *var = Makefiles::ensure_variable(name, MS->stack_frame);
+    var->value = Str::duplicate(value);
+
+
  • This code is used in §15.
diff --git a/docs/inweb/6-rw.html b/docs/inweb/6-rw.html index 14e76f5..e971944 100644 --- a/docs/inweb/6-rw.html +++ b/docs/inweb/6-rw.html @@ -87,7 +87,7 @@ uses these extra macros, is expanded to the final file, which does not. Streams::close(&file_to); }
-
  • The structure write_state is accessed in 1/apacs, 5/ptf, 5/tf, 5/hf, 5/df and here.
+
  • The structure write_state is accessed in 1/apacs, 5/ptf, 5/tf, 5/hf, 5/df, 6/mkf and here.

§2. The file consists of definitions of macros, made one at a time, and starting with @define and finishing with @end, and actual material.

diff --git a/docs/inweb/P-htpw.html b/docs/inweb/P-htpw.html index da4cb5a..5e1c19b 100644 --- a/docs/inweb/P-htpw.html +++ b/docs/inweb/P-htpw.html @@ -163,7 +163,6 @@ for example:

-web "The Twin Primes Conjecture": 1 section : 3 paragraphs : 55 lines
 Scan of source lines for '0'
 0000001  SECTION_HEADING.....  Main.
 0000002  COMMENT_BODY........  
@@ -679,7 +678,7 @@ the abstract and for no particular web.
 

§16. Translation mode. Or, finally, we're in TRANSLATE_MODE. We can:

-