1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-06-17 07:40:47 +03:00

Moved simple tangler out of Inter

This commit is contained in:
Graham Nelson 2021-11-28 23:34:30 +00:00
parent 30544466f0
commit 6595b35b32
8 changed files with 28 additions and 385 deletions

View file

@ -1,6 +1,6 @@
# Inform 7
v10.1.0-alpha.1+6T84 'Krypton' (26 November 2021)
v10.1.0-alpha.1+6T85 'Krypton' (28 November 2021)
## About Inform 7

View file

@ -1,3 +1,3 @@
Prerelease: alpha.1
Build Date: 26 November 2021
Build Number: 6T84
Build Date: 28 November 2021
Build Number: 6T85

View file

@ -78,10 +78,9 @@ int InterSkill::build_kit_internally(build_skill *skill, build_step *S,
inter_pipeline *SS =
ParsingPipelines::from_file(pipeline_as_file, pipeline_vars, search_list);
if (SS) {
linked_list *inter_paths = NEW_LINKED_LIST(pathname);
ADD_TO_LINKED_LIST(S->associated_copy->location_if_path, pathname, inter_paths);
linked_list *requirements_list = NEW_LINKED_LIST(attachment_instruction);
RunningPipelines::run(NULL, SS, NULL, inter_paths, requirements_list, S->for_vm);
RunningPipelines::run(NULL, SS, NULL, S->associated_copy->location_if_path,
requirements_list, S->for_vm);
return TRUE;
} else {
Errors::nowhere("build-kit pipeline could not be parsed");
@ -141,8 +140,7 @@ int InterSkill::code_generate_internally(build_skill *skill, build_step *S,
Errors::nowhere("inter pipeline file could not be parsed");
return FALSE;
}
RunningPipelines::run(Filenames::up(S->vertex->as_file),
pipeline, Emit::tree(), Kits::inter_paths(Projects::nest_list(project)),
RunningPipelines::run(Filenames::up(S->vertex->as_file), pipeline, Emit::tree(), NULL,
Projects::list_of_attachment_instructions(project), S->for_vm);
LOG("Back end elapsed time: %dcs\n",
((int) (clock() - back_end)) / (CLOCKS_PER_SEC/100));

View file

@ -92,15 +92,13 @@ form, which would be written to |*outt|.
Errors::fatal("-pipeline-text and -pipeline-file cannot be combined with inter files");
if ((pipeline_as_file) && (pipeline_as_text))
Errors::fatal("-pipeline-text and -pipeline-file are mutually exclusive");
linked_list *inter_paths = NEW_LINKED_LIST(pathname);
if (kit_to_build) ADD_TO_LINKED_LIST(kit_to_build, pathname, inter_paths);
inter_pipeline *SS;
if (pipeline_as_file)
SS = ParsingPipelines::from_file(pipeline_as_file, pipeline_vars, NULL);
else
SS = ParsingPipelines::from_text(pipeline_as_text, pipeline_vars);
linked_list *requirements_list = NEW_LINKED_LIST(attachment_instruction);
if (SS) RunningPipelines::run(domain_path, SS, NULL, inter_paths, requirements_list, NULL);
if (SS) RunningPipelines::run(domain_path, SS, NULL, kit_to_build, requirements_list, NULL);
else Errors::fatal("pipeline could not be parsed");
@<Read the list of inter files, and perhaps transcode them@> =

View file

@ -20,7 +20,7 @@ void RunningPipelines::clean_pipeline(inter_pipeline *pl) {
@ =
typedef struct pipeline_step_ephemera {
struct filename *parsed_filename;
struct linked_list *the_PP; /* of |pathname| */
struct pathname *the_kit; /* if one is involved */
int to_debugging_log;
int from_memory;
struct text_stream *to_stream;
@ -38,7 +38,7 @@ void RunningPipelines::clean_step(pipeline_step *step) {
step->ephemera.to_stream = NULL;
step->ephemera.to_debugging_log = FALSE;
step->ephemera.from_memory = FALSE;
step->ephemera.the_PP = NULL;
step->ephemera.the_kit = NULL;
step->ephemera.repository = NULL;
step->ephemera.pipeline = NULL;
step->ephemera.requirements_list = NEW_LINKED_LIST(attachment_instruction);
@ -57,7 +57,7 @@ steps in turn, timing how long each one took us.
pipeline_step *currently_running_pipeline_step = NULL;
void RunningPipelines::run(pathname *P, inter_pipeline *S, inter_tree *I,
linked_list *PP, linked_list *requirements_list, target_vm *VM) {
pathname *the_kit, linked_list *requirements_list, target_vm *VM) {
if (S == NULL) return;
if (I) S->ephemera.memory_repository = I;
stopwatch_timer *within = NULL;
@ -97,7 +97,7 @@ void RunningPipelines::run(pathname *P, inter_pipeline *S, inter_tree *I,
@<Prepare ephemeral data for this step@> =
RunningPipelines::clean_step(step);
step->ephemera.the_PP = PP;
step->ephemera.the_kit = the_kit;
if (S->ephemera.repositories[step->repository_argument] == NULL)
S->ephemera.repositories[step->repository_argument] = InterTree::new();
inter_tree *I = S->ephemera.repositories[step->repository_argument];

View file

@ -4,8 +4,7 @@ Two stages which accept raw I6-syntax material in the parse tree, either from
imsertions made using Inform 7's low-level features, or after reading the
source code for a kit.
@h The two stages.
These stages have more in common than they first appear. Both convert I6T-syntax
@ These stages have more in common than first appears. Both convert I6T-syntax
source code into a series of |SPLAT_IST| nodes in the Inter tree, with one
such node for each different directive in the I6T source.
@ -33,7 +32,7 @@ int ParsingStages::run_load_kit_source(pipeline_step *step) {
if (main_package) @<Create a module to hold the Inter read in from this kit@>;
simple_tangle_docket docket;
@<Make a suitable simple tangler docket@>;
SimpleTangler::tangle(&docket, NULL, I"all");
SimpleTangler::tangle_web(&docket);
return TRUE;
}
@ -80,14 +79,12 @@ void ParsingStages::visit_insertions(inter_tree *I, inter_tree_node *P, void *st
current_sentence = (parse_node *) Inode::ID_to_ref(P, P->W.data[REF_LINK_IFLD]);
#endif
simple_tangle_docket *docket = (simple_tangle_docket *) state;
SimpleTangler::tangle(docket, insertion, NULL);
SimpleTangler::tangle_text(docket, insertion);
}
@ So, then, both of those stages rely on (i) making something called an simple tangler docket,
then (ii) calling //SimpleTangler::tangle//.
Here's where we make the docket, which is really just a collection of settings for
the simple tangler. That comes down to:
@ So, then, both of those stages rely on making something called an simple
tangler docket, which is really just a collection of settings for the
simple tangler. That comes down to:
(a) the place to put any nodes generated,
(b) what to do with I6 source code, or with commands embedded in it, or errors
@ -103,15 +100,12 @@ in |K/Sections|.
RunningPipelines::get_symbol(step, plain_ptype_RPSYM));
inter_bookmark assimilation_point =
Inter::Bookmarks::at_end_of_this_package(assimilation_package);
linked_list *L = NEW_LINKED_LIST(pathname);
pathname *P;
LOOP_OVER_LINKED_LIST(P, pathname, step->ephemera.the_PP)
ADD_TO_LINKED_LIST(Pathnames::down(P, I"Sections"), pathname, L);
docket = SimpleTangler::new_docket(
&(ParsingStages::receive_raw),
&(ParsingStages::receive_command),
&(ParsingStages::receive_bplus),
&(PipelineErrors::kit_error),
L, &assimilation_point);
step->ephemera.the_kit, &assimilation_point);
@ Once the I6T reader has unpacked the literate-programming notation, it will
reduce the I6T code to pure Inform 6 source together with (perhaps) a handful of
@ -158,6 +152,15 @@ void ParsingStages::receive_command(OUTPUT_STREAM, text_stream *command,
}
}
@ We have similarly withdrawn the ability to write |(+| ... |+)| material
in kit files:
=
void ParsingStages::receive_bplus(text_stream *material, simple_tangle_docket *docket) {
(*(docket->error_callback))(
"use of (+ ... +) in kit source has been withdrawn: '%S'", material);
}
@ We very much do not ignore the raw I6 code read in, though. When the reader
gives us a chunk of this, we parse through it with a simple finite-state machine.
This can be summarised as "divide the code up at |;| boundaries, sending each

View file

@ -1,355 +0,0 @@
[SimpleTangler::] Simple Tangler.
Unravelling (a simple version of) Inweb's literate programming notation to
access the tangled content.
@h The I6T Reader.
The rest of this section, then, is a general-purpose reader of I6T-syntax code.
Although it is only used for one purpose in the Inform code base, it once had
multiple uses, and so it's written quite flexibly. There seems no reason to
get rid of that flexibility: perhaps we'll use it again some day.
So, then, this is the parcel of settings for controlling the I6T reader. The
|state| here is not used by the reader itself, but instead allows the callback
functions to have a shared state of their own.
=
typedef struct simple_tangle_docket {
void (*raw_callback)(struct text_stream *, struct simple_tangle_docket *);
void (*command_callback)(struct text_stream *, struct text_stream *,
struct text_stream *, struct simple_tangle_docket *);
void (*error_callback)(char *, struct text_stream *);
void *state;
struct linked_list *search_paths; /* of |pathname| */
} simple_tangle_docket;
@ =
simple_tangle_docket SimpleTangler::new_docket(
void (*A)(struct text_stream *, struct simple_tangle_docket *),
void (*B)(struct text_stream *, struct text_stream *,
struct text_stream *, struct simple_tangle_docket *),
void (*C)(char *, struct text_stream *),
linked_list *search_list, void *initial_state) {
simple_tangle_docket docket;
docket.raw_callback = A;
docket.command_callback = B;
docket.error_callback = C;
docket.state = initial_state;
docket.search_paths = search_list;
return docket;
}
@ I6T files use a literate programming notation which is, in effect, a much
simplified version of Inweb's. (Note that Inweb can therefore read kits as
if they were webs, and we use that to weave them for the source website.)
Many Inweb syntaxes are, however, not allowed in I6T: really, you should use
only |@h| headings and the |=| sign to divide commentary from text. Macros and
definitions, in particular, are not permitted; I6T is not really tangled as such.
The entire range of possibilities is shown here:
= (text as Inweb)
Circuses.
This hypothetical I6T file provides support for holding circuses.
@h Start.
This routine is called when a big top must be raised. Note that the
elephants must first be watered (see Livestock.i6t).
=
[ RaiseBT c;
...
];
=
...and so on. As with Inweb, the commentary is removed when we read this
code. While this doesn't allow for full-on literate programming, it does
permit a generous amount of annotation.
@ One restriction. It actually doesn't matter if a template file contains
lines longer than this, so long as they do not occur inside |{-lines:...}| and
|{-endlines}|, and so long as no individual braced command |{-...}| exceeds
this length.
@d MAX_I6T_LINE_LENGTH 1024
@ The I6T interpreter is then a single routine to implement the description
above, though note that it can act on interventions as well. (But in modern
Inform usage, often there won't be any, because templates for the Standard
Rules and so forth are assimilated in stand-alone runs of the code generator,
and therefore no interventions will have happened.)
=
void SimpleTangler::tangle(simple_tangle_docket *docket, text_stream *insertion, text_stream *segment) {
TEMPORARY_TEXT(T)
SimpleTangler::tangle_L2(T, insertion, segment, -1, docket, NULL);
(*(docket->raw_callback))(T, docket);
DISCARD_TEXT(T)
}
void SimpleTangler::tangle_L2(OUTPUT_STREAM, text_stream *sf,
text_stream *segment_name, int N_escape, simple_tangle_docket *docket, filename *Input_Filename) {
if (Str::eq(segment_name, I"all")) {
pathname *K;
LOOP_OVER_LINKED_LIST(K, pathname, docket->search_paths) {
pathname *P = Pathnames::up(K);
web_md *Wm = WebMetadata::get(P, NULL, V2_SYNTAX, NULL, FALSE, TRUE, NULL);
chapter_md *Cm;
LOOP_OVER_LINKED_LIST(Cm, chapter_md, Wm->chapters_md) {
section_md *Sm;
LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md) {
filename *SF = Sm->source_file_for_section;
SimpleTangler::tangle_L3(OUT, sf, Sm->sect_title, N_escape, docket, SF);
}
}
}
return;
}
SimpleTangler::tangle_L3(OUT, sf, segment_name, N_escape, docket, Input_Filename);
}
void SimpleTangler::tangle_L3(OUTPUT_STREAM, text_stream *sf,
text_stream *segment_name, int N_escape, simple_tangle_docket *docket, filename *Input_Filename) {
TEMPORARY_TEXT(heading_name)
int skip_part = FALSE, comment = TRUE, extract = FALSE;
int col = 1, cr, sfp = 0;
FILE *Input_File = NULL;
if ((Str::len(segment_name) > 0) || (Input_Filename)) {
@<Open the I6 template file@>;
comment = TRUE;
} else comment = FALSE;
@<Interpret the I6T file@>;
if (Input_File) { if (DL) STREAM_FLUSH(DL); fclose(Input_File); }
DISCARD_TEXT(heading_name)
}
@ We look for the |.i6t| files in a list of possible locations supplied as
part of the I6T docket.
@<Open the I6 template file@> =
if (Input_Filename)
Input_File = Filenames::fopen(Input_Filename, "r");
pathname *P;
LOOP_OVER_LINKED_LIST(P, pathname, docket->search_paths)
if (Input_File == NULL)
Input_File = Filenames::fopen(
Filenames::in(P, segment_name), "r");
if (Input_File == NULL)
(*(docket->error_callback))("unable to open the template segment '%S'", segment_name);
@
@<Interpret the I6T file@> =
TEMPORARY_TEXT(command)
TEMPORARY_TEXT(argument)
do {
Str::clear(command);
Str::clear(argument);
@<Read next character from I6T stream@>;
NewCharacter: if (cr == EOF) break;
if (((cr == '@') || (cr == '=')) && (col == 1)) {
int inweb_syntax = -1;
if (cr == '=') @<Read the rest of line as an equals-heading@>
else @<Read the rest of line as an at-heading@>;
@<Act on the heading, going in or out of comment mode as appropriate@>;
continue;
}
if (comment == FALSE) @<Deal with material which isn't commentary@>;
} while (cr != EOF);
DISCARD_TEXT(command)
DISCARD_TEXT(argument)
@ I6 template files are encoded as ISO Latin-1, not as Unicode UTF-8, so
ordinary |fgetc| is used, and no BOM marker is parsed. Lines are assumed
to be terminated with either |0x0a| or |0x0d|. (Since blank lines are
harmless, we take no trouble over |0a0d| or |0d0a| combinations.) The
built-in template files, almost always the only ones used, are line
terminated |0x0a| in Unix fashion.
@<Read next character from I6T stream@> =
if (Input_File) cr = fgetc(Input_File);
else if (sf) {
cr = Str::get_at(sf, sfp); if (cr == 0) cr = EOF; else sfp++;
} else cr = EOF;
col++; if ((cr == 10) || (cr == 13)) col = 0;
@ Anything following an at-character in the first column is looked at to see if
it's a heading, that is, an Inweb syntax. We recognise both |@h| and |@p| as
heading markers, in order to accommodate both old and new Inweb syntaxes.
@d INWEB_PARAGRAPH_SYNTAX 1
@d INWEB_CODE_SYNTAX 2
@d INWEB_DASH_SYNTAX 3
@d INWEB_PURPOSE_SYNTAX 4
@d INWEB_FIGURE_SYNTAX 5
@d INWEB_EQUALS_SYNTAX 6
@d INWEB_EXTRACT_SYNTAX 7
@<Read the rest of line as an at-heading@> =
TEMPORARY_TEXT(I6T_buffer)
int i = 0, committed = FALSE, unacceptable_character = FALSE;
while (i<MAX_I6T_LINE_LENGTH) {
@<Read next character from I6T stream@>;
if ((committed == FALSE) && ((cr == 10) || (cr == 13) || (cr == ' '))) {
if (Str::eq_wide_string(I6T_buffer, L"p"))
inweb_syntax = INWEB_PARAGRAPH_SYNTAX;
else if (Str::eq_wide_string(I6T_buffer, L"h"))
inweb_syntax = INWEB_PARAGRAPH_SYNTAX;
else if (Str::eq_wide_string(I6T_buffer, L"c"))
inweb_syntax = INWEB_CODE_SYNTAX;
else if (Str::get_first_char(I6T_buffer) == '-')
inweb_syntax = INWEB_DASH_SYNTAX;
else if (Str::begins_with_wide_string(I6T_buffer, L"Purpose:"))
inweb_syntax = INWEB_PURPOSE_SYNTAX;
committed = TRUE;
if (inweb_syntax == -1) {
if (unacceptable_character == FALSE) {
PUT_TO(OUT, '@');
WRITE_TO(OUT, "%S", I6T_buffer);
PUT_TO(OUT, cr);
break;
} else {
LOG("heading begins: <%S>\n", I6T_buffer);
(*(docket->error_callback))(
"unknown '@...' marker at column 0 in template matter: '%S'", I6T_buffer);
}
}
}
if (!(((cr >= 'A') && (cr <= 'Z')) || ((cr >= 'a') && (cr <= 'z'))
|| ((cr >= '0') && (cr <= '9'))
|| (cr == '-') || (cr == '>') || (cr == ':') || (cr == '_')))
unacceptable_character = TRUE;
if ((cr == 10) || (cr == 13)) break;
PUT_TO(I6T_buffer, cr);
}
Str::copy(command, I6T_buffer);
DISCARD_TEXT(I6T_buffer)
@<Read the rest of line as an equals-heading@> =
TEMPORARY_TEXT(I6T_buffer)
int i = 0;
while (i<MAX_I6T_LINE_LENGTH) {
@<Read next character from I6T stream@>;
if ((cr == 10) || (cr == 13)) break;
PUT_TO(I6T_buffer, cr);
}
DISCARD_TEXT(I6T_buffer)
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, I6T_buffer, L" %(text%c*%) *")) {
inweb_syntax = INWEB_EXTRACT_SYNTAX;
} else if (Regexp::match(&mr, I6T_buffer, L" %(figure%c*%) *")) {
inweb_syntax = INWEB_FIGURE_SYNTAX;
} else if (Regexp::match(&mr, I6T_buffer, L" %(%c*%) *")) {
(*(docket->error_callback))(
"unsupported '= (...)' marker at column 0 in template matter", NULL);
} else {
inweb_syntax = INWEB_EQUALS_SYNTAX;
}
Regexp::dispose_of(&mr);
@ As can be seen, only a small minority of Inweb syntaxes are allowed:
in particular, no definitions| or angle-bracketed macros. This reader is not
a full-fledged tangler.
@<Act on the heading, going in or out of comment mode as appropriate@> =
switch (inweb_syntax) {
case INWEB_PARAGRAPH_SYNTAX: {
Str::copy_tail(heading_name, command, 2);
int c;
while (((c = Str::get_last_char(heading_name)) != 0) &&
((c == ' ') || (c == '\t') || (c == '.')))
Str::delete_last_character(heading_name);
if (Str::len(heading_name) == 0)
(*(docket->error_callback))("Empty heading name in I6 template file", NULL);
extract = FALSE;
comment = TRUE; skip_part = FALSE;
break;
}
case INWEB_CODE_SYNTAX:
extract = FALSE;
if (skip_part == FALSE) comment = FALSE;
break;
case INWEB_EQUALS_SYNTAX:
if (extract) {
comment = TRUE; extract = FALSE;
} else {
if (skip_part == FALSE) comment = FALSE;
}
break;
case INWEB_EXTRACT_SYNTAX:
comment = TRUE; extract = TRUE;
break;
case INWEB_DASH_SYNTAX: break;
case INWEB_PURPOSE_SYNTAX: break;
case INWEB_FIGURE_SYNTAX: break;
}
@<Deal with material which isn't commentary@> =
if (cr == '{') {
@<Read next character from I6T stream@>;
if (cr == '-') {
@<Read up to the next close brace as an I6T command and argument@>;
if (Str::get_first_char(command) == '!') continue;
(*(docket->command_callback))(OUT, command, argument, docket);
continue;
} else if ((cr == 'N') && (N_escape >= 0)) {
@<Read next character from I6T stream@>;
if (cr == '}') {
WRITE("%d", N_escape);
continue;
}
WRITE("{N");
goto NewCharacter;
} else { /* otherwise the open brace was a literal */
PUT_TO(OUT, '{');
goto NewCharacter;
}
}
if (cr == '(') {
@<Read next character from I6T stream@>;
if (cr == '+') {
@<Read up to the next plus close-bracket as an I7 expression@>;
continue;
} else { /* otherwise the open bracket was a literal */
PUT_TO(OUT, '(');
goto NewCharacter;
}
}
PUT_TO(OUT, cr);
@ And here we read a normal command. The command name must not include |}|
or |:|. If there is no |:| then the argument is left unset (so that it will
be the empty string: see above). The argument must not include |}|.
@<Read up to the next close brace as an I6T command and argument@> =
Str::clear(command);
Str::clear(argument);
int com_mode = TRUE;
while (TRUE) {
@<Read next character from I6T stream@>;
if ((cr == '}') || (cr == EOF)) break;
if ((cr == ':') && (com_mode)) { com_mode = FALSE; continue; }
if (com_mode) PUT_TO(command, cr);
else PUT_TO(argument, cr);
}
@ And similarly, for the |(+| ... |+)| notation used to mark I7 material
within I6:
@<Read up to the next plus close-bracket as an I7 expression@> =
TEMPORARY_TEXT(i7_exp)
while (TRUE) {
@<Read next character from I6T stream@>;
if (cr == EOF) break;
if ((cr == ')') && (Str::get_last_char(i7_exp) == '+')) {
Str::delete_last_character(i7_exp); break; }
PUT_TO(i7_exp, cr);
}
DISCARD_TEXT(i7_exp)
(*(docket->error_callback))(
"use of (+ ... +) in the template has been withdrawn: '%S'", i7_exp);

View file

@ -21,7 +21,6 @@ Chapter 3: Linking Stages
Load Binary Kits Stage
Parsing Stages
Parse Linked Matter
Simple Tangler
Resolving Conditional Compilation
Assimilate
Resolve External Symbols