~ubuntu-branches/ubuntu/vivid/inform/vivid

« back to all changes in this revision

Viewing changes to src/lexer.c

  • Committer: Bazaar Package Importer
  • Author(s): Jan Christoph Nordholz
  • Date: 2008-05-26 22:09:44 UTC
  • mfrom: (2.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080526220944-ba7phz0d1k4vo7wx
Tags: 6.31.1+dfsg-1
* Remove a considerable number of files from the package
  due to unacceptable licensing terms.
* Repair library symlinks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* ------------------------------------------------------------------------- */
2
 
/*   "lexer" : Lexical analyser                                              */
3
 
/*                                                                           */
4
 
/*   Part of Inform 6.30                                                     */
5
 
/*   copyright (c) Graham Nelson 1993 - 2004                                 */
6
 
/*                                                                           */
7
 
/* ------------------------------------------------------------------------- */
8
 
 
9
 
#include "header.h"
10
 
 
11
 
int total_source_line_count,            /* Number of source lines so far     */
12
 
 
13
 
    no_hash_printed_yet,                /* Have not yet printed the first #  */
14
 
    hash_printed_since_newline,         /* A hash has been printed since the
15
 
                                           most recent new-line was printed
16
 
                                           (generally as a result of an error
17
 
                                           message or the start of pass)     */
18
 
    dont_enter_into_symbol_table,       /* Return names as text (with
19
 
                                           token type DQ_TT, i.e., as if
20
 
                                           they had double-quotes around)
21
 
                                           and not as entries in the symbol
22
 
                                           table, when TRUE                  */
23
 
    return_sp_as_variable;              /* When TRUE, the word "sp" denotes
24
 
                                           the stack pointer variable
25
 
                                           (used in assembly language only)  */
26
 
int next_token_begins_syntax_line;      /* When TRUE, start a new syntax
27
 
                                           line (for error reporting, etc.)
28
 
                                           on the source code line where
29
 
                                           the next token appears            */
30
 
 
31
 
int32 last_mapped_line;  /* Last syntax line reported to debugging file      */
32
 
 
33
 
/* ------------------------------------------------------------------------- */
34
 
/*   The lexer's output is a sequence of triples, each called a "token",     */
35
 
/*   representing one lexical unit (or "lexeme") each.  Instead of providing */
36
 
/*   "lookahead" (that is, always having available the next token after the  */
37
 
/*   current one, so that syntax analysers higher up in Inform can have      */
38
 
/*   advance knowledge of what is coming), the lexer instead has a system    */
39
 
/*   where tokens can be read in and then "put back again".                  */
40
 
/*   The meaning of the number (and to some extent the text) supplied with   */
41
 
/*   a token depends on its type: see "header.h" for the list of types.      */
42
 
/*   For example, the lexeme "$1e3" is understood by Inform as a hexadecimal */
43
 
/*   number, and translated to the token:                                    */
44
 
/*     type NUMBER_TT, value 483, text "$1e3"                                */
45
 
/* ------------------------------------------------------------------------- */
46
 
/*   These four variables are set to the current token on a call to          */
47
 
/*   get_next_token() (but are not changed by a call to put_token_back()).   */
48
 
/* ------------------------------------------------------------------------- */
49
 
 
50
 
int token_type;  int32 token_value;  char *token_text; dbgl token_line_ref;
51
 
 
52
 
/* ------------------------------------------------------------------------- */
53
 
/*   In order to be able to put tokens back efficiently, the lexer stores    */
54
 
/*   tokens in a "circle": the variable circle_position ranges between       */
55
 
/*   0 and CIRCLE_SIZE-1.  We only need a circle size as large as the        */
56
 
/*   maximum number of tokens ever put back at once, plus 1 (in effect, the  */
57
 
/*   maximum token lookahead ever needed in syntax analysis, plus 1).        */
58
 
/*                                                                           */
59
 
/*   Unlike some compilers, Inform does not have a context-free lexer: in    */
60
 
/*   fact it has 12288 different possible states.  However, the context only */
61
 
/*   affects the interpretation of "identifiers": lexemes beginning with a   */
62
 
/*   letter and containing up to 32 chars of alphanumeric and underscore     */
63
 
/*   chars.  (For example, "default" may refer to the directive or statement */
64
 
/*   of that name, and which token values are returned depends on the        */
65
 
/*   current lexical context.)                                               */
66
 
/*                                                                           */
67
 
/*   Along with each token, we also store the lexical context it was         */
68
 
/*   translated under; because if it is called for again, there may need     */
69
 
/*   to be a fresh interpretation of it if the context has changed.          */
70
 
/* ------------------------------------------------------------------------- */
71
 
 
72
 
#define CIRCLE_SIZE 6
73
 
 
74
 
/*   (The worst case for token lookahead is distinguishing between an
75
 
     old-style "objectloop (a in b)" and a new "objectloop (a in b ...)".)   */
76
 
 
77
 
static int circle_position;
78
 
static token_data circle[CIRCLE_SIZE];
79
 
 
80
 
static int token_contexts[CIRCLE_SIZE];
81
 
 
82
 
/* ------------------------------------------------------------------------- */
83
 
/*   A complication, however, is that the text of some lexemes needs to be   */
84
 
/*   held in Inform's memory for much longer periods: for example, a         */
85
 
/*   dictionary word lexeme (like "'south'") must have its text preserved    */
86
 
/*   until the code generation time for the expression it occurs in, when    */
87
 
/*   the dictionary reference is actually made.  Code generation in general  */
88
 
/*   occurs as early as possible in Inform: pending some better method of    */
89
 
/*   garbage collection, we simply use a buffer so large that unless         */
90
 
/*   expressions spread across 10K of source code are found, there can be    */
91
 
/*   no problem.                                                             */
92
 
/* ------------------------------------------------------------------------- */
93
 
 
94
 
static char *lexeme_memory;
95
 
static char *lex_p;                     /* Current write position            */
96
 
 
97
 
/* ------------------------------------------------------------------------- */
98
 
/*   The lexer itself needs up to 3 characters of lookahead (it uses an      */
99
 
/*   LR(3) grammar to translate characters into tokens).                     */
100
 
/* ------------------------------------------------------------------------- */
101
 
 
102
 
static int current, lookahead,          /* The latest character read, and    */
103
 
    lookahead2, lookahead3;             /* the three characters following it */
104
 
 
105
 
static int pipeline_made;               /* Whether or not the pipeline of
106
 
                                           characters has been constructed
107
 
                                           yet (this pass)                   */
108
 
 
109
 
static int (* get_next_char)(void);     /* Routine for reading the stream of
110
 
                                           characters: the lexer does not
111
 
                                           need any "ungetc" routine for
112
 
                                           putting them back again.  End of
113
 
                                           stream is signalled by returning
114
 
                                           zero.                             */
115
 
 
116
 
static char *source_to_analyse;         /* The current lexical source:
117
 
                                           NULL for "load from source files",
118
 
                                           otherwise this points to a string
119
 
                                           containing Inform code            */
120
 
 
121
 
static int tokens_put_back;             /* Count of the number of backward
122
 
                                           moves made from the last-read
123
 
                                           token                             */
124
 
 
125
 
extern void describe_token(token_data t)
126
 
{
127
 
    /*  Many of the token types are not set in this file, but later on in
128
 
        Inform's higher stages (for example, in the expression evaluator);
129
 
        but this routine describes them all.                                 */
130
 
 
131
 
    printf("{ ");
132
 
 
133
 
    switch(t.type)
134
 
    {
135
 
        /*  The following token types occur in lexer output:                 */
136
 
 
137
 
        case SYMBOL_TT:          printf("symbol ");
138
 
                                 describe_symbol(t.value);
139
 
                                 break;
140
 
        case NUMBER_TT:          printf("literal number %d", t.value);
141
 
                                 break;
142
 
        case DQ_TT:              printf("string \"%s\"", t.text);
143
 
                                 break;
144
 
        case SQ_TT:              printf("string '%s'", t.text);
145
 
                                 break;
146
 
        case SEP_TT:             printf("separator '%s'", t.text);
147
 
                                 break;
148
 
        case EOF_TT:             printf("end of file");
149
 
                                 break;
150
 
 
151
 
        case STATEMENT_TT:       printf("statement name '%s'", t.text);
152
 
                                 break;
153
 
        case SEGMENT_MARKER_TT:  printf("object segment marker '%s'", t.text);
154
 
                                 break;
155
 
        case DIRECTIVE_TT:       printf("directive name '%s'", t.text);
156
 
                                 break;
157
 
        case CND_TT:             printf("textual conditional '%s'", t.text);
158
 
                                 break;
159
 
        case OPCODE_NAME_TT:     printf("opcode name '%s'", t.text);
160
 
                                 break;
161
 
        case SYSFUN_TT:          printf("built-in function name '%s'", t.text);
162
 
                                 break;
163
 
        case LOCAL_VARIABLE_TT:  printf("local variable name '%s'", t.text);
164
 
                                 break;
165
 
        case MISC_KEYWORD_TT:    printf("statement keyword '%s'", t.text);
166
 
                                 break;
167
 
        case DIR_KEYWORD_TT:     printf("directive keyword '%s'", t.text);
168
 
                                 break;
169
 
        case TRACE_KEYWORD_TT:   printf("'trace' keyword '%s'", t.text);
170
 
                                 break;
171
 
        case SYSTEM_CONSTANT_TT: printf("system constant name '%s'", t.text);
172
 
                                 break;
173
 
 
174
 
        /*  The remaining are etoken types, not set by the lexer             */
175
 
 
176
 
        case OP_TT:              printf("operator '%s'",
177
 
                                     operators[t.value].description);
178
 
                                 break;
179
 
        case ENDEXP_TT:          printf("end of expression");
180
 
                                 break;
181
 
        case SUBOPEN_TT:         printf("open bracket");
182
 
                                 break;
183
 
        case SUBCLOSE_TT:        printf("close bracket");
184
 
                                 break;
185
 
        case LARGE_NUMBER_TT:    printf("large number: '%s'=%d",t.text,t.value);
186
 
                                 break;
187
 
        case SMALL_NUMBER_TT:    printf("small number: '%s'=%d",t.text,t.value);
188
 
                                 break;
189
 
        case VARIABLE_TT:        printf("variable '%s'=%d", t.text, t.value);
190
 
                                 break;
191
 
        case DICTWORD_TT:        printf("dictionary word '%s'", t.text);
192
 
                                 break;
193
 
        case ACTION_TT:          printf("action name '%s'", t.text);
194
 
                                 break;
195
 
 
196
 
        default:
197
 
            printf("** unknown token type %d, text='%s', value=%d **",
198
 
            t.type, t.text, t.value);
199
 
    }
200
 
    printf(" }");
201
 
}
202
 
 
203
 
/* ------------------------------------------------------------------------- */
204
 
/*   All but one of the 280 Inform keywords (118 of them opcode names used   */
205
 
/*   only by the assembler).  (The one left over is "sp", a keyword used in  */
206
 
/*   assembly language only.)                                                */
207
 
/*                                                                           */
208
 
/*   A "keyword group" is a set of keywords to be searched for.  If a match  */
209
 
/*   is made on an identifier, the token type becomes that given in the KG   */
210
 
/*   and the token value is its index in the KG.                             */
211
 
/*                                                                           */
212
 
/*   The keyword ordering must correspond with the appropriate #define's in  */
213
 
/*   "header.h" but is otherwise not significant.                            */
214
 
/* ------------------------------------------------------------------------- */
215
 
 
216
 
#define MAX_KEYWORDS 350
217
 
 
218
 
/* The values will be filled in at compile time, when we know
219
 
   which opcode set to use. */
220
 
keyword_group opcode_names =
221
 
{ { "" },
222
 
    OPCODE_NAME_TT, FALSE, TRUE
223
 
};
224
 
 
225
 
static char *opcode_list_z[] = {
226
 
    "je", "jl", "jg", "dec_chk", "inc_chk", "jin", "test", "or", "and",
227
 
    "test_attr", "set_attr", "clear_attr", "store", "insert_obj", "loadw",
228
 
    "loadb", "get_prop", "get_prop_addr", "get_next_prop", "add", "sub",
229
 
    "mul", "div", "mod", "call", "storew", "storeb", "put_prop", "sread",
230
 
    "print_char", "print_num", "random", "push", "pull", "split_window",
231
 
    "set_window", "output_stream", "input_stream", "sound_effect", "jz",
232
 
    "get_sibling", "get_child", "get_parent", "get_prop_len", "inc", "dec",
233
 
    "print_addr", "remove_obj", "print_obj", "ret", "jump", "print_paddr",
234
 
    "load", "not", "rtrue", "rfalse", "print", "print_ret", "nop", "save",
235
 
    "restore", "restart", "ret_popped", "pop", "quit", "new_line",
236
 
    "show_status", "verify", "call_2s", "call_vs", "aread", "call_vs2",
237
 
    "erase_window", "erase_line", "set_cursor", "get_cursor",
238
 
    "set_text_style", "buffer_mode", "read_char", "scan_table", "call_1s",
239
 
    "call_2n", "set_colour", "throw", "call_vn", "call_vn2", "tokenise",
240
 
    "encode_text", "copy_table", "print_table", "check_arg_count", "call_1n",
241
 
    "catch", "piracy", "log_shift", "art_shift", "set_font", "save_undo",
242
 
    "restore_undo", "draw_picture", "picture_data", "erase_picture",
243
 
    "set_margins", "move_window", "window_size", "window_style",
244
 
    "get_wind_prop", "scroll_window", "pop_stack", "read_mouse",
245
 
    "mouse_window", "push_stack", "put_wind_prop", "print_form",
246
 
    "make_menu", "picture_table", "print_unicode", "check_unicode",
247
 
    ""
248
 
};
249
 
 
250
 
static char *opcode_list_g[] = {
251
 
    "nop", "add", "sub", "mul", "div", "mod", "neg", "bitand", "bitor",
252
 
    "bitxor", "bitnot", "shiftl", "sshiftr", "ushiftr", "jump", "jz",
253
 
    "jnz", "jeq", "jne", "jlt", "jge", "jgt", "jle", 
254
 
    "jltu", "jgeu", "jgtu", "jleu", 
255
 
    "call", "return",
256
 
    "catch", "throw", "tailcall", 
257
 
    "copy", "copys", "copyb", "sexs", "sexb", "aload",
258
 
    "aloads", "aloadb", "aloadbit", "astore", "astores", "astoreb",
259
 
    "astorebit", "stkcount", "stkpeek", "stkswap", "stkroll", "stkcopy",
260
 
    "streamchar", "streamnum", "streamstr", 
261
 
    "gestalt", "debugtrap", "getmemsize", "setmemsize", "jumpabs",
262
 
    "random", "setrandom", "quit", "verify",
263
 
    "restart", "save", "restore", "saveundo", "restoreundo", "protect",
264
 
    "glk", "getstringtbl", "setstringtbl", "getiosys", "setiosys",
265
 
    "linearsearch", "binarysearch", "linkedsearch",
266
 
    "callf", "callfi", "callfii", "callfiii", 
267
 
    ""
268
 
};
269
 
 
270
 
keyword_group directives =
271
 
{ { "abbreviate", "array", "attribute", "class", "constant",
272
 
    "default", "dictionary", "end", "endif", "extend", "fake_action",
273
 
    "global", "ifdef", "ifndef", "ifnot", "ifv3", "ifv5", "iftrue",
274
 
    "iffalse", "import", "include", "link", "lowstring", "message",
275
 
    "nearby", "object", "property", "release", "replace",
276
 
    "serial", "switches", "statusline", "stub", "system_file", "trace",
277
 
    "verb", "version", "zcharacter",
278
 
    "" },
279
 
    DIRECTIVE_TT, FALSE, FALSE
280
 
};
281
 
 
282
 
keyword_group trace_keywords =
283
 
{ { "dictionary", "symbols", "objects", "verbs",
284
 
    "assembly", "expressions", "lines", "tokens", "linker",
285
 
    "on", "off", "" },
286
 
    TRACE_KEYWORD_TT, FALSE, TRUE
287
 
};
288
 
 
289
 
keyword_group segment_markers =
290
 
{ { "class", "has", "private", "with", "" },
291
 
    SEGMENT_MARKER_TT, FALSE, TRUE
292
 
};
293
 
 
294
 
keyword_group directive_keywords =
295
 
{ { "alias", "long", "additive",
296
 
    "score", "time",
297
 
    "noun", "held", "multi", "multiheld", "multiexcept",
298
 
    "multiinside", "creature", "special", "number", "scope", "topic",
299
 
    "reverse", "meta", "only", "replace", "first", "last",
300
 
    "string", "table", "buffer", "data", "initial", "initstr",
301
 
    "with", "private", "has", "class",
302
 
    "error", "fatalerror", "warning",
303
 
    "terminating",
304
 
    "" },
305
 
    DIR_KEYWORD_TT, FALSE, TRUE
306
 
};
307
 
 
308
 
keyword_group misc_keywords =
309
 
{ { "char", "name", "the", "a", "an", "The", "number",
310
 
    "roman", "reverse", "bold", "underline", "fixed", "on", "off",
311
 
    "to", "address", "string", "object", "near", "from", "property", "A", "" },
312
 
    MISC_KEYWORD_TT, FALSE, TRUE
313
 
};
314
 
 
315
 
keyword_group statements =
316
 
{ { "box", "break", "continue", "default", "do", "else", "font", "for",
317
 
    "give", "if", "inversion", "jump", "move", "new_line", "objectloop",
318
 
    "print", "print_ret", "quit", "read", "remove", "restore", "return",
319
 
    "rfalse", "rtrue", "save", "spaces", "string", "style", "switch",
320
 
    "until", "while", "" },
321
 
    STATEMENT_TT, FALSE, TRUE
322
 
};
323
 
 
324
 
keyword_group conditions =
325
 
{ { "has", "hasnt", "in", "notin", "ofclass", "or", "provides", "" },
326
 
    CND_TT, FALSE, TRUE
327
 
};
328
 
 
329
 
keyword_group system_functions =
330
 
{ { "child", "children", "elder", "eldest", "indirect", "parent", "random",
331
 
    "sibling", "younger", "youngest", "metaclass", "glk", "" },
332
 
    SYSFUN_TT, FALSE, TRUE
333
 
};
334
 
 
335
 
keyword_group system_constants =
336
 
{ { "adjectives_table", "actions_table", "classes_table",
337
 
    "identifiers_table", "preactions_table", "version_number",
338
 
    "largest_object", "strings_offset", "code_offset",
339
 
    "dict_par1", "dict_par2", "dict_par3", "actual_largest_object",
340
 
    "static_memory_offset", "array_names_offset", "readable_memory_offset",
341
 
    "cpv__start", "cpv__end", "ipv__start", "ipv__end",
342
 
    "array__start", "array__end",
343
 
    "lowest_attribute_number", "highest_attribute_number",
344
 
    "attribute_names_array",
345
 
    "lowest_property_number", "highest_property_number",
346
 
    "property_names_array",
347
 
    "lowest_action_number", "highest_action_number",
348
 
    "action_names_array",
349
 
    "lowest_fake_action_number", "highest_fake_action_number",
350
 
    "fake_action_names_array",
351
 
    "lowest_routine_number", "highest_routine_number", "routines_array",
352
 
    "routine_names_array", "routine_flags_array",
353
 
    "lowest_global_number", "highest_global_number", "globals_array",
354
 
    "global_names_array", "global_flags_array",
355
 
    "lowest_array_number", "highest_array_number", "arrays_array",
356
 
    "array_names_array", "array_flags_array",
357
 
    "lowest_constant_number", "highest_constant_number", "constants_array",
358
 
    "constant_names_array",
359
 
    "lowest_class_number", "highest_class_number", "class_objects_array",
360
 
    "lowest_object_number", "highest_object_number",
361
 
    "oddeven_packing",
362
 
    "grammar_table", "dictionary_table", "dynam_string_table",
363
 
    "" },
364
 
    SYSTEM_CONSTANT_TT, FALSE, TRUE
365
 
};
366
 
 
367
 
keyword_group *keyword_groups[11]
368
 
= { NULL, &opcode_names, &directives, &trace_keywords, &segment_markers,
369
 
    &directive_keywords, &misc_keywords, &statements, &conditions,
370
 
    &system_functions, &system_constants};
371
 
 
372
 
keyword_group local_variables =
373
 
{ { "" },                                 /* Filled in when routine declared */
374
 
    LOCAL_VARIABLE_TT, FALSE, FALSE
375
 
};
376
 
 
377
 
static int lexical_context(void)
378
 
{
379
 
    /*  The lexical context is a number representing all of the context
380
 
        information in the lexical analyser: the same input text will
381
 
        always translate to the same output tokens whenever the context
382
 
        is the same.
383
 
 
384
 
        In fact, for efficiency reasons this number omits the bit of
385
 
        information held in the variable "dont_enter_into_symbol_table".
386
 
        Inform never needs to backtrack through tokens parsed in that
387
 
        way (thankfully, as it would be expensive indeed to check
388
 
        the tokens).                                                         */
389
 
 
390
 
    int c = 0;
391
 
    if (opcode_names.enabled)         c |= 1;
392
 
    if (directives.enabled)           c |= 2;
393
 
    if (trace_keywords.enabled)       c |= 4;
394
 
    if (segment_markers.enabled)      c |= 8;
395
 
    if (directive_keywords.enabled)   c |= 16;
396
 
    if (misc_keywords.enabled)        c |= 32;
397
 
    if (statements.enabled)           c |= 64;
398
 
    if (conditions.enabled)           c |= 128;
399
 
    if (system_functions.enabled)     c |= 256;
400
 
    if (system_constants.enabled)     c |= 512;
401
 
    if (local_variables.enabled)      c |= 1024;
402
 
 
403
 
    if (return_sp_as_variable)        c |= 2048;
404
 
    return(c);
405
 
}
406
 
 
407
 
static void print_context(int c)
408
 
{
409
 
    if ((c & 1) != 0) printf("OPC ");
410
 
    if ((c & 2) != 0) printf("DIR ");
411
 
    if ((c & 4) != 0) printf("TK ");
412
 
    if ((c & 8) != 0) printf("SEG ");
413
 
    if ((c & 16) != 0) printf("DK ");
414
 
    if ((c & 32) != 0) printf("MK ");
415
 
    if ((c & 64) != 0) printf("STA ");
416
 
    if ((c & 128) != 0) printf("CND ");
417
 
    if ((c & 256) != 0) printf("SFUN ");
418
 
    if ((c & 512) != 0) printf("SCON ");
419
 
    if ((c & 1024) != 0) printf("LV ");
420
 
    if ((c & 2048) != 0) printf("sp ");
421
 
}
422
 
 
423
 
static int *keywords_hash_table;
424
 
static int *keywords_hash_ends_table;
425
 
static int *keywords_data_table;
426
 
 
427
 
static int *local_variable_hash_table;
428
 
static int *local_variable_hash_codes;
429
 
char **local_variable_texts;
430
 
static char *local_variable_text_table;
431
 
 
432
 
static char one_letter_locals[128];
433
 
 
434
 
static void make_keywords_tables(void)
435
 
{   int i, j, h, tp=0;
436
 
    char **oplist;
437
 
 
438
 
    if (!glulx_mode)
439
 
        oplist = opcode_list_z;
440
 
    else
441
 
        oplist = opcode_list_g;
442
 
 
443
 
    for (j=0; *(oplist[j]); j++) {
444
 
        opcode_names.keywords[j] = oplist[j];
445
 
    }
446
 
    opcode_names.keywords[j] = "";
447
 
 
448
 
    for (i=0; i<HASH_TAB_SIZE; i++)
449
 
    {   keywords_hash_table[i] = -1;
450
 
        keywords_hash_ends_table[i] = -1;
451
 
    }
452
 
 
453
 
    for (i=1; i<=10; i++)
454
 
    {   keyword_group *kg = keyword_groups[i];
455
 
        for (j=0; *(kg->keywords[j]) != 0; j++)
456
 
        {   h = hash_code_from_string(kg->keywords[j]);
457
 
            if (keywords_hash_table[h] == -1)
458
 
                keywords_hash_table[h] = tp;
459
 
            else
460
 
              *(keywords_data_table + 3*(keywords_hash_ends_table[h]) + 2) = tp;
461
 
            keywords_hash_ends_table[h] = tp;
462
 
            *(keywords_data_table + 3*tp) = i;
463
 
            *(keywords_data_table + 3*tp+1) = j;
464
 
            *(keywords_data_table + 3*tp+2) = -1;
465
 
            tp++;
466
 
        }
467
 
    }
468
 
}
469
 
 
470
 
extern void construct_local_variable_tables(void)
471
 
{   int i, h; char *p = local_variable_text_table;
472
 
    for (i=0; i<HASH_TAB_SIZE; i++) local_variable_hash_table[i] = -1;
473
 
    for (i=0; i<128; i++) one_letter_locals[i] = MAX_LOCAL_VARIABLES;
474
 
 
475
 
    for (i=0; i<no_locals; i++)
476
 
    {   char *q = local_variables.keywords[i];
477
 
        if (q[1] == 0)
478
 
        {   one_letter_locals[q[0]] = i;
479
 
            if (isupper(q[0])) one_letter_locals[tolower(q[0])] = i;
480
 
            if (islower(q[0])) one_letter_locals[toupper(q[0])] = i;
481
 
        }
482
 
        h = hash_code_from_string(q);
483
 
        if (local_variable_hash_table[h] == -1)
484
 
            local_variable_hash_table[h] = i;
485
 
        local_variable_hash_codes[i] = h;
486
 
        local_variable_texts[i] = p;
487
 
        strcpy(p, q);
488
 
        p += strlen(p)+1;
489
 
    }
490
 
    for (;i<MAX_LOCAL_VARIABLES-1;i++) 
491
 
      local_variable_texts[i] = "<no such local variable>";
492
 
}
493
 
 
494
 
static void interpret_identifier(int pos, int dirs_only_flag)
495
 
{   int index, hashcode; char *p = circle[pos].text;
496
 
 
497
 
    /*  An identifier is either a keyword or a "symbol", a name which the
498
 
        lexical analyser leaves to higher levels of Inform to understand.    */
499
 
 
500
 
    hashcode = hash_code_from_string(p);
501
 
 
502
 
    if (dirs_only_flag) goto KeywordSearch;
503
 
 
504
 
    /*  If this is assembly language, perhaps it is "sp"?                    */
505
 
 
506
 
    if (return_sp_as_variable && (p[0]=='s') && (p[1]=='p') && (p[2]==0))
507
 
    {   circle[pos].value = 0; circle[pos].type = LOCAL_VARIABLE_TT;
508
 
        return;
509
 
    }
510
 
 
511
 
    /*  Test for local variables first, quite quickly.                       */
512
 
 
513
 
    if (local_variables.enabled)
514
 
    {   if (p[1]==0)
515
 
        {   index = one_letter_locals[p[0]];
516
 
            if (index<MAX_LOCAL_VARIABLES)
517
 
            {   circle[pos].type = LOCAL_VARIABLE_TT;
518
 
                circle[pos].value = index+1;
519
 
                return;
520
 
            }
521
 
        }
522
 
        index = local_variable_hash_table[hashcode];
523
 
        if (index >= 0)
524
 
        {   for (;index<no_locals;index++)
525
 
            {   if (hashcode == local_variable_hash_codes[index])
526
 
                {   if (strcmpcis(p, local_variable_texts[index])==0)
527
 
                    {   circle[pos].type = LOCAL_VARIABLE_TT;
528
 
                        circle[pos].value = index+1;
529
 
                        return;
530
 
                    }
531
 
                }
532
 
            }
533
 
        }
534
 
    }
535
 
 
536
 
    /*  Now the bulk of the keywords.  Note that the lexer doesn't recognise
537
 
        the name of a system function which has been Replaced.               */
538
 
 
539
 
    KeywordSearch:
540
 
    index = keywords_hash_table[hashcode];
541
 
    while (index >= 0)
542
 
    {   int *i = keywords_data_table + 3*index;
543
 
        keyword_group *kg = keyword_groups[*i];
544
 
        if (((!dirs_only_flag) && (kg->enabled))
545
 
            || (dirs_only_flag && (kg == &directives)))
546
 
        {   char *q = kg->keywords[*(i+1)];
547
 
            if (((kg->case_sensitive) && (strcmp(p, q)==0))
548
 
                || ((!(kg->case_sensitive)) && (strcmpcis(p, q)==0)))
549
 
            {   if ((kg != &system_functions)
550
 
                    || (system_function_usage[*(i+1)]!=2))
551
 
                {   circle[pos].type = kg->change_token_type;
552
 
                    circle[pos].value = *(i+1);
553
 
                    return;
554
 
                }
555
 
            }
556
 
        }
557
 
        index = *(i+2);
558
 
    }
559
 
 
560
 
    if (dirs_only_flag) return;
561
 
 
562
 
    /*  Search for the name; create it if necessary.                         */
563
 
 
564
 
    circle[pos].value = symbol_index(p, hashcode);
565
 
    circle[pos].type = SYMBOL_TT;
566
 
}
567
 
 
568
 
 
569
 
/* ------------------------------------------------------------------------- */
570
 
/*   The tokeniser grid aids a rapid decision about the consequences of a    */
571
 
/*   character reached in the buffer.  In effect it is an efficiently stored */
572
 
/*   transition table using an algorithm similar to that of S. C. Johnson's  */
573
 
/*   "yacc" lexical analyser (see Aho, Sethi and Ullman, section 3.9).       */
574
 
/*   My thanks to Dilip Sequeira for suggesting this.                        */
575
 
/*                                                                           */
576
 
/*       tokeniser_grid[c]   is (16*n + m) if c is the first character of    */
577
 
/*                               separator numbers n, n+1, ..., n+m-1        */
578
 
/*                           or certain special values (QUOTE_CODE, etc)     */
579
 
/*                           or 0 otherwise                                  */
580
 
/*                                                                           */
581
 
/*   Since 1000/16 = 62, the code numbers below will need increasing if the  */
582
 
/*   number of separators supported exceeds 61.                              */
583
 
/* ------------------------------------------------------------------------- */
584
 
 
585
 
static int tokeniser_grid[256];
586
 
 
587
 
#define QUOTE_CODE      1000
588
 
#define DQUOTE_CODE     1001
589
 
#define NULL_CODE       1002
590
 
#define SPACE_CODE      1003
591
 
#define NEGATIVE_CODE   1004
592
 
#define DIGIT_CODE      1005
593
 
#define RADIX_CODE      1006
594
 
#define KEYWORD_CODE    1007
595
 
#define EOF_CODE        1008
596
 
#define WHITESPACE_CODE 1009
597
 
#define COMMENT_CODE    1010
598
 
#define IDENTIFIER_CODE 1011
599
 
 
600
 
/*  This list cannot safely be changed without also changing the header
601
 
    separator #defines.  The ordering is significant in that (i) all entries
602
 
    beginning with the same character must be adjacent and (ii) that if
603
 
    X is a an initial substring of Y then X must come before Y.
604
 
 
605
 
    E.g. --> must occur before -- to prevent "-->0" being tokenised
606
 
    wrongly as "--", ">", "0" rather than "-->", "0".                        */
607
 
 
608
 
static const char separators[NUMBER_SEPARATORS][4] =
609
 
{   "->", "-->", "--", "-", "++", "+", "*", "/", "%",
610
 
    "||", "|", "&&", "&", "~~",
611
 
    "~=", "~", "==", "=", ">=", ">",
612
 
    "<=", "<", "(", ")", ",",
613
 
    ".&", ".#", "..&", "..#", "..", ".",
614
 
    "::", ":", "@", ";", "[", "]", "{", "}",
615
 
    "$", "?~", "?",
616
 
    "#a$", "#n$", "#r$", "#w$", "##", "#"
617
 
};
618
 
 
619
 
static void make_tokeniser_grid(void)
620
 
{
621
 
    /*  Construct the grid to the specification above.                       */
622
 
 
623
 
    int i, j;
624
 
 
625
 
    for (i=0; i<256; i++) tokeniser_grid[i]=0;
626
 
 
627
 
    for (i=0; i<NUMBER_SEPARATORS; i++)
628
 
    {   j=separators[i][0];
629
 
        if (tokeniser_grid[j]==0)
630
 
            tokeniser_grid[j]=i*16+1; else tokeniser_grid[j]++;
631
 
    }
632
 
    tokeniser_grid['\''] = QUOTE_CODE;
633
 
    tokeniser_grid['\"'] = DQUOTE_CODE;
634
 
    tokeniser_grid[0]    = EOF_CODE;
635
 
    tokeniser_grid[' ']  = WHITESPACE_CODE;
636
 
    tokeniser_grid['\n'] = WHITESPACE_CODE;
637
 
    tokeniser_grid['$']  = RADIX_CODE;
638
 
    tokeniser_grid['!']  = COMMENT_CODE;
639
 
 
640
 
    tokeniser_grid['0']  = DIGIT_CODE;
641
 
    tokeniser_grid['1']  = DIGIT_CODE;
642
 
    tokeniser_grid['2']  = DIGIT_CODE;
643
 
    tokeniser_grid['3']  = DIGIT_CODE;
644
 
    tokeniser_grid['4']  = DIGIT_CODE;
645
 
    tokeniser_grid['5']  = DIGIT_CODE;
646
 
    tokeniser_grid['6']  = DIGIT_CODE;
647
 
    tokeniser_grid['7']  = DIGIT_CODE;
648
 
    tokeniser_grid['8']  = DIGIT_CODE;
649
 
    tokeniser_grid['9']  = DIGIT_CODE;
650
 
 
651
 
    tokeniser_grid['a']  = IDENTIFIER_CODE;
652
 
    tokeniser_grid['b']  = IDENTIFIER_CODE;
653
 
    tokeniser_grid['c']  = IDENTIFIER_CODE;
654
 
    tokeniser_grid['d']  = IDENTIFIER_CODE;
655
 
    tokeniser_grid['e']  = IDENTIFIER_CODE;
656
 
    tokeniser_grid['f']  = IDENTIFIER_CODE;
657
 
    tokeniser_grid['g']  = IDENTIFIER_CODE;
658
 
    tokeniser_grid['h']  = IDENTIFIER_CODE;
659
 
    tokeniser_grid['i']  = IDENTIFIER_CODE;
660
 
    tokeniser_grid['j']  = IDENTIFIER_CODE;
661
 
    tokeniser_grid['k']  = IDENTIFIER_CODE;
662
 
    tokeniser_grid['l']  = IDENTIFIER_CODE;
663
 
    tokeniser_grid['m']  = IDENTIFIER_CODE;
664
 
    tokeniser_grid['n']  = IDENTIFIER_CODE;
665
 
    tokeniser_grid['o']  = IDENTIFIER_CODE;
666
 
    tokeniser_grid['p']  = IDENTIFIER_CODE;
667
 
    tokeniser_grid['q']  = IDENTIFIER_CODE;
668
 
    tokeniser_grid['r']  = IDENTIFIER_CODE;
669
 
    tokeniser_grid['s']  = IDENTIFIER_CODE;
670
 
    tokeniser_grid['t']  = IDENTIFIER_CODE;
671
 
    tokeniser_grid['u']  = IDENTIFIER_CODE;
672
 
    tokeniser_grid['v']  = IDENTIFIER_CODE;
673
 
    tokeniser_grid['w']  = IDENTIFIER_CODE;
674
 
    tokeniser_grid['x']  = IDENTIFIER_CODE;
675
 
    tokeniser_grid['y']  = IDENTIFIER_CODE;
676
 
    tokeniser_grid['z']  = IDENTIFIER_CODE;
677
 
 
678
 
    tokeniser_grid['A']  = IDENTIFIER_CODE;
679
 
    tokeniser_grid['B']  = IDENTIFIER_CODE;
680
 
    tokeniser_grid['C']  = IDENTIFIER_CODE;
681
 
    tokeniser_grid['D']  = IDENTIFIER_CODE;
682
 
    tokeniser_grid['E']  = IDENTIFIER_CODE;
683
 
    tokeniser_grid['F']  = IDENTIFIER_CODE;
684
 
    tokeniser_grid['G']  = IDENTIFIER_CODE;
685
 
    tokeniser_grid['H']  = IDENTIFIER_CODE;
686
 
    tokeniser_grid['I']  = IDENTIFIER_CODE;
687
 
    tokeniser_grid['J']  = IDENTIFIER_CODE;
688
 
    tokeniser_grid['K']  = IDENTIFIER_CODE;
689
 
    tokeniser_grid['L']  = IDENTIFIER_CODE;
690
 
    tokeniser_grid['M']  = IDENTIFIER_CODE;
691
 
    tokeniser_grid['N']  = IDENTIFIER_CODE;
692
 
    tokeniser_grid['O']  = IDENTIFIER_CODE;
693
 
    tokeniser_grid['P']  = IDENTIFIER_CODE;
694
 
    tokeniser_grid['Q']  = IDENTIFIER_CODE;
695
 
    tokeniser_grid['R']  = IDENTIFIER_CODE;
696
 
    tokeniser_grid['S']  = IDENTIFIER_CODE;
697
 
    tokeniser_grid['T']  = IDENTIFIER_CODE;
698
 
    tokeniser_grid['U']  = IDENTIFIER_CODE;
699
 
    tokeniser_grid['V']  = IDENTIFIER_CODE;
700
 
    tokeniser_grid['W']  = IDENTIFIER_CODE;
701
 
    tokeniser_grid['X']  = IDENTIFIER_CODE;
702
 
    tokeniser_grid['Y']  = IDENTIFIER_CODE;
703
 
    tokeniser_grid['Z']  = IDENTIFIER_CODE;
704
 
 
705
 
    tokeniser_grid['_']  = IDENTIFIER_CODE;
706
 
}
707
 
 
708
 
/* ------------------------------------------------------------------------- */
709
 
/*   Definition of a lexical block: a source file or a string containing     */
710
 
/*   text for lexical analysis; an independent source from the point of      */
711
 
/*   view of issuing error reports.                                          */
712
 
/* ------------------------------------------------------------------------- */
713
 
 
714
 
typedef struct LexicalBlock_s
715
 
{   char *filename;                              /*  Full translated name    */
716
 
    int   main_flag;                             /*  TRUE if the main file
717
 
                                                     (the first one opened)  */
718
 
    int   sys_flag;                              /*  TRUE if a System_File   */
719
 
    int   source_line;                           /*  Line number count       */
720
 
    int   line_start;                            /*  Char number within file
721
 
                                                     where the current line
722
 
                                                     starts                  */
723
 
    int   chars_read;                            /*  Char number of read pos */
724
 
    int   file_no;                               /*  Or 255 if not from a
725
 
                                                     file; used for debug
726
 
                                                     information             */
727
 
} LexicalBlock;
728
 
 
729
 
static LexicalBlock NoFileOpen =
730
 
{   "<before compilation>", FALSE, FALSE, 0, 0, 0, 255 };
731
 
 
732
 
static LexicalBlock MakingOutput =
733
 
{   "<constructing output>", FALSE, FALSE, 0, 0, 0, 255 };
734
 
 
735
 
static LexicalBlock StringLB =
736
 
{   "<veneer routine>", FALSE, TRUE, 0, 0, 0, 255 };
737
 
 
738
 
static LexicalBlock *CurrentLB;                  /*  The current lexical
739
 
                                                     block of input text     */
740
 
 
741
 
extern void declare_systemfile(void)
742
 
{   CurrentLB->sys_flag = TRUE;
743
 
}
744
 
 
745
 
extern int is_systemfile(void)
746
 
{   return ((CurrentLB->sys_flag)?1:0);
747
 
}
748
 
 
749
 
extern dbgl get_current_dbgl(void)
750
 
{   dbgl X; int n;
751
 
    X.b1 = CurrentLB->file_no;
752
 
    X.b2 = (CurrentLB->source_line)/256;
753
 
    X.b3 = (CurrentLB->source_line)%256;
754
 
    n = CurrentLB->chars_read - CurrentLB->line_start;
755
 
    if (n>255) n = 255;
756
 
    X.cc = n;
757
 
    return X;
758
 
}
759
 
 
760
 
static dbgl ErrorReport_dbgl;
761
 
 
762
 
extern void report_errors_at_current_line(void)
763
 
{   ErrorReport.line_number = CurrentLB->source_line;
764
 
    ErrorReport.file_number = CurrentLB->file_no;
765
 
    if (ErrorReport.file_number == 255)
766
 
        ErrorReport.file_number = -1;
767
 
    ErrorReport.source      = CurrentLB->filename;
768
 
    ErrorReport.main_flag   = CurrentLB->main_flag;
769
 
    if (debugfile_switch)
770
 
        ErrorReport_dbgl = get_current_dbgl();
771
 
}
772
 
 
773
 
extern dbgl get_error_report_dbgl(void)
774
 
{   return ErrorReport_dbgl;
775
 
}
776
 
 
777
 
extern int32 get_current_line_start(void)
778
 
{   return CurrentLB->line_start;
779
 
}
780
 
 
781
 
/* ------------------------------------------------------------------------- */
782
 
/*   Hash printing and line counting                                         */
783
 
/* ------------------------------------------------------------------------- */
784
 
 
785
 
static void print_hash(void)
786
 
{
787
 
    /*  Hash-printing is the practice of printing a # character every 100
788
 
        lines of source code (the -x switch), reassuring the user that
789
 
        progress is being made                                               */
790
 
 
791
 
    if (no_hash_printed_yet)
792
 
    {   printf("::"); no_hash_printed_yet = FALSE;
793
 
    }
794
 
    printf("#"); hash_printed_since_newline = TRUE;
795
 
 
796
 
#ifndef MAC_FACE
797
 
    /*  On some systems, text output is buffered to a line at a time, and
798
 
        this would frustrate the point of hash-printing, so:                 */
799
 
 
800
 
    fflush(stdout);
801
 
#endif
802
 
}
803
 
 
804
 
static void reached_new_line(void)
805
 
{
806
 
    /*  Called to signal that a new line has been reached in the source code */
807
 
 
808
 
    forerrors_pointer = 0;
809
 
 
810
 
    CurrentLB->source_line++;
811
 
    CurrentLB->line_start = CurrentLB->chars_read;
812
 
 
813
 
    total_source_line_count++;
814
 
 
815
 
    if (total_source_line_count%100==0)
816
 
    {   if (hash_switch) print_hash();
817
 
#ifdef MAC_MPW
818
 
        SpinCursor(32);                    /* I.e., allow other tasks to run */
819
 
#endif
820
 
    }
821
 
 
822
 
#ifdef MAC_FACE
823
 
    if (total_source_line_count%((**g_pm_hndl).linespercheck) == 0)
824
 
    {   ProcessEvents (&g_proc);
825
 
        if (g_proc != true)
826
 
        {   free_arrays();
827
 
            close_all_source();
828
 
            if (temporary_files_switch)
829
 
                remove_temp_files();
830
 
            if (store_the_text)
831
 
                my_free(&all_text,"transcription text");
832
 
            abort_transcript_file();
833
 
            longjmp (g_fallback, 1);
834
 
        }
835
 
    }
836
 
#endif
837
 
}
838
 
 
839
 
static void new_syntax_line(void)
840
 
{   if (source_to_analyse != NULL) forerrors_pointer = 0;
841
 
    report_errors_at_current_line();
842
 
}
843
 
 
844
 
/* ------------------------------------------------------------------------- */
845
 
/*   Characters are read via a "pipeline" of variables, allowing us to look  */
846
 
/*       up to three characters ahead of the current position.               */
847
 
/*                                                                           */
848
 
/*   There are two possible sources: from the source files being loaded in,  */
849
 
/*   and from a string inside Inform (which is where the code for veneer     */
850
 
/*   routines comes from).  Each source has its own get-next-character       */
851
 
/*   routine.                                                                */
852
 
/* ------------------------------------------------------------------------- */
853
 
/*   Source 1: from files                                                    */
854
 
/*                                                                           */
855
 
/*   Note that file_load_chars(p, size) loads "size" bytes into buffer "p"   */
856
 
/*   from the current input file.  If the file runs out, then if it was      */
857
 
/*   the last source file 4 EOF characters are placed in the buffer: if it   */
858
 
/*   was only an Include file ending, then a '\n' character is placed there  */
859
 
/*   (essentially to force termination of any comment line) followed by      */
860
 
/*   three harmless spaces.                                                  */
861
 
/*                                                                           */
862
 
/*   The routine returns the number of characters it has written, and note   */
863
 
/*   that this conveniently ensures that all characters in the buffer come   */
864
 
/*   from the same file.                                                     */
865
 
/* ------------------------------------------------------------------------- */
866
 
 
867
 
#define SOURCE_BUFFER_SIZE 4096                  /*  Typical disc block size */
868
 
 
869
 
typedef struct Sourcefile_s
870
 
{   char *buffer;                                /*  Input buffer            */
871
 
    int   read_pos;                              /*  Read position in buffer */
872
 
    int   size;                                  /*  Number of meaningful
873
 
                                                     characters in buffer    */
874
 
    int   la, la2, la3;                          /*  Three characters of
875
 
                                                     lookahead pipeline      */
876
 
    int   file_no;                               /*  Internal file number
877
 
                                                     (1, 2, 3, ...)          */
878
 
    LexicalBlock LB;
879
 
} Sourcefile;
880
 
 
881
 
static Sourcefile *FileStack;
882
 
static int File_sp;                              /*  Stack pointer           */
883
 
 
884
 
static Sourcefile *CF;                           /*  Top entry on stack      */
885
 
 
886
 
static int last_no_files;
887
 
 
888
 
static void begin_buffering_file(int i, int file_no)
889
 
{   int j, cnt; uchar *p;
890
 
 
891
 
    if (i >= MAX_INCLUSION_DEPTH) 
892
 
       memoryerror("MAX_INCLUSION_DEPTH",MAX_INCLUSION_DEPTH);
893
 
 
894
 
    p = (uchar *) FileStack[i].buffer;
895
 
 
896
 
    if (i>0)
897
 
    {   FileStack[i-1].la  = lookahead;
898
 
        FileStack[i-1].la2 = lookahead2;
899
 
        FileStack[i-1].la3 = lookahead3;
900
 
    }
901
 
 
902
 
    FileStack[i].file_no = file_no;
903
 
    FileStack[i].size = file_load_chars(file_no,
904
 
        (char *) p, SOURCE_BUFFER_SIZE);
905
 
    lookahead  = source_to_iso_grid[p[0]];
906
 
    lookahead2 = source_to_iso_grid[p[1]];
907
 
    lookahead3 = source_to_iso_grid[p[2]];
908
 
    FileStack[i].read_pos = 3;
909
 
 
910
 
    if (file_no==1) FileStack[i].LB.main_flag = TRUE;
911
 
               else FileStack[i].LB.main_flag = FALSE;
912
 
    FileStack[i].LB.sys_flag = FALSE;
913
 
    FileStack[i].LB.source_line = 1;
914
 
    FileStack[i].LB.line_start = 0;
915
 
    FileStack[i].LB.chars_read = 3;
916
 
    FileStack[i].LB.filename = InputFiles[file_no-1].filename;
917
 
    FileStack[i].LB.file_no = file_no;
918
 
 
919
 
    CurrentLB = &(FileStack[i].LB);
920
 
    CF = &(FileStack[i]);
921
 
 
922
 
    /* Check for recursive inclusion */
923
 
    cnt = 0;
924
 
    for (j=0; j<i; j++)
925
 
    {   if (!strcmp(FileStack[i].LB.filename, FileStack[j].LB.filename))
926
 
            cnt++;
927
 
    }
928
 
    if (cnt==1)
929
 
        warning_named("File included more than once",
930
 
            FileStack[j].LB.filename);
931
 
}
932
 
 
933
 
static void create_char_pipeline(void)
934
 
{
935
 
    File_sp = 0;
936
 
    begin_buffering_file(File_sp++, 1);
937
 
    pipeline_made = TRUE; last_no_files = input_file;
938
 
}
939
 
 
940
 
static int get_next_char_from_pipeline(void)
941
 
{   uchar *p;
942
 
 
943
 
    while (last_no_files < input_file)
944
 
    {
945
 
        /*  An "Include" file must have opened since the last character
946
 
            was read...                                                      */
947
 
 
948
 
        begin_buffering_file(File_sp++, ++last_no_files);
949
 
    }
950
 
    last_no_files = input_file;
951
 
 
952
 
    if (File_sp == 0)
953
 
    {   lookahead  = 0; lookahead2 = 0; lookahead3 = 0; return 0;
954
 
    }
955
 
 
956
 
    if (CF->read_pos == CF->size)
957
 
    {   CF->size =
958
 
            file_load_chars(CF->file_no, CF->buffer, SOURCE_BUFFER_SIZE);
959
 
        CF->read_pos = 0;
960
 
    }
961
 
    else
962
 
    if (CF->read_pos == -(CF->size))
963
 
    {   File_sp--;
964
 
        if (File_sp == 0)
965
 
        {   lookahead  = 0; lookahead2 = 0; lookahead3 = 0; return 0;
966
 
        }
967
 
        CF = &(FileStack[File_sp-1]);
968
 
        CurrentLB = &(FileStack[File_sp-1].LB);
969
 
        lookahead  = CF->la; lookahead2 = CF->la2; lookahead3 = CF->la3;
970
 
        if (CF->read_pos == CF->size)
971
 
        {   CF->size =
972
 
                file_load_chars(CF->file_no, CF->buffer, SOURCE_BUFFER_SIZE);
973
 
            CF->read_pos = 0;
974
 
        }
975
 
    }
976
 
 
977
 
    p = (uchar *) (CF->buffer);
978
 
 
979
 
    current = lookahead;
980
 
    lookahead = lookahead2;
981
 
    lookahead2 = lookahead3;
982
 
    lookahead3 = source_to_iso_grid[p[CF->read_pos++]];
983
 
 
984
 
    CurrentLB->chars_read++;
985
 
    if (forerrors_pointer < 511)
986
 
        forerrors_buff[forerrors_pointer++] = current;
987
 
    if (current == '\n') reached_new_line();
988
 
    return(current);
989
 
}
990
 
 
991
 
/* ------------------------------------------------------------------------- */
992
 
/*   Source 2: from a string                                                 */
993
 
/* ------------------------------------------------------------------------- */
994
 
 
995
 
static int source_to_analyse_pointer;            /*  Current read position   */
996
 
 
997
 
static int get_next_char_from_string(void)
998
 
{   uchar *p = (uchar *) source_to_analyse + source_to_analyse_pointer++;
999
 
    current = source_to_iso_grid[p[0]];
1000
 
 
1001
 
    if (current == 0)    lookahead  = 0;
1002
 
                    else lookahead  = source_to_iso_grid[p[1]];
1003
 
    if (lookahead == 0)  lookahead2 = 0;
1004
 
                    else lookahead2 = source_to_iso_grid[p[2]];
1005
 
    if (lookahead2 == 0) lookahead3 = 0;
1006
 
                    else lookahead3 = source_to_iso_grid[p[3]];
1007
 
 
1008
 
    CurrentLB->chars_read++;
1009
 
    if (forerrors_pointer < 511)
1010
 
        forerrors_buff[forerrors_pointer++] = current;
1011
 
    if (current == '\n') reached_new_line();
1012
 
    return(current);
1013
 
}
1014
 
 
1015
 
/* ========================================================================= */
1016
 
/*   The interface between the lexer and Inform's higher levels:             */
1017
 
/*                                                                           */
1018
 
/*       put_token_back()            (effectively) move the read position    */
1019
 
/*                                       back by one token                   */
1020
 
/*                                                                           */
1021
 
/*       get_next_token()            copy the token at the current read      */
1022
 
/*                                       position into the triple            */
1023
 
/*                                   (token_type, token_value, token_text)   */
1024
 
/*                                       and move the read position forward  */
1025
 
/*                                       by one                              */
1026
 
/*                                                                           */
1027
 
/*       restart_lexer(source, name) if source is NULL, initialise the lexer */
1028
 
/*                                       to read from source files;          */
1029
 
/*                                   otherwise, to read from this string.    */
1030
 
/* ------------------------------------------------------------------------- */
1031
 
 
1032
 
extern void put_token_back(void)
1033
 
{   tokens_put_back++;
1034
 
 
1035
 
    if (tokens_trace_level > 0)
1036
 
    {   if (tokens_trace_level == 1) printf("<- ");
1037
 
        else printf("<-\n");
1038
 
    }
1039
 
 
1040
 
    /*  The following error, of course, should never happen!                 */
1041
 
 
1042
 
    if (tokens_put_back == CIRCLE_SIZE)
1043
 
    {   compiler_error("The lexical analyser has collapsed because of a wrong \
1044
 
assumption inside Inform");
1045
 
        tokens_put_back--;
1046
 
        return;
1047
 
    }
1048
 
}
1049
 
 
1050
 
extern void get_next_token(void)
1051
 
{   int d, i, j, k, quoted_size, e, radix, context; int32 n; char *r;
1052
 
 
1053
 
    context = lexical_context();
1054
 
 
1055
 
    if (tokens_put_back > 0)
1056
 
    {   i = circle_position - tokens_put_back + 1;
1057
 
        if (i<0) i += CIRCLE_SIZE;
1058
 
        tokens_put_back--;
1059
 
        if (context != token_contexts[i])
1060
 
        {   j = circle[i].type;
1061
 
            if ((j==0) || ((j>=100) && (j<200)))
1062
 
                interpret_identifier(i, FALSE);
1063
 
        }
1064
 
        goto ReturnBack;
1065
 
    }
1066
 
 
1067
 
    if (circle_position == CIRCLE_SIZE-1) circle_position = 0;
1068
 
    else circle_position++;
1069
 
 
1070
 
    if (lex_p > lexeme_memory + 4*MAX_QTEXT_SIZE)
1071
 
        lex_p = lexeme_memory;
1072
 
 
1073
 
    circle[circle_position].text = lex_p;
1074
 
    circle[circle_position].value = 0;
1075
 
    *lex_p = 0;
1076
 
 
1077
 
    StartTokenAgain:
1078
 
    d = (*get_next_char)();
1079
 
    e = tokeniser_grid[d];
1080
 
 
1081
 
    if (next_token_begins_syntax_line)
1082
 
    {   if ((e != WHITESPACE_CODE) && (e != COMMENT_CODE))
1083
 
        {   new_syntax_line();
1084
 
            next_token_begins_syntax_line = FALSE;
1085
 
        }
1086
 
    }
1087
 
 
1088
 
    circle[circle_position].line_ref = get_current_dbgl();
1089
 
 
1090
 
    switch(e)
1091
 
    {   case 0: char_error("Illegal character found in source:", d);
1092
 
            goto StartTokenAgain;
1093
 
 
1094
 
        case WHITESPACE_CODE:
1095
 
            while (tokeniser_grid[lookahead] == WHITESPACE_CODE)
1096
 
                (*get_next_char)();
1097
 
            goto StartTokenAgain;
1098
 
 
1099
 
        case COMMENT_CODE:
1100
 
            while ((lookahead != '\n') && (lookahead != 0))
1101
 
                (*get_next_char)();
1102
 
            goto StartTokenAgain;
1103
 
 
1104
 
        case EOF_CODE:
1105
 
            circle[circle_position].type = EOF_TT;
1106
 
            strcpy(lex_p, "<end of file>");
1107
 
            lex_p += strlen(lex_p) + 1;
1108
 
            break;
1109
 
 
1110
 
        case DIGIT_CODE:
1111
 
            radix = 10;
1112
 
            ReturnNumber:
1113
 
            n=0;
1114
 
            do
1115
 
            {   n = n*radix + character_digit_value[d];
1116
 
                *lex_p++ = d;
1117
 
            } while ((character_digit_value[lookahead] < radix)
1118
 
                     && (d = (*get_next_char)(), TRUE));
1119
 
 
1120
 
            *lex_p++ = 0;
1121
 
            circle[circle_position].type = NUMBER_TT;
1122
 
            circle[circle_position].value = n;
1123
 
            break;
1124
 
 
1125
 
        case RADIX_CODE:
1126
 
            radix = 16; d = (*get_next_char)();
1127
 
            if (d == '$') { d = (*get_next_char)(); radix = 2; }
1128
 
            if (character_digit_value[d] >= radix)
1129
 
            {   if (radix == 2)
1130
 
                    error("Binary number expected after '$$'");
1131
 
                else
1132
 
                    error("Hexadecimal number expected after '$'");
1133
 
            }
1134
 
            goto ReturnNumber;
1135
 
 
1136
 
        case QUOTE_CODE:     /* Single-quotes: scan a literal string */
1137
 
            quoted_size=0;
1138
 
            do
1139
 
            {   e = d; d = (*get_next_char)(); *lex_p++ = d;
1140
 
                if (quoted_size++==64)
1141
 
                {   error(
1142
 
                    "Too much text for one pair of quotations '...' to hold");
1143
 
                    *lex_p='\''; break;
1144
 
                }
1145
 
                if ((d == '\'') && (e != '@'))
1146
 
                {   if (quoted_size == 1)
1147
 
                    {   d = (*get_next_char)(); *lex_p++ = d;
1148
 
                        if (d != '\'')
1149
 
                            error("No text between quotation marks ''");
1150
 
                    }
1151
 
                    break;
1152
 
                }
1153
 
            } while (d != EOF);
1154
 
            if (d==EOF) ebf_error("'\''", "end of file");
1155
 
            *(lex_p-1) = 0;
1156
 
            circle[circle_position].type = SQ_TT;
1157
 
            break;
1158
 
 
1159
 
        case DQUOTE_CODE:    /* Double-quotes: scan a literal string */
1160
 
            quoted_size=0;
1161
 
            do
1162
 
            {   d = (*get_next_char)(); *lex_p++ = d;
1163
 
                if (quoted_size++==MAX_QTEXT_SIZE)
1164
 
                {   error(
1165
 
                  "Too much text for one pair of quotations \"...\" to hold");
1166
 
                    break;
1167
 
                }
1168
 
                if (d == '\n')
1169
 
                {   lex_p--;
1170
 
                    while (*(lex_p-1) == ' ') lex_p--;
1171
 
                    if (*(lex_p-1) != '^') *lex_p++ = ' ';
1172
 
                    while ((lookahead != EOF) &&
1173
 
                          (tokeniser_grid[lookahead] == WHITESPACE_CODE))
1174
 
                    (*get_next_char)();
1175
 
                }
1176
 
                else if (d == '\\')
1177
 
                {   int newline_passed = FALSE;
1178
 
                    lex_p--;
1179
 
                    while ((lookahead != EOF) &&
1180
 
                          (tokeniser_grid[lookahead] == WHITESPACE_CODE))
1181
 
                        if ((d = (*get_next_char)()) == '\n')
1182
 
                            newline_passed = TRUE;
1183
 
                    if (!newline_passed)
1184
 
                    {   char chb[4];
1185
 
                        chb[0] = '\"'; chb[1] = lookahead;
1186
 
                        chb[2] = '\"'; chb[3] = 0;
1187
 
                        ebf_error("empty rest of line after '\\' in string",
1188
 
                            chb);
1189
 
                    }
1190
 
                }
1191
 
            }   while ((d != EOF) && (d!='\"'));
1192
 
            if (d==EOF) ebf_error("'\"'", "end of file");
1193
 
            *(lex_p-1) = 0;
1194
 
            circle[circle_position].type = DQ_TT;
1195
 
            break;
1196
 
 
1197
 
        case IDENTIFIER_CODE:    /* Letter or underscore: an identifier */
1198
 
 
1199
 
            *lex_p++ = d; n=1;
1200
 
            while ((n<=MAX_IDENTIFIER_LENGTH)
1201
 
                   && ((tokeniser_grid[lookahead] == IDENTIFIER_CODE)
1202
 
                   || (tokeniser_grid[lookahead] == DIGIT_CODE)))
1203
 
                n++, *lex_p++ = (*get_next_char)();
1204
 
 
1205
 
            *lex_p++ = 0;
1206
 
 
1207
 
            if (n > MAX_IDENTIFIER_LENGTH)
1208
 
            {   char bad_length[100];
1209
 
                sprintf(bad_length,
1210
 
                    "Name exceeds the maximum length of %d characters:",
1211
 
                         MAX_IDENTIFIER_LENGTH);
1212
 
                error_named(bad_length, circle[circle_position].text);
1213
 
            }
1214
 
 
1215
 
            if (dont_enter_into_symbol_table)
1216
 
            {   circle[circle_position].type = DQ_TT;
1217
 
                circle[circle_position].value = 0;
1218
 
                if (dont_enter_into_symbol_table == -2)
1219
 
                    interpret_identifier(circle_position, TRUE);
1220
 
                break;
1221
 
            }
1222
 
 
1223
 
            interpret_identifier(circle_position, FALSE);
1224
 
            break;
1225
 
 
1226
 
        default:
1227
 
 
1228
 
            /*  The character is initial to at least one of the separators  */
1229
 
 
1230
 
            for (j=e>>4, k=j+(e&0x0f); j<k; j++)
1231
 
            {   r = (char *) separators[j];
1232
 
                if (r[1]==0)
1233
 
                {   *lex_p++=d; *lex_p++=0;
1234
 
                    goto SeparatorMatched;
1235
 
                }
1236
 
                else
1237
 
                if (r[2]==0)
1238
 
                {   if (*(r+1) == lookahead)
1239
 
                    {   *lex_p++=d;
1240
 
                        *lex_p++=(*get_next_char)();
1241
 
                        *lex_p++=0;
1242
 
                        goto SeparatorMatched;
1243
 
                    }
1244
 
                }
1245
 
                else
1246
 
                {   if ((*(r+1) == lookahead) && (*(r+2) == lookahead2))
1247
 
                    {   *lex_p++=d;
1248
 
                        *lex_p++=(*get_next_char)();
1249
 
                        *lex_p++=(*get_next_char)();
1250
 
                        *lex_p++=0;
1251
 
                        goto SeparatorMatched;
1252
 
                    }
1253
 
                }
1254
 
            }
1255
 
 
1256
 
            /*  The following contingency never in fact arises with the
1257
 
                current set of separators, but might in future  */
1258
 
 
1259
 
            *lex_p++ = d; *lex_p++ = lookahead; *lex_p++ = lookahead2;
1260
 
            *lex_p++ = 0;
1261
 
            error_named("Unrecognised combination in source:", lex_p);
1262
 
            goto StartTokenAgain;
1263
 
 
1264
 
            SeparatorMatched:
1265
 
 
1266
 
            circle[circle_position].type = SEP_TT;
1267
 
            circle[circle_position].value = j;
1268
 
            switch(j)
1269
 
            {   case SEMICOLON_SEP: break;
1270
 
                case HASHNDOLLAR_SEP:
1271
 
                case HASHWDOLLAR_SEP:
1272
 
                    if (tokeniser_grid[lookahead] == WHITESPACE_CODE)
1273
 
                    {   error_named("Character expected after",
1274
 
                            circle[circle_position].text);
1275
 
                        break;
1276
 
                    }
1277
 
                    lex_p--;
1278
 
                    *lex_p++ = (*get_next_char)();
1279
 
                    while ((tokeniser_grid[lookahead] == IDENTIFIER_CODE)
1280
 
                           || (tokeniser_grid[lookahead] == DIGIT_CODE))
1281
 
                        *lex_p++ = (*get_next_char)();
1282
 
                    *lex_p++ = 0;
1283
 
                    break;
1284
 
                case HASHADOLLAR_SEP:
1285
 
                case HASHRDOLLAR_SEP:
1286
 
                case HASHHASH_SEP:
1287
 
                    if (tokeniser_grid[lookahead] != IDENTIFIER_CODE)
1288
 
                    {   error_named("Alphabetic character expected after",
1289
 
                            circle[circle_position].text);
1290
 
                        break;
1291
 
                    }
1292
 
                    lex_p--;
1293
 
                    while ((tokeniser_grid[lookahead] == IDENTIFIER_CODE)
1294
 
                           || (tokeniser_grid[lookahead] == DIGIT_CODE))
1295
 
                        *lex_p++ = (*get_next_char)();
1296
 
                    *lex_p++ = 0;
1297
 
                    break;
1298
 
            }
1299
 
            break;
1300
 
    }
1301
 
 
1302
 
    i = circle_position;
1303
 
 
1304
 
    ReturnBack:
1305
 
    token_value = circle[i].value;
1306
 
    token_type = circle[i].type;
1307
 
    token_text = circle[i].text;
1308
 
    token_line_ref = circle[i].line_ref;
1309
 
    token_contexts[i] = context;
1310
 
 
1311
 
    if (tokens_trace_level > 0)
1312
 
    {   if (tokens_trace_level == 1)
1313
 
            printf("'%s' ", circle[i].text);
1314
 
        else
1315
 
        {   printf("-> "); describe_token(circle[i]);
1316
 
            printf(" ");
1317
 
            if (tokens_trace_level > 2) print_context(token_contexts[i]);
1318
 
            printf("\n");
1319
 
        }
1320
 
    }
1321
 
}
1322
 
 
1323
 
static char veneer_error_title[64];
1324
 
 
1325
 
extern void restart_lexer(char *lexical_source, char *name)
1326
 
{   int i;
1327
 
    circle_position = 0;
1328
 
    for (i=0; i<CIRCLE_SIZE; i++)
1329
 
    {   circle[i].type = 0;
1330
 
        circle[i].value = 0;
1331
 
        circle[i].text = "(if this is ever visible, there is a bug)";
1332
 
        token_contexts[i] = 0;
1333
 
    }
1334
 
 
1335
 
    lex_p = lexeme_memory;
1336
 
    tokens_put_back = 0;
1337
 
    forerrors_pointer = 0;
1338
 
    dont_enter_into_symbol_table = FALSE;
1339
 
    return_sp_as_variable = FALSE;
1340
 
    next_token_begins_syntax_line = TRUE;
1341
 
 
1342
 
    source_to_analyse = lexical_source;
1343
 
 
1344
 
    if (source_to_analyse == NULL)
1345
 
    {   get_next_char = get_next_char_from_pipeline;
1346
 
        if (!pipeline_made) create_char_pipeline();
1347
 
        forerrors_buff[0] = 0; forerrors_pointer = 0;
1348
 
    }
1349
 
    else
1350
 
    {   get_next_char = get_next_char_from_string;
1351
 
        source_to_analyse_pointer = 0;
1352
 
        CurrentLB = &StringLB;
1353
 
        sprintf(veneer_error_title, "<veneer routine '%s'>", name);
1354
 
        StringLB.filename = veneer_error_title;
1355
 
 
1356
 
        CurrentLB->source_line = 1;
1357
 
        CurrentLB->line_start  = 0;
1358
 
        CurrentLB->chars_read  = 0;
1359
 
    }
1360
 
}
1361
 
 
1362
 
/* ========================================================================= */
1363
 
/*   Data structure management routines                                      */
1364
 
/* ------------------------------------------------------------------------- */
1365
 
 
1366
 
extern void init_lexer_vars(void)
1367
 
{
1368
 
}
1369
 
 
1370
 
extern void lexer_begin_prepass(void)
1371
 
{   total_source_line_count = 0;
1372
 
    CurrentLB = &NoFileOpen;
1373
 
    report_errors_at_current_line();
1374
 
}
1375
 
 
1376
 
extern void lexer_begin_pass(void)
1377
 
{   no_hash_printed_yet = TRUE;
1378
 
    hash_printed_since_newline = FALSE;
1379
 
 
1380
 
    pipeline_made = FALSE;
1381
 
 
1382
 
    restart_lexer(NULL, NULL);
1383
 
}
1384
 
 
1385
 
extern void lexer_endpass(void)
1386
 
{   CurrentLB = &MakingOutput;
1387
 
    report_errors_at_current_line();
1388
 
}
1389
 
 
1390
 
extern void lexer_allocate_arrays(void)
1391
 
{   int i;
1392
 
 
1393
 
    FileStack = my_malloc(MAX_INCLUSION_DEPTH*sizeof(Sourcefile),
1394
 
        "filestack buffer");
1395
 
 
1396
 
    for (i=0; i<MAX_INCLUSION_DEPTH; i++)
1397
 
    FileStack[i].buffer = my_malloc(SOURCE_BUFFER_SIZE+4, "source file buffer");
1398
 
 
1399
 
    lexeme_memory = my_malloc(5*MAX_QTEXT_SIZE, "lexeme memory");
1400
 
 
1401
 
    keywords_hash_table = my_calloc(sizeof(int), HASH_TAB_SIZE,
1402
 
        "keyword hash table");
1403
 
    keywords_hash_ends_table = my_calloc(sizeof(int), HASH_TAB_SIZE,
1404
 
        "keyword hash end table");
1405
 
    keywords_data_table = my_calloc(sizeof(int), 3*MAX_KEYWORDS,
1406
 
        "keyword hashing linked list");
1407
 
    local_variable_hash_table = my_calloc(sizeof(int), HASH_TAB_SIZE,
1408
 
        "local variable hash table");
1409
 
    local_variable_text_table = my_malloc(
1410
 
        (MAX_LOCAL_VARIABLES-1)*(MAX_IDENTIFIER_LENGTH+1),
1411
 
        "text of local variable names");
1412
 
 
1413
 
    local_variable_hash_codes = my_calloc(sizeof(int), MAX_LOCAL_VARIABLES,
1414
 
        "local variable hash codes");
1415
 
    local_variable_texts = my_calloc(sizeof(char *), MAX_LOCAL_VARIABLES,
1416
 
        "local variable text pointers");
1417
 
 
1418
 
    make_tokeniser_grid();
1419
 
    make_keywords_tables();
1420
 
}
1421
 
 
1422
 
extern void lexer_free_arrays(void)
1423
 
{   int i; char *p;
1424
 
 
1425
 
    for (i=0; i<MAX_INCLUSION_DEPTH; i++)
1426
 
    {   p = FileStack[i].buffer;
1427
 
        my_free(&p, "source file buffer");
1428
 
    }
1429
 
    my_free(&FileStack, "filestack buffer");
1430
 
    my_free(&lexeme_memory, "lexeme memory");
1431
 
 
1432
 
    my_free(&keywords_hash_table, "keyword hash table");
1433
 
    my_free(&keywords_hash_ends_table, "keyword hash end table");
1434
 
    my_free(&keywords_data_table, "keyword hashing linked list");
1435
 
    my_free(&local_variable_hash_table, "local variable hash table");
1436
 
    my_free(&local_variable_text_table, "text of local variable names");
1437
 
 
1438
 
    my_free(&local_variable_hash_codes, "local variable hash codes");
1439
 
    my_free(&local_variable_texts, "local variable text pointers");
1440
 
}
1441
 
 
1442
 
/* ========================================================================= */