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

« back to all changes in this revision

Viewing changes to inform-6.31.1/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.31                                                     */
 
5
/*   copyright (c) Graham Nelson 1993 - 2006                                 */
 
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
/* ========================================================================= */