inweb-bootstrap/Chapter_4/ACME_Support.nw

369 lines
14 KiB
Text
Raw Permalink Normal View History

2020-04-04 06:51:07 +00:00
[ACMESupport::] ACME Support.
2020-04-04 12:07:08 +00:00
For generic programming languages by the ACME corporation.
2020-04-04 06:51:07 +00:00
2024-03-09 12:44:19 +00:00
@ \section{One Dozen ACME Explosive Tennis Balls.}
2020-04-04 12:07:08 +00:00
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
2020-04-04 19:46:43 +00:00
Corporation, which manufactured everything imaginable. See Wikipedia, "Acme
Corporation", for much else.
2020-04-04 12:07:08 +00:00
For us, ACME is an imaginary programming language, providing generic support
for comments and syntax colouring. Ironically, this code grew out of a language
actually called ACME: the 6502 assembler of the same name.
2020-04-04 06:51:07 +00:00
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-04 12:07:08 +00:00
void ACMESupport::add_fallbacks(programming_language *pl) {
2020-04-11 20:39:43 +00:00
if (Methods::provided(pl->methods, PARSE_TYPES_PAR_MTID) == FALSE)
METHOD_ADD(pl, PARSE_TYPES_PAR_MTID, ACMESupport::parse_types);
if (Methods::provided(pl->methods, PARSE_FUNCTIONS_PAR_MTID) == FALSE)
METHOD_ADD(pl, PARSE_FUNCTIONS_PAR_MTID, ACMESupport::parse_functions);
2020-04-16 22:51:03 +00:00
if (Methods::provided(pl->methods, ANALYSIS_ANA_MTID) == FALSE)
METHOD_ADD(pl, ANALYSIS_ANA_MTID, ACMESupport::analyse_code);
if (Methods::provided(pl->methods, POST_ANALYSIS_ANA_MTID) == FALSE)
METHOD_ADD(pl, POST_ANALYSIS_ANA_MTID, ACMESupport::post_analysis);
2020-04-04 12:07:08 +00:00
if (Methods::provided(pl->methods, PARSE_COMMENT_TAN_MTID) == FALSE)
METHOD_ADD(pl, PARSE_COMMENT_TAN_MTID, ACMESupport::parse_comment);
if (Methods::provided(pl->methods, COMMENT_TAN_MTID) == FALSE)
METHOD_ADD(pl, COMMENT_TAN_MTID, ACMESupport::comment);
if (Methods::provided(pl->methods, SHEBANG_TAN_MTID) == FALSE)
METHOD_ADD(pl, SHEBANG_TAN_MTID, ACMESupport::shebang);
if (Methods::provided(pl->methods, BEFORE_MACRO_EXPANSION_TAN_MTID) == FALSE)
METHOD_ADD(pl, BEFORE_MACRO_EXPANSION_TAN_MTID, ACMESupport::before_macro_expansion);
if (Methods::provided(pl->methods, AFTER_MACRO_EXPANSION_TAN_MTID) == FALSE)
METHOD_ADD(pl, AFTER_MACRO_EXPANSION_TAN_MTID, ACMESupport::after_macro_expansion);
if (Methods::provided(pl->methods, START_DEFN_TAN_MTID) == FALSE)
METHOD_ADD(pl, START_DEFN_TAN_MTID, ACMESupport::start_definition);
2020-04-04 13:15:50 +00:00
if (Methods::provided(pl->methods, PROLONG_DEFN_TAN_MTID) == FALSE)
METHOD_ADD(pl, PROLONG_DEFN_TAN_MTID, ACMESupport::prolong_definition);
2020-04-04 12:07:08 +00:00
if (Methods::provided(pl->methods, END_DEFN_TAN_MTID) == FALSE)
METHOD_ADD(pl, END_DEFN_TAN_MTID, ACMESupport::end_definition);
if (Methods::provided(pl->methods, OPEN_IFDEF_TAN_MTID) == FALSE)
METHOD_ADD(pl, OPEN_IFDEF_TAN_MTID, ACMESupport::I6_open_ifdef);
if (Methods::provided(pl->methods, CLOSE_IFDEF_TAN_MTID) == FALSE)
METHOD_ADD(pl, CLOSE_IFDEF_TAN_MTID, ACMESupport::I6_close_ifdef);
if (Methods::provided(pl->methods, INSERT_LINE_MARKER_TAN_MTID) == FALSE)
METHOD_ADD(pl, INSERT_LINE_MARKER_TAN_MTID, ACMESupport::insert_line_marker);
if (Methods::provided(pl->methods, SUPPRESS_DISCLAIMER_TAN_MTID) == FALSE)
METHOD_ADD(pl, SUPPRESS_DISCLAIMER_TAN_MTID, ACMESupport::suppress_disclaimer);
2020-04-04 19:46:43 +00:00
if (Methods::provided(pl->methods, BEGIN_WEAVE_WEA_MTID) == FALSE)
METHOD_ADD(pl, BEGIN_WEAVE_WEA_MTID, ACMESupport::begin_weave);
if (Methods::provided(pl->methods, RESET_SYNTAX_COLOURING_WEA_MTID) == FALSE)
METHOD_ADD(pl, RESET_SYNTAX_COLOURING_WEA_MTID, ACMESupport::reset_syntax_colouring);
if (Methods::provided(pl->methods, SYNTAX_COLOUR_WEA_MTID) == FALSE)
METHOD_ADD(pl, SYNTAX_COLOUR_WEA_MTID, ACMESupport::syntax_colour);
2020-04-04 12:07:08 +00:00
}
2024-03-09 12:44:19 +00:00
@ 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
2020-04-05 17:37:43 +00:00
files to crash Inweb.)
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-05 17:37:43 +00:00
void ACMESupport::expand(OUTPUT_STREAM, text_stream *prototype, text_stream *S,
int N, filename *F) {
2020-04-04 12:07:08 +00:00
if (Str::len(prototype) > 0) {
for (int i=0; i<Str::len(prototype); i++) {
wchar_t c = Str::get_at(prototype, i);
if ((c == '%') && (Str::get_at(prototype, i+1) == 'S') && (S)) {
WRITE("%S", S);
i++;
} else if ((c == '%') && (Str::get_at(prototype, i+1) == 'd') && (N >= 0)) {
WRITE("%d", N);
i++;
} else if ((c == '%') && (Str::get_at(prototype, i+1) == 'f') && (F)) {
WRITE("%/f", F);
i++;
} else {
PUT(c);
}
}
}
2020-04-04 06:51:07 +00:00
}
2024-03-09 12:44:19 +00:00
@ \section{Tangling methods.}
2020-04-04 06:51:07 +00:00
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-05 17:37:43 +00:00
void ACMESupport::shebang(programming_language *pl, text_stream *OUT, web *W,
tangle_target *target) {
2020-04-04 12:07:08 +00:00
ACMESupport::expand(OUT, pl->shebang, NULL, -1, NULL);
}
void ACMESupport::before_macro_expansion(programming_language *pl,
OUTPUT_STREAM, para_macro *pmac) {
ACMESupport::expand(OUT, pl->before_macro_expansion, NULL, -1, NULL);
}
void ACMESupport::after_macro_expansion(programming_language *pl,
OUTPUT_STREAM, para_macro *pmac) {
ACMESupport::expand(OUT, pl->after_macro_expansion, NULL, -1, NULL);
}
int ACMESupport::start_definition(programming_language *pl, text_stream *OUT,
text_stream *term, text_stream *start, section *S, source_line *L) {
2020-04-05 17:37:43 +00:00
if (LanguageMethods::supports_definitions(pl)) {
ACMESupport::expand(OUT, pl->start_definition, term, -1, NULL);
2020-04-16 22:51:03 +00:00
Tangler::tangle_line(OUT, start, S, L);
2020-04-05 17:37:43 +00:00
}
2020-04-04 12:07:08 +00:00
return TRUE;
}
2020-04-04 13:15:50 +00:00
int ACMESupport::prolong_definition(programming_language *pl,
text_stream *OUT, text_stream *more, section *S, source_line *L) {
2020-04-05 17:37:43 +00:00
if (LanguageMethods::supports_definitions(pl)) {
ACMESupport::expand(OUT, pl->prolong_definition, NULL, -1, NULL);
2020-04-16 22:51:03 +00:00
Tangler::tangle_line(OUT, more, S, L);
2020-04-05 17:37:43 +00:00
}
2020-04-04 13:15:50 +00:00
return TRUE;
}
2020-04-04 12:07:08 +00:00
int ACMESupport::end_definition(programming_language *pl,
text_stream *OUT, section *S, source_line *L) {
2020-04-05 17:37:43 +00:00
if (LanguageMethods::supports_definitions(pl)) {
ACMESupport::expand(OUT, pl->end_definition, NULL, -1, NULL);
}
2020-04-04 12:07:08 +00:00
return TRUE;
}
2020-04-05 17:37:43 +00:00
void ACMESupport::I6_open_ifdef(programming_language *pl,
text_stream *OUT, text_stream *symbol, int sense) {
2020-04-04 12:07:08 +00:00
if (sense) ACMESupport::expand(OUT, pl->start_ifdef, symbol, -1, NULL);
else ACMESupport::expand(OUT, pl->start_ifndef, symbol, -1, NULL);
}
2020-04-05 17:37:43 +00:00
void ACMESupport::I6_close_ifdef(programming_language *pl,
text_stream *OUT, text_stream *symbol, int sense) {
2020-04-04 12:07:08 +00:00
if (sense) ACMESupport::expand(OUT, pl->end_ifdef, symbol, -1, NULL);
else ACMESupport::expand(OUT, pl->end_ifndef, symbol, -1, NULL);
}
void ACMESupport::insert_line_marker(programming_language *pl,
text_stream *OUT, source_line *L) {
ACMESupport::expand(OUT, pl->line_marker, NULL,
L->source.line_count, L->source.text_file_filename);
}
void ACMESupport::comment(programming_language *pl,
2020-04-04 06:51:07 +00:00
text_stream *OUT, text_stream *comm) {
2020-04-04 12:07:08 +00:00
if (Str::len(pl->multiline_comment_open) > 0) {
ACMESupport::expand(OUT, pl->multiline_comment_open, NULL, -1, NULL);
WRITE(" %S ", comm);
ACMESupport::expand(OUT, pl->multiline_comment_close, NULL, -1, NULL);
WRITE("\n");
2020-04-05 17:37:43 +00:00
} else if (Str::len(pl->line_comment) > 0) {
2020-04-04 12:07:08 +00:00
ACMESupport::expand(OUT, pl->line_comment, NULL, -1, NULL);
WRITE(" %S\n", comm);
2020-04-05 17:37:43 +00:00
} else if (Str::len(pl->whole_line_comment) > 0) {
ACMESupport::expand(OUT, pl->whole_line_comment, NULL, -1, NULL);
WRITE(" %S\n", comm);
2020-04-04 12:07:08 +00:00
}
2020-04-04 06:51:07 +00:00
}
2024-03-09 12:44:19 +00:00
@ 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
2020-05-11 21:57:58 +00:00
comment, and 2 inside a multiline comment.
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-04 12:07:08 +00:00
int ACMESupport::parse_comment(programming_language *pl,
2020-04-04 06:51:07 +00:00
text_stream *line, text_stream *part_before_comment, text_stream *part_within_comment) {
2020-04-07 22:42:52 +00:00
int q_mode = 0, c_mode = 0, non_white_space = FALSE, c_position = -1, c_end = -1;
2020-04-04 12:07:08 +00:00
for (int i=0; i<Str::len(line); i++) {
wchar_t c = Str::get_at(line, i);
2020-05-11 21:57:58 +00:00
switch (c_mode) {
2024-03-09 12:44:19 +00:00
case 0: <<Outside commentary>>; break;
case 1: <<Inside a line comment>>; break;
case 2: <<Inside a multiline comment>>; break;
2020-04-04 12:07:08 +00:00
}
2020-04-04 06:51:07 +00:00
}
2020-05-11 21:57:58 +00:00
if (c_mode == 2) c_end = Str::len(line);
2020-04-04 12:07:08 +00:00
if ((c_position >= 0) && (non_white_space == FALSE)) {
2020-04-04 06:51:07 +00:00
Str::clear(part_before_comment);
2020-04-05 17:37:43 +00:00
for (int i=0; i<c_position; i++)
PUT_TO(part_before_comment, Str::get_at(line, i));
2020-04-04 12:07:08 +00:00
Str::clear(part_within_comment);
2020-04-05 17:37:43 +00:00
for (int i=c_position + 2; i<c_end; i++)
PUT_TO(part_within_comment, Str::get_at(line, i));
Str::trim_white_space_at_end(part_within_comment);
2020-04-04 06:51:07 +00:00
return TRUE;
}
return FALSE;
}
2024-03-09 12:44:19 +00:00
<<Inside a multiline comment>>=
2020-05-11 21:57:58 +00:00
if (Str::includes_at(line, i, pl->multiline_comment_close)) {
c_mode = 0; c_end = i; i += Str::len(pl->multiline_comment_close) - 1;
}
2024-03-09 12:44:19 +00:00
<<Inside a line comment>>=
2020-05-11 21:57:58 +00:00
;
2024-03-09 12:44:19 +00:00
<<Outside commentary>>=
2020-05-11 21:57:58 +00:00
switch (q_mode) {
2024-03-09 12:44:19 +00:00
case 0: <<Outside quoted matter>>; break;
case 1: <<Inside a literal character>>; break;
case 2: <<Inside a literal string>>; break;
2020-05-11 21:57:58 +00:00
}
2024-03-09 12:44:19 +00:00
<<Outside quoted matter>>=
2020-05-11 21:57:58 +00:00
if (!(Characters::is_whitespace(c))) non_white_space = TRUE;
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 (Str::includes_at(line, i, pl->multiline_comment_open)) {
c_mode = 2; c_position = i; non_white_space = FALSE;
i += Str::len(pl->multiline_comment_open) - 1;
} else if (Str::includes_at(line, i, pl->line_comment)) {
c_mode = 1; c_position = i; c_end = Str::len(line); non_white_space = FALSE;
i += Str::len(pl->line_comment) - 1;
} else if (Str::includes_at(line, i, pl->whole_line_comment)) {
int material_exists = FALSE;
for (int j=0; j<i; j++)
if (!(Characters::is_whitespace(Str::get_at(line, j))))
material_exists = TRUE;
if (material_exists == FALSE) {
c_mode = 1; c_position = i; c_end = Str::len(line);
non_white_space = FALSE;
i += Str::len(pl->whole_line_comment) - 1;
}
}
2024-03-09 12:44:19 +00:00
<<Inside a literal character>>=
2020-05-11 21:57:58 +00:00
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)) q_mode = 0;
q_mode = 0;
2024-03-09 12:44:19 +00:00
<<Inside a literal string>>=
2020-05-11 21:57:58 +00:00
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)) q_mode = 0;
q_mode = 0;
2020-04-11 20:39:43 +00:00
@
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-11 20:39:43 +00:00
void ACMESupport::parse_types(programming_language *self, web *W) {
if (W->main_language->type_notation[0]) {
chapter *C;
section *S;
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W)) {
if (S->sect_language == W->main_language) {
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, L->text, W->main_language->type_notation)) {
Functions::new_function(mr.exp[0], L);
2020-04-11 20:39:43 +00:00
}
Regexp::dispose_of(&mr);
}
}
}
}
@
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-11 20:39:43 +00:00
void ACMESupport::parse_functions(programming_language *self, web *W) {
if (W->main_language->function_notation[0]) {
chapter *C;
section *S;
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W)) {
if (S->sect_language == W->main_language) {
match_results mr = Regexp::create_mr();
if ((L->category != TEXT_EXTRACT_LCAT) &&
(Regexp::match(&mr, L->text, W->main_language->function_notation))) {
Functions::new_function(mr.exp[0], L);
2020-04-11 20:39:43 +00:00
}
Regexp::dispose_of(&mr);
}
}
}
}
2020-04-16 22:51:03 +00:00
@ 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
2024-03-09 12:44:19 +00:00
[[Whatever::name()]] whose definitions are not in the [[Whatever::]] section;
2020-04-16 22:51:03 +00:00
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
bibliographic variable, but don't do that.
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-16 22:51:03 +00:00
void ACMESupport::post_analysis(programming_language *self, web *W) {
int check_namespaces = FALSE;
if (Str::eq_wide_string(Bibliographic::get_datum(W->md, I"Namespaces"), L"On"))
check_namespaces = TRUE;
language_function *fn;
LOOP_OVER(fn, language_function) {
hash_table_entry *hte =
Analyser::find_hash_entry_for_section(fn->function_header_at->owning_section,
fn->function_name, FALSE);
if (hte) {
hash_table_entry_usage *hteu;
LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages) {
if ((hteu->form_of_usage & FCALL_USAGE) || (fn->within_namespace))
if (hteu->usage_recorded_at->under_section != fn->function_header_at->owning_section)
fn->called_from_other_sections = TRUE;
}
}
if ((fn->within_namespace != fn->called_from_other_sections)
&& (check_namespaces)
&& (fn->call_freely == FALSE)) {
if (fn->within_namespace)
Main::error_in_web(
I"Being internally called, this function mustn't belong to a :: namespace",
fn->function_header_at);
else
Main::error_in_web(
I"Being externally called, this function must belong to a :: namespace",
fn->function_header_at);
}
}
}
@ Having found all those functions and structure elements, we make sure they
are all known to Inweb's hash table of interesting identifiers:
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-16 22:51:03 +00:00
void ACMESupport::analyse_code(programming_language *self, web *W) {
language_function *fn;
LOOP_OVER(fn, language_function)
Analyser::find_hash_entry_for_section(fn->function_header_at->owning_section,
fn->function_name, TRUE);
language_type *str;
structure_element *elt;
LOOP_OVER_LINKED_LIST(str, language_type, W->language_types)
LOOP_OVER_LINKED_LIST(elt, structure_element, str->elements)
if (elt->allow_sharing == FALSE)
Analyser::find_hash_entry_for_section(elt->element_created_at->owning_section,
elt->element_name, TRUE);
}
2020-04-04 12:07:08 +00:00
@ This is here so that tangling the Standard Rules extension doesn't insert
a spurious comment betraying Inweb's involvement in the process.
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-04 12:07:08 +00:00
int ACMESupport::suppress_disclaimer(programming_language *pl) {
return pl->suppress_disclaimer;
}
2020-04-04 19:46:43 +00:00
@
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-16 22:51:03 +00:00
void ACMESupport::begin_weave(programming_language *pl, section *S, weave_order *wv) {
2020-04-04 19:46:43 +00:00
reserved_word *rw;
LOOP_OVER_LINKED_LIST(rw, reserved_word, pl->reserved_words)
2020-04-05 17:37:43 +00:00
Analyser::mark_reserved_word_for_section(S, rw->word, rw->colour);
2020-04-04 19:46:43 +00:00
}
2020-04-05 17:37:43 +00:00
@ ACME has all of its syntax-colouring done by the default engine:
2020-04-04 06:51:07 +00:00
2024-03-09 12:44:19 +00:00
<<*>>=
2020-04-04 12:07:08 +00:00
void ACMESupport::reset_syntax_colouring(programming_language *pl) {
2020-04-05 17:37:43 +00:00
Painter::reset_syntax_colouring(pl);
2020-04-04 06:51:07 +00:00
}
2020-04-20 22:26:08 +00:00
int ACMESupport::syntax_colour(programming_language *pl,
2020-04-16 22:51:03 +00:00
weave_order *wv, source_line *L, text_stream *matter, text_stream *colouring) {
2020-04-12 16:24:23 +00:00
section *S = L->owning_section;
2020-04-05 22:28:05 +00:00
hash_table *ht = &(S->sect_target->symbols);
if ((L->category == TEXT_EXTRACT_LCAT) && (pl != S->sect_language))
ht = &(pl->built_in_keywords);
return Painter::syntax_colour(pl, ht, matter, colouring, FALSE);
2020-04-04 06:51:07 +00:00
}