Chapter 4: Nowebify.
This commit is contained in:
parent
82d8056b15
commit
e1ca0836cd
7 changed files with 612 additions and 575 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
For generic programming languages by the ACME corporation.
|
For generic programming languages by the ACME corporation.
|
||||||
|
|
||||||
@h One Dozen ACME Explosive Tennis Balls.
|
@ \section{One Dozen ACME Explosive Tennis Balls.}
|
||||||
Older readers will remember that Wile E. Coyote, when wishing to frustrate
|
Older readers will remember that Wile E. Coyote, when wishing to frustrate
|
||||||
Road Runner with some ingenious device, would invariably buy it from the Acme
|
Road Runner with some ingenious device, would invariably buy it from the Acme
|
||||||
Corporation, which manufactured everything imaginable. See Wikipedia, "Acme
|
Corporation, which manufactured everything imaginable. See Wikipedia, "Acme
|
||||||
|
@ -12,7 +12,7 @@ For us, ACME is an imaginary programming language, providing generic support
|
||||||
for comments and syntax colouring. Ironically, this code grew out of a language
|
for comments and syntax colouring. Ironically, this code grew out of a language
|
||||||
actually called ACME: the 6502 assembler of the same name.
|
actually called ACME: the 6502 assembler of the same name.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::add_fallbacks(programming_language *pl) {
|
void ACMESupport::add_fallbacks(programming_language *pl) {
|
||||||
if (Methods::provided(pl->methods, PARSE_TYPES_PAR_MTID) == FALSE)
|
if (Methods::provided(pl->methods, PARSE_TYPES_PAR_MTID) == FALSE)
|
||||||
METHOD_ADD(pl, PARSE_TYPES_PAR_MTID, ACMESupport::parse_types);
|
METHOD_ADD(pl, PARSE_TYPES_PAR_MTID, ACMESupport::parse_types);
|
||||||
|
@ -54,11 +54,11 @@ void ACMESupport::add_fallbacks(programming_language *pl) {
|
||||||
METHOD_ADD(pl, SYNTAX_COLOUR_WEA_MTID, ACMESupport::syntax_colour);
|
METHOD_ADD(pl, SYNTAX_COLOUR_WEA_MTID, ACMESupport::syntax_colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ This utility does a very limited |WRITE|-like job. (We don't want to use
|
@ This utility does a very limited [[WRITE]]-like job. (We don't want to use
|
||||||
the actual |WRITE| because that would make it possible for malicious language
|
the actual [[WRITE]] because that would make it possible for malicious language
|
||||||
files to crash Inweb.)
|
files to crash Inweb.)
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::expand(OUTPUT_STREAM, text_stream *prototype, text_stream *S,
|
void ACMESupport::expand(OUTPUT_STREAM, text_stream *prototype, text_stream *S,
|
||||||
int N, filename *F) {
|
int N, filename *F) {
|
||||||
if (Str::len(prototype) > 0) {
|
if (Str::len(prototype) > 0) {
|
||||||
|
@ -80,9 +80,9 @@ void ACMESupport::expand(OUTPUT_STREAM, text_stream *prototype, text_stream *S,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Tangling methods.
|
@ \section{Tangling methods.}
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::shebang(programming_language *pl, text_stream *OUT, web *W,
|
void ACMESupport::shebang(programming_language *pl, text_stream *OUT, web *W,
|
||||||
tangle_target *target) {
|
tangle_target *target) {
|
||||||
ACMESupport::expand(OUT, pl->shebang, NULL, -1, NULL);
|
ACMESupport::expand(OUT, pl->shebang, NULL, -1, NULL);
|
||||||
|
@ -158,20 +158,20 @@ void ACMESupport::comment(programming_language *pl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ In the following, |q_mode| is 0 outside quotes, 1 inside a character literal,
|
@ In the following, [[q_mode]] is 0 outside quotes, 1 inside a character literal,
|
||||||
and 2 inside a string literal; |c_mode| is 0 outside comments, 1 inside a line
|
and 2 inside a string literal; [[c_mode]] is 0 outside comments, 1 inside a line
|
||||||
comment, and 2 inside a multiline comment.
|
comment, and 2 inside a multiline comment.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int ACMESupport::parse_comment(programming_language *pl,
|
int ACMESupport::parse_comment(programming_language *pl,
|
||||||
text_stream *line, text_stream *part_before_comment, text_stream *part_within_comment) {
|
text_stream *line, text_stream *part_before_comment, text_stream *part_within_comment) {
|
||||||
int q_mode = 0, c_mode = 0, non_white_space = FALSE, c_position = -1, c_end = -1;
|
int q_mode = 0, c_mode = 0, non_white_space = FALSE, c_position = -1, c_end = -1;
|
||||||
for (int i=0; i<Str::len(line); i++) {
|
for (int i=0; i<Str::len(line); i++) {
|
||||||
wchar_t c = Str::get_at(line, i);
|
wchar_t c = Str::get_at(line, i);
|
||||||
switch (c_mode) {
|
switch (c_mode) {
|
||||||
case 0: @<Outside commentary@>; break;
|
case 0: <<Outside commentary>>; break;
|
||||||
case 1: @<Inside a line comment@>; break;
|
case 1: <<Inside a line comment>>; break;
|
||||||
case 2: @<Inside a multiline comment@>; break;
|
case 2: <<Inside a multiline comment>>; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c_mode == 2) c_end = Str::len(line);
|
if (c_mode == 2) c_end = Str::len(line);
|
||||||
|
@ -188,22 +188,22 @@ int ACMESupport::parse_comment(programming_language *pl,
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Inside a multiline comment@> =
|
<<Inside a multiline comment>>=
|
||||||
if (Str::includes_at(line, i, pl->multiline_comment_close)) {
|
if (Str::includes_at(line, i, pl->multiline_comment_close)) {
|
||||||
c_mode = 0; c_end = i; i += Str::len(pl->multiline_comment_close) - 1;
|
c_mode = 0; c_end = i; i += Str::len(pl->multiline_comment_close) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Inside a line comment@> =
|
<<Inside a line comment>>=
|
||||||
;
|
;
|
||||||
|
|
||||||
@<Outside commentary@> =
|
<<Outside commentary>>=
|
||||||
switch (q_mode) {
|
switch (q_mode) {
|
||||||
case 0: @<Outside quoted matter@>; break;
|
case 0: <<Outside quoted matter>>; break;
|
||||||
case 1: @<Inside a literal character@>; break;
|
case 1: <<Inside a literal character>>; break;
|
||||||
case 2: @<Inside a literal string@>; break;
|
case 2: <<Inside a literal string>>; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Outside quoted matter@> =
|
<<Outside quoted matter>>=
|
||||||
if (!(Characters::is_whitespace(c))) non_white_space = TRUE;
|
if (!(Characters::is_whitespace(c))) non_white_space = TRUE;
|
||||||
if (c == Str::get_first_char(pl->string_literal)) q_mode = 2;
|
if (c == Str::get_first_char(pl->string_literal)) q_mode = 2;
|
||||||
else if (c == Str::get_first_char(pl->character_literal)) q_mode = 1;
|
else if (c == Str::get_first_char(pl->character_literal)) q_mode = 1;
|
||||||
|
@ -225,13 +225,13 @@ int ACMESupport::parse_comment(programming_language *pl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Inside a literal character@> =
|
<<Inside a literal character>>=
|
||||||
if (!(Characters::is_whitespace(c))) non_white_space = TRUE;
|
if (!(Characters::is_whitespace(c))) non_white_space = TRUE;
|
||||||
if (c == Str::get_first_char(pl->character_literal_escape)) i += 1;
|
if (c == Str::get_first_char(pl->character_literal_escape)) i += 1;
|
||||||
if (c == Str::get_first_char(pl->character_literal)) q_mode = 0;
|
if (c == Str::get_first_char(pl->character_literal)) q_mode = 0;
|
||||||
q_mode = 0;
|
q_mode = 0;
|
||||||
|
|
||||||
@<Inside a literal string@> =
|
<<Inside a literal string>>=
|
||||||
if (!(Characters::is_whitespace(c))) non_white_space = TRUE;
|
if (!(Characters::is_whitespace(c))) non_white_space = TRUE;
|
||||||
if (c == Str::get_first_char(pl->string_literal_escape)) i += 1;
|
if (c == Str::get_first_char(pl->string_literal_escape)) i += 1;
|
||||||
if (c == Str::get_first_char(pl->string_literal)) q_mode = 0;
|
if (c == Str::get_first_char(pl->string_literal)) q_mode = 0;
|
||||||
|
@ -239,7 +239,7 @@ int ACMESupport::parse_comment(programming_language *pl,
|
||||||
|
|
||||||
@
|
@
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::parse_types(programming_language *self, web *W) {
|
void ACMESupport::parse_types(programming_language *self, web *W) {
|
||||||
if (W->main_language->type_notation[0]) {
|
if (W->main_language->type_notation[0]) {
|
||||||
chapter *C;
|
chapter *C;
|
||||||
|
@ -258,7 +258,7 @@ void ACMESupport::parse_types(programming_language *self, web *W) {
|
||||||
|
|
||||||
@
|
@
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::parse_functions(programming_language *self, web *W) {
|
void ACMESupport::parse_functions(programming_language *self, web *W) {
|
||||||
if (W->main_language->function_notation[0]) {
|
if (W->main_language->function_notation[0]) {
|
||||||
chapter *C;
|
chapter *C;
|
||||||
|
@ -278,12 +278,12 @@ void ACMESupport::parse_functions(programming_language *self, web *W) {
|
||||||
|
|
||||||
@ The following is an opportunity for us to scold the author for any
|
@ The following is an opportunity for us to scold the author for any
|
||||||
violation of the namespace rules. We're going to look for functions named
|
violation of the namespace rules. We're going to look for functions named
|
||||||
|Whatever::name()| whose definitions are not in the |Whatever::| section;
|
[[Whatever::name()]] whose definitions are not in the [[Whatever::]] section;
|
||||||
in other words, we police the rule that functions actually are defined in the
|
in other words, we police the rule that functions actually are defined in the
|
||||||
namespace which their names imply. This can be turned off with a special
|
namespace which their names imply. This can be turned off with a special
|
||||||
bibliographic variable, but don't do that.
|
bibliographic variable, but don't do that.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::post_analysis(programming_language *self, web *W) {
|
void ACMESupport::post_analysis(programming_language *self, web *W) {
|
||||||
int check_namespaces = FALSE;
|
int check_namespaces = FALSE;
|
||||||
if (Str::eq_wide_string(Bibliographic::get_datum(W->md, I"Namespaces"), L"On"))
|
if (Str::eq_wide_string(Bibliographic::get_datum(W->md, I"Namespaces"), L"On"))
|
||||||
|
@ -319,7 +319,7 @@ void ACMESupport::post_analysis(programming_language *self, web *W) {
|
||||||
@ Having found all those functions and structure elements, we make sure they
|
@ Having found all those functions and structure elements, we make sure they
|
||||||
are all known to Inweb's hash table of interesting identifiers:
|
are all known to Inweb's hash table of interesting identifiers:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::analyse_code(programming_language *self, web *W) {
|
void ACMESupport::analyse_code(programming_language *self, web *W) {
|
||||||
language_function *fn;
|
language_function *fn;
|
||||||
LOOP_OVER(fn, language_function)
|
LOOP_OVER(fn, language_function)
|
||||||
|
@ -337,14 +337,14 @@ void ACMESupport::analyse_code(programming_language *self, web *W) {
|
||||||
@ This is here so that tangling the Standard Rules extension doesn't insert
|
@ This is here so that tangling the Standard Rules extension doesn't insert
|
||||||
a spurious comment betraying Inweb's involvement in the process.
|
a spurious comment betraying Inweb's involvement in the process.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int ACMESupport::suppress_disclaimer(programming_language *pl) {
|
int ACMESupport::suppress_disclaimer(programming_language *pl) {
|
||||||
return pl->suppress_disclaimer;
|
return pl->suppress_disclaimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@
|
@
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::begin_weave(programming_language *pl, section *S, weave_order *wv) {
|
void ACMESupport::begin_weave(programming_language *pl, section *S, weave_order *wv) {
|
||||||
reserved_word *rw;
|
reserved_word *rw;
|
||||||
LOOP_OVER_LINKED_LIST(rw, reserved_word, pl->reserved_words)
|
LOOP_OVER_LINKED_LIST(rw, reserved_word, pl->reserved_words)
|
||||||
|
@ -353,7 +353,7 @@ void ACMESupport::begin_weave(programming_language *pl, section *S, weave_order
|
||||||
|
|
||||||
@ ACME has all of its syntax-colouring done by the default engine:
|
@ ACME has all of its syntax-colouring done by the default engine:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void ACMESupport::reset_syntax_colouring(programming_language *pl) {
|
void ACMESupport::reset_syntax_colouring(programming_language *pl) {
|
||||||
Painter::reset_syntax_colouring(pl);
|
Painter::reset_syntax_colouring(pl);
|
||||||
}
|
}
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
To provide special features for the whole C family of languages.
|
To provide special features for the whole C family of languages.
|
||||||
|
|
||||||
@h What makes a language C-like?
|
@ \section{What makes a language C-like?}
|
||||||
This does:
|
This does:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void CLike::make_c_like(programming_language *pl) {
|
void CLike::make_c_like(programming_language *pl) {
|
||||||
METHOD_ADD(pl, PARSE_TYPES_PAR_MTID, CLike::parse_types);
|
METHOD_ADD(pl, PARSE_TYPES_PAR_MTID, CLike::parse_types);
|
||||||
METHOD_ADD(pl, PARSE_FUNCTIONS_PAR_MTID, CLike::parse_functions);
|
METHOD_ADD(pl, PARSE_FUNCTIONS_PAR_MTID, CLike::parse_functions);
|
||||||
|
@ -15,38 +15,38 @@ void CLike::make_c_like(programming_language *pl) {
|
||||||
METHOD_ADD(pl, ADDITIONAL_PREDECLARATIONS_TAN_MTID, CLike::additional_predeclarations);
|
METHOD_ADD(pl, ADDITIONAL_PREDECLARATIONS_TAN_MTID, CLike::additional_predeclarations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Parsing.
|
@ \section{Parsing.}
|
||||||
After a web has been read in and then parsed, code supporting its language
|
After a web has been read in and then parsed, code supporting its language
|
||||||
is then called to do any further parsing it might want to. The code below
|
is then called to do any further parsing it might want to. The code below
|
||||||
is run if the language is "C-like": regular C and InC both qualify.
|
is run if the language is "C-like": regular C and InC both qualify.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void CLike::parse_types(programming_language *self, web *W) {
|
void CLike::parse_types(programming_language *self, web *W) {
|
||||||
@<Find every typedef struct in the tangle@>;
|
<<Find every typedef struct in the tangle>>;
|
||||||
@<Work out which structs contain which others@>;
|
<<Work out which structs contain which others>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ We're going to assume that the C source code uses structures looking
|
@ We're going to assume that the C source code uses structures looking
|
||||||
something like this:
|
something like this:
|
||||||
= (text as C)
|
|
||||||
typedef struct fruit {
|
typedef struct fruit {
|
||||||
struct pip the_pips[5];
|
struct pip the_pips[5];
|
||||||
struct fruit *often_confused_with;
|
struct fruit *often_confused_with;
|
||||||
struct tree_species *grows_on;
|
struct tree_species *grows_on;
|
||||||
int typical_weight;
|
int typical_weight;
|
||||||
} fruit;
|
} fruit;
|
||||||
=
|
|
||||||
which adopts the traditional layout conventions of Kernighan and Ritchie.
|
which adopts the traditional layout conventions of Kernighan and Ritchie.
|
||||||
The structure definitions in this Inweb web all take the required form,
|
The structure definitions in this Inweb web all take the required form,
|
||||||
of course, and provide many more examples.
|
of course, and provide many more examples.
|
||||||
|
|
||||||
Note that a |fruit| structure contains a |pip| structure (in fact, five of
|
Note that a [[fruit]] structure contains a [[pip]] structure (in fact, five of
|
||||||
them), but only contains pointers to |tree_species| structures and itself.
|
them), but only contains pointers to [[tree_species]] structures and itself.
|
||||||
C requires therefore that the structure definition for |pip| must occur
|
C requires therefore that the structure definition for [[pip]] must occur
|
||||||
earlier in the code than that for |fruit|. This is a nuisance, so Inweb
|
earlier in the code than that for [[fruit]]. This is a nuisance, so Inweb
|
||||||
takes care of it automatically.
|
takes care of it automatically.
|
||||||
|
|
||||||
@<Find every typedef struct in the tangle@> =
|
<<Find every typedef struct in the tangle>>=
|
||||||
language_type *current_str = NULL;
|
language_type *current_str = NULL;
|
||||||
chapter *C;
|
chapter *C;
|
||||||
section *S;
|
section *S;
|
||||||
|
@ -61,7 +61,7 @@ takes care of it automatically.
|
||||||
current_str->typedef_ends = L;
|
current_str->typedef_ends = L;
|
||||||
current_str = NULL;
|
current_str = NULL;
|
||||||
} else if ((current_str) && (current_str->typedef_ends == NULL)) {
|
} else if ((current_str) && (current_str->typedef_ends == NULL)) {
|
||||||
@<Work through a line in the structure definition@>;
|
<<Work through a line in the structure definition>>;
|
||||||
} else if ((Regexp::match(&mr, L->text, L"typedef %c+")) &&
|
} else if ((Regexp::match(&mr, L->text, L"typedef %c+")) &&
|
||||||
(Regexp::match(&mr, L->text, L"%c+##%c+") == FALSE)) {
|
(Regexp::match(&mr, L->text, L"%c+##%c+") == FALSE)) {
|
||||||
if (L->owning_paragraph->placed_very_early == FALSE)
|
if (L->owning_paragraph->placed_very_early == FALSE)
|
||||||
|
@ -73,24 +73,24 @@ takes care of it automatically.
|
||||||
|
|
||||||
@ At this point we're reading a line within the structure's definition; for
|
@ At this point we're reading a line within the structure's definition; for
|
||||||
the sake of an illustrative example, let's suppose that line is:
|
the sake of an illustrative example, let's suppose that line is:
|
||||||
= (text)
|
|
||||||
unsigned long long int *val;
|
|
||||||
=
|
|
||||||
We need to extract the element name, |val|, and make a note of it.
|
|
||||||
|
|
||||||
@<Work through a line in the structure definition@> =
|
unsigned long long int *val;
|
||||||
|
|
||||||
|
We need to extract the element name, [[val]], and make a note of it.
|
||||||
|
|
||||||
|
<<Work through a line in the structure definition>>=
|
||||||
TEMPORARY_TEXT(p)
|
TEMPORARY_TEXT(p)
|
||||||
Str::copy(p, L->text);
|
Str::copy(p, L->text);
|
||||||
Str::trim_white_space(p);
|
Str::trim_white_space(p);
|
||||||
@<Remove C type modifiers from the front of p@>;
|
<<Remove C type modifiers from the front of p>>;
|
||||||
string_position pos = Str::start(p);
|
string_position pos = Str::start(p);
|
||||||
if (Str::get(pos) != '/') { /* a slash must introduce a comment here */
|
if (Str::get(pos) != '/') { /* a slash must introduce a comment here */
|
||||||
@<Move pos past the type name@>;
|
<<Move pos past the type name>>;
|
||||||
@<Move pos past any typographical type modifiers@>;
|
<<Move pos past any typographical type modifiers>>;
|
||||||
if (Str::in_range(pos)) {
|
if (Str::in_range(pos)) {
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
TEMPORARY_TEXT(elname)
|
TEMPORARY_TEXT(elname)
|
||||||
@<Copy the element name into elname@>;
|
<<Copy the element name into elname>>;
|
||||||
Functions::new_element(current_str, elname, L);
|
Functions::new_element(current_str, elname, L);
|
||||||
DISCARD_TEXT(elname)
|
DISCARD_TEXT(elname)
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
@ -98,9 +98,9 @@ We need to extract the element name, |val|, and make a note of it.
|
||||||
}
|
}
|
||||||
DISCARD_TEXT(p)
|
DISCARD_TEXT(p)
|
||||||
|
|
||||||
@ The following reduces |unsigned long long int *val;| to just |int *val;|.
|
@ The following reduces [[unsigned long long int *val;]] to just [[int *val;]].
|
||||||
|
|
||||||
@<Remove C type modifiers from the front of p@> =
|
<<Remove C type modifiers from the front of p>>=
|
||||||
wchar_t *modifier_patterns[] = {
|
wchar_t *modifier_patterns[] = {
|
||||||
L"(struct )(%C%c*)", L"(signed )(%C%c*)", L"(unsigned )(%C%c*)",
|
L"(struct )(%C%c*)", L"(signed )(%C%c*)", L"(unsigned )(%C%c*)",
|
||||||
L"(short )(%C%c*)", L"(long )(%C%c*)", L"(static )(%C%c*)", NULL };
|
L"(short )(%C%c*)", L"(long )(%C%c*)", L"(static )(%C%c*)", NULL };
|
||||||
|
@ -115,40 +115,40 @@ We need to extract the element name, |val|, and make a note of it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ At this point |p| has been reduced to |int *val;|, but the following moves
|
@ At this point [[p]] has been reduced to [[int *val;]], but the following moves
|
||||||
|pos| to point to the |*|:
|
[[pos]] to point to the [[*]]:
|
||||||
|
|
||||||
@<Move pos past the type name@> =
|
<<Move pos past the type name>>=
|
||||||
while ((Str::get(pos)) && (Characters::is_space_or_tab(Str::get(pos)) == FALSE))
|
while ((Str::get(pos)) && (Characters::is_space_or_tab(Str::get(pos)) == FALSE))
|
||||||
pos = Str::forward(pos);
|
pos = Str::forward(pos);
|
||||||
|
|
||||||
@ And this moves it past the |*| to point to the |v| in |int *val;|:
|
@ And this moves it past the [[*]] to point to the [[v]] in [[int *val;]]:
|
||||||
|
|
||||||
@<Move pos past any typographical type modifiers@> =
|
<<Move pos past any typographical type modifiers>>=
|
||||||
while ((Characters::is_space_or_tab(Str::get(pos))) || (Str::get(pos) == '*') ||
|
while ((Characters::is_space_or_tab(Str::get(pos))) [[| (Str::get(pos) == '*') |]]
|
||||||
(Str::get(pos) == '(') || (Str::get(pos) == ')')) pos = Str::forward(pos);
|
(Str::get(pos) == '(') || (Str::get(pos) == ')')) pos = Str::forward(pos);
|
||||||
|
|
||||||
@ This then first copies the substring |val;| into |elname|, then cuts that
|
@ This then first copies the substring [[val;]] into [[elname]], then cuts that
|
||||||
down to just the identifier characters at the front, i.e., to |val|.
|
down to just the identifier characters at the front, i.e., to [[val]].
|
||||||
|
|
||||||
@<Copy the element name into elname@> =
|
<<Copy the element name into elname>>=
|
||||||
Str::substr(elname, pos, Str::end(p));
|
Str::substr(elname, pos, Str::end(p));
|
||||||
if (Regexp::match(&mr, elname, L"(%i+)%c*")) Str::copy(elname, mr.exp[0]);
|
if (Regexp::match(&mr, elname, L"(%i+)%c*")) Str::copy(elname, mr.exp[0]);
|
||||||
|
|
||||||
@h Structure dependency.
|
@ \section{Structure dependency.}
|
||||||
We say that S depends on T if |struct S| has an element whose type is
|
We say that S depends on T if [[struct S]] has an element whose type is
|
||||||
|struct T|. That matters because if so then |struct T| has to be defined
|
[[struct T]]. That matters because if so then [[struct T]] has to be defined
|
||||||
before |struct S| in the tangled output.
|
before [[struct S]] in the tangled output.
|
||||||
|
|
||||||
|
It's important to note that [[struct S]] merely having a member of type
|
||||||
|
[[struct *T| does not create a dependency. In the code below, because [[%i]]
|
||||||
|
matches only identifier characters and [[*]] is not one of those, a line like
|
||||||
|
|
||||||
It's important to note that |struct S| merely having a member of type
|
|
||||||
|struct *T| does not create a dependency. In the code below, because |%i|
|
|
||||||
matches only identifier characters and |*| is not one of those, a line like
|
|
||||||
= (text)
|
|
||||||
struct fruit *often_confused_with;
|
struct fruit *often_confused_with;
|
||||||
=
|
|
||||||
will not trip the switch here.
|
will not trip the switch here.
|
||||||
|
|
||||||
@<Work out which structs contain which others@> =
|
<<Work out which structs contain which others>>=
|
||||||
language_type *current_str;
|
language_type *current_str;
|
||||||
LOOP_OVER(current_str, language_type) {
|
LOOP_OVER(current_str, language_type) {
|
||||||
for (source_line *L = current_str->structure_header_at;
|
for (source_line *L = current_str->structure_header_at;
|
||||||
|
@ -156,12 +156,12 @@ will not trip the switch here.
|
||||||
L = L->next_line) {
|
L = L->next_line) {
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, L->text, L" struct (%i+) %i%c*"))
|
if (Regexp::match(&mr, L->text, L" struct (%i+) %i%c*"))
|
||||||
@<One structure appears to contain a copy of another one@>;
|
<<One structure appears to contain a copy of another one>>;
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@<One structure appears to contain a copy of another one@> =
|
<<One structure appears to contain a copy of another one>>=
|
||||||
text_stream *used_structure = mr.exp[0];
|
text_stream *used_structure = mr.exp[0];
|
||||||
language_type *str;
|
language_type *str;
|
||||||
LOOP_OVER_LINKED_LIST(str, language_type, W->language_types)
|
LOOP_OVER_LINKED_LIST(str, language_type, W->language_types)
|
||||||
|
@ -169,8 +169,8 @@ will not trip the switch here.
|
||||||
(Str::eq(used_structure, str->structure_name)))
|
(Str::eq(used_structure, str->structure_name)))
|
||||||
ADD_TO_LINKED_LIST(str, language_type, current_str->incorporates);
|
ADD_TO_LINKED_LIST(str, language_type, current_str->incorporates);
|
||||||
|
|
||||||
@h Functions.
|
@ \section{Functions.}
|
||||||
This time, we will need to keep track of |#ifdef| and |#endif| pairs
|
This time, we will need to keep track of [[#ifdef]] and [[#endif]] pairs
|
||||||
in the source. This matters because we will want to predeclare functions;
|
in the source. This matters because we will want to predeclare functions;
|
||||||
but if functions are declared in conditional compilation, then their
|
but if functions are declared in conditional compilation, then their
|
||||||
predeclarations have to be made under the same conditions.
|
predeclarations have to be made under the same conditions.
|
||||||
|
@ -178,9 +178,10 @@ predeclarations have to be made under the same conditions.
|
||||||
The following stack holds the current set of conditional compilations which the
|
The following stack holds the current set of conditional compilations which the
|
||||||
source line being scanned lies within.
|
source line being scanned lies within.
|
||||||
|
|
||||||
@d MAX_CONDITIONAL_COMPILATION_STACK 8
|
<<*>>=
|
||||||
|
#define MAX_CONDITIONAL_COMPILATION_STACK 8
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int cc_sp = 0;
|
int cc_sp = 0;
|
||||||
source_line *cc_stack[MAX_CONDITIONAL_COMPILATION_STACK];
|
source_line *cc_stack[MAX_CONDITIONAL_COMPILATION_STACK];
|
||||||
|
|
||||||
|
@ -192,14 +193,14 @@ void CLike::parse_functions(programming_language *self, web *W) {
|
||||||
if ((L->category == CODE_BODY_LCAT) ||
|
if ((L->category == CODE_BODY_LCAT) ||
|
||||||
(L->category == BEGIN_DEFINITION_LCAT) ||
|
(L->category == BEGIN_DEFINITION_LCAT) ||
|
||||||
(L->category == CONT_DEFINITION_LCAT)) {
|
(L->category == CONT_DEFINITION_LCAT)) {
|
||||||
@<Look for conditional compilation on this line@>;
|
<<Look for conditional compilation on this line>>;
|
||||||
@<Look for a function definition on this line@>;
|
<<Look for a function definition on this line>>;
|
||||||
}
|
}
|
||||||
if (cc_sp > 0)
|
if (cc_sp > 0)
|
||||||
Main::error_in_web(I"program ended with conditional compilation open", NULL);
|
Main::error_in_web(I"program ended with conditional compilation open", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Look for conditional compilation on this line@> =
|
<<Look for conditional compilation on this line>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if ((Regexp::match(&mr, L->text, L" *#ifn*def %c+")) ||
|
if ((Regexp::match(&mr, L->text, L" *#ifn*def %c+")) ||
|
||||||
(Regexp::match(&mr, L->text, L" *#IFN*DEF %c+"))) {
|
(Regexp::match(&mr, L->text, L" *#IFN*DEF %c+"))) {
|
||||||
|
@ -217,27 +218,27 @@ void CLike::parse_functions(programming_language *self, web *W) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ So, then, we recognise a C function as being a line which takes the form
|
@ So, then, we recognise a C function as being a line which takes the form
|
||||||
= (text)
|
|
||||||
type identifier(args...
|
type identifier(args...
|
||||||
=
|
|
||||||
where we parse |type| only minimally. In InC (only), the identifier can
|
where we parse [[type]] only minimally. In InC (only), the identifier can
|
||||||
contain namespace dividers written |::|. Function declarations, we will assume,
|
contain namespace dividers written [[::]]. Function declarations, we will assume,
|
||||||
always begin on column 1 of their source files, and we expect them to take
|
always begin on column 1 of their source files, and we expect them to take
|
||||||
modern ANSI C style, not the long-deprecated late 1970s C style.
|
modern ANSI C style, not the long-deprecated late 1970s C style.
|
||||||
|
|
||||||
@<Look for a function definition on this line@> =
|
<<Look for a function definition on this line>>=
|
||||||
if (!(Characters::is_space_or_tab(Str::get_first_char(L->text)))) {
|
if (!(Characters::is_space_or_tab(Str::get_first_char(L->text)))) {
|
||||||
TEMPORARY_TEXT(qualifiers)
|
TEMPORARY_TEXT(qualifiers)
|
||||||
TEMPORARY_TEXT(modified)
|
TEMPORARY_TEXT(modified)
|
||||||
Str::copy(modified, L->text);
|
Str::copy(modified, L->text);
|
||||||
@<Parse past any type modifiers@>;
|
<<Parse past any type modifiers>>;
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, modified, L"(%i+) (%**)(%i+)%((%c*)")) {
|
if (Regexp::match(&mr, modified, L"(%i+) (%**)(%i+)%((%c*)")) {
|
||||||
TEMPORARY_TEXT(ftype) Str::copy(ftype, mr.exp[0]);
|
TEMPORARY_TEXT(ftype) Str::copy(ftype, mr.exp[0]);
|
||||||
TEMPORARY_TEXT(asts) Str::copy(asts, mr.exp[1]);
|
TEMPORARY_TEXT(asts) Str::copy(asts, mr.exp[1]);
|
||||||
TEMPORARY_TEXT(fname) Str::copy(fname, mr.exp[2]);
|
TEMPORARY_TEXT(fname) Str::copy(fname, mr.exp[2]);
|
||||||
TEMPORARY_TEXT(arguments) Str::copy(arguments, mr.exp[3]);
|
TEMPORARY_TEXT(arguments) Str::copy(arguments, mr.exp[3]);
|
||||||
@<A function definition was found@>;
|
<<A function definition was found>>;
|
||||||
DISCARD_TEXT(ftype)
|
DISCARD_TEXT(ftype)
|
||||||
DISCARD_TEXT(asts)
|
DISCARD_TEXT(asts)
|
||||||
DISCARD_TEXT(fname)
|
DISCARD_TEXT(fname)
|
||||||
|
@ -250,9 +251,9 @@ modern ANSI C style, not the long-deprecated late 1970s C style.
|
||||||
|
|
||||||
@ C has a whole soup of reserved words applying to types, but most of them
|
@ C has a whole soup of reserved words applying to types, but most of them
|
||||||
can't apply to the return type of a function. We do, however, iterate so that
|
can't apply to the return type of a function. We do, however, iterate so that
|
||||||
forms like |static long long int| will work.
|
forms like [[static long long int]] will work.
|
||||||
|
|
||||||
@<Parse past any type modifiers@> =
|
<<Parse past any type modifiers>>=
|
||||||
wchar_t *modifier_patterns[] = {
|
wchar_t *modifier_patterns[] = {
|
||||||
L"(signed )(%C%c*)", L"(unsigned )(%C%c*)",
|
L"(signed )(%C%c*)", L"(unsigned )(%C%c*)",
|
||||||
L"(short )(%C%c*)", L"(long )(%C%c*)", L"(static )(%C%c*)", NULL };
|
L"(short )(%C%c*)", L"(long )(%C%c*)", L"(static )(%C%c*)", NULL };
|
||||||
|
@ -269,8 +270,8 @@ forms like |static long long int| will work.
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@<A function definition was found@> =
|
<<A function definition was found>>=
|
||||||
@<Soak up further arguments from continuation lines after the declaration@>;
|
<<Soak up further arguments from continuation lines after the declaration>>;
|
||||||
language_function *fn = Functions::new_function(fname, L);
|
language_function *fn = Functions::new_function(fname, L);
|
||||||
fn->function_arguments = Str::duplicate(arguments);
|
fn->function_arguments = Str::duplicate(arguments);
|
||||||
WRITE_TO(fn->function_type, "%S%S %S", qualifiers, ftype, asts);
|
WRITE_TO(fn->function_type, "%S%S %S", qualifiers, ftype, asts);
|
||||||
|
@ -279,17 +280,18 @@ forms like |static long long int| will work.
|
||||||
for (int i=0; i<cc_sp; i++) fn->within_conditionals[i] = cc_stack[i];
|
for (int i=0; i<cc_sp; i++) fn->within_conditionals[i] = cc_stack[i];
|
||||||
|
|
||||||
@ In some cases the function's declaration runs over several lines:
|
@ In some cases the function's declaration runs over several lines:
|
||||||
= (text as code)
|
|
||||||
void World::Subjects::make_adj_const_domain(inference_subject *infs,|
|
void World::Subjects::make_adj_const_domain(inference_subject *infs,|
|
||||||
instance *nc, property *prn) {|
|
instance *nc, property *prn) {|
|
||||||
=
|
|
||||||
Having read the first line, |arguments| would contain |inference_subject *infs,|
|
Having read the first line, [[arguments]] would contain [[inference_subject *infs,]]
|
||||||
and would thus be incomplete. We continue across subsequent lines until we
|
and would thus be incomplete. We continue across subsequent lines until we
|
||||||
reach an open brace |{|.
|
reach an open brace [[{]].
|
||||||
|
|
||||||
@d MAX_ARG_LINES 32 /* maximum number of lines over which a function's header can extend */
|
<<*>>=
|
||||||
|
#define MAX_ARG_LINES 32 /* maximum number of lines over which a function's header can extend */
|
||||||
|
|
||||||
@<Soak up further arguments from continuation lines after the declaration@> =
|
<<Soak up further arguments from continuation lines after the declaration>>=
|
||||||
source_line *AL = L;
|
source_line *AL = L;
|
||||||
int arg_lc = 1;
|
int arg_lc = 1;
|
||||||
while ((AL) && (arg_lc <= MAX_ARG_LINES) && (Regexp::find_open_brace(arguments) == -1)) {
|
while ((AL) && (arg_lc <= MAX_ARG_LINES) && (Regexp::find_open_brace(arguments) == -1)) {
|
||||||
|
@ -307,12 +309,12 @@ reach an open brace |{|.
|
||||||
int n = Regexp::find_open_brace(arguments);
|
int n = Regexp::find_open_brace(arguments);
|
||||||
if (n >= 0) Str::truncate(arguments, n);
|
if (n >= 0) Str::truncate(arguments, n);
|
||||||
|
|
||||||
@h Subcategorisation.
|
@ \section{Subcategorisation.}
|
||||||
The following is called after the parser gives every line in the web a
|
The following is called after the parser gives every line in the web a
|
||||||
category; we can, if we wish, change that for a more exotic one. We simply
|
category; we can, if we wish, change that for a more exotic one. We simply
|
||||||
look for a |#include| of one of the ANSI C standard libraries.
|
look for a [[#include]] of one of the ANSI C standard libraries.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void CLike::subcategorise_code(programming_language *self, source_line *L) {
|
void CLike::subcategorise_code(programming_language *self, source_line *L) {
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, L->text, L"#include <(%C+)>%c*")) {
|
if (Regexp::match(&mr, L->text, L"#include <(%C+)>%c*")) {
|
||||||
|
@ -330,18 +332,18 @@ void CLike::subcategorise_code(programming_language *self, source_line *L) {
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Tangling extras.
|
@ \section{Tangling extras.}
|
||||||
"Additional early matter" is used for the inclusions of the ANSI library
|
"Additional early matter" is used for the inclusions of the ANSI library
|
||||||
files. We need to do that early, because otherwise types declared in them
|
files. We need to do that early, because otherwise types declared in them
|
||||||
(such as |FILE|) won't exist in time for the structure definitions we will
|
(such as [[FILE]]) won't exist in time for the structure definitions we will
|
||||||
be tangling next.
|
be tangling next.
|
||||||
|
|
||||||
It might seem reasonable to move all |#include| files up front this way,
|
It might seem reasonable to move all [[#include]] files up front this way,
|
||||||
not just the ANSI ones. But that would defeat any conditional compilation
|
not just the ANSI ones. But that would defeat any conditional compilation
|
||||||
around the inclusions; which Inform (for instance) needs in order to make
|
around the inclusions; which Inform (for instance) needs in order to make
|
||||||
platform-specific details to handle directories without POSIX in Windows.
|
platform-specific details to handle directories without POSIX in Windows.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void CLike::additional_early_matter(programming_language *self, text_stream *OUT, web *W, tangle_target *target) {
|
void CLike::additional_early_matter(programming_language *self, text_stream *OUT, web *W, tangle_target *target) {
|
||||||
chapter *C;
|
chapter *C;
|
||||||
section *S;
|
section *S;
|
||||||
|
@ -354,23 +356,23 @@ void CLike::additional_early_matter(programming_language *self, text_stream *OUT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Tangling predeclarations.
|
@ \section{Tangling predeclarations.}
|
||||||
This is where a language gets the chance to tangle predeclarations, early
|
This is where a language gets the chance to tangle predeclarations, early
|
||||||
on in the file. We use it first for the structures, and then the functions --
|
on in the file. We use it first for the structures, and then the functions --
|
||||||
in that order since the function types likely involve the typedef names for the
|
in that order since the function types likely involve the typedef names for the
|
||||||
structures.
|
structures.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void CLike::additional_predeclarations(programming_language *self, text_stream *OUT, web *W) {
|
void CLike::additional_predeclarations(programming_language *self, text_stream *OUT, web *W) {
|
||||||
@<Predeclare the structures in a well-founded order@>;
|
<<Predeclare the structures in a well-founded order>>;
|
||||||
@<Predeclare simple typedefs@>;
|
<<Predeclare simple typedefs>>;
|
||||||
@<Predeclare the functions@>;
|
<<Predeclare the functions>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ A "simple typedef" here means one that is aliasing something other than
|
@ A "simple typedef" here means one that is aliasing something other than
|
||||||
a structure: for example |typedef unsigned int uint;| would be a simple typedef.
|
a structure: for example [[typedef unsigned int uint;]] would be a simple typedef.
|
||||||
|
|
||||||
@<Predeclare simple typedefs@> =
|
<<Predeclare simple typedefs>>=
|
||||||
chapter *C;
|
chapter *C;
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
|
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
|
||||||
|
@ -385,11 +387,11 @@ a structure: for example |typedef unsigned int uint;| would be a simple typedef.
|
||||||
precede outer, but we need to be careful to be terminating if the source
|
precede outer, but we need to be careful to be terminating if the source
|
||||||
code we're given is not well founded because of an error by its programmer:
|
code we're given is not well founded because of an error by its programmer:
|
||||||
for example, that structure A contains B contains C contains A. We do this
|
for example, that structure A contains B contains C contains A. We do this
|
||||||
with the |tangled| flag, which is |FALSE| if a structure hasn't been
|
with the [[tangled]] flag, which is [[FALSE]] if a structure hasn't been
|
||||||
started yet, |NOT_APPLICABLE| if it's in progress, and |TRUE| if it's
|
started yet, [[NOT_APPLICABLE]] if it's in progress, and [[TRUE]] if it's
|
||||||
finished.
|
finished.
|
||||||
|
|
||||||
@<Predeclare the structures in a well-founded order@> =
|
<<Predeclare the structures in a well-founded order>>=
|
||||||
language_type *str;
|
language_type *str;
|
||||||
LOOP_OVER_LINKED_LIST(str, language_type, W->language_types)
|
LOOP_OVER_LINKED_LIST(str, language_type, W->language_types)
|
||||||
str->tangled = FALSE;
|
str->tangled = FALSE;
|
||||||
|
@ -398,7 +400,7 @@ finished.
|
||||||
|
|
||||||
@ Using the following recursion, which is therefore terminating:
|
@ Using the following recursion, which is therefore terminating:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void CLike::tangle_structure(OUTPUT_STREAM, programming_language *self, language_type *str) {
|
void CLike::tangle_structure(OUTPUT_STREAM, programming_language *self, language_type *str) {
|
||||||
if (str->tangled != FALSE) return;
|
if (str->tangled != FALSE) return;
|
||||||
str->tangled = NOT_APPLICABLE;
|
str->tangled = NOT_APPLICABLE;
|
||||||
|
@ -417,12 +419,12 @@ void CLike::tangle_structure(OUTPUT_STREAM, programming_language *self, language
|
||||||
}
|
}
|
||||||
|
|
||||||
@ Functions are rather easier to deal with. In general, if a function was
|
@ Functions are rather easier to deal with. In general, if a function was
|
||||||
defined within some number of nested |#ifdef| or |#ifndef| directives, then
|
defined within some number of nested [[#ifdef]] or [[#ifndef]] directives, then
|
||||||
we reproduce those around the predeclaration: except, as a special trick,
|
we reproduce those around the predeclaration: except, as a special trick,
|
||||||
if the line contains a particular comment. For example:
|
if the line contains a particular comment. For example:
|
||||||
= (text)
|
|
||||||
#ifdef SOLARIS /* inweb: always predeclare */
|
#ifdef SOLARIS /* inweb: always predeclare */
|
||||||
=
|
|
||||||
That exempts any functions inside this condition from meeting the condition
|
That exempts any functions inside this condition from meeting the condition
|
||||||
in order to be predeclared. It's a trick used in the foundation module just
|
in order to be predeclared. It's a trick used in the foundation module just
|
||||||
a couple of times: the idea is that although a definition of the functions
|
a couple of times: the idea is that although a definition of the functions
|
||||||
|
@ -431,7 +433,7 @@ provide alternative function definitions which would work without SOLARIS.
|
||||||
The functions therefore need predeclaration regardless, because they will
|
The functions therefore need predeclaration regardless, because they will
|
||||||
exist either way.
|
exist either way.
|
||||||
|
|
||||||
@<Predeclare the functions@> =
|
<<Predeclare the functions>>=
|
||||||
chapter *C;
|
chapter *C;
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
|
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
|
||||||
|
@ -467,6 +469,6 @@ exist either way.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Overriding regular code weaving.
|
@ \section{Overriding regular code weaving.}
|
||||||
We have the opportunity here to sidestep the regular weaving algorithm, and do
|
We have the opportunity here to sidestep the regular weaving algorithm, and do
|
||||||
our own thing. We decline.
|
our own thing. We decline.
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
To support a modest extension of C called InC.
|
To support a modest extension of C called InC.
|
||||||
|
|
||||||
@h Creation.
|
@ \section{Creation.}
|
||||||
As can be seen, InC is a basically C-like language, but in addition to having
|
As can be seen, InC is a basically C-like language, but in addition to having
|
||||||
all of those methods, it has a whole lot more of its own.
|
all of those methods, it has a whole lot more of its own.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::add_features(programming_language *pl) {
|
void InCSupport::add_features(programming_language *pl) {
|
||||||
METHOD_ADD(pl, FURTHER_PARSING_PAR_MTID, InCSupport::further_parsing);
|
METHOD_ADD(pl, FURTHER_PARSING_PAR_MTID, InCSupport::further_parsing);
|
||||||
|
|
||||||
|
@ -29,13 +29,13 @@ void InCSupport::add_features(programming_language *pl) {
|
||||||
|
|
||||||
@ We will apply this special tag wherever Preform grammar is defined:
|
@ We will apply this special tag wherever Preform grammar is defined:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
theme_tag *Preform_theme = NULL;
|
theme_tag *Preform_theme = NULL;
|
||||||
|
|
||||||
@h Parsing methods.
|
@ \section{Parsing methods.}
|
||||||
We only provide one parsing method, but it's a big one:
|
We only provide one parsing method, but it's a big one:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
preform_nonterminal *alphabetical_list_of_nonterminals = NULL;
|
preform_nonterminal *alphabetical_list_of_nonterminals = NULL;
|
||||||
|
|
||||||
void InCSupport::further_parsing(programming_language *self, web *W) {
|
void InCSupport::further_parsing(programming_language *self, web *W) {
|
||||||
|
@ -43,46 +43,47 @@ void InCSupport::further_parsing(programming_language *self, web *W) {
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
|
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
|
||||||
if ((L->category == CODE_BODY_LCAT) || (L->category == CONT_DEFINITION_LCAT)) {
|
if ((L->category == CODE_BODY_LCAT) || (L->category == CONT_DEFINITION_LCAT)) {
|
||||||
@<Detect and deal with Preform grammar@>;
|
<<Detect and deal with Preform grammar>>;
|
||||||
@<Detect and deal with I-literals@>
|
<<Detect and deal with I-literals>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Parsing Preform grammar.
|
@ \section{Parsing Preform grammar.}
|
||||||
This is where we look for declarations of nonterminals. Very little about
|
This is where we look for declarations of nonterminals. Very little about
|
||||||
the following code will make sense unless you've first read the Preform
|
the following code will make sense unless you've first read the Preform
|
||||||
section of the |words| module, which is what we're supporting, and seen
|
section of the [[words]] module, which is what we're supporting, and seen
|
||||||
some examples of Preform being used in the Inform source code.
|
some examples of Preform being used in the Inform source code.
|
||||||
|
|
||||||
In parsing, we categorise the opening lines |PREFORM_LCAT|. Subsequent lines
|
In parsing, we categorise the opening lines [[PREFORM_LCAT]]. Subsequent lines
|
||||||
of grammar are |PREFORM_GRAMMAR_LCAT|; but the lines of InC code inside an
|
of grammar are [[PREFORM_GRAMMAR_LCAT]]; but the lines of InC code inside an
|
||||||
|internal| definition remain just plain |CODE_BODY_LCAT| lines.
|
[[internal]] definition remain just plain [[CODE_BODY_LCAT]] lines.
|
||||||
|
|
||||||
@d NOT_A_NONTERMINAL -4
|
<<*>>=
|
||||||
@d A_FLEXIBLE_NONTERMINAL -3
|
#define NOT_A_NONTERMINAL -4
|
||||||
@d A_VORACIOUS_NONTERMINAL -2
|
#define A_FLEXIBLE_NONTERMINAL -3
|
||||||
@d A_GRAMMAR_NONTERMINAL -1
|
#define A_VORACIOUS_NONTERMINAL -2
|
||||||
|
#define A_GRAMMAR_NONTERMINAL -1
|
||||||
|
|
||||||
@<Detect and deal with Preform grammar@> =
|
<<Detect and deal with Preform grammar>>=
|
||||||
int form = NOT_A_NONTERMINAL; /* one of the four values above, or a non-negative word count */
|
int form = NOT_A_NONTERMINAL; /* one of the four values above, or a non-negative word count */
|
||||||
TEMPORARY_TEXT(pntname)
|
TEMPORARY_TEXT(pntname)
|
||||||
TEMPORARY_TEXT(header)
|
TEMPORARY_TEXT(header)
|
||||||
@<Parse a Preform nonterminal header line@>;
|
<<Parse a Preform nonterminal header line>>;
|
||||||
if (form != NOT_A_NONTERMINAL) @<Record a Preform nonterminal here@>;
|
if (form != NOT_A_NONTERMINAL) <<Record a Preform nonterminal here>>;
|
||||||
DISCARD_TEXT(pntname)
|
DISCARD_TEXT(pntname)
|
||||||
DISCARD_TEXT(header)
|
DISCARD_TEXT(header)
|
||||||
|
|
||||||
@ The keyword |internal| can be followed by an indication of the number
|
@ The keyword [[internal]] can be followed by an indication of the number
|
||||||
of words the nonterminal will match: usually a decimal non-negative number,
|
of words the nonterminal will match: usually a decimal non-negative number,
|
||||||
but optionally a question mark |?| to indicate voracity.
|
but optionally a question mark [[?]] to indicate voracity.
|
||||||
|
|
||||||
@<Parse a Preform nonterminal header line@> =
|
<<Parse a Preform nonterminal header line>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, L->text, L"(<%p+>) ::=%c*")) {
|
if (Regexp::match(&mr, L->text, L"(<%p+>) ::=%c*")) {
|
||||||
form = A_GRAMMAR_NONTERMINAL;
|
form = A_GRAMMAR_NONTERMINAL;
|
||||||
Str::copy(pntname, mr.exp[0]);
|
Str::copy(pntname, mr.exp[0]);
|
||||||
Str::copy(header, mr.exp[0]);
|
Str::copy(header, mr.exp[0]);
|
||||||
@<Parse the subsequent lines as Preform grammar@>;
|
<<Parse the subsequent lines as Preform grammar>>;
|
||||||
} else if (Regexp::match(&mr, L->text, L"((<%p+>) internal %?) {%c*")) {
|
} else if (Regexp::match(&mr, L->text, L"((<%p+>) internal %?) {%c*")) {
|
||||||
form = A_VORACIOUS_NONTERMINAL;
|
form = A_VORACIOUS_NONTERMINAL;
|
||||||
Str::copy(pntname, mr.exp[1]);
|
Str::copy(pntname, mr.exp[1]);
|
||||||
|
@ -101,16 +102,16 @@ but optionally a question mark |?| to indicate voracity.
|
||||||
@ Each Preform nonterminal defined in the tangle will cause one of these
|
@ Each Preform nonterminal defined in the tangle will cause one of these
|
||||||
structures to be created:
|
structures to be created:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct preform_nonterminal {
|
typedef struct preform_nonterminal {
|
||||||
struct text_stream *nt_name; /* e.g., |<action-clause>| */
|
struct text_stream *nt_name; /* e.g., [[<action-clause>]] */
|
||||||
struct text_stream *unangled_name; /* e.g., |action-clause| */
|
struct text_stream *unangled_name; /* e.g., [[action-clause]] */
|
||||||
struct text_stream *as_C_identifier; /* e.g., |action_clause_NTM| */
|
struct text_stream *as_C_identifier; /* e.g., [[action_clause_NTM]] */
|
||||||
int as_function; /* defined internally, that is, parsed by a C language_function */
|
int as_function; /* defined internally, that is, parsed by a C language_function */
|
||||||
int voracious; /* a voracious nonterminal: see "The English Syntax of Inform" */
|
int voracious; /* a voracious nonterminal: see "The English Syntax of Inform" */
|
||||||
int min_word_count; /* for internals only */
|
int min_word_count; /* for internals only */
|
||||||
int max_word_count;
|
int max_word_count;
|
||||||
int takes_pointer_result; /* right-hand formula defines |*XP|, not |*X| */
|
int takes_pointer_result; /* right-hand formula defines [[*XP|, not |*X]] */
|
||||||
struct source_line *where_defined;
|
struct source_line *where_defined;
|
||||||
struct preform_nonterminal *next_pnt_alphabetically;
|
struct preform_nonterminal *next_pnt_alphabetically;
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
|
@ -118,21 +119,21 @@ typedef struct preform_nonterminal {
|
||||||
|
|
||||||
@ We will
|
@ We will
|
||||||
|
|
||||||
@<Record a Preform nonterminal here@> =
|
<<Record a Preform nonterminal here>>=
|
||||||
preform_nonterminal *pnt = CREATE(preform_nonterminal);
|
preform_nonterminal *pnt = CREATE(preform_nonterminal);
|
||||||
pnt->where_defined = L;
|
pnt->where_defined = L;
|
||||||
pnt->nt_name = Str::duplicate(pntname);
|
pnt->nt_name = Str::duplicate(pntname);
|
||||||
pnt->unangled_name = Str::duplicate(pntname);
|
pnt->unangled_name = Str::duplicate(pntname);
|
||||||
pnt->as_C_identifier = Str::duplicate(pntname);
|
pnt->as_C_identifier = Str::duplicate(pntname);
|
||||||
pnt->next_pnt_alphabetically = NULL;
|
pnt->next_pnt_alphabetically = NULL;
|
||||||
@<Apply unangling cream to name@>;
|
<<Apply unangling cream to name>>;
|
||||||
@<Compose a C identifier for the nonterminal@>;
|
<<Compose a C identifier for the nonterminal>>;
|
||||||
@<Work out the parsing characteristics of the nonterminal@>;
|
<<Work out the parsing characteristics of the nonterminal>>;
|
||||||
|
|
||||||
@<Insertion-sort this this nonterminal into the alphabetical list@>;
|
<<Insertion-sort this this nonterminal into the alphabetical list>>;
|
||||||
@<Register the nonterminal with the line and paragraph from which it comes@>;
|
<<Register the nonterminal with the line and paragraph from which it comes>>;
|
||||||
|
|
||||||
@<Apply unangling cream to name@> =
|
<<Apply unangling cream to name>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, pntname, L"%<(%c*)%>")) pnt->unangled_name = Str::duplicate(mr.exp[0]);
|
if (Regexp::match(&mr, pntname, L"%<(%c*)%>")) pnt->unangled_name = Str::duplicate(mr.exp[0]);
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
@ -142,7 +143,7 @@ will be represented by a pointer to a unique data structure for it. Inweb
|
||||||
automatically compiles code to create these pointers; and here's how it
|
automatically compiles code to create these pointers; and here's how it
|
||||||
works out their names.
|
works out their names.
|
||||||
|
|
||||||
@<Compose a C identifier for the nonterminal@> =
|
<<Compose a C identifier for the nonterminal>>=
|
||||||
Str::delete_first_character(pnt->as_C_identifier);
|
Str::delete_first_character(pnt->as_C_identifier);
|
||||||
LOOP_THROUGH_TEXT(pos, pnt->as_C_identifier) {
|
LOOP_THROUGH_TEXT(pos, pnt->as_C_identifier) {
|
||||||
if (Str::get(pos) == '-') Str::put(pos, '_');
|
if (Str::get(pos) == '-') Str::put(pos, '_');
|
||||||
|
@ -155,9 +156,10 @@ de Scudéry, published around 1650, runs to 1,954,300 words. If you can write
|
||||||
an Inform source text 500 times longer than that, then you may need to raise
|
an Inform source text 500 times longer than that, then you may need to raise
|
||||||
the following definition:
|
the following definition:
|
||||||
|
|
||||||
@d INFINITE_WORD_COUNT 1000000000
|
<<*>>=
|
||||||
|
#define INFINITE_WORD_COUNT 1000000000
|
||||||
|
|
||||||
@<Work out the parsing characteristics of the nonterminal@> =
|
<<Work out the parsing characteristics of the nonterminal>>=
|
||||||
pnt->voracious = FALSE; if (form == A_VORACIOUS_NONTERMINAL) pnt->voracious = TRUE;
|
pnt->voracious = FALSE; if (form == A_VORACIOUS_NONTERMINAL) pnt->voracious = TRUE;
|
||||||
pnt->as_function = TRUE; if (form == A_GRAMMAR_NONTERMINAL) pnt->as_function = FALSE;
|
pnt->as_function = TRUE; if (form == A_GRAMMAR_NONTERMINAL) pnt->as_function = FALSE;
|
||||||
|
|
||||||
|
@ -174,7 +176,7 @@ the following definition:
|
||||||
pnt->min_word_count = min;
|
pnt->min_word_count = min;
|
||||||
pnt->max_word_count = max;
|
pnt->max_word_count = max;
|
||||||
|
|
||||||
@<Insertion-sort this this nonterminal into the alphabetical list@> =
|
<<Insertion-sort this this nonterminal into the alphabetical list>>=
|
||||||
if (alphabetical_list_of_nonterminals == NULL) alphabetical_list_of_nonterminals = pnt;
|
if (alphabetical_list_of_nonterminals == NULL) alphabetical_list_of_nonterminals = pnt;
|
||||||
else {
|
else {
|
||||||
int placed = FALSE;
|
int placed = FALSE;
|
||||||
|
@ -197,24 +199,24 @@ the following definition:
|
||||||
if (placed == FALSE) last->next_pnt_alphabetically = pnt;
|
if (placed == FALSE) last->next_pnt_alphabetically = pnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Register the nonterminal with the line and paragraph from which it comes@> =
|
<<Register the nonterminal with the line and paragraph from which it comes>>=
|
||||||
L->preform_nonterminal_defined = pnt;
|
L->preform_nonterminal_defined = pnt;
|
||||||
if (Preform_theme) Tags::add_to_paragraph(L->owning_paragraph, Preform_theme, NULL);
|
if (Preform_theme) Tags::add_to_paragraph(L->owning_paragraph, Preform_theme, NULL);
|
||||||
L->category = PREFORM_LCAT;
|
L->category = PREFORM_LCAT;
|
||||||
L->text_operand = Str::duplicate(header);
|
L->text_operand = Str::duplicate(header);
|
||||||
|
|
||||||
@h Parsing the body of Preform grammar.
|
@ \section{Parsing the body of Preform grammar.}
|
||||||
After a line like |<action-clause> ::=|, Preform grammar follows on subsequent
|
After a line like [[<action-clause> ::=]], Preform grammar follows on subsequent
|
||||||
lines until we hit the end of the paragraph, or a white-space line, whichever
|
lines until we hit the end of the paragraph, or a white-space line, whichever
|
||||||
comes first. Each line of grammar is categorised |PREFORM_GRAMMAR_LCAT|.
|
comes first. Each line of grammar is categorised [[PREFORM_GRAMMAR_LCAT]].
|
||||||
If we have a line with an arrow, like so:
|
If we have a line with an arrow, like so:
|
||||||
= (text)
|
|
||||||
porcupine tree ==> { 2, - }{}
|
|
||||||
=
|
|
||||||
then the text on the left goes into |text_operand| and the right into
|
|
||||||
|text_operand2|, with the arrow itself (and white space around it) cut out.
|
|
||||||
|
|
||||||
@<Parse the subsequent lines as Preform grammar@> =
|
porcupine tree ==> { 2, - }{}
|
||||||
|
|
||||||
|
then the text on the left goes into [[text_operand]] and the right into
|
||||||
|
[[text_operand2]], with the arrow itself (and white space around it) cut out.
|
||||||
|
|
||||||
|
<<Parse the subsequent lines as Preform grammar>>=
|
||||||
Tags::add_by_name(L->owning_paragraph, I"Preform");
|
Tags::add_by_name(L->owning_paragraph, I"Preform");
|
||||||
source_line *AL;
|
source_line *AL;
|
||||||
for (AL = L; (AL) && (AL->category == CODE_BODY_LCAT); AL = AL->next_line) {
|
for (AL = L; (AL) && (AL->category == CODE_BODY_LCAT); AL = AL->next_line) {
|
||||||
|
@ -228,29 +230,29 @@ then the text on the left goes into |text_operand| and the right into
|
||||||
AL->text_operand = AL->text;
|
AL->text_operand = AL->text;
|
||||||
AL->text_operand2 = Str::new();
|
AL->text_operand2 = Str::new();
|
||||||
}
|
}
|
||||||
@<Remove any C comment from the left side of the arrow@>;
|
<<Remove any C comment from the left side of the arrow>>;
|
||||||
@<Detect any nonterminal variables being set on the right side of the arrow@>;
|
<<Detect any nonterminal variables being set on the right side of the arrow>>;
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ In case we have a comment at the end of the grammar, like this:
|
@ In case we have a comment at the end of the grammar, like this:
|
||||||
= (text)
|
|
||||||
porcupine tree /* what happens now? */
|
porcupine tree /* what happens now? */
|
||||||
=
|
|
||||||
we want to remove it. The regular expression here isn't terribly legible, but
|
we want to remove it. The regular expression here isn't terribly legible, but
|
||||||
trust me, it's correct.
|
trust me, it's correct.
|
||||||
|
|
||||||
@<Remove any C comment from the left side of the arrow@> =
|
<<Remove any C comment from the left side of the arrow>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, AL->text_operand, L"(%c*)%/%*%c*%*%/ *"))
|
if (Regexp::match(&mr, AL->text_operand, L"(%c*)%/%*%c*%*%/ *"))
|
||||||
AL->text_operand = Str::duplicate(mr.exp[0]);
|
AL->text_operand = Str::duplicate(mr.exp[0]);
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
|
||||||
@ Note that nonterminal variables are, by default, integers. If their names
|
@ Note that nonterminal variables are, by default, integers. If their names
|
||||||
are divided internally with a colon, however, as |<<structure:name>>|, then
|
are divided internally with a colon, however, as [[<<structure:name>>]], then
|
||||||
they have the type |structure *|.
|
they have the type [[structure *]].
|
||||||
|
|
||||||
@<Detect any nonterminal variables being set on the right side of the arrow@> =
|
<<Detect any nonterminal variables being set on the right side of the arrow>>=
|
||||||
TEMPORARY_TEXT(to_scan) Str::copy(to_scan, AL->text_operand2);
|
TEMPORARY_TEXT(to_scan) Str::copy(to_scan, AL->text_operand2);
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
while (Regexp::match(&mr, to_scan, L"%c*?<<(%P+?)>> =(%c*)")) {
|
while (Regexp::match(&mr, to_scan, L"%c*?<<(%P+?)>> =(%c*)")) {
|
||||||
|
@ -265,7 +267,7 @@ they have the type |structure *|.
|
||||||
LOOP_OVER(ntv, nonterminal_variable)
|
LOOP_OVER(ntv, nonterminal_variable)
|
||||||
if (Str::eq(ntv->ntv_name, var_given))
|
if (Str::eq(ntv->ntv_name, var_given))
|
||||||
break;
|
break;
|
||||||
if (ntv == NULL) @<This one's new, so create a new nonterminal variable@>;
|
if (ntv == NULL) <<This one's new, so create a new nonterminal variable>>;
|
||||||
DISCARD_TEXT(var_given)
|
DISCARD_TEXT(var_given)
|
||||||
DISCARD_TEXT(type_given)
|
DISCARD_TEXT(type_given)
|
||||||
}
|
}
|
||||||
|
@ -273,19 +275,19 @@ they have the type |structure *|.
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
|
||||||
@ Nonterminal variables are actually just global C variables, and their C
|
@ Nonterminal variables are actually just global C variables, and their C
|
||||||
identifiers need to avoid hyphens and colons. For example, |<<kind:ref>>|
|
identifiers need to avoid hyphens and colons. For example, [[<<kind:ref>>]]
|
||||||
has identifier |"kind_ref_NTMV"|. Each one is recorded in a structure thus:
|
has identifier [["kind_ref_NTMV"]]. Each one is recorded in a structure thus:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct nonterminal_variable {
|
typedef struct nonterminal_variable {
|
||||||
struct text_stream *ntv_name; /* e.g., |"num"| */
|
struct text_stream *ntv_name; /* e.g., [["num"]] */
|
||||||
struct text_stream *ntv_type; /* e.g., |"int"| */
|
struct text_stream *ntv_type; /* e.g., [["int"]] */
|
||||||
struct text_stream *ntv_identifier; /* e.g., |"num_NTMV"| */
|
struct text_stream *ntv_identifier; /* e.g., [["num_NTMV"]] */
|
||||||
struct source_line *first_mention; /* first usage */
|
struct source_line *first_mention; /* first usage */
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} nonterminal_variable;
|
} nonterminal_variable;
|
||||||
|
|
||||||
@<This one's new, so create a new nonterminal variable@> =
|
<<This one's new, so create a new nonterminal variable>>=
|
||||||
ntv = CREATE(nonterminal_variable);
|
ntv = CREATE(nonterminal_variable);
|
||||||
ntv->ntv_name = Str::duplicate(var_given);
|
ntv->ntv_name = Str::duplicate(var_given);
|
||||||
ntv->ntv_type = Str::duplicate(type_given);
|
ntv->ntv_type = Str::duplicate(type_given);
|
||||||
|
@ -296,12 +298,12 @@ typedef struct nonterminal_variable {
|
||||||
WRITE_TO(ntv->ntv_identifier, "%S_NTMV", var_given);
|
WRITE_TO(ntv->ntv_identifier, "%S_NTMV", var_given);
|
||||||
ntv->first_mention = AL;
|
ntv->first_mention = AL;
|
||||||
|
|
||||||
@h Parsing I-literals.
|
@ \section{Parsing I-literals.}
|
||||||
A simpler but useful further addition to C is that we recognise a new form
|
A simpler but useful further addition to C is that we recognise a new form
|
||||||
of string literal: |I"quartz"| makes a constant text stream with the content
|
of string literal: [[I"quartz"]] makes a constant text stream with the content
|
||||||
"quartz".
|
"quartz".
|
||||||
|
|
||||||
@<Detect and deal with I-literals@> =
|
<<Detect and deal with I-literals>>=
|
||||||
for (int i = 0, quoted = FALSE; i < Str::len(L->text); i++) {
|
for (int i = 0, quoted = FALSE; i < Str::len(L->text); i++) {
|
||||||
if (Str::get_at(L->text, i) == '"')
|
if (Str::get_at(L->text, i) == '"')
|
||||||
if ((Str::get_at(L->text, i-1) != '\\') &&
|
if ((Str::get_at(L->text, i-1) != '\\') &&
|
||||||
|
@ -309,10 +311,10 @@ of string literal: |I"quartz"| makes a constant text stream with the content
|
||||||
quoted = quoted?FALSE:TRUE;
|
quoted = quoted?FALSE:TRUE;
|
||||||
if ((fundamental_mode != WEAVE_MODE) && (quoted == FALSE) &&
|
if ((fundamental_mode != WEAVE_MODE) && (quoted == FALSE) &&
|
||||||
(Str::get_at(L->text, i) == 'I') && (Str::get_at(L->text, i+1) == '"'))
|
(Str::get_at(L->text, i) == 'I') && (Str::get_at(L->text, i+1) == '"'))
|
||||||
@<This looks like an I-literal@>;
|
<<This looks like an I-literal>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<This looks like an I-literal@> =
|
<<This looks like an I-literal>>=
|
||||||
TEMPORARY_TEXT(lit)
|
TEMPORARY_TEXT(lit)
|
||||||
int i_was = i;
|
int i_was = i;
|
||||||
int ended = FALSE;
|
int ended = FALSE;
|
||||||
|
@ -321,14 +323,14 @@ of string literal: |I"quartz"| makes a constant text stream with the content
|
||||||
if (Str::get_at(L->text, i) == '"') { ended = TRUE; break; }
|
if (Str::get_at(L->text, i) == '"') { ended = TRUE; break; }
|
||||||
PUT_TO(lit, Str::get_at(L->text, i++));
|
PUT_TO(lit, Str::get_at(L->text, i++));
|
||||||
}
|
}
|
||||||
if (ended) @<This is definitely an I-literal@>;
|
if (ended) <<This is definitely an I-literal>>;
|
||||||
DISCARD_TEXT(lit)
|
DISCARD_TEXT(lit)
|
||||||
|
|
||||||
@ Each I-literal results in an instance of the following being created. The
|
@ Each I-literal results in an instance of the following being created. The
|
||||||
I-literal |I"quartz"| would have content |quartz| and identifier something
|
I-literal [[I"quartz"]] would have content [[quartz]] and identifier something
|
||||||
like |TL_IS_123|.
|
like [[TL_IS_123]].
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct text_literal {
|
typedef struct text_literal {
|
||||||
struct text_stream *tl_identifier;
|
struct text_stream *tl_identifier;
|
||||||
struct text_stream *tl_content;
|
struct text_stream *tl_content;
|
||||||
|
@ -336,14 +338,14 @@ typedef struct text_literal {
|
||||||
} text_literal;
|
} text_literal;
|
||||||
|
|
||||||
@ So suppose we've got a line of web such as
|
@ So suppose we've got a line of web such as
|
||||||
= (text)
|
|
||||||
text_stream *T = I"quartz";
|
text_stream *T = I"quartz";
|
||||||
=
|
|
||||||
We create the necessary I-literal, and splice the line so that it now reads
|
We create the necessary I-literal, and splice the line so that it now reads
|
||||||
|text_stream *T = TL_IS_123;|. (That's why we don't call any of this on a
|
[[text_stream *T = TL_IS_123;]]. (That's why we don't call any of this on a
|
||||||
weave run; we're actually amending the code of the web.)
|
weave run; we're actually amending the code of the web.)
|
||||||
|
|
||||||
@<This is definitely an I-literal@> =
|
<<This is definitely an I-literal>>=
|
||||||
text_literal *tl = CREATE(text_literal);
|
text_literal *tl = CREATE(text_literal);
|
||||||
tl->tl_identifier = Str::new();
|
tl->tl_identifier = Str::new();
|
||||||
WRITE_TO(tl->tl_identifier, "TL_IS_%d", tl->allocation_id);
|
WRITE_TO(tl->tl_identifier, "TL_IS_%d", tl->allocation_id);
|
||||||
|
@ -360,11 +362,11 @@ weave run; we're actually amending the code of the web.)
|
||||||
DISCARD_TEXT(before)
|
DISCARD_TEXT(before)
|
||||||
DISCARD_TEXT(after)
|
DISCARD_TEXT(after)
|
||||||
|
|
||||||
@h Tangling methods.
|
@ \section{Tangling methods.}
|
||||||
Suppress the expansion of macros occurring on a line introduced by a |//|
|
Suppress the expansion of macros occurring on a line introduced by a [[//]]
|
||||||
comment. (This avoids problems when tangling code that's been commented out.)
|
comment. (This avoids problems when tangling code that's been commented out.)
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int InCSupport::suppress_expansion(programming_language *self, text_stream *material) {
|
int InCSupport::suppress_expansion(programming_language *self, text_stream *material) {
|
||||||
if ((Str::get_at(material, 0) == '/') && (Str::get_at(material, 1) == '/'))
|
if ((Str::get_at(material, 0) == '/') && (Str::get_at(material, 1) == '/'))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -372,23 +374,23 @@ int InCSupport::suppress_expansion(programming_language *self, text_stream *mate
|
||||||
}
|
}
|
||||||
|
|
||||||
@ InC does three things which C doesn't: it allows the namespaced function
|
@ InC does three things which C doesn't: it allows the namespaced function
|
||||||
names like |Section::function()|; it allows Foundation-class-style string
|
names like [[Section::function()]]; it allows Foundation-class-style string
|
||||||
literals marked with an I, |I"like this"|, which we will call I-literals;
|
literals marked with an I, [[I"like this"]], which we will call I-literals;
|
||||||
and it allows Preform natural language grammar to be mixed in with code.
|
and it allows Preform natural language grammar to be mixed in with code.
|
||||||
|
|
||||||
The following routine is a hook needed for two of these. It recognises
|
The following routine is a hook needed for two of these. It recognises
|
||||||
two special tangling commands:
|
two special tangling commands:
|
||||||
|
|
||||||
(a) |[[nonterminals]]| tangles to code which initialises the Preform
|
(a) [[[[nonterminals]]]] tangles to code which initialises the Preform
|
||||||
grammar. (The grammar defines the meaning of nonterminals such as
|
grammar. (The grammar defines the meaning of nonterminals such as
|
||||||
|<sentence>|. They're not terminal in the sense that they are defined
|
[[<sentence>]]. They're not terminal in the sense that they are defined
|
||||||
as combinations of other things.) In practice, this needs to appear once
|
as combinations of other things.) In practice, this needs to appear once
|
||||||
in any program using Preform. For the Inform project, that's done in the
|
in any program using Preform. For the Inform project, that's done in the
|
||||||
|words| module of the Inform 7 compiler.
|
[[words]] module of the Inform 7 compiler.
|
||||||
|
|
||||||
(b) |[[textliterals]]| tangles to code which initialises the I-literals.
|
(b) [[[[textliterals]]]] tangles to code which initialises the I-literals.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int InCSupport::special_tangle_command(programming_language *me, OUTPUT_STREAM, text_stream *data) {
|
int InCSupport::special_tangle_command(programming_language *me, OUTPUT_STREAM, text_stream *data) {
|
||||||
if (Str::eq_wide_string(data, L"nonterminals")) {
|
if (Str::eq_wide_string(data, L"nonterminals")) {
|
||||||
WRITE("register_tangled_nonterminals();\n");
|
WRITE("register_tangled_nonterminals();\n");
|
||||||
|
@ -404,15 +406,15 @@ int InCSupport::special_tangle_command(programming_language *me, OUTPUT_STREAM,
|
||||||
@ Time to predeclare things. InC is going to create a special function, right
|
@ Time to predeclare things. InC is going to create a special function, right
|
||||||
at the end of the code, which "registers" the nonterminals, creating their
|
at the end of the code, which "registers" the nonterminals, creating their
|
||||||
run-time data structures; we must predeclare this function. It will set values
|
run-time data structures; we must predeclare this function. It will set values
|
||||||
for the pointers |action_clause_NTM|, and so on; these are global variables,
|
for the pointers [[action_clause_NTM]], and so on; these are global variables,
|
||||||
which we initially declare as |NULL|.
|
which we initially declare as [[NULL]].
|
||||||
|
|
||||||
We also declare the nonterminal variables like |kind_ref_NTMV|, initialising
|
We also declare the nonterminal variables like [[kind_ref_NTMV]], initialising
|
||||||
all integers to zero and all pointers to |NULL|.
|
all integers to zero and all pointers to [[NULL]].
|
||||||
|
|
||||||
We do something similar, but simpler, to declare text stream constants.
|
We do something similar, but simpler, to declare text stream constants.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::additional_predeclarations(programming_language *self, text_stream *OUT, web *W) {
|
void InCSupport::additional_predeclarations(programming_language *self, text_stream *OUT, web *W) {
|
||||||
chapter *C;
|
chapter *C;
|
||||||
section *S;
|
section *S;
|
||||||
|
@ -441,7 +443,7 @@ void InCSupport::additional_predeclarations(programming_language *self, text_str
|
||||||
@ And here are the promised routines, which appear at the very end of the code.
|
@ And here are the promised routines, which appear at the very end of the code.
|
||||||
They make use of macros and data structures defined in the Inform 7 web.
|
They make use of macros and data structures defined in the Inform 7 web.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::gnabehs(programming_language *self, text_stream *OUT, web *W) {
|
void InCSupport::gnabehs(programming_language *self, text_stream *OUT, web *W) {
|
||||||
WRITE("void register_tangled_nonterminals(void) {\n");
|
WRITE("void register_tangled_nonterminals(void) {\n");
|
||||||
chapter *C;
|
chapter *C;
|
||||||
|
@ -472,10 +474,10 @@ void InCSupport::gnabehs(programming_language *self, text_stream *OUT, web *W) {
|
||||||
@ That's it for big structural additions to the tangled C code. Now we turn
|
@ That's it for big structural additions to the tangled C code. Now we turn
|
||||||
to how to tangle the lines we've given special categories to.
|
to how to tangle the lines we've given special categories to.
|
||||||
|
|
||||||
We need to tangle |PREFORM_LCAT| lines (those holding nonterminal declarations)
|
We need to tangle [[PREFORM_LCAT]] lines (those holding nonterminal declarations)
|
||||||
in a special way...
|
in a special way...
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int InCSupport::will_insert_in_tangle(programming_language *self, source_line *L) {
|
int InCSupport::will_insert_in_tangle(programming_language *self, source_line *L) {
|
||||||
if (L->category == PREFORM_LCAT) return TRUE;
|
if (L->category == PREFORM_LCAT) return TRUE;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -483,20 +485,20 @@ int InCSupport::will_insert_in_tangle(programming_language *self, source_line *L
|
||||||
|
|
||||||
@ ...and this is how. As can be seen, each nonterminal turns into a C function.
|
@ ...and this is how. As can be seen, each nonterminal turns into a C function.
|
||||||
In the case of an internal definition, like
|
In the case of an internal definition, like
|
||||||
= (text)
|
|
||||||
<k-kind-for-template> internal {
|
<k-kind-for-template> internal {
|
||||||
=
|
|
||||||
we tangle this opening line to
|
we tangle this opening line to
|
||||||
= (text as code)
|
|
||||||
int k_kind_for_template_NTM(wording W, int *X, void **XP) {
|
int k_kind_for_template_NTM(wording W, int *X, void **XP) {
|
||||||
=
|
|
||||||
that is, to a function which returns |TRUE| if it makes a match on the text
|
that is, to a function which returns [[TRUE]] if it makes a match on the text
|
||||||
excerpt in Inform's source text, |FALSE| otherwise; if it matches and produces
|
excerpt in Inform's source text, [[FALSE]] otherwise; if it matches and produces
|
||||||
an integer and/or pointer result, these are copied into |*X| and |*XP|. The
|
an integer and/or pointer result, these are copied into [[*X]] and [[*XP]]. The
|
||||||
remaining lines of the function are tangled unaltered, i.e., following the
|
remaining lines of the function are tangled unaltered, i.e., following the
|
||||||
same rules as for the body of any other C function.
|
same rules as for the body of any other C function.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::insert_in_tangle(programming_language *self, text_stream *OUT, source_line *L) {
|
void InCSupport::insert_in_tangle(programming_language *self, text_stream *OUT, source_line *L) {
|
||||||
preform_nonterminal *pnt = L->preform_nonterminal_defined;
|
preform_nonterminal *pnt = L->preform_nonterminal_defined;
|
||||||
if (pnt->as_function) {
|
if (pnt->as_function) {
|
||||||
|
@ -505,73 +507,73 @@ void InCSupport::insert_in_tangle(programming_language *self, text_stream *OUT,
|
||||||
} else {
|
} else {
|
||||||
WRITE("int %SC(int *X, void **XP, int *R, void **RP, wording *FW, wording W) {\n",
|
WRITE("int %SC(int *X, void **XP, int *R, void **RP, wording *FW, wording W) {\n",
|
||||||
pnt->as_C_identifier);
|
pnt->as_C_identifier);
|
||||||
@<Compile the body of the compositor function@>;
|
<<Compile the body of the compositor function>>;
|
||||||
WRITE("}\n");
|
WRITE("}\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ On the other hand, a grammar nonterminal tangles to a "compositor function".
|
@ On the other hand, a grammar nonterminal tangles to a "compositor function".
|
||||||
Thus the opening line
|
Thus the opening line
|
||||||
= (text)
|
|
||||||
<action-clause> ::=
|
<action-clause> ::=
|
||||||
=
|
|
||||||
tangles to a function header:
|
tangles to a function header:
|
||||||
= (text as code)
|
|
||||||
int action_clause_NTMC(int *X, void **XP, int *R, void **RP, wording *FW, wording W) {
|
int action_clause_NTMC(int *X, void **XP, int *R, void **RP, wording *FW, wording W) {
|
||||||
=
|
|
||||||
Subsequent lines of the nonterminal are categorised |PREFORM_GRAMMAR_LCAT|
|
Subsequent lines of the nonterminal are categorised [[PREFORM_GRAMMAR_LCAT]]
|
||||||
and thus won't tangle to code at all, by the usual rules; so we tangle from
|
and thus won't tangle to code at all, by the usual rules; so we tangle from
|
||||||
them directly here.
|
them directly here.
|
||||||
|
|
||||||
Composition is what happens after a successful match of the text in the
|
Composition is what happens after a successful match of the text in the
|
||||||
word range |W|. The idea is that, especially if the pattern was
|
word range [[W]]. The idea is that, especially if the pattern was
|
||||||
complicated, we will need to "compose" the results of parsing individual
|
complicated, we will need to "compose" the results of parsing individual
|
||||||
pieces of it into a result for the whole. These partial results can be found
|
pieces of it into a result for the whole. These partial results can be found
|
||||||
in the arrays |R[n]| and |RP[n]| passed as parameters; recall that every
|
in the arrays [[R[n]]] and [[RP[n]]] passed as parameters; recall that every
|
||||||
nonterminal has in principle both an integer and a pointer result, though
|
nonterminal has in principle both an integer and a pointer result, though
|
||||||
often one or both is undefined.
|
often one or both is undefined.
|
||||||
|
|
||||||
A simple example would be
|
A simple example would be
|
||||||
= (text)
|
|
||||||
<cardinal-number> + <cardinal-number> ==> R[1] + R[2]
|
<cardinal-number> + <cardinal-number> ==> R[1] + R[2]
|
||||||
=
|
|
||||||
where the composition function would be called on a match of, say, "$5 + 7$",
|
where the composition function would be called on a match of, say, "$5 + 7$",
|
||||||
and would find the values 5 and 7 in |R[1]| and |R[2]| respectively. It would
|
and would find the values 5 and 7 in [[R[1]]] and [[R[2]]] respectively. It would
|
||||||
then add these together, store 12 in |*X|, and return |TRUE| to show that all
|
then add these together, store 12 in [[*X]], and return [[TRUE]] to show that all
|
||||||
was well.
|
was well.
|
||||||
|
|
||||||
A more typical example, drawn from the actual Inform 7 web, is:
|
A more typical example, drawn from the actual Inform 7 web, is:
|
||||||
= (text)
|
|
||||||
<k-kind-of-kind> <k-formal-variable> ==> { - , Kinds::var_construction(R[2], RP[1]) }
|
<k-kind-of-kind> <k-formal-variable> ==> { - , Kinds::var_construction(R[2], RP[1]) }
|
||||||
=
|
|
||||||
which says that the composite result -- the right-hand formula -- is formed by
|
which says that the composite result -- the right-hand formula -- is formed by
|
||||||
calling a particular routine on the integer result of subexpression 2
|
calling a particular routine on the integer result of subexpression 2
|
||||||
(|<k-formal-variable>|) and the pointer result of subexpression 1
|
([[<k-formal-variable>]]) and the pointer result of subexpression 1
|
||||||
(|<k-kind-of-kind>|). The answer, the composite result, that is, must be
|
([[<k-kind-of-kind>]]). The answer, the composite result, that is, must be
|
||||||
placed in |*X| and |*XP|. (Composition functions are also allowed to
|
placed in [[*X]] and [[*XP]]. (Composition functions are also allowed to
|
||||||
invalidate the result, by returning |FALSE|, and have other tricks up their
|
invalidate the result, by returning [[FALSE]], and have other tricks up their
|
||||||
sleeves, but none of that is handled by Inweb: see the Inform 7 web for more
|
sleeves, but none of that is handled by Inweb: see the Inform 7 web for more
|
||||||
on this.)
|
on this.)
|
||||||
|
|
||||||
@<Compile the body of the compositor function@> =
|
<<Compile the body of the compositor function>>=
|
||||||
int needs_collation = FALSE;
|
int needs_collation = FALSE;
|
||||||
for (source_line *AL = L->next_line;
|
for (source_line *AL = L->next_line;
|
||||||
((AL) && (AL->category == PREFORM_GRAMMAR_LCAT));
|
((AL) && (AL->category == PREFORM_GRAMMAR_LCAT));
|
||||||
AL = AL->next_line)
|
AL = AL->next_line)
|
||||||
if (Str::len(AL->text_operand2) > 0)
|
if (Str::len(AL->text_operand2) > 0)
|
||||||
needs_collation = TRUE;
|
needs_collation = TRUE;
|
||||||
if (needs_collation) @<At least one of the grammar lines provided an arrow and formula@>
|
if (needs_collation) <<At least one of the grammar lines provided an arrow and formula>>
|
||||||
else @<None of the grammar lines provided an arrow and formula@>;
|
else <<None of the grammar lines provided an arrow and formula>>;
|
||||||
WRITE("\treturn TRUE;\n");
|
WRITE("\treturn TRUE;\n");
|
||||||
|
|
||||||
@ In the absence of any |==>| formulae, we simply set |*X| to the default
|
@ In the absence of any [[==>]] formulae, we simply set [[*X]] to the default
|
||||||
result supplied; this is the production number within the grammar (0 for the
|
result supplied; this is the production number within the grammar (0 for the
|
||||||
first line, 1 for the second, and so on) by default, with an undefined pointer.
|
first line, 1 for the second, and so on) by default, with an undefined pointer.
|
||||||
|
|
||||||
@<None of the grammar lines provided an arrow and formula@> =
|
<<None of the grammar lines provided an arrow and formula>>=
|
||||||
WRITE("\t*X = R[0];\n");
|
WRITE("\t*X = R[0];\n");
|
||||||
|
|
||||||
@<At least one of the grammar lines provided an arrow and formula@> =
|
<<At least one of the grammar lines provided an arrow and formula>>=
|
||||||
WRITE("\tswitch(R[0]) {\n");
|
WRITE("\tswitch(R[0]) {\n");
|
||||||
int c = 0;
|
int c = 0;
|
||||||
for (source_line *AL = L->next_line;
|
for (source_line *AL = L->next_line;
|
||||||
|
@ -581,7 +583,7 @@ first line, 1 for the second, and so on) by default, with an undefined pointer.
|
||||||
if (Str::len(formula) > 0) {
|
if (Str::len(formula) > 0) {
|
||||||
LanguageMethods::insert_line_marker(OUT, AL->owning_section->sect_language, AL);
|
LanguageMethods::insert_line_marker(OUT, AL->owning_section->sect_language, AL);
|
||||||
WRITE("\t\tcase %d: ", c);
|
WRITE("\t\tcase %d: ", c);
|
||||||
@<Tangle the formula on the right-hand side of the arrow@>;
|
<<Tangle the formula on the right-hand side of the arrow>>;
|
||||||
WRITE(";\n");
|
WRITE(";\n");
|
||||||
WRITE("#pragma clang diagnostic push\n");
|
WRITE("#pragma clang diagnostic push\n");
|
||||||
WRITE("#pragma clang diagnostic ignored \"-Wunreachable-code\"\n");
|
WRITE("#pragma clang diagnostic ignored \"-Wunreachable-code\"\n");
|
||||||
|
@ -597,19 +599,19 @@ and that it produces an integer or a pointer according to what the
|
||||||
non-terminal expects as its main result. But we make one exception: if
|
non-terminal expects as its main result. But we make one exception: if
|
||||||
the formula begins with a paragraph macro, then it can't be an expression,
|
the formula begins with a paragraph macro, then it can't be an expression,
|
||||||
and instead we read it as code in a void context. (This code will, we
|
and instead we read it as code in a void context. (This code will, we
|
||||||
assume, set |*X| and/or |*XP| in some ingenious way of its own.)
|
assume, set [[*X]] and/or [[*XP]] in some ingenious way of its own.)
|
||||||
|
|
||||||
Within the body of the formula, we allow a pseudo-macro to work: |WR[n]|
|
Within the body of the formula, we allow a pseudo-macro to work: [[WR[n]]]
|
||||||
expands to word range |n| in the match which we're compositing. This actually
|
expands to word range [[n]] in the match which we're compositing. This actually
|
||||||
expands like so:
|
expands like so:
|
||||||
= (text as code)
|
|
||||||
action_clause_NTM->range_result[n]
|
action_clause_NTM->range_result[n]
|
||||||
=
|
|
||||||
which saves a good deal of typing. (A regular C preprocessor macro couldn't
|
which saves a good deal of typing. (A regular C preprocessor macro couldn't
|
||||||
easily do this, because it needs to include the identifier name of the
|
easily do this, because it needs to include the identifier name of the
|
||||||
nonterminal being parsed.)
|
nonterminal being parsed.)
|
||||||
|
|
||||||
@<Tangle the formula on the right-hand side of the arrow@> =
|
<<Tangle the formula on the right-hand side of the arrow>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, formula, L"{ *(%c*?) *} *(%c*)")) {
|
if (Regexp::match(&mr, formula, L"{ *(%c*?) *} *(%c*)")) {
|
||||||
TEMPORARY_TEXT(rewritten)
|
TEMPORARY_TEXT(rewritten)
|
||||||
|
@ -619,7 +621,7 @@ nonterminal being parsed.)
|
||||||
InCSupport::expand_formula(OUT, AL, pnt, mr.exp[1], TRUE);
|
InCSupport::expand_formula(OUT, AL, pnt, mr.exp[1], TRUE);
|
||||||
DISCARD_TEXT(rewritten)
|
DISCARD_TEXT(rewritten)
|
||||||
} else {
|
} else {
|
||||||
if (!Regexp::match(&mr, formula, L"@<%c*")) {
|
if (!Regexp::match(&mr, formula, L"<<%c*")) {
|
||||||
if (pnt->takes_pointer_result) WRITE("*XP = ");
|
if (pnt->takes_pointer_result) WRITE("*XP = ");
|
||||||
else WRITE("*X = ");
|
else WRITE("*X = ");
|
||||||
}
|
}
|
||||||
|
@ -629,7 +631,7 @@ nonterminal being parsed.)
|
||||||
|
|
||||||
@
|
@
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::expand_formula(text_stream *OUT, source_line *AL, preform_nonterminal *pnt,
|
void InCSupport::expand_formula(text_stream *OUT, source_line *AL, preform_nonterminal *pnt,
|
||||||
text_stream *formula, int full) {
|
text_stream *formula, int full) {
|
||||||
TEMPORARY_TEXT(expanded)
|
TEMPORARY_TEXT(expanded)
|
||||||
|
@ -657,7 +659,7 @@ void InCSupport::expand_formula(text_stream *OUT, source_line *AL, preform_nonte
|
||||||
@ Going down from line level to the tangling of little excerpts of C code,
|
@ Going down from line level to the tangling of little excerpts of C code,
|
||||||
we also provide for some other special extensions to C.
|
we also provide for some other special extensions to C.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int InCSupport::tangle_line(programming_language *self, text_stream *OUT, text_stream *original) {
|
int InCSupport::tangle_line(programming_language *self, text_stream *OUT, text_stream *original) {
|
||||||
InCSupport::tangle_line_inner(OUT, NULL, NULL, original);
|
InCSupport::tangle_line_inner(OUT, NULL, NULL, original);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -666,13 +668,13 @@ int InCSupport::tangle_line(programming_language *self, text_stream *OUT, text_s
|
||||||
void InCSupport::tangle_line_inner(text_stream *OUT, source_line *AL, preform_nonterminal *pnt, text_stream *original) {
|
void InCSupport::tangle_line_inner(text_stream *OUT, source_line *AL, preform_nonterminal *pnt, text_stream *original) {
|
||||||
int fcall_pos = -1;
|
int fcall_pos = -1;
|
||||||
for (int i = 0; i < Str::len(original); i++) {
|
for (int i = 0; i < Str::len(original); i++) {
|
||||||
@<Double-colons are namespace dividers in function names@>;
|
<<Double-colons are namespace dividers in function names>>;
|
||||||
@<Long arrow and braces assigns Preform results@>;
|
<<Long arrow and braces assigns Preform results>>;
|
||||||
if (Str::get_at(original, i) == '<') {
|
if (Str::get_at(original, i) == '<') {
|
||||||
if (Str::get_at(original, i+1) == '<') {
|
if (Str::get_at(original, i+1) == '<') {
|
||||||
@<Double-angles sometimes delimit Preform variable names@>;
|
<<Double-angles sometimes delimit Preform variable names>>;
|
||||||
} else {
|
} else {
|
||||||
@<Single-angles sometimes delimit Preform nonterminal names@>;
|
<<Single-angles sometimes delimit Preform nonterminal names>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == fcall_pos) {
|
if (i == fcall_pos) {
|
||||||
|
@ -683,35 +685,36 @@ void InCSupport::tangle_line_inner(text_stream *OUT, source_line *AL, preform_no
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ For example, a function name like |Text::Parsing::get_next| must be rewritten
|
@ For example, a function name like [[Text::Parsing::get_next]] must be rewritten
|
||||||
as |Text__Parsing__get_next| since colons aren't valid in C identifiers. The
|
as [[Text__Parsing__get_next]] since colons aren't valid in C identifiers. The
|
||||||
following is prone to all kinds of misreadings, of course; it picks up any use
|
following is prone to all kinds of misreadings, of course; it picks up any use
|
||||||
of |::| between an alphanumberic character and a letter. In particular, code
|
of [[::]] between an alphanumberic character and a letter. In particular, code
|
||||||
like
|
like
|
||||||
= (text)
|
|
||||||
printf("Trying Text::Parsing::get_next now.\n");
|
printf("Trying Text::Parsing::get_next now.\n");
|
||||||
=
|
|
||||||
will be rewritten as
|
will be rewritten as
|
||||||
= (text as code)
|
|
||||||
printf("Trying Text__Parsing__get_next now.\n");
|
printf("Trying Text__Parsing__get_next now.\n");
|
||||||
=
|
|
||||||
This is probably unwanted, but it doesn't matter, because these Inform-only
|
This is probably unwanted, but it doesn't matter, because these Inform-only
|
||||||
extension features of Inweb aren't intended for general use: only for
|
extension features of Inweb aren't intended for general use: only for
|
||||||
Inform, where no misreadings occur.
|
Inform, where no misreadings occur.
|
||||||
|
|
||||||
@<Double-colons are namespace dividers in function names@> =
|
<<Double-colons are namespace dividers in function names>>=
|
||||||
if ((i > 0) && (Str::get_at(original, i) == ':') && (Str::get_at(original, i+1) == ':') &&
|
if ((i > 0) && (Str::get_at(original, i) == ':') && (Str::get_at(original, i+1) == ':') &&
|
||||||
(isalpha(Str::get_at(original, i+2))) && (isalnum(Str::get_at(original, i-1)))) {
|
(isalpha(Str::get_at(original, i+2))) && (isalnum(Str::get_at(original, i-1)))) {
|
||||||
WRITE("__"); i++;
|
WRITE("__"); i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ For example, |==> { A, B }| assigns the expressions A and B as the results
|
@ For example, [[==> { A, B }]] assigns the expressions A and B as the results
|
||||||
of parsing a Preform nonterminal.
|
of parsing a Preform nonterminal.
|
||||||
|
|
||||||
@d MAX_PREFORM_RESULT_CLAUSES 10
|
<<*>>=
|
||||||
|
#define MAX_PREFORM_RESULT_CLAUSES 10
|
||||||
|
|
||||||
@<Long arrow and braces assigns Preform results@> =
|
<<Long arrow and braces assigns Preform results>>=
|
||||||
if ((Str::get_at(original, i) == '=') &&
|
if ((Str::get_at(original, i) == '=') &&
|
||||||
(Str::get_at(original, i+1) == '=') &&
|
(Str::get_at(original, i+1) == '=') &&
|
||||||
(Str::get_at(original, i+2) == '>') &&
|
(Str::get_at(original, i+2) == '>') &&
|
||||||
|
@ -719,11 +722,11 @@ of parsing a Preform nonterminal.
|
||||||
(Str::get_at(original, i+4) == '{')) {
|
(Str::get_at(original, i+4) == '{')) {
|
||||||
int clauses, err = FALSE;
|
int clauses, err = FALSE;
|
||||||
text_stream *clause[MAX_PREFORM_RESULT_CLAUSES];
|
text_stream *clause[MAX_PREFORM_RESULT_CLAUSES];
|
||||||
@<Find the clauses@>;
|
<<Find the clauses>>;
|
||||||
TEMPORARY_TEXT(extra)
|
TEMPORARY_TEXT(extra)
|
||||||
if (clauses == 1) @<Recognise one-clause specials@>;
|
if (clauses == 1) <<Recognise one-clause specials>>;
|
||||||
if (clauses < 2) err = TRUE;
|
if (clauses < 2) err = TRUE;
|
||||||
if (err == FALSE) @<Write the assignments@>;
|
if (err == FALSE) <<Write the assignments>>;
|
||||||
if (err) {
|
if (err) {
|
||||||
Main::error_in_web(I"malformed '{ , }' formula", AL);
|
Main::error_in_web(I"malformed '{ , }' formula", AL);
|
||||||
if (AL == NULL) WRITE_TO(STDERR, "%S\n", original);
|
if (AL == NULL) WRITE_TO(STDERR, "%S\n", original);
|
||||||
|
@ -734,7 +737,7 @@ of parsing a Preform nonterminal.
|
||||||
@ The clauses are a comma-separated list inside the braces, except that the
|
@ The clauses are a comma-separated list inside the braces, except that the
|
||||||
commas need to be outside of any parentheses.
|
commas need to be outside of any parentheses.
|
||||||
|
|
||||||
@<Find the clauses@> =
|
<<Find the clauses>>=
|
||||||
clauses = 1;
|
clauses = 1;
|
||||||
clause[0] = Str::new();
|
clause[0] = Str::new();
|
||||||
int bl = 0;
|
int bl = 0;
|
||||||
|
@ -761,7 +764,7 @@ commas need to be outside of any parentheses.
|
||||||
are implemented by rewriting them in two clauses, and sometimes adding some
|
are implemented by rewriting them in two clauses, and sometimes adding some
|
||||||
extra code to execute after the assignments.
|
extra code to execute after the assignments.
|
||||||
|
|
||||||
@<Recognise one-clause specials@> =
|
<<Recognise one-clause specials>>=
|
||||||
if (Str::eq(clause[0], I"fail")) {
|
if (Str::eq(clause[0], I"fail")) {
|
||||||
clause[1] = Str::new(); clauses = 2;
|
clause[1] = Str::new(); clauses = 2;
|
||||||
WRITE_TO(extra, "return FAIL_NONTERMINAL;");
|
WRITE_TO(extra, "return FAIL_NONTERMINAL;");
|
||||||
|
@ -808,10 +811,10 @@ extra code to execute after the assignments.
|
||||||
for the current nonterminal; any subsequent clauses must specify which
|
for the current nonterminal; any subsequent clauses must specify which
|
||||||
variable is to be set. A dash means make no assignment.
|
variable is to be set. A dash means make no assignment.
|
||||||
|
|
||||||
For example, |{ R[1], - , <<to>> = R[2] }| sets |*X| to |R[1]|, does not
|
For example, [[{ R[1], - , <<to>> = R[2] }]] sets [[*X]] to [[R[1]]], does not
|
||||||
alter |*XP|, and sets |<<to>>| to |R[2]|.
|
alter [[*XP]], and sets [[<<to>>]] to [[R[2]]].
|
||||||
|
|
||||||
@<Write the assignments@> =
|
<<Write the assignments>>=
|
||||||
for (int c=0; c<clauses; c++) {
|
for (int c=0; c<clauses; c++) {
|
||||||
if (Str::ne(clause[c], I"-")) {
|
if (Str::ne(clause[c], I"-")) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
@ -842,11 +845,11 @@ alter |*XP|, and sets |<<to>>| to |R[2]|.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ Angle brackets around a valid Preform variable name expand into its
|
@ Angle brackets around a valid Preform variable name expand into its
|
||||||
C identifier; for example, |<<R>>| becomes |most_recent_result|.
|
C identifier; for example, [[<<R>>]] becomes [[most_recent_result]].
|
||||||
We take no action if it's not a valid name, so |<<fish>>| becomes
|
We take no action if it's not a valid name, so [[<<fish>>]] becomes
|
||||||
just |<<fish>>|.
|
just [[<<fish>>]].
|
||||||
|
|
||||||
@<Double-angles sometimes delimit Preform variable names@> =
|
<<Double-angles sometimes delimit Preform variable names>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
TEMPORARY_TEXT(check_this)
|
TEMPORARY_TEXT(check_this)
|
||||||
Str::substr(check_this, Str::at(original, i), Str::end(original));
|
Str::substr(check_this, Str::at(original, i), Str::end(original));
|
||||||
|
@ -863,22 +866,22 @@ just |<<fish>>|.
|
||||||
DISCARD_TEXT(check_this)
|
DISCARD_TEXT(check_this)
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
|
||||||
@ Similarly for nonterminals; |<k-kind>| might become |k_kind_NTM|.
|
@ Similarly for nonterminals; [[<k-kind>]] might become [[k_kind_NTM]].
|
||||||
Here, though, there's a complication:
|
Here, though, there's a complication:
|
||||||
= (text)
|
|
||||||
if (<k-kind>(W)) { ...
|
if (<k-kind>(W)) { ...
|
||||||
=
|
|
||||||
must expand to:
|
must expand to:
|
||||||
= (text as code)
|
|
||||||
if (Text__Languages__parse_nt_against_word_range(k_kind_NTM, W, NULL, NULL)) { ...
|
if (Text__Languages__parse_nt_against_word_range(k_kind_NTM, W, NULL, NULL)) { ...
|
||||||
=
|
|
||||||
This is all syntactic sugar to make it easier to see parsing in action.
|
This is all syntactic sugar to make it easier to see parsing in action.
|
||||||
Anyway, it means we have to set |fcall_pos| to remember to add in the
|
Anyway, it means we have to set [[fcall_pos]] to remember to add in the
|
||||||
two |NULL| arguments when we hit the |)| a little later. We're doing all
|
two [[NULL]] arguments when we hit the [[)]] a little later. We're doing all
|
||||||
of this fairly laxly, but as before: it only needs to work for Inform,
|
of this fairly laxly, but as before: it only needs to work for Inform,
|
||||||
and Inform doesn't cause any trouble.
|
and Inform doesn't cause any trouble.
|
||||||
|
|
||||||
@<Single-angles sometimes delimit Preform nonterminal names@> =
|
<<Single-angles sometimes delimit Preform nonterminal names>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
TEMPORARY_TEXT(check_this)
|
TEMPORARY_TEXT(check_this)
|
||||||
Str::substr(check_this, Str::at(original, i), Str::end(original));
|
Str::substr(check_this, Str::at(original, i), Str::end(original));
|
||||||
|
@ -912,7 +915,7 @@ name. They're not very efficient, but experience shows that even on a web
|
||||||
the size of Inform 7, there's no significant gain from speeding them up
|
the size of Inform 7, there's no significant gain from speeding them up
|
||||||
(with, say, a hash table).
|
(with, say, a hash table).
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
preform_nonterminal *InCSupport::nonterminal_by_name(text_stream *name) {
|
preform_nonterminal *InCSupport::nonterminal_by_name(text_stream *name) {
|
||||||
preform_nonterminal *pnt;
|
preform_nonterminal *pnt;
|
||||||
LOOP_OVER(pnt, preform_nonterminal)
|
LOOP_OVER(pnt, preform_nonterminal)
|
||||||
|
@ -921,12 +924,12 @@ preform_nonterminal *InCSupport::nonterminal_by_name(text_stream *name) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ The special variables |<<R>>| and |<<RP>>| hold the results,
|
@ The special variables [[<<R>>]] and [[<<RP>>]] hold the results,
|
||||||
integer and pointer, for the most recent successful match. They're defined
|
integer and pointer, for the most recent successful match. They're defined
|
||||||
in the Inform 7 web (see the code for parsing text against Preform grammars),
|
in the Inform 7 web (see the code for parsing text against Preform grammars),
|
||||||
not by Inweb.
|
not by Inweb.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
text_stream *InCSupport::nonterminal_variable_identifier(text_stream *name) {
|
text_stream *InCSupport::nonterminal_variable_identifier(text_stream *name) {
|
||||||
if (Str::eq_wide_string(name, L"r")) return I"most_recent_result";
|
if (Str::eq_wide_string(name, L"r")) return I"most_recent_result";
|
||||||
if (Str::eq_wide_string(name, L"rp")) return I"most_recent_result_p";
|
if (Str::eq_wide_string(name, L"rp")) return I"most_recent_result_p";
|
||||||
|
@ -944,10 +947,10 @@ simply thrown away. It doesn't appear anywhere in the C code tangled by
|
||||||
Inweb.
|
Inweb.
|
||||||
|
|
||||||
So what does happen to it? The answer is that it's transcribed into an
|
So what does happen to it? The answer is that it's transcribed into an
|
||||||
auxiliary file called |Syntax.preform|, which Inform, once it is compiled,
|
auxiliary file called [[Syntax.preform]], which Inform, once it is compiled,
|
||||||
will read in at run-time. This is how that happens:
|
will read in at run-time. This is how that happens:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::additional_tangling(programming_language *self, web *W, tangle_target *target) {
|
void InCSupport::additional_tangling(programming_language *self, web *W, tangle_target *target) {
|
||||||
if (NUMBER_CREATED(preform_nonterminal) > 0) {
|
if (NUMBER_CREATED(preform_nonterminal) > 0) {
|
||||||
pathname *P = Reader::tangled_folder(W);
|
pathname *P = Reader::tangled_folder(W);
|
||||||
|
@ -965,7 +968,7 @@ void InCSupport::additional_tangling(programming_language *self, web *W, tangle_
|
||||||
if (Bibliographic::data_exists(W->md, I"Preform Language"))
|
if (Bibliographic::data_exists(W->md, I"Preform Language"))
|
||||||
WRITE("language %S\n", Bibliographic::get_datum(W->md, I"Preform Language"));
|
WRITE("language %S\n", Bibliographic::get_datum(W->md, I"Preform Language"));
|
||||||
|
|
||||||
@<Actually write out the Preform syntax@>;
|
<<Actually write out the Preform syntax>>;
|
||||||
STREAM_CLOSE(OUT);
|
STREAM_CLOSE(OUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -976,13 +979,13 @@ right-hand side of the arrow in a grammar line uses a paragraph macro which
|
||||||
mentions a problem message, then we transcribe a Preform comment to that
|
mentions a problem message, then we transcribe a Preform comment to that
|
||||||
effect. (This really is a comment: Inform ignores it, but it makes the
|
effect. (This really is a comment: Inform ignores it, but it makes the
|
||||||
file more comprehensible to human eyes.) For example,
|
file more comprehensible to human eyes.) For example,
|
||||||
= (text)
|
|
||||||
<article> kind ==> @<Issue C8PropertyOfKind problem@>
|
<article> kind ==> <<Issue C8PropertyOfKind problem>>
|
||||||
=
|
|
||||||
(The code in this paragraph macro will indeed issue this problem message, we
|
(The code in this paragraph macro will indeed issue this problem message, we
|
||||||
assume.)
|
assume.)
|
||||||
|
|
||||||
@<Actually write out the Preform syntax@> =
|
<<Actually write out the Preform syntax>>=
|
||||||
chapter *C;
|
chapter *C;
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_WITHIN_TANGLE(C, S, target)
|
LOOP_WITHIN_TANGLE(C, S, target)
|
||||||
|
@ -1004,13 +1007,13 @@ assume.)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Weaving.
|
@ \section{Weaving.}
|
||||||
The following isn't a method, but is called by the weaver directly. It adds
|
The following isn't a method, but is called by the weaver directly. It adds
|
||||||
additional endnotes to the woven form of a paragraph which includes Preform
|
additional endnotes to the woven form of a paragraph which includes Preform
|
||||||
nonterminal definitions; it is meaningful only in the TeX format, and should
|
nonterminal definitions; it is meaningful only in the TeX format, and should
|
||||||
probably be dropped.
|
probably be dropped.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::weave_grammar_index(OUTPUT_STREAM) {
|
void InCSupport::weave_grammar_index(OUTPUT_STREAM) {
|
||||||
WRITE("\\raggedright\\tolerance=10000");
|
WRITE("\\raggedright\\tolerance=10000");
|
||||||
preform_nonterminal *pnt;
|
preform_nonterminal *pnt;
|
||||||
|
@ -1022,8 +1025,8 @@ void InCSupport::weave_grammar_index(OUTPUT_STREAM) {
|
||||||
(pnt->as_function)?" (internal)":"",
|
(pnt->as_function)?" (internal)":"",
|
||||||
pnt->where_defined->owning_section->md->sect_range);
|
pnt->where_defined->owning_section->md->sect_range);
|
||||||
int said_something = FALSE;
|
int said_something = FALSE;
|
||||||
@<List where the nonterminal appears in other Preform declarations@>;
|
<<List where the nonterminal appears in other Preform declarations>>;
|
||||||
@<List where the nonterminal is called from Inform code@>;
|
<<List where the nonterminal is called from Inform code>>;
|
||||||
if (said_something == FALSE)
|
if (said_something == FALSE)
|
||||||
WRITE("\\par\\hangindent=3em{\\it unused}\n\n");
|
WRITE("\\par\\hangindent=3em{\\it unused}\n\n");
|
||||||
}
|
}
|
||||||
|
@ -1032,7 +1035,7 @@ void InCSupport::weave_grammar_index(OUTPUT_STREAM) {
|
||||||
WRITE("\\hrule\\smallbreak\n");
|
WRITE("\\hrule\\smallbreak\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@<List where the nonterminal is called from Inform code@> =
|
<<List where the nonterminal is called from Inform code>>=
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_OVER(S, section) S->scratch_flag = FALSE;
|
LOOP_OVER(S, section) S->scratch_flag = FALSE;
|
||||||
hash_table_entry *hte = Analyser::find_hash_entry_for_section(
|
hash_table_entry *hte = Analyser::find_hash_entry_for_section(
|
||||||
|
@ -1057,7 +1060,7 @@ void InCSupport::weave_grammar_index(OUTPUT_STREAM) {
|
||||||
WRITE("\n\n");
|
WRITE("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@<List where the nonterminal appears in other Preform declarations@> =
|
<<List where the nonterminal appears in other Preform declarations>>=
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_OVER(S, section) S->scratch_flag = FALSE;
|
LOOP_OVER(S, section) S->scratch_flag = FALSE;
|
||||||
hash_table_entry *hte = Analyser::find_hash_entry_for_section(
|
hash_table_entry *hte = Analyser::find_hash_entry_for_section(
|
||||||
|
@ -1082,11 +1085,11 @@ void InCSupport::weave_grammar_index(OUTPUT_STREAM) {
|
||||||
WRITE("\n\n");
|
WRITE("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Weaving methods.
|
@ \section{Weaving methods.}
|
||||||
If we're weaving just a document of Preform grammar, then we skip any lines
|
If we're weaving just a document of Preform grammar, then we skip any lines
|
||||||
of C code which appear in |internal| nonterminal definitions:
|
of C code which appear in [[internal]] nonterminal definitions:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int skipping_internal = FALSE, preform_production_count = 0;
|
int skipping_internal = FALSE, preform_production_count = 0;
|
||||||
|
|
||||||
int InCSupport::skip_in_weaving(programming_language *self, weave_order *wv, source_line *L) {
|
int InCSupport::skip_in_weaving(programming_language *self, weave_order *wv, source_line *L) {
|
||||||
|
@ -1103,7 +1106,7 @@ int InCSupport::skip_in_weaving(programming_language *self, weave_order *wv, sou
|
||||||
|
|
||||||
@ And here is the TeX code for displaying Preform grammar:
|
@ And here is the TeX code for displaying Preform grammar:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int InCSupport::weave_code_line(programming_language *self, text_stream *OUT,
|
int InCSupport::weave_code_line(programming_language *self, text_stream *OUT,
|
||||||
weave_order *wv, web *W, chapter *C, section *S, source_line *L,
|
weave_order *wv, web *W, chapter *C, section *S, source_line *L,
|
||||||
text_stream *matter, text_stream *concluding_comment) {
|
text_stream *matter, text_stream *concluding_comment) {
|
||||||
|
@ -1114,17 +1117,17 @@ int InCSupport::weave_code_line(programming_language *self, text_stream *OUT,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ In paragraphs where we spot Preform nonterminals being defined, we're
|
@ In paragraphs where we spot Preform nonterminals being defined, we're
|
||||||
going to automatically apply the tag |^"Preform"|, but only if it already
|
going to automatically apply the tag [[^"Preform"]], but only if it already
|
||||||
exists. We watch for it here:
|
exists. We watch for it here:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::new_tag_declared(programming_language *self, theme_tag *tag) {
|
void InCSupport::new_tag_declared(programming_language *self, theme_tag *tag) {
|
||||||
if (Str::eq_wide_string(tag->tag_name, L"Preform")) Preform_theme = tag;
|
if (Str::eq_wide_string(tag->tag_name, L"Preform")) Preform_theme = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Analysis methods.
|
@ \section{Analysis methods.}
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void InCSupport::analyse_code(programming_language *self, web *W) {
|
void InCSupport::analyse_code(programming_language *self, web *W) {
|
||||||
preform_nonterminal *pnt;
|
preform_nonterminal *pnt;
|
||||||
LOOP_OVER(pnt, preform_nonterminal)
|
LOOP_OVER(pnt, preform_nonterminal)
|
|
@ -3,12 +3,12 @@
|
||||||
To characterise the relevant differences in behaviour between the
|
To characterise the relevant differences in behaviour between the
|
||||||
various programming languages supported.
|
various programming languages supported.
|
||||||
|
|
||||||
@h Introduction.
|
@ \section{Introduction.}
|
||||||
The conventions for writing, weaving and tangling a web are really quite
|
The conventions for writing, weaving and tangling a web are really quite
|
||||||
independent of the programming language being written, woven or tangled;
|
independent of the programming language being written, woven or tangled;
|
||||||
Knuth began literate programming with Pascal, but now uses C, and the original
|
Knuth began literate programming with Pascal, but now uses C, and the original
|
||||||
Pascal webs were mechanically translated into C ones with remarkably little
|
Pascal webs were mechanically translated into C ones with remarkably little
|
||||||
fuss or bother. Modern LP tools, such as |noweb|, aim to be language-agnostic.
|
fuss or bother. Modern LP tools, such as [[noweb]], aim to be language-agnostic.
|
||||||
But of course if you act the same on all languages, you give up the benefits
|
But of course if you act the same on all languages, you give up the benefits
|
||||||
which might follow from knowing something about the languages you actually
|
which might follow from knowing something about the languages you actually
|
||||||
write in.
|
write in.
|
||||||
|
@ -23,64 +23,69 @@ all of them made from this section. That means a lot of simple wrapper routines
|
||||||
which don't do very much. This section may still be useful to read, since it
|
which don't do very much. This section may still be useful to read, since it
|
||||||
documents what amounts to an API.
|
documents what amounts to an API.
|
||||||
|
|
||||||
@h Parsing methods.
|
@ \section{Parsing methods.}
|
||||||
We begin with parsing extensions. When these are used, we have already read
|
We begin with parsing extensions. When these are used, we have already read
|
||||||
the web into chapters, sections and paragraphs, but for some languages we will
|
the web into chapters, sections and paragraphs, but for some languages we will
|
||||||
need a more detailed picture.
|
need a more detailed picture.
|
||||||
|
|
||||||
|PARSE_TYPES_PAR_MTID| gives a language to look for type declarations.
|
[[PARSE_TYPES_PAR_MTID]] gives a language to look for type declarations.
|
||||||
|
|
||||||
@e PARSE_TYPES_PAR_MTID
|
<<*>>=
|
||||||
|
enum PARSE_TYPES_PAR_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(PARSE_TYPES_PAR_MTID, programming_language *pl, web *W)
|
VOID_METHOD_TYPE(PARSE_TYPES_PAR_MTID, programming_language *pl, web *W)
|
||||||
void LanguageMethods::parse_types(web *W, programming_language *pl) {
|
void LanguageMethods::parse_types(web *W, programming_language *pl) {
|
||||||
VOID_METHOD_CALL(pl, PARSE_TYPES_PAR_MTID, W);
|
VOID_METHOD_CALL(pl, PARSE_TYPES_PAR_MTID, W);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ |PARSE_FUNCTIONS_PAR_MTID| is, similarly, for function declarations.
|
@ [[PARSE_FUNCTIONS_PAR_MTID]] is, similarly, for function declarations.
|
||||||
|
|
||||||
@e PARSE_FUNCTIONS_PAR_MTID
|
<<*>>=
|
||||||
|
enum PARSE_FUNCTIONS_PAR_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(PARSE_FUNCTIONS_PAR_MTID, programming_language *pl, web *W)
|
VOID_METHOD_TYPE(PARSE_FUNCTIONS_PAR_MTID, programming_language *pl, web *W)
|
||||||
void LanguageMethods::parse_functions(web *W, programming_language *pl) {
|
void LanguageMethods::parse_functions(web *W, programming_language *pl) {
|
||||||
VOID_METHOD_CALL(pl, PARSE_FUNCTIONS_PAR_MTID, W);
|
VOID_METHOD_CALL(pl, PARSE_FUNCTIONS_PAR_MTID, W);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ |FURTHER_PARSING_PAR_MTID| is "further" in that it is called when the main
|
@ [[FURTHER_PARSING_PAR_MTID]] is "further" in that it is called when the main
|
||||||
parser has finished work; it typically looks over the whole web for something
|
parser has finished work; it typically looks over the whole web for something
|
||||||
of interest.
|
of interest.
|
||||||
|
|
||||||
@e FURTHER_PARSING_PAR_MTID
|
<<*>>=
|
||||||
|
enum FURTHER_PARSING_PAR_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(FURTHER_PARSING_PAR_MTID, programming_language *pl, web *W)
|
VOID_METHOD_TYPE(FURTHER_PARSING_PAR_MTID, programming_language *pl, web *W)
|
||||||
void LanguageMethods::further_parsing(web *W, programming_language *pl) {
|
void LanguageMethods::further_parsing(web *W, programming_language *pl) {
|
||||||
VOID_METHOD_CALL(pl, FURTHER_PARSING_PAR_MTID, W);
|
VOID_METHOD_CALL(pl, FURTHER_PARSING_PAR_MTID, W);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ |SUBCATEGORISE_LINE_PAR_MTID| looks at a single line, after the main parser
|
@ [[SUBCATEGORISE_LINE_PAR_MTID]] looks at a single line, after the main parser
|
||||||
has given it a category. The idea is not so much to second-guess the parser
|
has given it a category. The idea is not so much to second-guess the parser
|
||||||
(although we can) but to change to a more exotic category which it would
|
(although we can) but to change to a more exotic category which it would
|
||||||
otherwise never produce.
|
otherwise never produce.
|
||||||
|
|
||||||
@e SUBCATEGORISE_LINE_PAR_MTID
|
<<*>>=
|
||||||
|
enum SUBCATEGORISE_LINE_PAR_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(SUBCATEGORISE_LINE_PAR_MTID, programming_language *pl, source_line *L)
|
VOID_METHOD_TYPE(SUBCATEGORISE_LINE_PAR_MTID, programming_language *pl, source_line *L)
|
||||||
void LanguageMethods::subcategorise_line(programming_language *pl, source_line *L) {
|
void LanguageMethods::subcategorise_line(programming_language *pl, source_line *L) {
|
||||||
VOID_METHOD_CALL(pl, SUBCATEGORISE_LINE_PAR_MTID, L);
|
VOID_METHOD_CALL(pl, SUBCATEGORISE_LINE_PAR_MTID, L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ Comments have different syntax in different languages. The method here is
|
@ Comments have different syntax in different languages. The method here is
|
||||||
expected to look for a comment on the |line|, and if so to return |TRUE|,
|
expected to look for a comment on the [[line]], and if so to return [[TRUE]],
|
||||||
but not before splicing the non-comment parts of the line before and
|
but not before splicing the non-comment parts of the line before and
|
||||||
within the comment into the supplied strings.
|
within the comment into the supplied strings.
|
||||||
|
|
||||||
@e PARSE_COMMENT_TAN_MTID
|
<<*>>=
|
||||||
|
enum PARSE_COMMENT_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(PARSE_COMMENT_TAN_MTID, programming_language *pl, text_stream *line, text_stream *before, text_stream *within)
|
INT_METHOD_TYPE(PARSE_COMMENT_TAN_MTID, programming_language *pl, text_stream *line, text_stream *before, text_stream *within)
|
||||||
|
|
||||||
int LanguageMethods::parse_comment(programming_language *pl,
|
int LanguageMethods::parse_comment(programming_language *pl,
|
||||||
|
@ -90,17 +95,18 @@ int LanguageMethods::parse_comment(programming_language *pl,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Tangling methods.
|
@ \section{Tangling methods.}
|
||||||
We take these roughly in order of their effects on the tangled output, from
|
We take these roughly in order of their effects on the tangled output, from
|
||||||
the top to the bottom of the file.
|
the top to the bottom of the file.
|
||||||
|
|
||||||
The top of the tangled file is a header called the "shebang". By default,
|
The top of the tangled file is a header called the "shebang". By default,
|
||||||
there's nothing there, but |SHEBANG_TAN_MTID| allows the language to add one.
|
there's nothing there, but [[SHEBANG_TAN_MTID]] allows the language to add one.
|
||||||
For example, Perl prints |#!/usr/bin/perl| here.
|
For example, Perl prints [[#!/usr/bin/perl]] here.
|
||||||
|
|
||||||
@e SHEBANG_TAN_MTID
|
<<*>>=
|
||||||
|
enum SHEBANG_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(SHEBANG_TAN_MTID, programming_language *pl, text_stream *OUT, web *W, tangle_target *target)
|
VOID_METHOD_TYPE(SHEBANG_TAN_MTID, programming_language *pl, text_stream *OUT, web *W, tangle_target *target)
|
||||||
void LanguageMethods::shebang(OUTPUT_STREAM, programming_language *pl, web *W, tangle_target *target) {
|
void LanguageMethods::shebang(OUTPUT_STREAM, programming_language *pl, web *W, tangle_target *target) {
|
||||||
VOID_METHOD_CALL(pl, SHEBANG_TAN_MTID, OUT, W, target);
|
VOID_METHOD_CALL(pl, SHEBANG_TAN_MTID, OUT, W, target);
|
||||||
|
@ -109,9 +115,10 @@ void LanguageMethods::shebang(OUTPUT_STREAM, programming_language *pl, web *W, t
|
||||||
@ Next is the disclaimer, text warning the human reader that she is looking
|
@ Next is the disclaimer, text warning the human reader that she is looking
|
||||||
at tangled (therefore not original) material.
|
at tangled (therefore not original) material.
|
||||||
|
|
||||||
@e SUPPRESS_DISCLAIMER_TAN_MTID
|
<<*>>=
|
||||||
|
enum SUPPRESS_DISCLAIMER_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(SUPPRESS_DISCLAIMER_TAN_MTID, programming_language *pl)
|
INT_METHOD_TYPE(SUPPRESS_DISCLAIMER_TAN_MTID, programming_language *pl)
|
||||||
void LanguageMethods::disclaimer(text_stream *OUT, programming_language *pl, web *W, tangle_target *target) {
|
void LanguageMethods::disclaimer(text_stream *OUT, programming_language *pl, web *W, tangle_target *target) {
|
||||||
int rv = FALSE;
|
int rv = FALSE;
|
||||||
|
@ -123,25 +130,27 @@ void LanguageMethods::disclaimer(text_stream *OUT, programming_language *pl, web
|
||||||
@ Next is the disclaimer, text warning the human reader that she is looking
|
@ Next is the disclaimer, text warning the human reader that she is looking
|
||||||
at tangled (therefore not original) material.
|
at tangled (therefore not original) material.
|
||||||
|
|
||||||
@e ADDITIONAL_EARLY_MATTER_TAN_MTID
|
<<*>>=
|
||||||
|
enum ADDITIONAL_EARLY_MATTER_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(ADDITIONAL_EARLY_MATTER_TAN_MTID, programming_language *pl, text_stream *OUT, web *W, tangle_target *target)
|
VOID_METHOD_TYPE(ADDITIONAL_EARLY_MATTER_TAN_MTID, programming_language *pl, text_stream *OUT, web *W, tangle_target *target)
|
||||||
void LanguageMethods::additional_early_matter(text_stream *OUT, programming_language *pl, web *W, tangle_target *target) {
|
void LanguageMethods::additional_early_matter(text_stream *OUT, programming_language *pl, web *W, tangle_target *target) {
|
||||||
VOID_METHOD_CALL(pl, ADDITIONAL_EARLY_MATTER_TAN_MTID, OUT, W, target);
|
VOID_METHOD_CALL(pl, ADDITIONAL_EARLY_MATTER_TAN_MTID, OUT, W, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ A tangled file then normally declares "definitions". The following write a
|
@ A tangled file then normally declares "definitions". The following write a
|
||||||
definition of the constant named |term| as the value given. If the value spans
|
definition of the constant named [[term]] as the value given. If the value spans
|
||||||
multiple lines, the first-line part is supplied to |START_DEFN_TAN_MTID| and
|
multiple lines, the first-line part is supplied to [[START_DEFN_TAN_MTID]] and
|
||||||
then subsequent lines are fed in order to |PROLONG_DEFN_TAN_MTID|. At the end,
|
then subsequent lines are fed in order to [[PROLONG_DEFN_TAN_MTID]]. At the end,
|
||||||
|END_DEFN_TAN_MTID| is called.
|
[[END_DEFN_TAN_MTID]] is called.
|
||||||
|
|
||||||
@e START_DEFN_TAN_MTID
|
<<*>>=
|
||||||
@e PROLONG_DEFN_TAN_MTID
|
enum START_DEFN_TAN_MTID
|
||||||
@e END_DEFN_TAN_MTID
|
enum PROLONG_DEFN_TAN_MTID
|
||||||
|
enum END_DEFN_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(START_DEFN_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *term, text_stream *start, section *S, source_line *L)
|
INT_METHOD_TYPE(START_DEFN_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *term, text_stream *start, section *S, source_line *L)
|
||||||
INT_METHOD_TYPE(PROLONG_DEFN_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *more, section *S, source_line *L)
|
INT_METHOD_TYPE(PROLONG_DEFN_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *more, section *S, source_line *L)
|
||||||
INT_METHOD_TYPE(END_DEFN_TAN_MTID, programming_language *pl, text_stream *OUT, section *S, source_line *L)
|
INT_METHOD_TYPE(END_DEFN_TAN_MTID, programming_language *pl, text_stream *OUT, section *S, source_line *L)
|
||||||
|
@ -171,9 +180,10 @@ void LanguageMethods::end_definition(OUTPUT_STREAM, programming_language *pl,
|
||||||
@ Then we have some "predeclarations"; for example, for C-like languages we
|
@ Then we have some "predeclarations"; for example, for C-like languages we
|
||||||
automatically predeclare all functions, obviating the need for header files.
|
automatically predeclare all functions, obviating the need for header files.
|
||||||
|
|
||||||
@e ADDITIONAL_PREDECLARATIONS_TAN_MTID
|
<<*>>=
|
||||||
|
enum ADDITIONAL_PREDECLARATIONS_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(ADDITIONAL_PREDECLARATIONS_TAN_MTID, programming_language *pl, text_stream *OUT, web *W)
|
INT_METHOD_TYPE(ADDITIONAL_PREDECLARATIONS_TAN_MTID, programming_language *pl, text_stream *OUT, web *W)
|
||||||
void LanguageMethods::additional_predeclarations(OUTPUT_STREAM, programming_language *pl, web *W) {
|
void LanguageMethods::additional_predeclarations(OUTPUT_STREAM, programming_language *pl, web *W) {
|
||||||
VOID_METHOD_CALL(pl, ADDITIONAL_PREDECLARATIONS_TAN_MTID, OUT, W);
|
VOID_METHOD_CALL(pl, ADDITIONAL_PREDECLARATIONS_TAN_MTID, OUT, W);
|
||||||
|
@ -184,9 +194,10 @@ the more routine matter, tangling ordinary paragraphs into code.
|
||||||
|
|
||||||
Languages have the ability to suppress paragraph macro expansion:
|
Languages have the ability to suppress paragraph macro expansion:
|
||||||
|
|
||||||
@e SUPPRESS_EXPANSION_TAN_MTID
|
<<*>>=
|
||||||
|
enum SUPPRESS_EXPANSION_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(SUPPRESS_EXPANSION_TAN_MTID, programming_language *pl, text_stream *material)
|
INT_METHOD_TYPE(SUPPRESS_EXPANSION_TAN_MTID, programming_language *pl, text_stream *material)
|
||||||
int LanguageMethods::allow_expansion(programming_language *pl, text_stream *material) {
|
int LanguageMethods::allow_expansion(programming_language *pl, text_stream *material) {
|
||||||
int rv = FALSE;
|
int rv = FALSE;
|
||||||
|
@ -195,12 +206,13 @@ int LanguageMethods::allow_expansion(programming_language *pl, text_stream *mate
|
||||||
}
|
}
|
||||||
|
|
||||||
@ Inweb supports very few "tangle commands", that is, instructions written
|
@ Inweb supports very few "tangle commands", that is, instructions written
|
||||||
inside double squares |[[Thus]]|. These can be handled by attaching methods
|
inside double squares [[[[Thus]]]]. These can be handled by attaching methods
|
||||||
as follows, which return |TRUE| if they recognised and acted on the command.
|
as follows, which return [[TRUE]] if they recognised and acted on the command.
|
||||||
|
|
||||||
@e TANGLE_COMMAND_TAN_MTID
|
<<*>>=
|
||||||
|
enum TANGLE_COMMAND_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(TANGLE_COMMAND_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *data)
|
INT_METHOD_TYPE(TANGLE_COMMAND_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *data)
|
||||||
|
|
||||||
int LanguageMethods::special_tangle_command(OUTPUT_STREAM, programming_language *pl, text_stream *data) {
|
int LanguageMethods::special_tangle_command(OUTPUT_STREAM, programming_language *pl, text_stream *data) {
|
||||||
|
@ -210,14 +222,15 @@ int LanguageMethods::special_tangle_command(OUTPUT_STREAM, programming_language
|
||||||
}
|
}
|
||||||
|
|
||||||
@ The following methods make it possible for languages to tangle unorthodox
|
@ The following methods make it possible for languages to tangle unorthodox
|
||||||
lines into code. Ordinarily, only |CODE_BODY_LCAT| lines are tangled, but
|
lines into code. Ordinarily, only [[CODE_BODY_LCAT]] lines are tangled, but
|
||||||
we can intervene to say that we want to tangle a different line; and if we
|
we can intervene to say that we want to tangle a different line; and if we
|
||||||
do so, we should then act on that basis.
|
do so, we should then act on that basis.
|
||||||
|
|
||||||
@e WILL_TANGLE_EXTRA_LINE_TAN_MTID
|
<<*>>=
|
||||||
@e TANGLE_EXTRA_LINE_TAN_MTID
|
enum WILL_TANGLE_EXTRA_LINE_TAN_MTID
|
||||||
|
enum TANGLE_EXTRA_LINE_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(WILL_TANGLE_EXTRA_LINE_TAN_MTID, programming_language *pl, source_line *L)
|
INT_METHOD_TYPE(WILL_TANGLE_EXTRA_LINE_TAN_MTID, programming_language *pl, source_line *L)
|
||||||
VOID_METHOD_TYPE(TANGLE_EXTRA_LINE_TAN_MTID, programming_language *pl, text_stream *OUT, source_line *L)
|
VOID_METHOD_TYPE(TANGLE_EXTRA_LINE_TAN_MTID, programming_language *pl, text_stream *OUT, source_line *L)
|
||||||
int LanguageMethods::will_insert_in_tangle(programming_language *pl, source_line *L) {
|
int LanguageMethods::will_insert_in_tangle(programming_language *pl, source_line *L) {
|
||||||
|
@ -231,12 +244,13 @@ void LanguageMethods::insert_in_tangle(OUTPUT_STREAM, programming_language *pl,
|
||||||
|
|
||||||
@ In order for C compilers to report C syntax errors on the correct line,
|
@ In order for C compilers to report C syntax errors on the correct line,
|
||||||
despite rearranging by automatic tools, C conventionally recognises the
|
despite rearranging by automatic tools, C conventionally recognises the
|
||||||
preprocessor directive |#line| to tell it that a contiguous extract follows
|
preprocessor directive [[#line]] to tell it that a contiguous extract follows
|
||||||
from the given file; we generate this automatically.
|
from the given file; we generate this automatically.
|
||||||
|
|
||||||
@e INSERT_LINE_MARKER_TAN_MTID
|
<<*>>=
|
||||||
|
enum INSERT_LINE_MARKER_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(INSERT_LINE_MARKER_TAN_MTID, programming_language *pl, text_stream *OUT, source_line *L)
|
VOID_METHOD_TYPE(INSERT_LINE_MARKER_TAN_MTID, programming_language *pl, text_stream *OUT, source_line *L)
|
||||||
void LanguageMethods::insert_line_marker(OUTPUT_STREAM, programming_language *pl, source_line *L) {
|
void LanguageMethods::insert_line_marker(OUTPUT_STREAM, programming_language *pl, source_line *L) {
|
||||||
VOID_METHOD_CALL(pl, INSERT_LINE_MARKER_TAN_MTID, OUT, L);
|
VOID_METHOD_CALL(pl, INSERT_LINE_MARKER_TAN_MTID, OUT, L);
|
||||||
|
@ -244,12 +258,13 @@ void LanguageMethods::insert_line_marker(OUTPUT_STREAM, programming_language *pl
|
||||||
|
|
||||||
@ The following hooks are provided so that we can top and/or tail the expansion
|
@ The following hooks are provided so that we can top and/or tail the expansion
|
||||||
of paragraph macros in the code. For example, C-like languages, use this to
|
of paragraph macros in the code. For example, C-like languages, use this to
|
||||||
splice |{| and |}| around the expanded matter.
|
splice [[{]] and [[}]] around the expanded matter.
|
||||||
|
|
||||||
@e BEFORE_MACRO_EXPANSION_TAN_MTID
|
<<*>>=
|
||||||
@e AFTER_MACRO_EXPANSION_TAN_MTID
|
enum BEFORE_MACRO_EXPANSION_TAN_MTID
|
||||||
|
enum AFTER_MACRO_EXPANSION_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(BEFORE_MACRO_EXPANSION_TAN_MTID, programming_language *pl, text_stream *OUT, para_macro *pmac)
|
VOID_METHOD_TYPE(BEFORE_MACRO_EXPANSION_TAN_MTID, programming_language *pl, text_stream *OUT, para_macro *pmac)
|
||||||
VOID_METHOD_TYPE(AFTER_MACRO_EXPANSION_TAN_MTID, programming_language *pl, text_stream *OUT, para_macro *pmac)
|
VOID_METHOD_TYPE(AFTER_MACRO_EXPANSION_TAN_MTID, programming_language *pl, text_stream *OUT, para_macro *pmac)
|
||||||
void LanguageMethods::before_macro_expansion(OUTPUT_STREAM, programming_language *pl, para_macro *pmac) {
|
void LanguageMethods::before_macro_expansion(OUTPUT_STREAM, programming_language *pl, para_macro *pmac) {
|
||||||
|
@ -261,12 +276,13 @@ void LanguageMethods::after_macro_expansion(OUTPUT_STREAM, programming_language
|
||||||
|
|
||||||
@ It's a sad necessity, but sometimes we have to unconditionally tangle code
|
@ It's a sad necessity, but sometimes we have to unconditionally tangle code
|
||||||
for a preprocessor to conditionally read: that is, to tangle code which contains
|
for a preprocessor to conditionally read: that is, to tangle code which contains
|
||||||
|#ifdef| or similar preprocessor directive.
|
[[#ifdef]] or similar preprocessor directive.
|
||||||
|
|
||||||
@e OPEN_IFDEF_TAN_MTID
|
<<*>>=
|
||||||
@e CLOSE_IFDEF_TAN_MTID
|
enum OPEN_IFDEF_TAN_MTID
|
||||||
|
enum CLOSE_IFDEF_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(OPEN_IFDEF_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *symbol, int sense)
|
VOID_METHOD_TYPE(OPEN_IFDEF_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *symbol, int sense)
|
||||||
VOID_METHOD_TYPE(CLOSE_IFDEF_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *symbol, int sense)
|
VOID_METHOD_TYPE(CLOSE_IFDEF_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *symbol, int sense)
|
||||||
void LanguageMethods::open_ifdef(OUTPUT_STREAM, programming_language *pl, text_stream *symbol, int sense) {
|
void LanguageMethods::open_ifdef(OUTPUT_STREAM, programming_language *pl, text_stream *symbol, int sense) {
|
||||||
|
@ -278,9 +294,10 @@ void LanguageMethods::close_ifdef(OUTPUT_STREAM, programming_language *pl, text_
|
||||||
|
|
||||||
@ Now a routine to tangle a comment. Languages without comment should write nothing.
|
@ Now a routine to tangle a comment. Languages without comment should write nothing.
|
||||||
|
|
||||||
@e COMMENT_TAN_MTID
|
<<*>>=
|
||||||
|
enum COMMENT_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(COMMENT_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *comm)
|
VOID_METHOD_TYPE(COMMENT_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *comm)
|
||||||
void LanguageMethods::comment(OUTPUT_STREAM, programming_language *pl, text_stream *comm) {
|
void LanguageMethods::comment(OUTPUT_STREAM, programming_language *pl, text_stream *comm) {
|
||||||
VOID_METHOD_CALL(pl, COMMENT_TAN_MTID, OUT, comm);
|
VOID_METHOD_CALL(pl, COMMENT_TAN_MTID, OUT, comm);
|
||||||
|
@ -288,11 +305,12 @@ void LanguageMethods::comment(OUTPUT_STREAM, programming_language *pl, text_stre
|
||||||
|
|
||||||
@ The inner code tangler now acts on all code known not to contain CWEB
|
@ The inner code tangler now acts on all code known not to contain CWEB
|
||||||
macros or double-square substitutions. In almost every language this simply
|
macros or double-square substitutions. In almost every language this simply
|
||||||
passes the code straight through, printing |original| to |OUT|.
|
passes the code straight through, printing [[original]] to [[OUT]].
|
||||||
|
|
||||||
@e TANGLE_LINE_UNUSUALLY_TAN_MTID
|
<<*>>=
|
||||||
|
enum TANGLE_LINE_UNUSUALLY_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(TANGLE_LINE_UNUSUALLY_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *original)
|
INT_METHOD_TYPE(TANGLE_LINE_UNUSUALLY_TAN_MTID, programming_language *pl, text_stream *OUT, text_stream *original)
|
||||||
void LanguageMethods::tangle_line(OUTPUT_STREAM, programming_language *pl, text_stream *original) {
|
void LanguageMethods::tangle_line(OUTPUT_STREAM, programming_language *pl, text_stream *original) {
|
||||||
int rv = FALSE;
|
int rv = FALSE;
|
||||||
|
@ -302,9 +320,10 @@ void LanguageMethods::tangle_line(OUTPUT_STREAM, programming_language *pl, text_
|
||||||
|
|
||||||
@ We finally reach the bottom of the tangled file, a footer called the "gnabehs":
|
@ We finally reach the bottom of the tangled file, a footer called the "gnabehs":
|
||||||
|
|
||||||
@e GNABEHS_TAN_MTID
|
<<*>>=
|
||||||
|
enum GNABEHS_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(GNABEHS_TAN_MTID, programming_language *pl, text_stream *OUT, web *W)
|
VOID_METHOD_TYPE(GNABEHS_TAN_MTID, programming_language *pl, text_stream *OUT, web *W)
|
||||||
void LanguageMethods::gnabehs(OUTPUT_STREAM, programming_language *pl, web *W) {
|
void LanguageMethods::gnabehs(OUTPUT_STREAM, programming_language *pl, web *W) {
|
||||||
VOID_METHOD_CALL(pl, GNABEHS_TAN_MTID, OUT, W);
|
VOID_METHOD_CALL(pl, GNABEHS_TAN_MTID, OUT, W);
|
||||||
|
@ -314,21 +333,23 @@ void LanguageMethods::gnabehs(OUTPUT_STREAM, programming_language *pl, web *W) {
|
||||||
sidekick files alongside the main tangle file. This method exists to give
|
sidekick files alongside the main tangle file. This method exists to give
|
||||||
them the opportunity.
|
them the opportunity.
|
||||||
|
|
||||||
@e ADDITIONAL_TANGLING_TAN_MTID
|
<<*>>=
|
||||||
|
enum ADDITIONAL_TANGLING_TAN_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(ADDITIONAL_TANGLING_TAN_MTID, programming_language *pl, web *W, tangle_target *target)
|
VOID_METHOD_TYPE(ADDITIONAL_TANGLING_TAN_MTID, programming_language *pl, web *W, tangle_target *target)
|
||||||
void LanguageMethods::additional_tangling(programming_language *pl, web *W, tangle_target *target) {
|
void LanguageMethods::additional_tangling(programming_language *pl, web *W, tangle_target *target) {
|
||||||
VOID_METHOD_CALL(pl, ADDITIONAL_TANGLING_TAN_MTID, W, target);
|
VOID_METHOD_CALL(pl, ADDITIONAL_TANGLING_TAN_MTID, W, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Weaving methods.
|
@ \section{Weaving methods.}
|
||||||
This metnod shouldn't do any actual weaving: it should simply initialise
|
This metnod shouldn't do any actual weaving: it should simply initialise
|
||||||
anything that the language in question might need later.
|
anything that the language in question might need later.
|
||||||
|
|
||||||
@e BEGIN_WEAVE_WEA_MTID
|
<<*>>=
|
||||||
|
enum BEGIN_WEAVE_WEA_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(BEGIN_WEAVE_WEA_MTID, programming_language *pl, section *S, weave_order *wv)
|
VOID_METHOD_TYPE(BEGIN_WEAVE_WEA_MTID, programming_language *pl, section *S, weave_order *wv)
|
||||||
void LanguageMethods::begin_weave(section *S, weave_order *wv) {
|
void LanguageMethods::begin_weave(section *S, weave_order *wv) {
|
||||||
VOID_METHOD_CALL(S->sect_language, BEGIN_WEAVE_WEA_MTID, S, wv);
|
VOID_METHOD_CALL(S->sect_language, BEGIN_WEAVE_WEA_MTID, S, wv);
|
||||||
|
@ -336,9 +357,10 @@ void LanguageMethods::begin_weave(section *S, weave_order *wv) {
|
||||||
|
|
||||||
@ This method allows languages to tell the weaver to ignore certain lines.
|
@ This method allows languages to tell the weaver to ignore certain lines.
|
||||||
|
|
||||||
@e SKIP_IN_WEAVING_WEA_MTID
|
<<*>>=
|
||||||
|
enum SKIP_IN_WEAVING_WEA_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(SKIP_IN_WEAVING_WEA_MTID, programming_language *pl, weave_order *wv, source_line *L)
|
INT_METHOD_TYPE(SKIP_IN_WEAVING_WEA_MTID, programming_language *pl, weave_order *wv, source_line *L)
|
||||||
int LanguageMethods::skip_in_weaving(programming_language *pl, weave_order *wv, source_line *L) {
|
int LanguageMethods::skip_in_weaving(programming_language *pl, weave_order *wv, source_line *L) {
|
||||||
int rv = FALSE;
|
int rv = FALSE;
|
||||||
|
@ -351,9 +373,10 @@ a comment, inside qupted text, and so on); the following method is provided
|
||||||
to reset that state, if so. Inweb runs it once per paragraph for safety's
|
to reset that state, if so. Inweb runs it once per paragraph for safety's
|
||||||
sake, which minimises the knock-on effect of any colouring mistakes.
|
sake, which minimises the knock-on effect of any colouring mistakes.
|
||||||
|
|
||||||
@e RESET_SYNTAX_COLOURING_WEA_MTID
|
<<*>>=
|
||||||
|
enum RESET_SYNTAX_COLOURING_WEA_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(RESET_SYNTAX_COLOURING_WEA_MTID, programming_language *pl)
|
VOID_METHOD_TYPE(RESET_SYNTAX_COLOURING_WEA_MTID, programming_language *pl)
|
||||||
void LanguageMethods::reset_syntax_colouring(programming_language *pl) {
|
void LanguageMethods::reset_syntax_colouring(programming_language *pl) {
|
||||||
VOID_METHOD_CALL_WITHOUT_ARGUMENTS(pl, RESET_SYNTAX_COLOURING_WEA_MTID);
|
VOID_METHOD_CALL_WITHOUT_ARGUMENTS(pl, RESET_SYNTAX_COLOURING_WEA_MTID);
|
||||||
|
@ -361,9 +384,10 @@ void LanguageMethods::reset_syntax_colouring(programming_language *pl) {
|
||||||
|
|
||||||
@ And this is where colouring is done.
|
@ And this is where colouring is done.
|
||||||
|
|
||||||
@e SYNTAX_COLOUR_WEA_MTID
|
<<*>>=
|
||||||
|
enum SYNTAX_COLOUR_WEA_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int colouring_state = PLAIN_COLOUR;
|
int colouring_state = PLAIN_COLOUR;
|
||||||
|
|
||||||
INT_METHOD_TYPE(SYNTAX_COLOUR_WEA_MTID, programming_language *pl,
|
INT_METHOD_TYPE(SYNTAX_COLOUR_WEA_MTID, programming_language *pl,
|
||||||
|
@ -386,13 +410,14 @@ int LanguageMethods::syntax_colour(programming_language *pl,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ This method is called for each code line to be woven. If it returns |FALSE|, the
|
@ This method is called for each code line to be woven. If it returns [[FALSE]], the
|
||||||
weaver carries on in the normal way. If not, it does nothing, assuming that the
|
weaver carries on in the normal way. If not, it does nothing, assuming that the
|
||||||
method has already woven something more attractive.
|
method has already woven something more attractive.
|
||||||
|
|
||||||
@e WEAVE_CODE_LINE_WEA_MTID
|
<<*>>=
|
||||||
|
enum WEAVE_CODE_LINE_WEA_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(WEAVE_CODE_LINE_WEA_MTID, programming_language *pl, text_stream *OUT, weave_order *wv, web *W,
|
INT_METHOD_TYPE(WEAVE_CODE_LINE_WEA_MTID, programming_language *pl, text_stream *OUT, weave_order *wv, web *W,
|
||||||
chapter *C, section *S, source_line *L, text_stream *matter, text_stream *concluding_comment)
|
chapter *C, section *S, source_line *L, text_stream *matter, text_stream *concluding_comment)
|
||||||
int LanguageMethods::weave_code_line(OUTPUT_STREAM, programming_language *pl, weave_order *wv,
|
int LanguageMethods::weave_code_line(OUTPUT_STREAM, programming_language *pl, weave_order *wv,
|
||||||
|
@ -402,11 +427,12 @@ int LanguageMethods::weave_code_line(OUTPUT_STREAM, programming_language *pl, we
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ When Inweb creates a new |^"Theme"|, it lets everybody know about that.
|
@ When Inweb creates a new [[^"Theme"]], it lets everybody know about that.
|
||||||
|
|
||||||
@e NOTIFY_NEW_TAG_WEA_MTID
|
<<*>>=
|
||||||
|
enum NOTIFY_NEW_TAG_WEA_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(NOTIFY_NEW_TAG_WEA_MTID, programming_language *pl, theme_tag *tag)
|
VOID_METHOD_TYPE(NOTIFY_NEW_TAG_WEA_MTID, programming_language *pl, theme_tag *tag)
|
||||||
void LanguageMethods::new_tag_declared(theme_tag *tag) {
|
void LanguageMethods::new_tag_declared(theme_tag *tag) {
|
||||||
programming_language *pl;
|
programming_language *pl;
|
||||||
|
@ -414,7 +440,7 @@ void LanguageMethods::new_tag_declared(theme_tag *tag) {
|
||||||
VOID_METHOD_CALL(pl, NOTIFY_NEW_TAG_WEA_MTID, tag);
|
VOID_METHOD_CALL(pl, NOTIFY_NEW_TAG_WEA_MTID, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Analysis methods.
|
@ \section{Analysis methods.}
|
||||||
These are really a little miscellaneous, but they all have to do with looking
|
These are really a little miscellaneous, but they all have to do with looking
|
||||||
at the code in a web and working out what's going on, rather than producing
|
at the code in a web and working out what's going on, rather than producing
|
||||||
any weave or tangle output.
|
any weave or tangle output.
|
||||||
|
@ -425,10 +451,11 @@ are called first and last in the process, respectively. (What happens in
|
||||||
between is essentially that Inweb looks for identifiers, for later syntax
|
between is essentially that Inweb looks for identifiers, for later syntax
|
||||||
colouring purposes.)
|
colouring purposes.)
|
||||||
|
|
||||||
@e ANALYSIS_ANA_MTID
|
<<*>>=
|
||||||
@e POST_ANALYSIS_ANA_MTID
|
enum ANALYSIS_ANA_MTID
|
||||||
|
enum POST_ANALYSIS_ANA_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
VOID_METHOD_TYPE(ANALYSIS_ANA_MTID, programming_language *pl, web *W)
|
VOID_METHOD_TYPE(ANALYSIS_ANA_MTID, programming_language *pl, web *W)
|
||||||
VOID_METHOD_TYPE(POST_ANALYSIS_ANA_MTID, programming_language *pl, web *W)
|
VOID_METHOD_TYPE(POST_ANALYSIS_ANA_MTID, programming_language *pl, web *W)
|
||||||
void LanguageMethods::early_preweave_analysis(programming_language *pl, web *W) {
|
void LanguageMethods::early_preweave_analysis(programming_language *pl, web *W) {
|
||||||
|
@ -441,9 +468,10 @@ void LanguageMethods::late_preweave_analysis(programming_language *pl, web *W) {
|
||||||
@ And finally: in InC only, a few structure element names are given very slightly
|
@ And finally: in InC only, a few structure element names are given very slightly
|
||||||
special treatment, and this method decides which.
|
special treatment, and this method decides which.
|
||||||
|
|
||||||
@e SHARE_ELEMENT_ANA_MTID
|
<<*>>=
|
||||||
|
enum SHARE_ELEMENT_ANA_MTID
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
INT_METHOD_TYPE(SHARE_ELEMENT_ANA_MTID, programming_language *pl, text_stream *element_name)
|
INT_METHOD_TYPE(SHARE_ELEMENT_ANA_MTID, programming_language *pl, text_stream *element_name)
|
||||||
int LanguageMethods::share_element(programming_language *pl, text_stream *element_name) {
|
int LanguageMethods::share_element(programming_language *pl, text_stream *element_name) {
|
||||||
int rv = FALSE;
|
int rv = FALSE;
|
||||||
|
@ -451,9 +479,9 @@ int LanguageMethods::share_element(programming_language *pl, text_stream *elemen
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h What we support.
|
@ \section{What we support.}
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int LanguageMethods::supports_definitions(programming_language *pl) {
|
int LanguageMethods::supports_definitions(programming_language *pl) {
|
||||||
if (Str::len(pl->start_definition) > 0) return TRUE;
|
if (Str::len(pl->start_definition) > 0) return TRUE;
|
||||||
if (Str::len(pl->prolong_definition) > 0) return TRUE;
|
if (Str::len(pl->prolong_definition) > 0) return TRUE;
|
|
@ -3,34 +3,34 @@
|
||||||
Defining the programming languages supported by Inweb, loading in their
|
Defining the programming languages supported by Inweb, loading in their
|
||||||
definitions from files.
|
definitions from files.
|
||||||
|
|
||||||
@h Languages.
|
@ \section{Languages.}
|
||||||
Programming languages are identified by name: for example, |C++| or |Perl|.
|
Programming languages are identified by name: for example, [[C++]] or [[Perl]].
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
programming_language *Languages::find_by_name(text_stream *lname, web *W,
|
programming_language *Languages::find_by_name(text_stream *lname, web *W,
|
||||||
int error_if_not_found) {
|
int error_if_not_found) {
|
||||||
programming_language *pl;
|
programming_language *pl;
|
||||||
@<If this is the name of a language already known, return that@>;
|
<<If this is the name of a language already known, return that>>;
|
||||||
@<Read the language definition file with this name@>;
|
<<Read the language definition file with this name>>;
|
||||||
if (Str::ne(pl->language_name, lname))
|
if (Str::ne(pl->language_name, lname))
|
||||||
Errors::fatal_with_text(
|
Errors::fatal_with_text(
|
||||||
"definition of programming language '%S' is for something else", lname);
|
"definition of programming language '%S' is for something else", lname);
|
||||||
return pl;
|
return pl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<If this is the name of a language already known, return that@> =
|
<<If this is the name of a language already known, return that>>=
|
||||||
LOOP_OVER(pl, programming_language)
|
LOOP_OVER(pl, programming_language)
|
||||||
if (Str::eq(lname, pl->language_name))
|
if (Str::eq(lname, pl->language_name))
|
||||||
return pl;
|
return pl;
|
||||||
|
|
||||||
@<Read the language definition file with this name@> =
|
<<Read the language definition file with this name>>=
|
||||||
filename *F = NULL;
|
filename *F = NULL;
|
||||||
if (W) {
|
if (W) {
|
||||||
pathname *P = Pathnames::down(W->md->path_to_web, I"Dialects");
|
pathname *P = Pathnames::down(W->md->path_to_web, I"Dialects");
|
||||||
@<Try P@>;
|
<<Try P>>;
|
||||||
}
|
}
|
||||||
pathname *P = Languages::default_directory();
|
pathname *P = Languages::default_directory();
|
||||||
@<Try P@>;
|
<<Try P>>;
|
||||||
if (F == NULL) {
|
if (F == NULL) {
|
||||||
if (error_if_not_found)
|
if (error_if_not_found)
|
||||||
Errors::fatal_with_text(
|
Errors::fatal_with_text(
|
||||||
|
@ -39,7 +39,7 @@ programming_language *Languages::find_by_name(text_stream *lname, web *W,
|
||||||
}
|
}
|
||||||
pl = Languages::read_definition(F);
|
pl = Languages::read_definition(F);
|
||||||
|
|
||||||
@<Try P@> =
|
<<Try P>>=
|
||||||
if (F == NULL) {
|
if (F == NULL) {
|
||||||
TEMPORARY_TEXT(leaf)
|
TEMPORARY_TEXT(leaf)
|
||||||
WRITE_TO(leaf, "%S.ildf", lname);
|
WRITE_TO(leaf, "%S.ildf", lname);
|
||||||
|
@ -50,7 +50,7 @@ programming_language *Languages::find_by_name(text_stream *lname, web *W,
|
||||||
|
|
||||||
@ I'm probably showing my age here.
|
@ I'm probably showing my age here.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
programming_language *Languages::default(web *W) {
|
programming_language *Languages::default(web *W) {
|
||||||
return Languages::find_by_name(I"C", W, TRUE);
|
return Languages::find_by_name(I"C", W, TRUE);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ void Languages::show(OUTPUT_STREAM) {
|
||||||
Memory::I7_free(sorted_table, ARRAY_SORTING_MREASON, N*((int) sizeof(programming_language *)));
|
Memory::I7_free(sorted_table, ARRAY_SORTING_MREASON, N*((int) sizeof(programming_language *)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
int Languages::compare_names(const void *ent1, const void *ent2) {
|
int Languages::compare_names(const void *ent1, const void *ent2) {
|
||||||
text_stream *tx1 = (*((const programming_language **) ent1))->language_name;
|
text_stream *tx1 = (*((const programming_language **) ent1))->language_name;
|
||||||
text_stream *tx2 = (*((const programming_language **) ent2))->language_name;
|
text_stream *tx2 = (*((const programming_language **) ent2))->language_name;
|
||||||
|
@ -80,7 +80,7 @@ int Languages::compare_names(const void *ent1, const void *ent2) {
|
||||||
|
|
||||||
@ We can read every language in a directory:
|
@ We can read every language in a directory:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Languages::read_definitions(pathname *P) {
|
void Languages::read_definitions(pathname *P) {
|
||||||
if (P == NULL) P = Languages::default_directory();
|
if (P == NULL) P = Languages::default_directory();
|
||||||
scan_directory *D = Directories::open(P);
|
scan_directory *D = Directories::open(P);
|
||||||
|
@ -102,7 +102,7 @@ pathname *Languages::default_directory(void) {
|
||||||
@ So, then, languages are defined by files which are read in, and parsed
|
@ So, then, languages are defined by files which are read in, and parsed
|
||||||
into the following structure (one per language):
|
into the following structure (one per language):
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct programming_language {
|
typedef struct programming_language {
|
||||||
text_stream *language_name; /* identifies it: see above */
|
text_stream *language_name; /* identifies it: see above */
|
||||||
|
|
||||||
|
@ -139,20 +139,20 @@ typedef struct programming_language {
|
||||||
int suppress_disclaimer;
|
int suppress_disclaimer;
|
||||||
int C_like; /* languages with this set have access to extra features */
|
int C_like; /* languages with this set have access to extra features */
|
||||||
|
|
||||||
struct linked_list *reserved_words; /* of |reserved_word| */
|
struct linked_list *reserved_words; /* of [[reserved_word]] */
|
||||||
struct hash_table built_in_keywords;
|
struct hash_table built_in_keywords;
|
||||||
struct colouring_language_block *program; /* algorithm for syntax colouring */
|
struct colouring_language_block *program; /* algorithm for syntax colouring */
|
||||||
struct method_set *methods;
|
struct method_set *methods;
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} programming_language;
|
} programming_language;
|
||||||
|
|
||||||
@ This is a simple one-pass compiler. The |language_reader_state| provides
|
@ This is a simple one-pass compiler. The [[language_reader_state]] provides
|
||||||
the only state preserved as we work through line by line, except of course
|
the only state preserved as we work through line by line, except of course
|
||||||
that we are also working on the programming language it is |defining|. The
|
that we are also working on the programming language it is [[defining]]. The
|
||||||
|current_block| is the braced block of colouring instructions we are
|
[[current_block]] is the braced block of colouring instructions we are
|
||||||
currently inside.
|
currently inside.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct language_reader_state {
|
typedef struct language_reader_state {
|
||||||
struct programming_language *defining;
|
struct programming_language *defining;
|
||||||
struct colouring_language_block *current_block;
|
struct colouring_language_block *current_block;
|
||||||
|
@ -160,17 +160,17 @@ typedef struct language_reader_state {
|
||||||
|
|
||||||
programming_language *Languages::read_definition(filename *F) {
|
programming_language *Languages::read_definition(filename *F) {
|
||||||
programming_language *pl = CREATE(programming_language);
|
programming_language *pl = CREATE(programming_language);
|
||||||
@<Initialise the language to a plain-text state@>;
|
<<Initialise the language to a plain-text state>>;
|
||||||
language_reader_state lrs;
|
language_reader_state lrs;
|
||||||
lrs.defining = pl;
|
lrs.defining = pl;
|
||||||
lrs.current_block = NULL;
|
lrs.current_block = NULL;
|
||||||
TextFiles::read(F, FALSE, "can't open programming language definition file",
|
TextFiles::read(F, FALSE, "can't open programming language definition file",
|
||||||
TRUE, Languages::read_definition_line, NULL, (void *) &lrs);
|
TRUE, Languages::read_definition_line, NULL, (void *) &lrs);
|
||||||
@<Add method calls to the language@>;
|
<<Add method calls to the language>>;
|
||||||
return pl;
|
return pl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Initialise the language to a plain-text state@> =
|
<<Initialise the language to a plain-text state>>=
|
||||||
pl->language_name = NULL;
|
pl->language_name = NULL;
|
||||||
pl->file_extension = NULL;
|
pl->file_extension = NULL;
|
||||||
pl->supports_namespaces = FALSE;
|
pl->supports_namespaces = FALSE;
|
||||||
|
@ -212,35 +212,35 @@ itself C-like has functionality for function and structure definitions;
|
||||||
the language whose name is InC gets even more, without having to ask.
|
the language whose name is InC gets even more, without having to ask.
|
||||||
|
|
||||||
Languages have effect through their method calls, which is how those
|
Languages have effect through their method calls, which is how those
|
||||||
extra features are provided. The call to |ACMESupport::add_fallbacks|
|
extra features are provided. The call to [[ACMESupport::add_fallbacks]]
|
||||||
adds generic method calls to give effect to the settings in the definition.
|
adds generic method calls to give effect to the settings in the definition.
|
||||||
|
|
||||||
@<Add method calls to the language@> =
|
<<Add method calls to the language>>=
|
||||||
if (pl->C_like) CLike::make_c_like(pl);
|
if (pl->C_like) CLike::make_c_like(pl);
|
||||||
if (Str::eq(pl->language_name, I"InC")) InCSupport::add_features(pl);
|
if (Str::eq(pl->language_name, I"InC")) InCSupport::add_features(pl);
|
||||||
ACMESupport::add_fallbacks(pl);
|
ACMESupport::add_fallbacks(pl);
|
||||||
|
|
||||||
@ So, then, the above reads the file and feeds it line by line to this:
|
@ So, then, the above reads the file and feeds it line by line to this:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Languages::read_definition_line(text_stream *line, text_file_position *tfp, void *v_state) {
|
void Languages::read_definition_line(text_stream *line, text_file_position *tfp, void *v_state) {
|
||||||
language_reader_state *state = (language_reader_state *) v_state;
|
language_reader_state *state = (language_reader_state *) v_state;
|
||||||
programming_language *pl = state->defining;
|
programming_language *pl = state->defining;
|
||||||
|
|
||||||
Str::trim_white_space(line); /* ignore trailing space */
|
Str::trim_white_space(line); /* ignore trailing space */
|
||||||
if (Str::len(line) == 0) return; /* ignore blank lines */
|
if (Str::len(line) == 0) return; /* ignore blank lines */
|
||||||
if (Str::get_first_char(line) == '#') return; /* lines opening with |#| are comments */
|
if (Str::get_first_char(line) == '#') return; /* lines opening with [[#]] are comments */
|
||||||
|
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (state->current_block) @<Syntax inside a colouring program@>
|
if (state->current_block) <<Syntax inside a colouring program>>
|
||||||
else @<Syntax outside a colouring program@>;
|
else <<Syntax outside a colouring program>>;
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ Outside a colouring program, you can do three things: start a program,
|
@ Outside a colouring program, you can do three things: start a program,
|
||||||
declare a reserved keyword, or set a key to a value.
|
declare a reserved keyword, or set a key to a value.
|
||||||
|
|
||||||
@<Syntax outside a colouring program@> =
|
<<Syntax outside a colouring program>>=
|
||||||
if (Regexp::match(&mr, line, L"colouring {")) {
|
if (Regexp::match(&mr, line, L"colouring {")) {
|
||||||
if (pl->program) Errors::in_text_file("duplicate colouring program", tfp);
|
if (pl->program) Errors::in_text_file("duplicate colouring program", tfp);
|
||||||
pl->program = Languages::new_block(NULL, WHOLE_LINE_CRULE_RUN);
|
pl->program = Languages::new_block(NULL, WHOLE_LINE_CRULE_RUN);
|
||||||
|
@ -323,7 +323,7 @@ declare a reserved keyword, or set a key to a value.
|
||||||
the entire program), open a new block to apply to each character or to
|
the entire program), open a new block to apply to each character or to
|
||||||
runs of a given colour, or give an if-X-then-Y rule:
|
runs of a given colour, or give an if-X-then-Y rule:
|
||||||
|
|
||||||
@<Syntax inside a colouring program@> =
|
<<Syntax inside a colouring program>>=
|
||||||
if (Str::eq(line, I"}")) {
|
if (Str::eq(line, I"}")) {
|
||||||
state->current_block = state->current_block->parent;
|
state->current_block = state->current_block->parent;
|
||||||
} else if (Regexp::match(&mr, line, L"characters {")) {
|
} else if (Regexp::match(&mr, line, L"characters {")) {
|
||||||
|
@ -379,32 +379,33 @@ runs of a given colour, or give an if-X-then-Y rule:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Blocks.
|
@ \section{Blocks.}
|
||||||
These are code blocks of colouring instructions. A block whose |parent| is |NULL|
|
These are code blocks of colouring instructions. A block whose [[parent]] is [[NULL]]
|
||||||
represents a complete program.
|
represents a complete program.
|
||||||
|
|
||||||
@d WHOLE_LINE_CRULE_RUN -1 /* This block applies to the whole snippet being coloured */
|
<<*>>=
|
||||||
@d CHARACTERS_CRULE_RUN -2 /* This block applies to each character in turn */
|
#define WHOLE_LINE_CRULE_RUN -1 /* This block applies to the whole snippet being coloured */
|
||||||
@d CHARACTERS_IN_CRULE_RUN -3 /* This block applies to each character from a set in turn */
|
#define CHARACTERS_CRULE_RUN -2 /* This block applies to each character in turn */
|
||||||
@d INSTANCES_CRULE_RUN -4 /* This block applies to each instance in turn */
|
#define CHARACTERS_IN_CRULE_RUN -3 /* This block applies to each character from a set in turn */
|
||||||
@d MATCHES_CRULE_RUN -5 /* This block applies to each match against a regexp in turn */
|
#define INSTANCES_CRULE_RUN -4 /* This block applies to each instance in turn */
|
||||||
@d BRACKETS_CRULE_RUN -6 /* This block applies to bracketed subexpressions in a regexp */
|
#define MATCHES_CRULE_RUN -5 /* This block applies to each match against a regexp in turn */
|
||||||
|
#define BRACKETS_CRULE_RUN -6 /* This block applies to bracketed subexpressions in a regexp */
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct colouring_language_block {
|
typedef struct colouring_language_block {
|
||||||
struct linked_list *rules; /* of |colouring_rule| */
|
struct linked_list *rules; /* of [[colouring_rule]] */
|
||||||
struct colouring_language_block *parent; /* or |NULL| for the topmost one */
|
struct colouring_language_block *parent; /* or [[NULL]] for the topmost one */
|
||||||
int run; /* one of the |*_CRULE_RUN| values, or else a colour */
|
int run; /* one of the [[*_CRULE_RUN]] values, or else a colour */
|
||||||
struct text_stream *run_instance; /* used only for |INSTANCES_CRULE_RUN| */
|
struct text_stream *run_instance; /* used only for [[INSTANCES_CRULE_RUN]] */
|
||||||
struct text_stream *char_set; /* used only for |CHARACTERS_IN_CRULE_RUN| */
|
struct text_stream *char_set; /* used only for [[CHARACTERS_IN_CRULE_RUN]] */
|
||||||
wchar_t match_regexp_text[MAX_ILDF_REGEXP_LENGTH]; /* used for |MATCHES_CRULE_RUN|, |BRACKETS_CRULE_RUN| */
|
wchar_t match_regexp_text[MAX_ILDF_REGEXP_LENGTH]; /* used for [[MATCHES_CRULE_RUN|, |BRACKETS_CRULE_RUN]] */
|
||||||
|
|
||||||
/* workspace during painting */
|
/* workspace during painting */
|
||||||
struct match_results mr; /* of a regular expression */
|
struct match_results mr; /* of a regular expression */
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} colouring_language_block;
|
} colouring_language_block;
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
colouring_language_block *Languages::new_block(colouring_language_block *within, int r) {
|
colouring_language_block *Languages::new_block(colouring_language_block *within, int r) {
|
||||||
colouring_language_block *block = CREATE(colouring_language_block);
|
colouring_language_block *block = CREATE(colouring_language_block);
|
||||||
block->rules = NEW_LINKED_LIST(colouring_rule);
|
block->rules = NEW_LINKED_LIST(colouring_rule);
|
||||||
|
@ -417,37 +418,38 @@ colouring_language_block *Languages::new_block(colouring_language_block *within,
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Colouring Rules.
|
@ \section{Colouring Rules.}
|
||||||
Each individual rule has the form: if a premiss, then a conclusion. It will be
|
Each individual rule has the form: if a premiss, then a conclusion. It will be
|
||||||
applied to a snippet of text, and the premiss can test that, together with a
|
applied to a snippet of text, and the premiss can test that, together with a
|
||||||
little context before it (where available).
|
little context before it (where available).
|
||||||
|
|
||||||
Note that rules can be unconditional, in that the premiss always passes.
|
Note that rules can be unconditional, in that the premiss always passes.
|
||||||
|
|
||||||
@d NOT_A_RULE_PREFIX 1 /* this isn't a prefix rule */
|
<<*>>=
|
||||||
@d UNSPACED_RULE_PREFIX 2 /* for |prefix P| */
|
#define NOT_A_RULE_PREFIX 1 /* this isn't a prefix rule */
|
||||||
@d SPACED_RULE_PREFIX 3 /* for |spaced prefix P| */
|
#define UNSPACED_RULE_PREFIX 2 /* for [[prefix P]] */
|
||||||
@d OPTIONALLY_SPACED_RULE_PREFIX 4 /* for |optionally spaced prefix P| */
|
#define SPACED_RULE_PREFIX 3 /* for [[spaced prefix P]] */
|
||||||
@d UNSPACED_RULE_SUFFIX 5 /* for |suffix P| */
|
#define OPTIONALLY_SPACED_RULE_PREFIX 4 /* for [[optionally spaced prefix P]] */
|
||||||
@d SPACED_RULE_SUFFIX 6 /* for |spaced suffix P| */
|
#define UNSPACED_RULE_SUFFIX 5 /* for [[suffix P]] */
|
||||||
@d OPTIONALLY_SPACED_RULE_SUFFIX 7 /* for |optionally spaced suffix P| */
|
#define SPACED_RULE_SUFFIX 6 /* for [[spaced suffix P]] */
|
||||||
|
#define OPTIONALLY_SPACED_RULE_SUFFIX 7 /* for [[optionally spaced suffix P]] */
|
||||||
|
|
||||||
@d MAX_ILDF_REGEXP_LENGTH 64
|
#define MAX_ILDF_REGEXP_LENGTH 64
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct colouring_rule {
|
typedef struct colouring_rule {
|
||||||
/* the premiss: */
|
/* the premiss: */
|
||||||
int sense; /* |FALSE| to negate the condition */
|
int sense; /* [[FALSE]] to negate the condition */
|
||||||
wchar_t match_colour; /* for |coloured C|, or else |NOT_A_COLOUR| */
|
wchar_t match_colour; /* for [[coloured C|, or else |NOT_A_COLOUR]] */
|
||||||
wchar_t match_keyword_of_colour; /* for |keyword C|, or else |NOT_A_COLOUR| */
|
wchar_t match_keyword_of_colour; /* for [[keyword C|, or else |NOT_A_COLOUR]] */
|
||||||
struct text_stream *match_text; /* or length 0 to mean "anything" */
|
struct text_stream *match_text; /* or length 0 to mean "anything" */
|
||||||
int match_prefix; /* one of the |*_RULE_PREFIX| values above */
|
int match_prefix; /* one of the [[*_RULE_PREFIX]] values above */
|
||||||
wchar_t match_regexp_text[MAX_ILDF_REGEXP_LENGTH];
|
wchar_t match_regexp_text[MAX_ILDF_REGEXP_LENGTH];
|
||||||
int number; /* for |number N| rules; 0 for others */
|
int number; /* for [[number N]] rules; 0 for others */
|
||||||
int number_of; /* for |number N of M| rules; 0 for others */
|
int number_of; /* for [[number N of M]] rules; 0 for others */
|
||||||
|
|
||||||
/* the conclusion: */
|
/* the conclusion: */
|
||||||
struct colouring_language_block *execute_block; /* or |NULL|, in which case... */
|
struct colouring_language_block *execute_block; /* or [[NULL]], in which case... */
|
||||||
wchar_t set_to_colour; /* ...paint the snippet in this colour */
|
wchar_t set_to_colour; /* ...paint the snippet in this colour */
|
||||||
wchar_t set_prefix_to_colour; /* ...also paint this (same for suffix) */
|
wchar_t set_prefix_to_colour; /* ...also paint this (same for suffix) */
|
||||||
int debug; /* ...or print debugging text to console */
|
int debug; /* ...or print debugging text to console */
|
||||||
|
@ -458,7 +460,7 @@ typedef struct colouring_rule {
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} colouring_rule;
|
} colouring_rule;
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
colouring_rule *Languages::new_rule(colouring_language_block *within) {
|
colouring_rule *Languages::new_rule(colouring_language_block *within) {
|
||||||
if (within == NULL) internal_error("rule outside block");
|
if (within == NULL) internal_error("rule outside block");
|
||||||
colouring_rule *rule = CREATE(colouring_rule);
|
colouring_rule *rule = CREATE(colouring_rule);
|
||||||
|
@ -482,18 +484,18 @@ colouring_rule *Languages::new_rule(colouring_language_block *within) {
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
void Languages::parse_rule(language_reader_state *state, text_stream *premiss,
|
void Languages::parse_rule(language_reader_state *state, text_stream *premiss,
|
||||||
text_stream *action, text_file_position *tfp) {
|
text_stream *action, text_file_position *tfp) {
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
colouring_rule *rule = Languages::new_rule(state->current_block);
|
colouring_rule *rule = Languages::new_rule(state->current_block);
|
||||||
Str::trim_white_space(premiss); Str::trim_white_space(action);
|
Str::trim_white_space(premiss); Str::trim_white_space(action);
|
||||||
@<Parse the premiss@>;
|
<<Parse the premiss>>;
|
||||||
@<Parse the conclusion@>;
|
<<Parse the conclusion>>;
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Parse the premiss@> =
|
<<Parse the premiss>>=
|
||||||
while (Regexp::match(&mr, premiss, L"not (%c+)")) {
|
while (Regexp::match(&mr, premiss, L"not (%c+)")) {
|
||||||
rule->sense = (rule->sense)?FALSE:TRUE;
|
rule->sense = (rule->sense)?FALSE:TRUE;
|
||||||
Str::clear(premiss); Str::copy(premiss, mr.exp[0]);
|
Str::clear(premiss); Str::copy(premiss, mr.exp[0]);
|
||||||
|
@ -533,7 +535,7 @@ void Languages::parse_rule(language_reader_state *state, text_stream *premiss,
|
||||||
rule->match_text = Languages::text(premiss, tfp, FALSE);
|
rule->match_text = Languages::text(premiss, tfp, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Parse the conclusion@> =
|
<<Parse the conclusion>>=
|
||||||
if (Str::eq(action, I"{")) {
|
if (Str::eq(action, I"{")) {
|
||||||
rule->execute_block =
|
rule->execute_block =
|
||||||
Languages::new_block(state->current_block, WHOLE_LINE_CRULE_RUN);
|
Languages::new_block(state->current_block, WHOLE_LINE_CRULE_RUN);
|
||||||
|
@ -553,10 +555,10 @@ void Languages::parse_rule(language_reader_state *state, text_stream *premiss,
|
||||||
Errors::in_text_file("action after '=>' illegible", tfp);
|
Errors::in_text_file("action after '=>' illegible", tfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Reserved words.
|
@ \section{Reserved words.}
|
||||||
Note that these can come in any colour, though usually it's |!reserved|.
|
Note that these can come in any colour, though usually it's [[!reserved]].
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct reserved_word {
|
typedef struct reserved_word {
|
||||||
struct text_stream *word;
|
struct text_stream *word;
|
||||||
int colour;
|
int colour;
|
||||||
|
@ -578,28 +580,29 @@ reserved_word *Languages::reserved(programming_language *pl, text_stream *W, wch
|
||||||
return rw;
|
return rw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Expressions.
|
@ \section{Expressions.}
|
||||||
Language definition files have three types of data: colours, booleans, and
|
Language definition files have three types of data: colours, booleans, and
|
||||||
text. Colours first. Note that there are two pseudo-colours used above,
|
text. Colours first. Note that there are two pseudo-colours used above,
|
||||||
but which are not expressible in the syntax of this file.
|
but which are not expressible in the syntax of this file.
|
||||||
|
|
||||||
@d DEFINITION_COLOUR 'd'
|
<<*>>=
|
||||||
@d FUNCTION_COLOUR 'f'
|
#define DEFINITION_COLOUR 'd'
|
||||||
@d RESERVED_COLOUR 'r'
|
#define FUNCTION_COLOUR 'f'
|
||||||
@d ELEMENT_COLOUR 'e'
|
#define RESERVED_COLOUR 'r'
|
||||||
@d IDENTIFIER_COLOUR 'i'
|
#define ELEMENT_COLOUR 'e'
|
||||||
@d CHARACTER_COLOUR 'c'
|
#define IDENTIFIER_COLOUR 'i'
|
||||||
@d CONSTANT_COLOUR 'n'
|
#define CHARACTER_COLOUR 'c'
|
||||||
@d STRING_COLOUR 's'
|
#define CONSTANT_COLOUR 'n'
|
||||||
@d PLAIN_COLOUR 'p'
|
#define STRING_COLOUR 's'
|
||||||
@d EXTRACT_COLOUR 'x'
|
#define PLAIN_COLOUR 'p'
|
||||||
@d COMMENT_COLOUR '!'
|
#define EXTRACT_COLOUR 'x'
|
||||||
@d NEWLINE_COLOUR '\n'
|
#define COMMENT_COLOUR '!'
|
||||||
|
#define NEWLINE_COLOUR '\n'
|
||||||
|
|
||||||
@d NOT_A_COLOUR ' '
|
#define NOT_A_COLOUR ' '
|
||||||
@d UNQUOTED_COLOUR '_'
|
#define UNQUOTED_COLOUR '_'
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
wchar_t Languages::colour(text_stream *T, text_file_position *tfp) {
|
wchar_t Languages::colour(text_stream *T, text_file_position *tfp) {
|
||||||
if (Str::get_first_char(T) != '!') {
|
if (Str::get_first_char(T) != '!') {
|
||||||
Errors::in_text_file("colour names must begin with !", tfp);
|
Errors::in_text_file("colour names must begin with !", tfp);
|
||||||
|
@ -622,9 +625,9 @@ wchar_t Languages::colour(text_stream *T, text_file_position *tfp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ A boolean must be written as |true| or |false|.
|
@ A boolean must be written as [[true]] or [[false]].
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int Languages::boolean(text_stream *T, text_file_position *tfp) {
|
int Languages::boolean(text_stream *T, text_file_position *tfp) {
|
||||||
if (Str::eq(T, I"true")) return TRUE;
|
if (Str::eq(T, I"true")) return TRUE;
|
||||||
else if (Str::eq(T, I"false")) return FALSE;
|
else if (Str::eq(T, I"false")) return FALSE;
|
||||||
|
@ -634,11 +637,11 @@ int Languages::boolean(text_stream *T, text_file_position *tfp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ In text, |\n| represents a newline, |\s| a space and |\t| a tab. Spaces
|
@ In text, [[\n]] represents a newline, [[\s]] a space and [[\t]] a tab. Spaces
|
||||||
can be given in the ordinary way inside a text in any case. |\\| is a
|
can be given in the ordinary way inside a text in any case. [[\\]] is a
|
||||||
literal backslash.
|
literal backslash.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
text_stream *Languages::text(text_stream *T, text_file_position *tfp, int allow) {
|
text_stream *Languages::text(text_stream *T, text_file_position *tfp, int allow) {
|
||||||
text_stream *V = Str::new();
|
text_stream *V = Str::new();
|
||||||
if (Str::len(T) > 0) {
|
if (Str::len(T) > 0) {
|
||||||
|
@ -725,7 +728,7 @@ text_stream *Languages::text(text_stream *T, text_file_position *tfp, int allow)
|
||||||
|
|
||||||
@ And regular expressions.
|
@ And regular expressions.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Languages::regexp(wchar_t *write_to, text_stream *T, text_file_position *tfp) {
|
void Languages::regexp(wchar_t *write_to, text_stream *T, text_file_position *tfp) {
|
||||||
if (write_to == NULL) internal_error("no buffer");
|
if (write_to == NULL) internal_error("no buffer");
|
||||||
write_to[0] = 0;
|
write_to[0] = 0;
|
|
@ -3,38 +3,38 @@
|
||||||
A simple syntax-colouring engine.
|
A simple syntax-colouring engine.
|
||||||
|
|
||||||
@ This is a very simple syntax colouring algorithm. The work is done by the
|
@ This is a very simple syntax colouring algorithm. The work is done by the
|
||||||
function |Painter::syntax_colour|, which can in principle be applied to texts
|
function [[Painter::syntax_colour]], which can in principle be applied to texts
|
||||||
of any length. But it's usually convenient to run it on a long file one line
|
of any length. But it's usually convenient to run it on a long file one line
|
||||||
at a time, so that it is called repeatedly. The variable |colouring_state|
|
at a time, so that it is called repeatedly. The variable [[colouring_state]]
|
||||||
remembers where we were at the end of the previous line, so that we can pick
|
remembers where we were at the end of the previous line, so that we can pick
|
||||||
up again later at the start of the next.
|
up again later at the start of the next.
|
||||||
|
|
||||||
Because of that, we need to call the following before we begin a run of calls
|
Because of that, we need to call the following before we begin a run of calls
|
||||||
to |Painter::syntax_colour|:
|
to [[Painter::syntax_colour]]:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int painter_count = 1;
|
int painter_count = 1;
|
||||||
void Painter::reset_syntax_colouring(programming_language *pl) {
|
void Painter::reset_syntax_colouring(programming_language *pl) {
|
||||||
colouring_state = PLAIN_COLOUR;
|
colouring_state = PLAIN_COLOUR;
|
||||||
painter_count = 1;
|
painter_count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ As we begin, the text to colour is in |matter|, while |colouring| is an
|
@ As we begin, the text to colour is in [[matter]], while [[colouring]] is an
|
||||||
equal-length text where each character represents the colour of its
|
equal-length text where each character represents the colour of its
|
||||||
corresponding character in |matter|. For example, we might start as:
|
corresponding character in [[matter]]. For example, we might start as:
|
||||||
= (text as PainterOutput)
|
|
||||||
int x = 55;
|
int x = 55;
|
||||||
ppppppppppp
|
ppppppppppp
|
||||||
=
|
|
||||||
with every character having |PLAIN_COLOUR|, but end up with:
|
with every character having [[PLAIN_COLOUR]], but end up with:
|
||||||
= (text as PainterOutput)
|
|
||||||
int x = 55;
|
int x = 55;
|
||||||
rrrpipppnnp
|
rrrpipppnnp
|
||||||
=
|
|
||||||
We get to that by using a language's rules on literals, and then executing
|
We get to that by using a language's rules on literals, and then executing
|
||||||
its colouring program.
|
its colouring program.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int Painter::syntax_colour(programming_language *pl,
|
int Painter::syntax_colour(programming_language *pl,
|
||||||
hash_table *HT, text_stream *matter, text_stream *colouring, int with_comments) {
|
hash_table *HT, text_stream *matter, text_stream *colouring, int with_comments) {
|
||||||
int from = 0, to = Str::len(matter) - 1;
|
int from = 0, to = Str::len(matter) - 1;
|
||||||
|
@ -57,12 +57,12 @@ int Painter::syntax_colour(programming_language *pl,
|
||||||
|
|
||||||
void Painter::syntax_colour_inner(programming_language *pl,
|
void Painter::syntax_colour_inner(programming_language *pl,
|
||||||
hash_table *HT, text_stream *matter, text_stream *colouring, int from, int to) {
|
hash_table *HT, text_stream *matter, text_stream *colouring, int from, int to) {
|
||||||
@<Spot identifiers, literal text and character constants@>;
|
<<Spot identifiers, literal text and character constants>>;
|
||||||
@<Spot literal numerical constants@>;
|
<<Spot literal numerical constants>>;
|
||||||
@<Now run the colouring program@>;
|
<<Now run the colouring program>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Spot identifiers, literal text and character constants@> =
|
<<Spot identifiers, literal text and character constants>>=
|
||||||
int squote = Str::get_first_char(pl->character_literal);
|
int squote = Str::get_first_char(pl->character_literal);
|
||||||
int squote_escape = Str::get_first_char(pl->character_literal_escape);
|
int squote_escape = Str::get_first_char(pl->character_literal_escape);
|
||||||
int dquote = Str::get_first_char(pl->string_literal);
|
int dquote = Str::get_first_char(pl->string_literal);
|
||||||
|
@ -106,7 +106,7 @@ void Painter::syntax_colour_inner(programming_language *pl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Spot literal numerical constants@> =
|
<<Spot literal numerical constants>>=
|
||||||
int base = -1, dec_possible = TRUE;
|
int base = -1, dec_possible = TRUE;
|
||||||
for (int i=from; i <= to; i++) {
|
for (int i=from; i <= to; i++) {
|
||||||
if ((Str::get_at(colouring, i) == PLAIN_COLOUR) ||
|
if ((Str::get_at(colouring, i) == PLAIN_COLOUR) ||
|
||||||
|
@ -148,8 +148,8 @@ void Painter::syntax_colour_inner(programming_language *pl,
|
||||||
case 10: if (Characters::isdigit(c)) pass = TRUE; break;
|
case 10: if (Characters::isdigit(c)) pass = TRUE; break;
|
||||||
case 16: if (Characters::isdigit(c)) pass = TRUE;
|
case 16: if (Characters::isdigit(c)) pass = TRUE;
|
||||||
int d = Characters::tolower(c);
|
int d = Characters::tolower(c);
|
||||||
if ((d == 'a') || (d == 'b') || (d == 'c') ||
|
if ((d == 'a') [[| (d == 'b') || (d == 'c') |]]
|
||||||
(d == 'd') || (d == 'e') || (d == 'f')) pass = TRUE;
|
(d == 'd') [[| (d == 'e') |]] (d == 'f')) pass = TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pass) {
|
if (pass) {
|
||||||
|
@ -165,9 +165,9 @@ void Painter::syntax_colour_inner(programming_language *pl,
|
||||||
@ For the moment, we always adopt the C rules on identifiers: they have to
|
@ For the moment, we always adopt the C rules on identifiers: they have to
|
||||||
begin with an underscore or letter, then continue with underscores or
|
begin with an underscore or letter, then continue with underscores or
|
||||||
alphanumeric characters, except that if the language allows it then they
|
alphanumeric characters, except that if the language allows it then they
|
||||||
can contain a |::| namespace divider.
|
can contain a [[::]] namespace divider.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int Painter::identifier_at(programming_language *pl,
|
int Painter::identifier_at(programming_language *pl,
|
||||||
text_stream *matter, text_stream *colouring, int i) {
|
text_stream *matter, text_stream *colouring, int i) {
|
||||||
wchar_t c = Str::get_at(matter, i);
|
wchar_t c = Str::get_at(matter, i);
|
||||||
|
@ -191,7 +191,7 @@ int Painter::identifier_at(programming_language *pl,
|
||||||
@ With those preliminaries out of the way, the language's colouring program
|
@ With those preliminaries out of the way, the language's colouring program
|
||||||
takes over.
|
takes over.
|
||||||
|
|
||||||
@<Now run the colouring program@> =
|
<<Now run the colouring program>>=
|
||||||
if (pl->program)
|
if (pl->program)
|
||||||
Painter::execute(HT, pl->program, matter, colouring, from, to, painter_count++);
|
Painter::execute(HT, pl->program, matter, colouring, from, to, painter_count++);
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ whole snippet of text, or each character on its own, or each run of characters
|
||||||
of a given sort. Note that we work width-first, as it were: we complete each
|
of a given sort. Note that we work width-first, as it were: we complete each
|
||||||
rule across the whole snippet before moving on to the next.
|
rule across the whole snippet before moving on to the next.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Painter::execute(hash_table *HT, colouring_language_block *block, text_stream *matter,
|
void Painter::execute(hash_table *HT, colouring_language_block *block, text_stream *matter,
|
||||||
text_stream *colouring, int from, int to, int N) {
|
text_stream *colouring, int from, int to, int N) {
|
||||||
if (block == NULL) internal_error("no block");
|
if (block == NULL) internal_error("no block");
|
||||||
|
@ -281,7 +281,7 @@ void Painter::execute(hash_table *HT, colouring_language_block *block, text_stre
|
||||||
|
|
||||||
@ Rules have the form: if X, then Y.
|
@ Rules have the form: if X, then Y.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Painter::execute_rule(hash_table *HT, colouring_rule *rule, text_stream *matter,
|
void Painter::execute_rule(hash_table *HT, colouring_rule *rule, text_stream *matter,
|
||||||
text_stream *colouring, int from, int to, int N) {
|
text_stream *colouring, int from, int to, int N) {
|
||||||
if (Painter::satisfies(HT, rule, matter, colouring, from, to, N) == rule->sense)
|
if (Painter::satisfies(HT, rule, matter, colouring, from, to, N) == rule->sense)
|
||||||
|
@ -290,14 +290,15 @@ void Painter::execute_rule(hash_table *HT, colouring_rule *rule, text_stream *ma
|
||||||
|
|
||||||
@ Here we test the "if X":
|
@ Here we test the "if X":
|
||||||
|
|
||||||
@d UNSPACED_RULE_PREFIX 2 /* for |prefix P| */
|
<<*>>=
|
||||||
@d SPACED_RULE_PREFIX 3 /* for |spaced prefix P| */
|
#define UNSPACED_RULE_PREFIX 2 /* for [[prefix P]] */
|
||||||
@d OPTIONALLY_SPACED_RULE_PREFIX 4 /* for |optionally spaced prefix P| */
|
#define SPACED_RULE_PREFIX 3 /* for [[spaced prefix P]] */
|
||||||
@d UNSPACED_RULE_SUFFIX 5 /* for |suffix P| */
|
#define OPTIONALLY_SPACED_RULE_PREFIX 4 /* for [[optionally spaced prefix P]] */
|
||||||
@d SPACED_RULE_SUFFIX 6 /* for |spaced suffix P| */
|
#define UNSPACED_RULE_SUFFIX 5 /* for [[suffix P]] */
|
||||||
@d OPTIONALLY_SPACED_RULE_SUFFIX 7 /* for |optionally spaced suffix P| */
|
#define SPACED_RULE_SUFFIX 6 /* for [[spaced suffix P]] */
|
||||||
|
#define OPTIONALLY_SPACED_RULE_SUFFIX 7 /* for [[optionally spaced suffix P]] */
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int Painter::satisfies(hash_table *HT, colouring_rule *rule, text_stream *matter,
|
int Painter::satisfies(hash_table *HT, colouring_rule *rule, text_stream *matter,
|
||||||
text_stream *colouring, int from, int to, int N) {
|
text_stream *colouring, int from, int to, int N) {
|
||||||
if (rule->number > 0) {
|
if (rule->number > 0) {
|
||||||
|
@ -361,12 +362,12 @@ int Painter::satisfies(hash_table *HT, colouring_rule *rule, text_stream *matter
|
||||||
|
|
||||||
@ And here we carry out the "then Y":
|
@ And here we carry out the "then Y":
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Painter::follow(hash_table *HT, colouring_rule *rule, text_stream *matter,
|
void Painter::follow(hash_table *HT, colouring_rule *rule, text_stream *matter,
|
||||||
text_stream *colouring, int from, int to) {
|
text_stream *colouring, int from, int to) {
|
||||||
if (rule->execute_block)
|
if (rule->execute_block)
|
||||||
Painter::execute(HT, rule->execute_block, matter, colouring, from, to, 0);
|
Painter::execute(HT, rule->execute_block, matter, colouring, from, to, 0);
|
||||||
else if (rule->debug) @<Print some debugging text@>
|
else if (rule->debug) <<Print some debugging text>>
|
||||||
else {
|
else {
|
||||||
if (rule->set_to_colour != NOT_A_COLOUR)
|
if (rule->set_to_colour != NOT_A_COLOUR)
|
||||||
for (int i=from; i<=to; i++)
|
for (int i=from; i<=to; i++)
|
||||||
|
@ -377,7 +378,7 @@ void Painter::follow(hash_table *HT, colouring_rule *rule, text_stream *matter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Print some debugging text@> =
|
<<Print some debugging text>>=
|
||||||
PRINT("[%d, %d] text: ", from, to);
|
PRINT("[%d, %d] text: ", from, to);
|
||||||
for (int i=from; i<=to; i++)
|
for (int i=from; i<=to; i++)
|
||||||
PUT_TO(STDOUT, Str::get_at(matter, i));
|
PUT_TO(STDOUT, Str::get_at(matter, i));
|
||||||
|
@ -386,9 +387,9 @@ void Painter::follow(hash_table *HT, colouring_rule *rule, text_stream *matter,
|
||||||
PUT_TO(STDOUT, Str::get_at(colouring, i));
|
PUT_TO(STDOUT, Str::get_at(colouring, i));
|
||||||
PRINT("\n");
|
PRINT("\n");
|
||||||
|
|
||||||
@h Painting a file.
|
@ \section{Painting a file.}
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
linked_list *Painter::lines(filename *F) {
|
linked_list *Painter::lines(filename *F) {
|
||||||
linked_list *L = NEW_LINKED_LIST(text_stream);
|
linked_list *L = NEW_LINKED_LIST(text_stream);
|
||||||
TextFiles::read(F, FALSE, "unable to read file of textual extract", TRUE,
|
TextFiles::read(F, FALSE, "unable to read file of textual extract", TRUE,
|
|
@ -2,33 +2,33 @@
|
||||||
|
|
||||||
Basic support for languages to recognise structure and function declarations.
|
Basic support for languages to recognise structure and function declarations.
|
||||||
|
|
||||||
@ For each |typedef struct| we find, we will make one of these:
|
@ For each [[typedef struct]] we find, we will make one of these:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct language_type {
|
typedef struct language_type {
|
||||||
struct text_stream *structure_name;
|
struct text_stream *structure_name;
|
||||||
int tangled; /* whether the structure definition has been tangled out */
|
int tangled; /* whether the structure definition has been tangled out */
|
||||||
struct source_line *structure_header_at; /* opening line of |typedef| */
|
struct source_line *structure_header_at; /* opening line of [[typedef]] */
|
||||||
struct source_line *typedef_ends; /* closing line, where |}| appears */
|
struct source_line *typedef_ends; /* closing line, where [[}]] appears */
|
||||||
struct linked_list *incorporates; /* of |language_type| */
|
struct linked_list *incorporates; /* of [[language_type]] */
|
||||||
struct linked_list *elements; /* of |structure_element| */
|
struct linked_list *elements; /* of [[structure_element]] */
|
||||||
struct language_type *next_cst_alphabetically;
|
struct language_type *next_cst_alphabetically;
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} language_type;
|
} language_type;
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
language_type *first_cst_alphabetically = NULL;
|
language_type *first_cst_alphabetically = NULL;
|
||||||
|
|
||||||
language_type *Functions::new_struct(web *W, text_stream *name, source_line *L) {
|
language_type *Functions::new_struct(web *W, text_stream *name, source_line *L) {
|
||||||
language_type *str = CREATE(language_type);
|
language_type *str = CREATE(language_type);
|
||||||
@<Initialise the language type structure@>;
|
<<Initialise the language type structure>>;
|
||||||
Analyser::mark_reserved_word_at_line(L, str->structure_name, RESERVED_COLOUR);
|
Analyser::mark_reserved_word_at_line(L, str->structure_name, RESERVED_COLOUR);
|
||||||
@<Add this to the lists for its web and its paragraph@>;
|
<<Add this to the lists for its web and its paragraph>>;
|
||||||
@<Insertion-sort this into the alphabetical list of all structures found@>;
|
<<Insertion-sort this into the alphabetical list of all structures found>>;
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Initialise the language type structure@> =
|
<<Initialise the language type structure>>=
|
||||||
str->structure_name = Str::duplicate(name);
|
str->structure_name = Str::duplicate(name);
|
||||||
str->structure_header_at = L;
|
str->structure_header_at = L;
|
||||||
str->tangled = FALSE;
|
str->tangled = FALSE;
|
||||||
|
@ -36,12 +36,12 @@ language_type *Functions::new_struct(web *W, text_stream *name, source_line *L)
|
||||||
str->incorporates = NEW_LINKED_LIST(language_type);
|
str->incorporates = NEW_LINKED_LIST(language_type);
|
||||||
str->elements = NEW_LINKED_LIST(structure_element);
|
str->elements = NEW_LINKED_LIST(structure_element);
|
||||||
|
|
||||||
@<Add this to the lists for its web and its paragraph@> =
|
<<Add this to the lists for its web and its paragraph>>=
|
||||||
Tags::add_by_name(L->owning_paragraph, I"Structures");
|
Tags::add_by_name(L->owning_paragraph, I"Structures");
|
||||||
ADD_TO_LINKED_LIST(str, language_type, W->language_types);
|
ADD_TO_LINKED_LIST(str, language_type, W->language_types);
|
||||||
ADD_TO_LINKED_LIST(str, language_type, L->owning_paragraph->structures);
|
ADD_TO_LINKED_LIST(str, language_type, L->owning_paragraph->structures);
|
||||||
|
|
||||||
@<Insertion-sort this into the alphabetical list of all structures found@> =
|
<<Insertion-sort this into the alphabetical list of all structures found>>=
|
||||||
str->next_cst_alphabetically = NULL;
|
str->next_cst_alphabetically = NULL;
|
||||||
if (first_cst_alphabetically == NULL) first_cst_alphabetically = str;
|
if (first_cst_alphabetically == NULL) first_cst_alphabetically = str;
|
||||||
else {
|
else {
|
||||||
|
@ -65,16 +65,16 @@ language_type *Functions::new_struct(web *W, text_stream *name, source_line *L)
|
||||||
if (placed == FALSE) last->next_cst_alphabetically = str;
|
if (placed == FALSE) last->next_cst_alphabetically = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ A language can also create an instance of |structure_element| to record the
|
@ A language can also create an instance of [[structure_element]] to record the
|
||||||
existence of the element |val|, and add it to the linked list of elements of
|
existence of the element [[val]], and add it to the linked list of elements of
|
||||||
the structure being defined.
|
the structure being defined.
|
||||||
|
|
||||||
In InC, only, certain element names used often in Inform's source code are
|
In InC, only, certain element names used often in Inform's source code are
|
||||||
given mildly special treatment. This doesn't amount to much. |allow_sharing|
|
given mildly special treatment. This doesn't amount to much. [[allow_sharing]]
|
||||||
has no effect on tangling, so it doesn't change the program. It simply
|
has no effect on tangling, so it doesn't change the program. It simply
|
||||||
affects the reports in the woven code about where structures are used.
|
affects the reports in the woven code about where structures are used.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct structure_element {
|
typedef struct structure_element {
|
||||||
struct text_stream *element_name;
|
struct text_stream *element_name;
|
||||||
struct source_line *element_created_at;
|
struct source_line *element_created_at;
|
||||||
|
@ -82,7 +82,7 @@ typedef struct structure_element {
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} structure_element;
|
} structure_element;
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
structure_element *Functions::new_element(language_type *str, text_stream *elname,
|
structure_element *Functions::new_element(language_type *str, text_stream *elname,
|
||||||
source_line *L) {
|
source_line *L) {
|
||||||
Analyser::mark_reserved_word_at_line(L, elname, ELEMENT_COLOUR);
|
Analyser::mark_reserved_word_at_line(L, elname, ELEMENT_COLOUR);
|
||||||
|
@ -96,7 +96,7 @@ structure_element *Functions::new_element(language_type *str, text_stream *elnam
|
||||||
return elt;
|
return elt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
language_type *Functions::find_structure(web *W, text_stream *name) {
|
language_type *Functions::find_structure(web *W, text_stream *name) {
|
||||||
language_type *str;
|
language_type *str;
|
||||||
LOOP_OVER_LINKED_LIST(str, language_type, W->language_types)
|
LOOP_OVER_LINKED_LIST(str, language_type, W->language_types)
|
||||||
|
@ -105,14 +105,14 @@ language_type *Functions::find_structure(web *W, text_stream *name) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Functions.
|
@ \section{Functions.}
|
||||||
Each function definition found results in one of these structures being made:
|
Each function definition found results in one of these structures being made:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct language_function {
|
typedef struct language_function {
|
||||||
struct text_stream *function_name; /* e.g., |"cultivate"| */
|
struct text_stream *function_name; /* e.g., [["cultivate"]] */
|
||||||
struct text_stream *function_type; /* e.g., |"tree *"| */
|
struct text_stream *function_type; /* e.g., [["tree *"]] */
|
||||||
struct text_stream *function_arguments; /* e.g., |"int rainfall)"|: note |)| */
|
struct text_stream *function_arguments; /* e.g., [["int rainfall)"|: note |)]] */
|
||||||
struct source_line *function_header_at; /* where the first line of the header begins */
|
struct source_line *function_header_at; /* where the first line of the header begins */
|
||||||
int within_namespace; /* written using InC namespace dividers */
|
int within_namespace; /* written using InC namespace dividers */
|
||||||
int called_from_other_sections;
|
int called_from_other_sections;
|
||||||
|
@ -123,23 +123,23 @@ typedef struct language_function {
|
||||||
CLASS_DEFINITION
|
CLASS_DEFINITION
|
||||||
} language_function;
|
} language_function;
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
language_function *Functions::new_function(text_stream *fname, source_line *L) {
|
language_function *Functions::new_function(text_stream *fname, source_line *L) {
|
||||||
hash_table_entry *hte =
|
hash_table_entry *hte =
|
||||||
Analyser::mark_reserved_word_at_line(L, fname, FUNCTION_COLOUR);
|
Analyser::mark_reserved_word_at_line(L, fname, FUNCTION_COLOUR);
|
||||||
language_function *fn = CREATE(language_function);
|
language_function *fn = CREATE(language_function);
|
||||||
hte->as_function = fn;
|
hte->as_function = fn;
|
||||||
@<Initialise the function structure@>;
|
<<Initialise the function structure>>;
|
||||||
@<Add the function to its paragraph and line@>;
|
<<Add the function to its paragraph and line>>;
|
||||||
if (L->owning_section->sect_language->supports_namespaces)
|
if (L->owning_section->sect_language->supports_namespaces)
|
||||||
@<Check that the function has its namespace correctly declared@>;
|
<<Check that the function has its namespace correctly declared>>;
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ Note that we take a snapshot of the conditional compilation stack as
|
@ Note that we take a snapshot of the conditional compilation stack as
|
||||||
part of the function structure. We'll need it when predeclaring the function.
|
part of the function structure. We'll need it when predeclaring the function.
|
||||||
|
|
||||||
@<Initialise the function structure@> =
|
<<Initialise the function structure>>=
|
||||||
fn->function_name = Str::duplicate(fname);
|
fn->function_name = Str::duplicate(fname);
|
||||||
fn->function_arguments = Str::new();
|
fn->function_arguments = Str::new();
|
||||||
fn->function_type = Str::new();
|
fn->function_type = Str::new();
|
||||||
|
@ -153,12 +153,12 @@ part of the function structure. We'll need it when predeclaring the function.
|
||||||
fn->usage_described = TRUE;
|
fn->usage_described = TRUE;
|
||||||
fn->no_conditionals = 0;
|
fn->no_conditionals = 0;
|
||||||
|
|
||||||
@<Add the function to its paragraph and line@> =
|
<<Add the function to its paragraph and line>>=
|
||||||
paragraph *P = L->owning_paragraph;
|
paragraph *P = L->owning_paragraph;
|
||||||
if (P) ADD_TO_LINKED_LIST(fn, language_function, P->functions);
|
if (P) ADD_TO_LINKED_LIST(fn, language_function, P->functions);
|
||||||
L->function_defined = fn;
|
L->function_defined = fn;
|
||||||
|
|
||||||
@<Check that the function has its namespace correctly declared@> =
|
<<Check that the function has its namespace correctly declared>>=
|
||||||
text_stream *declared_namespace = NULL;
|
text_stream *declared_namespace = NULL;
|
||||||
text_stream *ambient_namespace = L->owning_section->sect_namespace;
|
text_stream *ambient_namespace = L->owning_section->sect_namespace;
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
|
@ -188,7 +188,7 @@ part of the function structure. We'll need it when predeclaring the function.
|
||||||
@ "Elsewhere" here means "in a paragraph of code other than the one in which the
|
@ "Elsewhere" here means "in a paragraph of code other than the one in which the
|
||||||
function's definition appears".
|
function's definition appears".
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int Functions::used_elsewhere(language_function *fn) {
|
int Functions::used_elsewhere(language_function *fn) {
|
||||||
paragraph *P = fn->function_header_at->owning_paragraph;
|
paragraph *P = fn->function_header_at->owning_paragraph;
|
||||||
hash_table_entry *hte =
|
hash_table_entry *hte =
|
||||||
|
@ -205,11 +205,11 @@ int Functions::used_elsewhere(language_function *fn) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Cataloguing.
|
@ \section{Cataloguing.}
|
||||||
This implements the additional information in the |-structures| and |-functions|
|
This implements the additional information in the [[-structures]] and [[-functions]]
|
||||||
forms of section catalogue.
|
forms of section catalogue.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Functions::catalogue(section *S, int functions_too) {
|
void Functions::catalogue(section *S, int functions_too) {
|
||||||
language_type *str;
|
language_type *str;
|
||||||
LOOP_OVER(str, language_type)
|
LOOP_OVER(str, language_type)
|
Loading…
Reference in a new issue