Pol  Revision:4b29d2b
parser.cpp
Go to the documentation of this file.
1 
43 #include "parser.h"
44 
45 #include <cctype>
46 #include <cstddef>
47 #include <cstdlib>
48 #include <cstring>
49 #include <mutex>
50 #include <string>
51 #include <unordered_map>
52 
53 #include "../clib/clib.h"
54 #include "../clib/logfacility.h"
55 #include "../clib/passert.h"
56 #include "../clib/rawtypes.h"
57 #include "../clib/strutil.h"
58 #include "../clib/unittest.h"
59 #include "compctx.h"
60 #include "compilercfg.h"
61 #include "fmodule.h"
62 #include "modules.h"
63 #include "objmembers.h"
64 #include "objmethods.h"
65 #include "token.h"
66 #include "tokens.h"
67 #include <format/format.h>
68 
69 namespace Pol
70 {
71 namespace Bscript
72 {
73 static void init_tables();
74 
75 Parser::Parser() : quiet( 0 ), err( PERR_NONE ), contains_tabs( false )
76 {
77  memset( &ext_err, 0, sizeof( ext_err ) );
78  memset( &buffer, 0, sizeof( buffer ) );
79  init_tables();
80 }
81 
82 const char* ParseErrorStr[PERR_NUM_ERRORS] = {"(No Error, or not specified)",
83  "Unexpected ')'",
84  "Missing '('",
85  "Missing ')'",
86  "Bad Token",
87  "Unknown Operator",
88  "Waaah!",
89  "Unterminated String Literal",
90  "Invalid escape sequence in String",
91  "Too Few Arguments",
92  "Too Many Arguments",
93  "Unexpected Comma",
94  "Illegal Construction",
95  "Missing ';'",
96  "Token not legal here",
97  "Procedure calls not allowed here",
98  "Unexpected Semicolon",
99  "Expected 'while'",
100  "Unexpected ']'",
101  "Missing ']'"};
102 char operator_brk[] = "+-/*(),<=>,:;%";
103 
105  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
106  "abcdefghijklmnopqrstuvwxyz"
107  "0123456789"
108  "_"; // $%@& removed 9/17/1998 Syz
109 
110 
111 int allowed_table[8][8] = {
112  /* this token is a(n)... */
113  /* binary unary
114  * TERMINATOR OPERAND OPERATOR OPERATOR LPAREN RPAREN LBRACK RBRACK*/
115  /* Last token was a(n)... */
116  {1, 1, 0, 1, 1, 0, 0, 0}, /* TERMINATOR */
117  {1, 0, 1, 0, 0, 1, 1, 1}, /* OPERAND */
118  {0, 1, 0, 1, 1, 0, 0, 0}, /* BINARY OPERATOR */
119  {0, 1, 0, 0, 1, 0, 0, 0}, /* UNARY OPERATOR */
120  {0, 1, 0, 1, 1, 0, 0, 0}, /* LEFT PARENTHESIS */
121  {1, 0, 1, 0, 0, 1, 0, 1}, /* RIGHT PARENTHESIS */
122  {0, 1, 0, 0, 1, 0, 1, 0}, /* LEFT BRACKET */
123  {1, 0, 1, 0, 0, 1, 1, 1}, /* RIGHT BRACKET */
124  /* Separators are always allowed. Terminators are mostly always allowed. */
125 };
126 /* examples matrix: -- connected denotes unary operator
127 
128 legal:
129 { TT T AB T -AB T ( },
130 { AB T AB + AB ) A[ A] },
131 { * AB *- * ( },
132 { -AB -( },
133 { ( AB (- ( ( },
134 { ) T ) - ) ) )] }
135 { [A . [- [( [[ . }
136 { ] T . ] * . . ]) ][ ][ }
137 
138 illegal:
139 { T b-A T ) T [ T ] },
140 { AB AB AB~ AB ( },
141 { * T * / - ) *[ *] },
142 { b- T -* -- -) -[ -] },
143 { ( T ( * ( ) ([ (] },
144 { ) AB )- ) ( )[ }
145 { [ T [+ [) [] }
146 { ]A ]- ] ( . }
147 
148 */
149 
150 
151 /* operator characters // (and precedence table)
152  ( ) [ ]
153  + - (unary-arithmetic) ! (unary-logical) ~ (unary-boolean)
154  * / %
155  + -
156  < <= > >=
157  == <>
158  & (band ?)
159  ^ (bxor ?)
160  | (bor ?)
161  and
162  or
163  :=
164  ,
165 
166  Problem: How to recognize a unary operator?
167  Proposed solution: If a binary operator would be legal, use it
168  otherwise, it must be a unary operator.
169  Examples: (-5+7)
170  1) grabs '('. now only lparens and operands are legal.
171  2) grabs '-'. Since this is a binary operator (TYP_OPERATOR)
172  by default, this is illegal. Since it is illegal,
173  parseToken() tries the unary operator table, and finds it.
174  ( now, only left parens and operands are legal. )
175  3) gets 5, an operand.
176  Alternative: Pass getToken()a parameter denoting what is legal and
177  let it ignore possibilities that are illegal.
178 
179  See end of file for handling unary operators
180  */
181 
182 /*
183  The precedence table should be split from the operator
184  recognition table, because some operators are words and
185  have to be picked up in the reserved word table.
186  */
187 
188 
190 
191  {"(", TOK_LPAREN, PREC_PAREN, TYP_LEFTPAREN, false, false},
192  {")", TOK_RPAREN, PREC_PAREN, TYP_RIGHTPAREN, false, false},
193  {"[", TOK_LBRACKET, PREC_PAREN, TYP_LEFTBRACKET, false, false},
194  {"]", TOK_RBRACKET, PREC_PAREN, TYP_RIGHTBRACKET, false, false},
195  {"{", TOK_LBRACE, PREC_PAREN, TYP_LEFTBRACE, false, false},
196  {"}", TOK_RBRACE, PREC_PAREN, TYP_RIGHTBRACE, false, false},
197 
198  {".", TOK_MEMBER, PREC_PAREN, TYP_OPERATOR, true, false},
199  {"->", TOK_DICTKEY, PREC_ASSIGN, TYP_RESERVED, false, false},
200 
201  {"*", TOK_MULT, PREC_MULT, TYP_OPERATOR, true, false},
202  {"/", TOK_DIV, PREC_MULT, TYP_OPERATOR, true, false},
203  {"%", TOK_MODULUS, PREC_MULT, TYP_OPERATOR, true, false},
204 
205  {"+", TOK_ADD, PREC_PLUS, TYP_OPERATOR, true, false},
206  {"-", TOK_SUBTRACT, PREC_PLUS, TYP_OPERATOR, true, false},
207 
208  {"+=", TOK_PLUSEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false},
209  {"-=", TOK_MINUSEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false},
210  {"*=", TOK_TIMESEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false},
211  {"/=", TOK_DIVIDEEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false},
212  {"%=", TOK_MODULUSEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false},
213 
214  {"<=", TOK_LESSEQ, PREC_LESSTHAN, TYP_OPERATOR, false, false},
215  {"<", TOK_LESSTHAN, PREC_LESSTHAN, TYP_OPERATOR, true, false},
216  {">=", TOK_GREQ, PREC_LESSTHAN, TYP_OPERATOR, false, false},
217  {">", TOK_GRTHAN, PREC_LESSTHAN, TYP_OPERATOR, true, false},
218 
219  {">>", TOK_BSRIGHT, PREC_BSRIGHT, TYP_OPERATOR, false, false},
220  {"<<", TOK_BSLEFT, PREC_BSLEFT, TYP_OPERATOR, false, false},
221  {"&", TOK_BITAND, PREC_BITAND, TYP_OPERATOR, true, false},
222  {"^", TOK_BITXOR, PREC_BITXOR, TYP_OPERATOR, false, false},
223  {"|", TOK_BITOR, PREC_BITOR, TYP_OPERATOR, true, false},
224 
225  {"<>", TOK_NEQ, PREC_EQUALTO, TYP_OPERATOR, false, false},
226  {"!=", TOK_NEQ, PREC_EQUALTO, TYP_OPERATOR, false, false},
227  {"=", TOK_EQUAL1, PREC_EQUALTO, TYP_OPERATOR, true, false}, // deprecated: :=/==
228  {"==", TOK_EQUAL, PREC_EQUALTO, TYP_OPERATOR, false, false},
229 
230  // { "and", TOK_AND, PREC_LOGAND, TYP_OPERATOR, false, false },
231  {"&&", TOK_AND, PREC_LOGAND, TYP_OPERATOR, false, false},
232 
233  // { "or", TOK_OR, PREC_LOGOR, TYP_OPERATOR, false, false },
234  {"||", TOK_OR, PREC_LOGOR, TYP_OPERATOR, false, false},
235 
236  {":=", TOK_ASSIGN, PREC_ASSIGN, TYP_OPERATOR, false, false},
237  {".+", TOK_ADDMEMBER, PREC_ASSIGN, TYP_OPERATOR, false, false},
238  {".-", TOK_DELMEMBER, PREC_ASSIGN, TYP_OPERATOR, false, false},
239  {".?", TOK_CHKMEMBER, PREC_ASSIGN, TYP_OPERATOR, false, false},
240 
241  {",", TOK_COMMA, PREC_COMMA, TYP_SEPARATOR, false, false},
242  {"", TOK_TERM, PREC_TERMINATOR, TYP_TERMINATOR, false, false}};
243 int n_operators = sizeof binary_operators / sizeof binary_operators[0];
244 
246  {"+", TOK_UNPLUS, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false},
247  {"-", TOK_UNMINUS, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false},
248  {"!", TOK_LOG_NOT, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false},
249  {"~", TOK_BITWISE_NOT, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false},
250  {"@", TOK_FUNCREF, PREC_UNARY_OPS, TYP_FUNCREF, false, false}
251  // { "not", TOK_LOG_NOT, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false }
252  // "refto", TOK_REFTO, 12, TYP_UNARY_OPERATOR, false, false
253 };
254 int n_unary = sizeof unary_operators / sizeof unary_operators[0];
255 
257  // MBR_*, "name", read_only
258  {MBR_X, "x", true}, // 0
259  {MBR_Y, "y", true}, // 1
260  {MBR_Z, "z", true},
261  {MBR_NAME, "name", false},
262  {MBR_OBJTYPE, "objtype", true},
263  {MBR_GRAPHIC, "graphic", false}, // 5
264  {MBR_SERIAL, "serial", true},
265  {MBR_COLOR, "color", false},
266  {MBR_HEIGHT, "height", true},
267  {MBR_FACING, "facing", false},
268  {MBR_DIRTY, "dirty", true}, // 10
269  {MBR_WEIGHT, "weight", true},
270  {MBR_MULTI, "multi", true},
271  {MBR_AMOUNT, "amount", true}, // item
272  {MBR_LAYER, "layer", true},
273  {MBR_CONTAINER, "container", true}, // 15
274  {MBR_USESCRIPT, "usescript", false},
275  {MBR_EQUIPSCRIPT, "equipscript", false},
276  {MBR_UNEQUIPSCRIPT, "unequipscript", false},
277  {MBR_DESC, "desc", false},
278  {MBR_MOVABLE, "movable", false}, // 20
279  {MBR_INVISIBLE, "invisible", false},
280  {MBR_DECAYAT, "decayat", false},
281  {MBR_SELLPRICE, "sellprice", false},
282  {MBR_BUYPRICE, "buyprice", false},
283  {MBR_NEWBIE, "newbie", false}, // 25
284  {MBR_ITEM_COUNT, "item_count", true},
285  {MBR_WARMODE, "warmode", true}, // character
286  {MBR_GENDER, "gender", false},
287  {MBR_TRUEOBJTYPE, "trueobjtype", false},
288  {MBR_TRUECOLOR, "truecolor", false}, // 30
289  {MBR_AR_MOD, "ar_mod", false},
290  {MBR_HIDDEN, "hidden", false},
291  {MBR_CONCEALED, "concealed", false},
292  {MBR_FROZEN, "frozen", false},
293  {MBR_PARALYZED, "paralyzed", false}, // 35
294  {MBR_POISONED, "poisoned", false},
295  {MBR_STEALTHSTEPS, "stealthsteps", false},
296  {MBR_SQUELCHED, "squelched", true},
297  {MBR_DEAD, "dead", true},
298  {MBR_AR, "ar", true}, // 40
299  {MBR_BACKPACK, "backpack", true},
300  {MBR_WEAPON, "weapon", true},
301  {MBR_SHIELD, "shield", true},
302  {MBR_ACCTNAME, "acctname", true},
303  {MBR_ACCT, "acct", true}, // 45
304  {MBR_CMDLEVEL, "cmdlevel", false},
305  {MBR_CMDLEVELSTR, "cmdlevelstr", true},
306  {MBR_CRIMINAL, "criminal", true},
307  {MBR_IP, "ip", true},
308  {MBR_GOLD, "gold", true}, // 50
309  {MBR_TITLE_PREFIX, "title_prefix", false},
310  {MBR_TITLE_SUFFIX, "title_suffix", false},
311  {MBR_TITLE_GUILD, "title_guild", false},
312  {MBR_TITLE_RACE, "title_race", false},
313  {MBR_GUILDID, "guildid", true}, // 55
314  {MBR_GUILD, "guild", true},
315  {MBR_MURDERER, "murderer", false},
316  {MBR_ATTACHED, "attached", true},
317  {MBR_CLIENTVERSION, "clientversion", true},
318  {MBR_REPORTABLES, "reportables", true}, // 60
319  {MBR_SCRIPT, "script", false}, // npc
320  {MBR_NPCTEMPLATE, "npctemplate", true},
321  {MBR_MASTER, "master", true},
322  {MBR_PROCESS, "process", true},
323  {MBR_EVENTMASK, "eventmask", true}, // 65
324  {MBR_SPEECH_COLOR, "speech_color", false},
325  {MBR_SPEECH_FONT, "speech_font", false},
326  {MBR_USE_ADJUSTMENTS, "use_adjustments", false},
327  {MBR_RUN_SPEED, "run_speed", false},
328  {MBR_LOCKED, "locked", false}, // lockable //70
329  {MBR_CORPSETYPE, "corpsetype", true}, // corpse
330  {MBR_TILLERMAN, "tillerman", true}, // boat
331  {MBR_PORTPLANK, "portplank", true},
332  {MBR_STARBOARDPLANK, "starboardplank", true},
333  {MBR_HOLD, "hold", true}, // 75
334  {MBR_HAS_OFFLINE_MOBILES, "has_offline_mobiles", true},
335  {MBR_COMPONENTS, "components", true}, // house
336  {MBR_ITEMS, "items", true}, // multi
337  {MBR_MOBILES, "mobiles", true},
338  {MBR_XEAST, "xeast", false}, // map //80
339  {MBR_XWEST, "xwest", false},
340  {MBR_YNORTH, "ynorth", false},
341  {MBR_YSOUTH, "ysouth", false},
342  {MBR_GUMPWIDTH, "gumpwidth", false},
343  {MBR_GUMPHEIGHT, "gumpheight", false}, // 85
344  {MBR_ISOPEN, "isopen", true}, // door
345  {MBR_QUALITY, "quality", false}, // equipment
346  {MBR_HP, "hp", false},
347  {MBR_MAXHP_MOD, "maxhp_mod", false},
348  {MBR_MAXHP, "maxhp", true}, // 90
349  {MBR_DMG_MOD, "dmg_mod", false}, // weapon
350  {MBR_ATTRIBUTE, "attribute", true},
351  {MBR_INTRINSIC, "intrinsic", true},
352  {MBR_HITSCRIPT, "hitscript", false},
353  {MBR_AR_BASE, "ar_base", true}, // 95
354  {MBR_ONHIT_SCRIPT, "onhitscript", false},
355  {MBR_ENABLED, "enabled", true}, // account
356  {MBR_BANNED, "banned", true},
357  {MBR_USERNAMEPASSWORDHASH, "usernamepasswordhash", true},
358  {MBR_MEMBERS, "members", true}, // guild //100
359  {MBR_ALLYGUILDS, "allyguilds", true},
360  {MBR_ENEMYGUILDS, "enemyguilds", true},
361  {MBR_PID, "pid", true}, // script
362  {MBR_STATE, "state", true},
363  {MBR_INSTR_CYCLES, "instr_cycles", true}, // 105
364  {MBR_SLEEP_CYCLES, "sleep_cycles", true},
365  {MBR_CONSEC_CYCLES, "consec_cycles", true},
366  {MBR_PC, "pc", true},
367  {MBR_CALL_DEPTH, "call_depth", true},
368  {MBR_NUM_GLOBALS, "num_globals", true}, // 110
369  {MBR_VAR_SIZE, "var_size", true},
370  {MBR_REALM, "realm", true},
371  {MBR_UO_EXPANSION, "uo_expansion", true},
372  {MBR_CUSTOM, "custom", true}, // house
373  {MBR_GLOBALS, "globals", true}, // 115
374  {MBR_FOOTPRINT, "footprint", true},
375  {MBR_CLIENTINFO, "clientinfo", true},
376  {MBR_DELAY_MOD, "delay_mod", false},
377  {MBR_CREATEDAT, "createdat", true},
378  {MBR_OPPONENT, "opponent", true}, // 120
379  {MBR_CONNECTED, "connected", true},
380  {MBR_ATTACHED_TO, "attached_to", true},
381  {MBR_CONTROLLER, "controller", true},
382  {MBR_OWNERSERIAL, "ownerserial", true},
383  {MBR_DEFAULTCMDLEVEL, "defaultcmdlevel", true}, // 125
384  {MBR_UCLANG, "uclang", true},
385  {MBR_RACE, "race", false},
386  {MBR_TRADING_WITH, "trading_with", false},
387  {MBR_TRADE_CONTAINER, "trade_container", false},
388  {MBR_ALIGNMENT, "alignment", false}, // 130
389  {MBR_CURSOR, "cursor", false},
390  {MBR_GUMP, "gump", false},
391  {MBR_PROMPT, "prompt", false},
392  {MBR_STACKABLE, "stackable", false},
393  {MBR_MOVEMODE, "movemode", false}, // 135
394  {MBR_HITCHANCE_MOD, "hitchance_mod", false},
395  {MBR_EVASIONCHANCE_MOD, "evasionchance_mod", false},
396  {MBR_TILE_LAYER, "tile_layer", true},
397  {MBR_CLIENTVERSIONDETAIL, "clientver_detail", true},
398  {MBR_SAVEONEXIT, "saveonexit", true}, // 140
399  {MBR_FIRE_RESIST, "resist_fire", true},
400  {MBR_COLD_RESIST, "resist_cold", true},
401  {MBR_ENERGY_RESIST, "resist_energy", true},
402  {MBR_POISON_RESIST, "resist_poison", true},
403  {MBR_PHYSICAL_RESIST, "resist_physical", true}, // 145
404  {MBR_FIRE_RESIST_MOD, "resist_fire_mod", true},
405  {MBR_COLD_RESIST_MOD, "resist_cold_mod", true},
406  {MBR_ENERGY_RESIST_MOD, "resist_energy_mod", true},
407  {MBR_POISON_RESIST_MOD, "resist_poison_mod", true},
408  {MBR_PHYSICAL_RESIST_MOD, "resist_physical_mod", true}, // 150
409  {MBR_STATCAP, "statcap", false},
410  {MBR_SKILLCAP, "skillcap", false},
411  {MBR_LUCK, "luck", false},
412  {MBR_FOLLOWERSMAX, "followers_max", false},
413  {MBR_TITHING, "tithing", false}, // 155
414  {MBR_FOLLOWERS, "followers", false},
415  {MBR_FIRE_DAMAGE, "damage_fire", true},
416  {MBR_COLD_DAMAGE, "damage_cold", true},
417  {MBR_ENERGY_DAMAGE, "damage_energy", true},
418  {MBR_POISON_DAMAGE, "damage_poison", true}, // 160
419  {MBR_PHYSICAL_DAMAGE, "damage_physical", true},
420  {MBR_FIRE_DAMAGE_MOD, "damage_fire_mod", true},
421  {MBR_COLD_DAMAGE_MOD, "damage_cold_mod", true},
422  {MBR_ENERGY_DAMAGE_MOD, "damage_energy_mod", true},
423  {MBR_POISON_DAMAGE_MOD, "damage_poison_mod", true}, // 165
424  {MBR_PHYSICAL_DAMAGE_MOD, "damage_physical_mod", true},
425  {MBR_PARTY, "party", true},
426  {MBR_LEADER, "leader", true},
427  {MBR_PARTYLOOT, "partycanloot", true},
428  {MBR_CANDIDATE_OF_PARTY, "candidate_of_party", true}, // 170
429  {MBR_CANDIDATES, "candidates", true},
430  {MBR_MOVECOST_WALK, "movecost_walk_mod", true},
431  {MBR_MOVECOST_RUN, "movecost_run_mod", true},
432  {MBR_MOVECOST_WALK_MOUNTED, "movecost_walk_mounted_mod", true},
433  {MBR_MOVECOST_RUN_MOUNTED, "movecost_run_mounted_mod", true}, // 175
434  {MBR_AGGRESSORTO, "aggressorto", true},
435  {MBR_LAWFULLYDAMAGED, "lawfullydamaged", true},
436  {MBR_GETGOTTENBY, "getgottenby", true},
437  {MBR_UO_EXPANSION_CLIENT, "uo_expansion_client", true},
438  {MBR_CLIENTTYPE, "clienttype", true}, // 180
439  {MBR_DEAFENED, "deafed", true},
440  {MBR_CLIENT, "client", true},
441  {MBR_TYPE, "type", true},
442  {MBR_ATTRIBUTES, "attributes", true},
443  {MBR_EDITING, "house_editing", true}, // 185
444  {MBR_HOUSEPARTS, "house_parts", true},
445  {MBR_DOUBLECLICKRANGE, "doubleclickrange", false},
446  {MBR_MOUNTEDSTEPS, "mountedsteps", false},
447  // New boat stuff start
448  {MBR_ROPE, "rope", true},
449  {MBR_WHEEL, "wheel", true}, // 190
450  {MBR_HULL, "hull", true},
451  {MBR_TILLER, "tiller", true},
452  {MBR_RUDDER, "rudder", true},
453  {MBR_SAILS, "sails", true},
454  {MBR_STORAGE, "storage", true}, // 195
455  {MBR_WEAPONSLOT, "weaponslot", true},
456  // New boat stuff end
457  {MBR_MULTIID, "multiid", true},
458  {MBR_TRADEWINDOW, "tradewindow", true},
459  {MBR_LASTCOORD, "lastcoord", true},
460  {MBR_FACETID, "facetid", true}, // 200
461  {MBR_EDITABLE, "editable", true},
462  {MBR_ACTIVE_SKILL, "active_skill", true},
463  {MBR_CASTING_SPELL, "casting_spell", true},
464  {MBR_CARRYINGCAPACITY_MOD, "carrying_capacity_mod", false},
465  {MBR_MAX_ITEMS_MOD, "max_items_mod", false}, // 205
466  {MBR_MAX_WEIGHT_MOD, "max_weight_mod", false},
467  {MBR_MAX_SLOTS_MOD, "max_slots_mod", false},
468  {MBR_SPEED_MOD, "speed_mod", false},
469  {MBR_NAME_SUFFIX, "name_suffix", false},
470  {MBR_TEMPORALLY_CRIMINAL, "temporally_criminal", true}, // 210
471  {MBR_LAST_TEXTCOLOR, "last_textcolor", true},
472  {MBR_INSURED, "insured", false},
473  {MBR_LAST_ACTIVITY_AT, "last_activity_at", false},
474  {MBR_LAST_PACKET_AT, "last_packet_at", false},
475  {MBR_HOUSE, "house", true}, // 215, Item
476  {MBR_SPECIFIC_NAME, "specific_name", true},
477  {MBR_CARRYINGCAPACITY, "carrying_capacity", true},
478  {MBR_NO_DROP, "no_drop", false},
479  {MBR_NO_DROP_EXCEPTION, "no_drop_exception", false},
480  {MBR_PORT, "port", false},
481 };
482 int n_objmembers = sizeof object_members / sizeof object_members[0];
483 ObjMember* getKnownObjMember( const char* token )
484 {
485  static auto cache = []() -> std::unordered_map<std::string, ObjMember*> {
486  std::unordered_map<std::string, ObjMember*> m;
487  for ( int i = 0; i < n_objmembers; ++i )
488  {
489  m[object_members[i].code] = &object_members[i];
490  }
491  return m;
492  }();
493  std::string temp( token );
494  std::transform( temp.begin(), temp.end(), temp.begin(),
495  []( char c ) { return static_cast<char>(::tolower( c ) ); } );
496  auto member = cache.find( temp );
497  if ( member != cache.end() )
498  return member->second;
499  return nullptr;
500 }
502 {
503  if ( id >= n_objmembers )
504  return nullptr;
505  else
506  return &( object_members[id] );
507 }
508 
510  {MTH_ISA, "isa", false}, // 0
511  {MTH_SET_MEMBER, "set_member", false}, // 1
512  {MTH_GET_MEMBER, "get_member", false},
513  {MTH_SETPOISONED, "setpoisoned", false},
514  {MTH_SETPARALYZED, "setparalyzed", false},
515  {MTH_SETCRIMINAL, "setcriminal", false}, // 5
516  {MTH_SETLIGHTLEVEL, "setlightlevel", false},
517  {MTH_SQUELCH, "squelch", false},
518  {MTH_ENABLE, "enable", false},
519  {MTH_DISABLE, "disable", false},
520  {MTH_ENABLED, "enabled", false}, // 10
521  {MTH_SETCMDLEVEL, "setcmdlevel", false},
522  {MTH_SPENDGOLD, "spendgold", false},
523  {MTH_SETMURDERER, "setmurderer", false},
524  {MTH_REMOVEREPORTABLE, "removereportable", false},
525  {MTH_GETGOTTENITEM, "getgottenitem", false}, // 15
526  {MTH_CLEARGOTTENITEM, "cleargottenitem", false},
527  {MTH_SETWARMODE, "setwarmode", false},
528  {MTH_SETMASTER, "setmaster", false}, // npc
529  {MTH_MOVE_OFFLINE_MOBILES, "move_offline_mobiles", false}, // boat
530  {MTH_SETCUSTOM, "setcustom", false}, // house //20
531  {MTH_GETPINS, "getpins", false}, // map
532  {MTH_INSERTPIN, "insertpin", false},
533  {MTH_APPENDPIN, "appendpin", false},
534  {MTH_ERASEPIN, "erasepin", false},
535  {MTH_OPEN, "open", false}, // door //25
536  {MTH_CLOSE, "close", false},
537  {MTH_TOGGLE, "toggle", false},
538  {MTH_BAN, "ban", false}, // account
539  {MTH_UNBAN, "unban", false},
540  {MTH_SETPASSWORD, "setpassword", false}, // 30
541  {MTH_CHECKPASSWORD, "checkpassword", false},
542  {MTH_SETNAME, "setname", false},
543  {MTH_GETCHARACTER, "getcharacter", false},
544  {MTH_DELETECHARACTER, "deletecharacter", false},
545  {MTH_GETPROP, "getprop", false}, // 35
546  {MTH_SETPROP, "setprop", false},
547  {MTH_ERASEPROP, "eraseprop", false},
548  {MTH_PROPNAMES, "propnames", false},
549  {MTH_ISMEMBER, "ismember", false}, // guild
550  {MTH_ISALLYGUILD, "isallyguild", false}, // 40
551  {MTH_ISENEMYGUILD, "isenemyguild", false},
552  {MTH_ADDMEMBER, "addmember", false},
553  {MTH_ADDALLYGUILD, "addallyguild", false},
554  {MTH_ADDENEMYGUILD, "addenemyguild", false},
555  {MTH_REMOVEMEMBER, "removemember", false}, // 45
556  {MTH_REMOVEALLYGUILD, "removeallyguild", false},
557  {MTH_REMOVEENEMYGUILD, "removeenemyguild", false},
558  {MTH_SIZE, "size", false}, // ARRAY
559  {MTH_ERASE, "erase", false},
560  {MTH_INSERT, "insert", false}, // 50
561  {MTH_SHRINK, "shrink", false},
562  {MTH_APPEND, "append", false},
563  {MTH_REVERSE, "reverse", false},
564  {MTH_SORT, "sort", false}, // dict
565  {MTH_EXISTS, "exists", false}, // 55
566  {MTH_KEYS, "keys", false},
567  {MTH_SENDPACKET, "sendpacket", false}, // packet
568  {MTH_SENDAREAPACKET, "sendareapacket", false},
569  {MTH_GETINT8, "getint8", false},
570  {MTH_GETINT16, "getint16", false}, // 60
571  {MTH_GETINT32, "getint32", false},
572  {MTH_SETINT8, "setint8", false},
573  {MTH_SETINT16, "setint16", false},
574  {MTH_SETINT32, "setint32", false},
575  {MTH_GETSTRING, "getstring", false}, // 65
576  {MTH_GETUNICODESTRING, "getunicodestring", false},
577  {MTH_SETSTRING, "setstring", false},
578  {MTH_SETUNICODESTRING, "setunicodestring", false},
579  {MTH_GETSIZE, "getsize", false},
580  {MTH_SETSIZE, "setsize", false}, // 70
581  {MTH_CREATEELEMENT, "createelement", false}, // datastore
582  {MTH_FINDELEMENT, "findelement", false},
583  {MTH_DELETEELEMENT, "deleteelement", false},
584  {MTH_SENDEVENT, "sendevent", false}, // script
585  {MTH_KILL, "kill", false}, // 75
586  {MTH_LOADSYMBOLS, "loadsymbols", false},
587  {MTH_SET_UO_EXPANSION, "set_uo_expansion", false},
588  {MTH_CLEAR_EVENT_QUEUE, "clear_event_queue", false},
589  {MTH_ADD_COMPONENT, "add_component", false},
590  {MTH_ERASE_COMPONENT, "erase_component", false}, // 80
591  {MTH_DELETE, "delete", false},
592  {MTH_SPLIT, "split", false},
593  {MTH_MOVE_CHAR, "move_char", false},
594  {MTH_GETINT16FLIPPED, "getint16flipped", false},
595  {MTH_GETINT32FLIPPED, "getint32flipped", false}, // 85
596  {MTH_SETINT16FLIPPED, "setint16flipped", false},
597  {MTH_SETINT32FLIPPED, "setint32flipped", false},
598  {MTH_GETCORPSE, "getcorpse", false},
599  {MTH_SETDEFAULTCMDLEVEL, "setdefaultcmdlevel", false},
600  {MTH_PRIVILEGES, "privileges", false}, // 90
601  {MTH_GETUNICODESTRINGFLIPPED, "getunicodestringflipped", false},
602  {MTH_SETUNICODESTRINGFLIPPED, "setunicodestringflipped", false},
603  {MTH_ADD_CHARACTER, "addcharacter", false},
604  {MTH_SET_SWINGTIMER, "setswingtimer", false},
605  {MTH_ATTACK_ONCE, "attack_once", false}, // 95
606  {MTH_SETFACING, "setfacing", false},
607  {MTH_COMPAREVERSION, "compareversion", false},
608  {MTH_SETLEADER, "setleader", false},
609  {MTH_ADDCANDIDATE, "addcandidate", false},
610  {MTH_REMOVECANDIDATE, "removecandidate", false}, // 100
611  {MTH_RANDOMENTRY, "randomentry", false},
612  {MTH_SEEK, "seek", false},
613  {MTH_PEEK, "peek", false},
614  {MTH_TELL, "tell", false},
615  {MTH_FLUSH, "flush", false}, // 105
616  {MTH_GETSINT8, "getsint8", false},
617  {MTH_GETSINT16, "getsint16", false},
618  {MTH_GETSINT32, "getsint32", false},
619  {MTH_SETSINT8, "setsint8", false},
620  {MTH_SETSINT16, "setsint16", false}, // 110
621  {MTH_SETSINT32, "setsint32", false},
622  {MTH_SETAGGRESSORTO, "setaggressorto", false},
623  {MTH_SETLAWFULLYDAMAGEDTO, "setlawfullydamagedto", false},
624  {MTH_CLEARAGGRESSORTO, "clearaggressorto", false},
625  {MTH_CLEARLAWFULLYDAMAGEDTO, "clearlawfullydamagedto", false}, // 115
626  {MTH_HASSPELL, "hasspell", false},
627  {MTH_SPELLS, "spells", false},
628  {MTH_REMOVESPELL, "removespell", false},
629  {MTH_ADDSPELL, "addspell", false},
630  {MTH_DEAF, "deaf", false}, // 120
631  {MTH_SETSEASON, "setseason", false},
632  {MTH_NEXTSIBLING, "nextxmlsibling", false},
633  {MTH_FIRSTCHILD, "firstxmlchild", false},
634  {MTH_SAVEXML, "savexml", false},
635  {MTH_APPENDNODE, "appendxmlnode", false}, // 125
636  {MTH_SETDECLARATION, "setxmldeclaration", false},
637  {MTH_SETATTRIBUTE, "setxmlattribute", false},
638  {MTH_REMOVEATTRIBUTE, "removexmlattribute", false},
639  {MTH_REMOVENODE, "removexmlnode", false},
640  {MTH_APPENDTEXT, "appendxmltext", false}, // 130
641  {MTH_XMLTOSTRING, "xmltostring", false},
642  {MTH_APPENDXMLCOMMENT, "appendxmlcomment", false},
643  {MTH_ADD_HOUSE_PART, "addhousepart", false},
644  {MTH_ERASE_HOUSE_PART, "erasehousepart", false},
645  {MTH_ACCEPT_COMMIT, "acceptcommit", false}, // 135
646  {MTH_SPLITSTACK_AT, "splitstackat", false},
647  {MTH_SPLITSTACK_INTO, "splitstackinto", false},
648  {MTH_CANCEL_EDITING, "cancelediting", false},
649  {MTH_CLONENODE, "clonenode", false},
650  {MTH_HAS_EXISTING_STACK, "hasexistingstack", false}, // 140
651  {MTH_LENGTH, "length", false},
652  {MTH_JOIN, "join", false},
653  {MTH_FIND, "find", false},
654  {MTH_UPPER, "upper", false},
655  {MTH_LOWER, "lower", false}, // 145
656  {MTH_FORMAT, "format", false},
657  {MTH_DISABLE_SKILLS_FOR, "disableskillsfor", false},
658  {MTH_CYCLE, "cycle", false},
659  {MTH_ADD_BUFF, "addbuff", false},
660  {MTH_DEL_BUFF, "delbuff", false}, // 150
661  {MTH_CLEAR_BUFFS, "clearbuffs", false},
662  {MTH_CALL, "call", false},
663 };
664 int n_objmethods = sizeof object_methods / sizeof object_methods[0];
665 ObjMethod* getKnownObjMethod( const char* token )
666 {
667  // cache needs to hold a pointer to the original structure! eprog_read sets the override member
668  static auto cache = []() -> std::unordered_map<std::string, ObjMethod*> {
669  std::unordered_map<std::string, ObjMethod*> m;
670  for ( int i = 0; i < n_objmethods; ++i )
671  {
672  m[object_methods[i].code] = &object_methods[i];
673  }
674  return m;
675  }();
676  std::string temp( token );
677  std::transform( temp.begin(), temp.end(), temp.begin(),
678  []( char c ) { return static_cast<char>(::tolower( c ) ); } );
679  auto method = cache.find( temp );
680  if ( method != cache.end() )
681  return method->second;
682  return nullptr;
683 }
685 {
686  if ( id >= n_objmethods )
687  return nullptr;
688  else
689  return &( object_methods[id] );
690 }
691 
693 {
694  for ( int i = 0; i < n_objmethods; i++ )
695  {
696  if ( object_methods[i].id != i )
697  {
698  INFO_PRINT << "ERROR: Object Method definition of " << object_methods[i].code
699  << " has an invalid index!\n";
700  }
701  auto c = reinterpret_cast<unsigned char*>( object_methods[i].code );
702  while ( *c )
703  {
704  if ( *c != tolower( *c ) )
705  {
706  INFO_PRINT << "ERROR: Object Method definition of " << object_methods[i].code
707  << " is not lowercase!\n";
708  break;
709  }
710  ++c;
711  }
712  }
713  for ( int i = 0; i < n_objmembers; i++ )
714  {
715  if ( object_members[i].id != i )
716  {
717  INFO_PRINT << "ERROR: Object Member definition of " << object_members[i].code
718  << " has an invalid index!\n";
719  }
720  auto c = reinterpret_cast<unsigned char*>( object_members[i].code );
721  while ( *c )
722  {
723  if ( *c != tolower( *c ) )
724  {
725  INFO_PRINT << "ERROR: Object Member definition of " << object_members[i].code
726  << " is not lowercase!\n";
727  break;
728  }
729  ++c;
730  }
731  }
732 }
734 
735 void matchOperators( Operator* oplist, int n_ops, char* buf, int* nPartial,
736  Operator** pTotalMatchOperator )
737 {
738  // all operators are 1 or 2 characters.
739  // they also don't have case.
740  int lenbuf = buf[1] ? 2 : 1;
741 
742  *nPartial = 0;
743  *pTotalMatchOperator = NULL;
744 
745  if ( lenbuf == 1 )
746  {
747  Operator* op = &oplist[0];
748  for ( int i = 0; i < n_ops; ++i, ++op )
749  {
750  if ( op->code[0] == buf[0] )
751  {
752  if ( op->code[1] == '\0' )
753  {
754  ( *pTotalMatchOperator ) = op;
755  if ( !op->ambig )
756  return;
757  }
758  else
759  {
760  ( *nPartial )++;
761  }
762  }
763  }
764  }
765  else // lenbuf == 2
766  {
767  Operator* op = &oplist[0];
768  for ( int i = 0; i < n_ops; ++i, ++op )
769  {
770  if ( op->code[0] == buf[0] && op->code[1] == buf[1] )
771  {
772  ( *pTotalMatchOperator ) = op;
773  return;
774  }
775  }
776  }
777 }
778 
779 /*
780 void matchOperators(char *buf, int *nPartial, Operator** ppMatch)
781 {
782 matchOperators(binary_operators, n_operators,
783 buf, nPartial, ppMatch);
784 }
785 void matchUnaryOperators(char *buf, int *nPartial, Operator** ppMatch)
786 {
787 matchOperators(unary_operators, n_unary,
788 buf, nPartial, ppMatch);
789 }
790 */
791 
792 // FIXME this really should be a word, type, and id
793 // (or better yet, eliminate type altogether.)
794 // reserved words first get read out as literals, then
795 // are recognized to be reserved words.
796 // the "and", "or", and "not" operators won't be recognized
797 // right, for the moment.
798 
799 typedef struct
800 {
801  const char* word;
806 } ReservedWord;
807 
808 
810  {"if", RSV_ST_IF, TYP_RESERVED, PREC_TERMINATOR, false},
811  {"then", RSV_THEN, TYP_RESERVED, PREC_TERMINATOR, false},
812  {"elseif", RSV_ELSEIF, TYP_RESERVED, PREC_TERMINATOR, false},
813  {"endif", RSV_ENDIF, TYP_RESERVED, PREC_TERMINATOR, false},
814  {"else", RSV_ELSE, TYP_RESERVED, PREC_TERMINATOR, false},
815  {"_OptionBracketed", RSV_OPTION_BRACKETED, TYP_RESERVED, PREC_TERMINATOR, false},
816 
817  {"goto", RSV_GOTO, TYP_RESERVED, PREC_TERMINATOR, false},
818  {"gosub", RSV_GOSUB, TYP_RESERVED, PREC_TERMINATOR, false},
819  {"return", RSV_RETURN, TYP_RESERVED, PREC_TERMINATOR, false},
820 
821  // { "global", RSV_GLOBAL, TYP_RESERVED, PREC_DEPRECATED, true }, // internal only
822  // { "local", RSV_LOCAL, TYP_RESERVED, PREC_DEPRECATED, true }, // internal only
823  {"const", RSV_CONST, TYP_RESERVED, PREC_TERMINATOR, false},
824  {"var", RSV_VAR, TYP_RESERVED, PREC_TERMINATOR, false},
825 
826  // { "begin", RSV_BEGIN, TYP_RESERVED, PREC_DEPRECATED, true }, // deprecated
827  // { "end", RSV_ENDB, TYP_RESERVED, PREC_DEPRECATED, true }, // deprecated
828 
829  {"do", RSV_DO, TYP_RESERVED, PREC_TERMINATOR, false},
830  {"dowhile", RSV_DOWHILE, TYP_RESERVED, PREC_TERMINATOR, false},
831  {"while", RSV_WHILE, TYP_RESERVED, PREC_TERMINATOR, false},
832  {"endwhile", RSV_ENDWHILE, TYP_RESERVED, PREC_TERMINATOR, false},
833 
834  {"exit", RSV_EXIT, TYP_RESERVED, PREC_TERMINATOR, false},
835 
836  {"declare", RSV_DECLARE, TYP_RESERVED, PREC_TERMINATOR, false},
837 
838  {"function", RSV_FUNCTION, TYP_RESERVED, PREC_TERMINATOR, false},
839  {"endfunction", RSV_ENDFUNCTION, TYP_RESERVED, PREC_TERMINATOR, false},
840  {"exported", RSV_EXPORTED, TYP_RESERVED, PREC_TERMINATOR, false},
841 
842  {"use", RSV_USE_MODULE, TYP_RESERVED, PREC_TERMINATOR, false},
843  {"include", RSV_INCLUDE_FILE, TYP_RESERVED, PREC_TERMINATOR, false},
844 
845  {"break", RSV_BREAK, TYP_RESERVED, PREC_TERMINATOR, false},
846  {"continue", RSV_CONTINUE, TYP_RESERVED, PREC_TERMINATOR, false},
847 
848  {"for", RSV_FOR, TYP_RESERVED, PREC_TERMINATOR, false},
849  {"endfor", RSV_ENDFOR, TYP_RESERVED, PREC_TERMINATOR, false},
850  {"to", RSV_TO, TYP_RESERVED, PREC_TERMINATOR, false},
851  {"next", RSV_NEXT, TYP_RESERVED, PREC_TERMINATOR, false},
852 
853  {"foreach", RSV_FOREACH, TYP_RESERVED, PREC_TERMINATOR, false},
854  {"endforeach", RSV_ENDFOREACH, TYP_RESERVED, PREC_TERMINATOR, false},
855 
856  {"repeat", RSV_REPEAT, TYP_RESERVED, PREC_TERMINATOR, false},
857  {"until", RSV_UNTIL, TYP_RESERVED, PREC_TERMINATOR, false},
858 
859  {"program", RSV_PROGRAM, TYP_RESERVED, PREC_TERMINATOR, false},
860  {"endprogram", RSV_ENDPROGRAM, TYP_RESERVED, PREC_TERMINATOR, false},
861 
862  {"case", RSV_SWITCH, TYP_RESERVED, PREC_TERMINATOR, false},
863  // { "case", RSV_CASE, TYP_RESERVED, PREC_TERMINATOR, false },
864  {"default", RSV_DEFAULT, TYP_RESERVED, PREC_TERMINATOR, false},
865  {"endcase", RSV_ENDSWITCH, TYP_RESERVED, PREC_TERMINATOR, false},
866 
867  {"enum", RSV_ENUM, TYP_RESERVED, PREC_TERMINATOR, false},
868  {"endenum", RSV_ENDENUM, TYP_RESERVED, PREC_TERMINATOR, false},
869 
870  {"downto", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
871  {"step", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
872  {"reference", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
873  {"out", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
874  {"inout", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
875  // { "ByRef", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false },
876  {"ByVal", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
877 
878  {"string", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
879  {"long", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
880  {"integer", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
881  {"unsigned", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
882  {"signed", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
883  {"real", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
884  {"float", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
885  {"double", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
886  {"as", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
887  {"is", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
888 
889  {"and", TOK_AND, TYP_OPERATOR, PREC_LOGAND, false},
890  {"or", TOK_OR, TYP_OPERATOR, PREC_LOGOR, false},
892  {"byref", TOK_REFTO, TYP_RESERVED, PREC_TERMINATOR, false}, // UNARY_OPERATOR, 12 },
893  {"unused", TOK_UNUSED, TYP_RESERVED, PREC_TERMINATOR, false},
894  {"error", TOK_ERROR, TYP_OPERAND, PREC_TERMINATOR, false},
895  {"hash", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false},
896  {"dictionary", TOK_DICTIONARY, TYP_OPERAND, PREC_TERMINATOR, false},
897  {"struct", TOK_STRUCT, TYP_OPERAND, PREC_TERMINATOR, false},
898  {"array", TOK_ARRAY, TYP_OPERAND, PREC_TERMINATOR, false},
899  {"stack", TOK_STACK, TYP_OPERAND, PREC_TERMINATOR, false},
900  {"in", TOK_IN, TYP_OPERATOR, PREC_EQUALTO, false}
901  // { "bitand", TOK_BITAND, TYP_OPERATOR, PREC_BITAND },
902  // { "bitxor", TOK_BITXOR, TYP_OPERATOR, PREC_BITXOR },
903  // { "bitor", TOK_BITOR, TYP_OPERATOR, PREC_BITOR }
904  /* "/""*", RSV_COMMENT_START,
905  "*""/", RSV_COMMENT_END,
906  "/""/", RSV_COMMENT_TO_EOL,
907  "--", RSV_COMMENT_TO_EOL
908  */
909 };
910 unsigned n_reserved = sizeof reserved_words / sizeof reserved_words[0];
911 
912 typedef std::map<std::string, ReservedWord*, Clib::ci_cmp_pred> ReservedWords;
913 ReservedWords reservedWordsByName;
914 static void init_tables()
915 {
916  static std::once_flag flag;
917  std::call_once( flag, []() {
918  for ( unsigned i = 0; i < n_reserved; ++i )
919  {
920  reservedWordsByName[reserved_words[i].word] = &reserved_words[i];
921  }
922  } );
923 }
924 
925 void Parser::write_words( std::ostream& os )
926 {
927  os << "Reserved:" << std::endl;
928  for ( unsigned i = 0; i < n_reserved; ++i )
929  {
930  os << reserved_words[i].word << ( reserved_words[i].deprecated ? " (deprecated)" : "" )
931  << std::endl;
932  }
933  os << std::endl;
934  os << "Binary:" << std::endl;
935  for ( int i = 0; i < n_operators; ++i )
936  {
937  os << binary_operators[i].code << ( binary_operators[i].deprecated ? " (deprecated)" : "" )
938  << std::endl;
939  }
940  os << std::endl;
941  os << "Unary:" << std::endl;
942  for ( int i = 0; i < n_unary; ++i )
943  {
944  os << unary_operators[i].code << ( unary_operators[i].deprecated ? " (deprecated)" : "" )
945  << std::endl;
946  }
947  os << std::endl;
948  os << "Methodlist:" << std::endl;
949  for ( int i = 0; i < n_objmethods; i++ )
950  {
951  os << object_methods[i].id << " " << object_methods[i].code << std::endl;
952  }
953  os << std::endl;
954  os << "Memberlist:" << std::endl;
955  for ( int i = 0; i < n_objmembers; i++ )
956  {
957  os << object_members[i].id << " " << object_members[i].code << std::endl;
958  }
959 }
960 
961 #if 0
962  void matchReservedWords(char *buf,
963  int *nPartial,
964  int *nTotal)
965  {
966  int lenbuf = strlen(buf);
967  assert(nPartial && nTotal);
968  *nPartial = 0;
969  *nTotal = 0;
970  for(int i = 0; i < n_reserved; i++)
971  {
972  if (strnicmp(reserved_words[i].word, buf, lenbuf)==0)
973  (*nPartial)++;
974  if (stricmp(reserved_words[i].word, buf)==0)
975  (*nTotal)++;
976  }
977  }
978 #endif
979 
980  /*
981  WTF is going on in this function? It seems like it waits for a match, followed
982  by a nonmatch? eh?
983 
984  What does this mean for variables like "IfDone" etc?
985  */
986 
987 #if 0
988  int Parser::tryReservedWord(Token& tok, char *t, char **s)
989  {
990  char opbuf[10];
991  int bufp = 0;
992  int thisMatchPartial, thisMatchTotal;
993  int lastMatchTotal = 0;
994 
995  while (t && *t) {
996  /* let's try to match it as we go. */
997  if (bufp==10) { err = PERR_BADTOKEN; return -1; }
998  opbuf[bufp++] = *t++;
999  opbuf[bufp] = '\0';
1000  matchReservedWords(opbuf, &thisMatchPartial, &thisMatchTotal);
1001  if (!thisMatchPartial) { /* can't match a bloody thing! */
1002  switch(lastMatchTotal) {
1003  case 0:
1004  return 0; // this just wasn't a reserved word..
1005  case 1: // this is the only way it will work..
1006  // here, we don't match now but if we don't count
1007  // this character, it was a unique match.
1008  opbuf[bufp-1] = '\0';
1009  tok.nulStr();
1010  recognize_reserved_word(tok, opbuf);
1011  *s = t-1;
1012  return 1;
1013  case 2: // here, with this character there is no match
1014  // but before there were multiple matches.
1015  // really shouldn't happen.
1016  err = PERR_BADOPER;
1017  return -1;
1018  }
1019  } else { /* this partially matches.. */
1020  // "Remember....."
1021  lastMatchTotal = thisMatchTotal;
1022  }
1023  }
1024 
1025  if (thisMatchTotal == 1) {
1026  tok.nulStr();
1027  recognize_reserved_word( tok, opbuf );
1028  *s = t;
1029  return 1;
1030  }
1031 
1032  return 0; // didn't find one!
1033  }
1034 #endif
1035 
1047 int Parser::tryOperator( Token& tok, const char* t, const char** s, Operator* opList, int n_ops,
1048  char* opbuf )
1049 {
1050  int bufp = 0;
1051  int thisMatchPartial;
1052  Operator* pLastMatch = NULL;
1053  Operator* pMatch = NULL;
1054 
1055  while ( t && *t )
1056  {
1057  // if (strchr(operator_brk, *t)) mustBeOperator = 1;
1058 
1059  /* let's try to match it as we go. */
1060  if ( bufp == 4 )
1061  {
1062  err = PERR_BADTOKEN;
1063  return -1;
1064  }
1065  opbuf[bufp++] = *t++;
1066  opbuf[bufp] = '\0';
1067  matchOperators( opList, n_ops, opbuf, &thisMatchPartial, &pMatch );
1068  if ( !thisMatchPartial )
1069  {
1070  if ( pMatch )
1071  { // no partial matches, but a total match.
1072  break;
1073  }
1074  else if ( !pLastMatch )
1075  {
1076  // no total matches, no partial matches.
1077  return 0;
1078  }
1079  else // (pLastMatch)
1080  {
1081  // had a match before, but no partials this time.
1082  break;
1083  }
1084  }
1085  else
1086  { /* this partially matches.. */
1087  // "Remember....."
1088  if ( pMatch )
1089  {
1090  pLastMatch = pMatch;
1091  }
1092  }
1093  }
1094 
1095  if ( pMatch == NULL )
1096  pMatch = pLastMatch;
1097 
1098  if ( pMatch )
1099  {
1100  *s += strlen( pMatch->code );
1101 
1102  tok.module = Mod_Basic;
1103  tok.id = pMatch->id;
1104  tok.type = pMatch->type;
1105  tok.precedence = pMatch->precedence;
1106  tok.deprecated = pMatch->deprecated;
1107 
1108  tok.setStr( pMatch->code );
1109  return 1;
1110  }
1111 
1112  return 0; // didn't find one!
1113 }
1114 
1123 {
1124  int res;
1125  char opbuf[10];
1126 
1127  res = tryOperator( tok, ctx.s, &ctx.s, binary_operators, n_operators, opbuf );
1128 
1129  return res;
1130 }
1131 
1140 {
1141  int res;
1142  char opbuf[10];
1143 
1144  res = tryOperator( tok, ctx.s, &ctx.s, unary_operators, n_unary, opbuf );
1145 
1146  return res;
1147 }
1148 
1157 {
1158  if ( isdigit( ctx.s[0] ) || ctx.s[0] == '.' )
1159  {
1160  char *endptr, *endptr2;
1161  int l = strtol( ctx.s, &endptr, 0 );
1162  double d = strtod( ctx.s, &endptr2 );
1163 
1164  // 2015-01-21 Bodom: weird trick to remove an unwanted feature from Microsoft compiler
1165  // interpreting 'd' as 'e' (exponent), but 'd' in UO means dice,
1166  // leading to confusion
1167  // TODO: The best solution would be to reimplement the int/double parsing
1168  if ( !( ctx.s[0] == '0' && ctx.s[1] && ( ctx.s[1] == 'x' || ctx.s[1] == 'X' ) ) )
1169  {
1170  // This is not hex, so no 'd' can be valid
1171  for ( const char* i = ctx.s; i < endptr2; i++ )
1172  {
1173  if ( *i == 'd' || *i == 'D' )
1174  {
1175  // A 'd' has been eaten, bug could have occurred:
1176  // re-perform parsing on a cleaned version of the string
1177  size_t safelen = i - ctx.s + 1;
1178  std::unique_ptr<char[]> safeptr( new char[safelen]() );
1179  strncpy( safeptr.get(), ctx.s, safelen - 1 );
1180  d = strtod( safeptr.get(), &endptr2 );
1181  size_t newlen = endptr2 - safeptr.get();
1182  endptr2 = const_cast<char*>( ctx.s + newlen );
1183  break;
1184  }
1185  }
1186  }
1187 
1188  tok.type = TYP_OPERAND;
1189  if ( endptr >= endptr2 )
1190  { // long got more out of it, we'll go with that
1191  tok.id = TOK_LONG;
1192  tok.lval = l;
1193  ctx.s = endptr;
1194  return 1;
1195  }
1196  else
1197  {
1198  tok.id = TOK_DOUBLE;
1199  tok.dval = d;
1200  ctx.s = endptr2;
1201  return 1;
1202  }
1203  }
1204  return 0; // not numeric
1205 }
1206 
1215 {
1216  if ( ctx.s[0] == '\"' )
1217  {
1218  const char* end = &ctx.s[1];
1219  std::string lit;
1220  bool escnext = false; // true when waiting for 2nd char in an escape sequence
1221  u8 hexnext = 0; // tells how many more chars in a \xNN escape sequence
1222  char hexstr[3]; // will contain the \x escape chars to be processed
1223  memset( hexstr, 0, 3 );
1224 
1225  for ( ;; )
1226  {
1227  if ( !*end )
1228  {
1230  return -1;
1231  }
1232 
1233  passert_always_r( !( escnext && hexnext ),
1234  "Bug in the compiler. Please report this on the forums." );
1235 
1236  if ( escnext )
1237  {
1238  // waiting for 2nd character after a backslash
1239  escnext = false;
1240  if ( *end == 'n' )
1241  lit += '\n';
1242  else if ( *end == 't' )
1243  lit += '\t';
1244  else if ( *end == 'x' )
1245  hexnext = 2;
1246  else
1247  lit += *end;
1248  }
1249  else if ( hexnext )
1250  {
1251  // waiting for next (two) chars in hex escape sequence (eg. \xFF)
1252  hexstr[2 - hexnext] = *end;
1253  if ( !--hexnext )
1254  {
1255  char* endptr;
1256  char ord = static_cast<char>( strtol( hexstr, &endptr, 16 ) );
1257  if ( *endptr != '\0' )
1258  {
1259  err = PERR_INVESCAPE;
1260  return -1;
1261  }
1262  lit += ord;
1263  }
1264  }
1265  else
1266  {
1267  if ( *end == '\\' )
1268  escnext = true;
1269  else if ( *end == '\"' )
1270  break;
1271  else
1272  lit += *end;
1273  }
1274  ++end;
1275  }
1276  /*
1277  char *end = strchr(&ctx.s[1], '\"');
1278  if (!end)
1279  {
1280  err = PERR_UNTERMSTRING;
1281  return -1;
1282  }
1283  */
1284  // int len = end - ctx.s; // "abd" len = 5-1 = 4
1285  tok.id = TOK_STRING; // this is a misnomer I think!
1286  tok.type = TYP_OPERAND;
1287  tok.copyStr( lit.c_str() );
1288 
1289  ctx.s = end + 1; // skip past the ending delimiter
1290  return 1;
1291  }
1292  else if ( isalpha( ctx.s[0] ) || ctx.s[0] == '_' )
1293  { // we have a variable/label/verb.
1294  const char* end = ctx.s;
1295  while ( *end && !isspace( *end ) && strchr( ident_allowed, *end ) )
1296  {
1297  ++end;
1298  }
1299  // Catch identifiers of the form module::name
1300  if ( end[0] == ':' && end[1] == ':' )
1301  {
1302  end += 2;
1303  while ( *end && !isspace( *end ) && strchr( ident_allowed, *end ) )
1304  {
1305  ++end;
1306  }
1307  }
1308 
1309  int len = static_cast<int>( end - ctx.s + 1 ); // "abcd"
1310 
1311  tok.copyStr( ctx.s, len - 1 );
1312 
1313  tok.id = TOK_IDENT;
1314  tok.type = TYP_OPERAND;
1315 
1316  ctx.s = end;
1317  return 1;
1318  }
1319  return 0;
1320 }
1321 
1322 int Parser::recognize_binary( Token& tok, const char* buf, const char** /*s*/ )
1323 {
1324  for ( int i = 0; i < n_operators; i++ )
1325  {
1326  if ( stricmp( buf, binary_operators[i].code ) == 0 )
1327  {
1328  tok.module = Mod_Basic;
1329  tok.id = binary_operators[i].id;
1330  tok.type = binary_operators[i].type;
1331  tok.precedence = binary_operators[i].precedence;
1332  /*
1333  if (tok.type == TYP_OPERATOR &&
1334  (tok.id == TOK_ADDMEMBER ||
1335  tok.id == TOK_DELMEMBER ||
1336  tok.id == TOK_CHKMEMBER ||
1337  tok.id == TOK_MEMBER) )
1338  {
1339  int res;
1340  Token tk2;
1341  res = getToken( s, tk2 );
1342  if (res < 0) return res;
1343  if (tk2.type != TYP_OPERAND || tk2.id != TOK_IDENT)
1344  return -1;
1345  tok.copyStr( tk2.tokval() );
1346  }
1347  else
1348  {
1349  */
1350  tok.setStr( binary_operators[i].code );
1351  // tok.nulStr();
1352  /*
1353  }
1354  */
1355 
1356  return 1;
1357  }
1358  }
1359  return 0;
1360 }
1361 
1362 int Parser::recognize_unary( Token& tok, const char* buf )
1363 {
1364  for ( int i = 0; i < n_unary; i++ )
1365  {
1366  if ( stricmp( buf, unary_operators[i].code ) == 0 )
1367  {
1368  tok.module = Mod_Basic;
1369  tok.id = unary_operators[i].id;
1370  tok.type = unary_operators[i].type;
1371  tok.precedence = unary_operators[i].precedence;
1372  tok.setStr( unary_operators[i].code );
1373 
1374  return 1;
1375  }
1376  }
1377  return 0;
1378 }
1379 
1380 
1381 int Parser::recognize( Token& tok, const char* buf, const char** s )
1382 {
1383  if ( recognize_binary( tok, buf, s ) )
1384  return 1;
1385  return recognize_unary( tok, buf );
1386 }
1387 
1388 
1389 bool Parser::recognize_reserved_word( Token& tok, const char* buf )
1390 {
1391  if ( tok.id != TOK_IDENT )
1392  return false;
1393 
1394  auto itr = reservedWordsByName.find( buf );
1395  if ( itr != reservedWordsByName.end() )
1396  {
1397  ReservedWord* rv = ( *itr ).second;
1398 
1399  tok.module = Mod_Basic;
1400  tok.id = rv->id;
1401  tok.type = rv->type;
1402  tok.precedence = rv->precedence;
1403  tok.deprecated = rv->deprecated;
1404  tok.setStr( rv->word );
1405  return true;
1406  }
1407  return false;
1408 }
1409 
1410 
1411 /*
1412 int isOperator(Token& token)
1413 {
1414 for(int i=0;i < n_operators; i++) {
1415 if (stricmp(binary_operators[i].code, token )==0) {
1416 token.setStr(binary_operators[i].code);
1417 token.id = binary_operators[i].id;
1418 token.type = binary_operators[i].type;
1419 return 1;
1420 }
1421 }
1422 return 0;
1423 }
1424 */
1425 
1427 {
1428  return static_cast<Precedence>( token.precedence );
1429 }
1430 
1432 {
1433  // qCA.clear();
1434  while ( !ex.CA.empty() )
1435  {
1436  delete ex.CA.front();
1437  ex.CA.pop();
1438  }
1439 
1440  // TX.clear()
1441  while ( !ex.TX.empty() )
1442  {
1443  delete ex.TX.top();
1444  ex.TX.pop();
1445  }
1446 
1447 
1448  err = PERR_NONE;
1449  ext_err[0] = '\0';
1450 }
1451 
1482 int Parser::getToken( CompilerContext& ctx, Token& tok, Expression* /* expr not used */ )
1483 {
1484  int hit = 0;
1485 
1486  int res = ctx.skipcomments();
1487  if ( res )
1488  return res;
1489 
1490  tok.dbg_filenum = ctx.dbg_filenum;
1491  tok.dbg_linenum = ctx.line;
1492 
1493  if ( ctx.s[0] == ';' )
1494  {
1495  tok.module = Mod_Basic;
1496  tok.id = TOK_SEMICOLON;
1497  tok.type = TYP_DELIMITER;
1498  tok.setStr( ";" );
1499  ctx.s++;
1500  return 0;
1501  }
1502 
1503  hit = tryLiteral( tok, ctx );
1504  if ( hit == -1 )
1505  {
1506  return -1;
1507  }
1508  else if ( hit )
1509  {
1510  recognize_reserved_word( tok, tok.tokval() );
1511  return 0;
1512  }
1513 
1514  /*
1515  hit = tryReservedWord(tok, t, s);
1516  if (hit==-1) return -1;
1517  else if (hit) return 0;
1518  */
1519 
1520  hit = tryBinaryOperator( tok, ctx );
1521  if ( hit == -1 )
1522  return -1;
1523  else if ( hit )
1524  return 0;
1525 
1526  hit = tryUnaryOperator( tok, ctx );
1527  if ( hit == -1 )
1528  return -1;
1529  else if ( hit )
1530  return 0;
1531 
1532  // label:
1533  // A:=4;
1534 
1535 
1536  hit = tryNumeric( tok, ctx );
1537  if ( hit == -1 )
1538  return -1;
1539  else if ( hit )
1540  return 0;
1541 
1542  if ( ctx.s[0] == ':' )
1543  {
1544  ++ctx.s;
1545  tok.id = RSV_COLON;
1546  tok.type = TYP_RESERVED;
1547  tok.setStr( ":" );
1548  return 0;
1549  }
1550 
1551  INFO_PRINT << "Your syntax frightens and confuses me.\n";
1552  err = PERR_WAAH;
1553  return -1;
1554 }
1555 
1561 int Parser::peekToken( const CompilerContext& ctx, Token& token, Expression* expr )
1562 {
1563  CompilerContext tctx( ctx );
1564  return getToken( tctx, token, expr );
1565 }
1566 /* Parser::parseToken deleted. */
1567 /* not used? ens 12/10/1998
1568 int Parser::IP(Expression& expr, char *s)
1569 {
1570 reinit(expr);
1571 
1572 Token *ptoken;
1573 if (!quiet) cout << "Parsing \"" << s << '\"' << endl;
1574 
1575 expr.TX.push(new Token); // push terminator token
1576 
1577 ptoken = new Token;
1578 while (getToken(&s, *ptoken)==0)
1579 {
1580 parseToken(expr, ptoken);
1581 ptoken = new Token;
1582 }
1583 parseToken(expr, ptoken);
1584 
1585 return 0;
1586 }
1587 */
1588 
1589 
1590 int SmartParser::isOkay( const Token& token, BTokenType last_type )
1591 {
1592  BTokenType this_type = token.type;
1593  if ( !quiet )
1594  INFO_PRINT << "isOkay(" << this_type << "," << last_type << ")\n";
1595  if ( last_type == TYP_FUNC || last_type == TYP_USERFUNC || last_type == TYP_METHOD ||
1596  last_type == TYP_FUNCREF )
1597  last_type = TYP_OPERAND;
1598  if ( this_type == TYP_FUNC || this_type == TYP_USERFUNC || this_type == TYP_METHOD ||
1599  this_type == TYP_FUNCREF )
1600  this_type = TYP_OPERAND;
1601  if ( token.id == TOK_LBRACE ) // an array declared somewhere out there
1602  this_type = TYP_OPERAND;
1603 
1604  if ( last_type > TYP_TESTMAX )
1605  return 1; // assumed okay
1606  if ( this_type > TYP_TESTMAX )
1607  return 1; // assumed okay
1608  return allowed_table[last_type][this_type]; // maybe okay
1609 }
1610 
1611 /*
1612 int SmartParser::isFunc(Token& token, Verb **v)
1613 {
1614 if (token.id != TOK_IDENT) return 0; // ain't no verb..
1615 // note that this catches string literals.
1616 assert(token.tokval());
1617 
1618 if (isInTable(parser_verbs, n_parser_verbs, token.tokval(), v))
1619 {
1620 token.id = (*v)->id;
1621 token.lval = (*v)->narg;
1622 token.type = TYP_FUNC;
1623 token.module = Mod_Basic;
1624 return 1;
1625 }
1626 return 0;
1627 }
1628 */
1629 
1642 {
1643  int res;
1644  res = Parser::tryLiteral( tok, ctx );
1645  if ( res == 1 && tok.id == TOK_IDENT )
1646  { // check for "label:"
1647 
1648  // whitespace used to be skipped here.
1649  // while (*t && isspace(*t)) t++;
1650  if ( !ctx.s[0] ) // ident EOF can't be a label
1651  {
1652  return 1;
1653  }
1654 
1655 // this might be a nice place to look for module::function, too.
1656 #if 0
1657  if (t[0] == ':' && t[1] == ':')
1658  {
1659  t += 2;
1660  *s = t;
1661  Token tok2;
1662  int res2 = Parser::tryLiteral( tok2, t, s );
1663  if (res2 < 0) return res2;
1664  if (res2 == 0)
1665  return -1;
1666  // append '::{tok2 tokval}' to tok.tokval
1667  // (easier when/if token uses string)
1668  }
1669 #endif
1670  if ( ctx.s[0] == ':' && ( ctx.s[1] == '\0' || isspace( ctx.s[1] ) ) )
1671  {
1672  tok.id = CTRL_LABEL;
1673  tok.type = TYP_LABEL;
1674  ++ctx.s;
1675  }
1676  return 1;
1677  }
1678  return res;
1679 }
1680 
1682 {
1683  // return Parser::parseToken(token);
1684  if ( !quiet )
1685  {
1686  fmt::Writer _tmp;
1687  _tmp << "parseToken( " << *token << ")\n";
1688  _tmp << " CA: ";
1689  std::queue<Token*> ca( expr.CA );
1690  while ( !ca.empty() )
1691  {
1692  Token* tk = ca.front();
1693  _tmp << *tk << " ";
1694  ca.pop();
1695  }
1696  _tmp << "\n";
1697  _tmp << " TX: ";
1698  std::stack<Token*> tx( expr.TX );
1699  while ( !tx.empty() )
1700  {
1701  Token* tk = tx.top();
1702  _tmp << *tk << " ";
1703  tx.pop();
1704  }
1705  INFO_PRINT << _tmp.str() << "\n";
1706  }
1707 
1708  for ( ;; )
1709  {
1710  Token* last = expr.TX.top();
1711  switch ( token->type )
1712  {
1713  case TYP_FUNC:
1714  case TYP_USERFUNC:
1715  case TYP_OPERAND:
1716  expr.CA.push( token );
1717  return 0;
1718  case TYP_TERMINATOR:
1719  switch ( last->type )
1720  {
1721  case TYP_TERMINATOR:
1722  delete token;
1723  delete expr.TX.top();
1724  expr.TX.pop();
1725  return 1; // all done!
1726  case TYP_UNARY_OPERATOR:
1727  expr.CA.push( expr.TX.top() );
1728  expr.TX.pop();
1729  break;
1730  case TYP_OPERATOR:
1731  expr.CA.push( expr.TX.top() );
1732  expr.TX.pop();
1733  break;
1734  case TYP_LEFTPAREN:
1735  err = PERR_MISSRPAREN;
1736  return -1;
1737  case TYP_LEFTBRACKET:
1739  return -1;
1740  default:
1741  err = PERR_WAAH;
1742  return -1;
1743  }
1744  break;
1745  case TYP_UNARY_OPERATOR:
1746  case TYP_OPERATOR:
1747  case TYP_LEFTBRACKET:
1748  // these start out at 0, but need to start at one.
1749  if ( token->type == TYP_LEFTBRACKET )
1750  token->lval = 1;
1751  switch ( last->type )
1752  {
1753  case TYP_TERMINATOR:
1754  case TYP_LEFTPAREN:
1755  case TYP_LEFTBRACKET:
1756  // possible check CA for array dereference operator here.
1757  // if one found, remove it, and change this assignment to a "deref and assign"
1758  expr.TX.push( token );
1759  return 0;
1760  case TYP_UNARY_OPERATOR:
1761  case TYP_OPERATOR:
1762  int this_prec;
1763  int last_prec;
1764 
1765  this_prec = find_precedence( *token );
1766  last_prec = find_precedence( *last );
1767  if ( this_prec > last_prec )
1768  {
1769  expr.TX.push( token );
1770  return 0;
1771  }
1772  else
1773  {
1774  expr.CA.push( expr.TX.top() );
1775  expr.TX.pop();
1776  }
1777  break;
1778  default:
1779  err = PERR_WAAH;
1780  return -1;
1781  break;
1782  }
1783  break;
1784  case TYP_LEFTPAREN:
1785  expr.TX.push( token );
1786  return 0;
1787  case TYP_RIGHTPAREN:
1788  switch ( last->type )
1789  {
1790  case TYP_TERMINATOR:
1791  err = PERR_UNEXRPAREN;
1792  return -1;
1793  case TYP_UNARY_OPERATOR:
1794  case TYP_OPERATOR:
1795  expr.CA.push( expr.TX.top() );
1796  expr.TX.pop();
1797  break;
1798  case TYP_LEFTPAREN:
1799  delete token;
1800  delete expr.TX.top();
1801  expr.TX.pop();
1802  return 0;
1803  default:
1804  INFO_PRINT << "Unmatched ')' in expression. (Trying to match against a '" << *last << "')\n"
1805  << ctx;
1806 
1807  ERROR_PRINT << "parseToken(): Not sure what to do.\n"
1808  << "Token: " << *token << "\n"
1809  << "Last: " << *last << "\n";
1810  throw std::runtime_error( "Error in parseToken() (1)" );
1811  }
1812  break;
1813 
1814  // case TYP_LEFTBRACKET:
1815  // expr.TX.push(token);
1816  // return 0;
1817  case TYP_RIGHTBRACKET:
1818  switch ( last->type )
1819  {
1820  case TYP_TERMINATOR:
1822  return -1;
1823  case TYP_UNARY_OPERATOR:
1824  case TYP_OPERATOR:
1825  expr.CA.push( expr.TX.top() );
1826  expr.TX.pop();
1827  break;
1828  case TYP_LEFTBRACKET:
1829  Token* ptkn;
1830  ptkn = new Token( TOK_ARRAY_SUBSCRIPT, TYP_OPERATOR );
1831  ptkn->lval = last->lval;
1832  ptkn->dbg_filenum = token->dbg_filenum;
1833  ptkn->dbg_linenum = token->dbg_linenum;
1834 
1835  if ( ptkn->lval != 1 )
1836  ptkn->id = INS_MULTISUBSCRIPT;
1837  expr.CA.push( ptkn );
1838 
1839  delete token;
1840  delete expr.TX.top();
1841  expr.TX.pop();
1842  return 0;
1843  default:
1844  INFO_PRINT << "Unmatched ']' in expression. (Trying to match against a '" << *last << "')\n"
1845  << ctx;
1846  ERROR_PRINT << "parseToken(): Not sure what to do.\n"
1847  << "Token: " << *token << "\n"
1848  << "Last: " << *last << "\n";
1849  throw std::runtime_error( "Error in parseToken() (2)" );
1850  }
1851  break;
1852 
1853  /* Special Case: two syntaxes are supported for array indexing:
1854  A[3][4] and A[3,4].
1855  If this is a comma, and we are working in a left bracket area,
1856  increase the number of indices (stored in lval of the leftbracket)
1857  */
1858  case TYP_SEPARATOR:
1859  if ( last->type == TYP_LEFTBRACKET )
1860  {
1861  ++last->lval;
1862  // Token *ptkn = new Token( Mod_Basic, TOK_ARRAY_SUBSCRIPT, TYP_OPERATOR );
1863  // ptkn->lval = last->lval++;
1864  // expr.CA.push( ptkn );
1865 
1866  delete token;
1867  return 0;
1868  }
1869  else if ( last->type == TYP_UNARY_OPERATOR || last->type == TYP_OPERATOR )
1870  {
1871  expr.CA.push( expr.TX.top() );
1872  expr.TX.pop();
1873  break; // try again
1874  }
1875  else
1876  {
1877  err = PERR_UNEXPCOMMA;
1878  return -1;
1879  }
1880 
1881  default:
1882  INFO_PRINT << "Don't know what to do with '" << *token << "' in SmartParser::parseToken\n"
1883  << ctx;
1884  err = PERR_WAAH;
1885  return -1;
1886  }
1887  }
1888 }
1889 
1923 {
1924  int res = Parser::getToken( ctx, token );
1925  if ( res == -1 )
1926  return -1;
1927  if ( token.id == TOK_IDENT )
1928  {
1929  if ( pexpr )
1930  {
1931  if ( !pexpr->TX.empty() )
1932  {
1933  Token* tkn = pexpr->TX.top();
1934  if ( tkn->id == TOK_MEMBER || tkn->id == TOK_ADDMEMBER || tkn->id == TOK_DELMEMBER ||
1935  tkn->id == TOK_CHKMEMBER )
1936  {
1937  token.id = TOK_STRING;
1938  return res;
1939  }
1940  }
1941  }
1942  if ( isFunc( token, &modfunc_ ) )
1943  return 0;
1944  if ( isUserFunc( token, &userfunc_ ) )
1945  return 0;
1946  }
1947  return res;
1948 }
1949 
1950 
1952 {
1953  unsigned nargs = 0;
1954  int res = 0;
1955  ModuleFunction* mfUse = modfunc_;
1956  Token token;
1957  // int nullArgOk = 0;
1958  // int nArgsUse = v->narg;
1959 
1960  /*
1961  if (vUse->narg == -1) {
1962  nullArgOk = 1;
1963  nArgsUse = 1;
1964  }
1965  */
1966 
1967  switch ( mfUse->nargs )
1968  {
1969  case 0: // verb[()];, so INPUT(); or INPUT;
1970  peekToken( ctx, token );
1971  if ( token.id == TOK_LPAREN )
1972  {
1973  getToken( ctx, token );
1974  peekToken( ctx, token );
1975  if ( token.id != TOK_RPAREN )
1976  {
1978  return -1;
1979  }
1980  else
1981  {
1982  getToken( ctx, token );
1983  }
1984  }
1985  break;
1986  case 1: // for one arg, make parens optional.
1987  peekToken( ctx, token );
1988  if ( token.id != TOK_LPAREN )
1989  {
1990  if ( token.id == TOK_SEMICOLON )
1991  {
1992  // if (nullArgOk) return 0;
1993  err = PERR_TOOFEWARGS;
1994  return -1;
1995  }
1997  break;
1998  }
1999  // if left paren specified
2000  // FALLTHROUGH
2001  default:
2002  // for more than one arg, or voluntary left paren on single
2003  // arg, so parens are required
2004  // grab the opening parenthesis
2005  getToken( ctx, token );
2006  if ( token.id != TOK_LPAREN )
2007  {
2008  res = -1;
2009  err = PERR_MISSLPAREN;
2010  return -1;
2011  }
2012  for ( nargs = 0; nargs < mfUse->nargs; nargs++ )
2013  {
2014  if ( nargs ) // grab the comma.
2015  {
2016  getToken( ctx, token );
2017  if ( token.id != TOK_COMMA )
2018  {
2019  // if (nullArgOk) break;
2020  res = -1;
2021  err = PERR_TOOFEWARGS;
2022  break;
2023  }
2024  }
2026  if ( res < 0 )
2027  break;
2028  err = PERR_NONE;
2029  }
2030  if ( nargs != mfUse->nargs )
2031  {
2032  err = PERR_TOOFEWARGS;
2033  return -1;
2034  ;
2035  }
2036  // get the right paren.
2037  getToken( ctx, token );
2038  if ( token.id != TOK_RPAREN )
2039  {
2041  res = -1;
2042  }
2043 
2044  break;
2045  }
2046 
2047  // now have parsed the arguments and parenthesis.
2048  // leave the delimiter ';', it will be picked up later, or
2049  // may not be there (i.e. MID(A,5) + A; )
2050  return res;
2051 }
2052 
2054 {
2055  // if we have something like x.foo(), change to call-method
2056  CompilerContext tctx( ctx );
2057  Token tk_name, tk_paren;
2058 
2059  if ( getToken( tctx, tk_name ) == 0 && getToken( tctx, tk_paren ) == 0 &&
2060  ( tk_name.id == TOK_IDENT || tk_name.id == TOK_FUNC || tk_name.id == TOK_USERFUNC ) &&
2061  tk_paren.id == TOK_LPAREN )
2062  {
2063  return true;
2064  }
2065  return false;
2066 }
2067 
2076 int SmartParser::IIP( Expression& expr, CompilerContext& ctx, unsigned flags )
2077 {
2078  BTokenType last_type = TYP_TERMINATOR;
2079  Token last_token;
2080  // Token* debug_last_tx_token = NULL;
2081  int done = 0;
2082  int res = 0; // 1=done, -1=error, 0=not done
2083 
2084  int semicolon_term_allowed = flags & EXPR_FLAG_SEMICOLON_TERM_ALLOWED;
2085  int comma_term_allowed = flags & EXPR_FLAG_COMMA_TERM_ALLOWED;
2086  int rightparen_term_allowed = flags & EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED;
2087  int rightbrace_term_allowed = flags & EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED;
2088  int dictkey_term_allowed = flags & EXPR_FLAG_DICTKEY_TERM_ALLOWED;
2089  int endenum_term_allowed = flags & EXPR_FLAG_ENDENUM_TERM_ALLOWED;
2090  int to_term_allowed = flags & EXPR_FLAG_TO_TERM_ALLOWED;
2091  int auto_term_allowed = flags & EXPR_FLAG_AUTO_TERM_ALLOWED;
2092  if ( !quiet )
2093  {
2094  char buf[80];
2095  Clib::stracpy( buf, ctx.s, 80 );
2096  strtok( buf, "\r\n" );
2097  INFO_PRINT << "Parsing " << buf << "\n";
2098  }
2099 
2100  expr.TX.push( new Token ); /* push a terminator token */
2101  int leftbracket_count = 0;
2102  while ( !done && ( res == 0 ) )
2103  {
2104  const char* t = ctx.s;
2105  Token token;
2106 
2107  res = peekToken( ctx, token );
2108 
2109  if ( res )
2110  break;
2111  if ( !isLegal( token ) )
2112  {
2114  res = -1;
2115  break;
2116  }
2117 
2118  // if (token.type == TYP_DELIMITER) break;
2119  // debug_last_tx_token = expr.TX.top();
2120 
2121  /*
2122  if (comma_term_allowed)
2123  {
2124  Token* tkn = expr.TX.top();
2125  if (leftbracket_count > 0) // tkn != NULL && tkn->id == TOK_LBRACKET )
2126  {
2127  // ignore commas if we're in a left bracket situation.
2128  ;
2129  }
2130  else if (token.id == TOK_COMMA || token.id == TOK_SEMICOLON)
2131  {
2132  break;
2133  }
2134  }
2135  */
2136  if ( leftbracket_count == 0 )
2137  {
2138  if ( comma_term_allowed && ( token.id == TOK_COMMA || token.id == TOK_SEMICOLON ) )
2139  {
2140  break;
2141  }
2142  if ( endenum_term_allowed && ( token.id == RSV_ENDENUM ) )
2143  {
2144  break;
2145  }
2146  if ( to_term_allowed && ( token.id == RSV_TO ) )
2147  {
2148  break;
2149  }
2150  }
2151 
2152  if ( rightbrace_term_allowed && token.id == TOK_RBRACE )
2153  {
2154  break;
2155  }
2156  if ( dictkey_term_allowed && token.id == TOK_DICTKEY )
2157  {
2158  break;
2159  }
2160 
2161  /*
2162  else
2163  {
2164  if (token.type == TYP_SEPARATOR)
2165  {
2166  err = PERR_UNEXPCOMMA;
2167  res = -1;
2168  break;
2169  }
2170  }
2171  */
2172 
2173  // auto-terminated expressions need to not eat what broke them out...
2174  CompilerContext save_ctx( ctx );
2175 
2176  res = getToken( ctx, token, &expr );
2177 
2178  if ( semicolon_term_allowed && token.id == TOK_SEMICOLON )
2179  break;
2180 
2181  if ( token.id == TOK_LBRACKET )
2182  ++leftbracket_count;
2183  else if ( token.id == TOK_RBRACKET )
2184  --leftbracket_count;
2185 
2186  if ( !isOkay( token, last_type ) )
2187  {
2188  if ( token.type == TYP_OPERATOR ) // check for a unary operator that looks the same.
2189  recognize_unary( token, token.tokval() );
2190 
2191  if ( !isOkay( token, last_type ) )
2192  {
2193  if ( auto_term_allowed )
2194  {
2195  ctx = save_ctx;
2196  break;
2197  }
2198 
2199  res = -1;
2201  ctx.s = t; // FIXME operator=
2202  INFO_PRINT << "Token '" << token << "' cannot follow token '" << last_token << "'\n";
2203  if ( last_token.type == TYP_OPERAND && token.id == TOK_LPAREN )
2204  {
2205  INFO_PRINT << "Function " << last_token << "() is not defined.\n";
2206  }
2207  break;
2208  }
2209  }
2210  else if ( last_type == TYP_TERMINATOR && token.type == TYP_LEFTBRACE &&
2213  {
2214  INFO_PRINT
2215  << "Warning: Using { } is inappropriate; please define array, struct or dictionary.\n";
2217  throw std::runtime_error( "Warnings treated as errors." );
2218  else
2219  INFO_PRINT << ctx;
2220  }
2221  last_type = token.type;
2222  last_token = token;
2223 
2224  //
2225  // only certain types of things can be in an auto-terminated expr
2226  // stuff like 'var' and statements can't.
2227  //
2228  if ( auto_term_allowed )
2229  {
2230  if ( token.type != TYP_FUNC && token.type != TYP_USERFUNC && token.type != TYP_OPERAND &&
2231  token.type != TYP_UNARY_OPERATOR && token.type != TYP_OPERATOR &&
2232  token.type != TYP_LEFTPAREN && token.type != TYP_RIGHTPAREN &&
2233  token.type != TYP_LEFTBRACKET && token.type != TYP_RIGHTBRACKET &&
2234  token.type != TYP_SEPARATOR && token.id != TOK_LBRACE && token.id != TOK_RBRACE )
2235  {
2236  ctx = save_ctx;
2237  break;
2238  }
2239  }
2240 
2241  Token* ptok2;
2242  if ( token.type == TYP_USERFUNC )
2243  {
2244  res = getUserArgs( expr, ctx );
2245  if ( res < 0 )
2246  return res;
2247 
2248  ptok2 = new Token( token );
2249  res = parseToken( ctx, expr, ptok2 );
2250  if ( res < 0 )
2251  {
2252  delete ptok2;
2253  ctx.s = t;
2254  }
2255  }
2256  else if ( token.type == TYP_FUNC )
2257  {
2258  userfunc_ = modfunc_->uf;
2259  res = getUserArgs( expr, ctx, false );
2260  if ( res < 0 )
2261  {
2262  INFO_PRINT << "Error getting arguments for function " << token.tokval() << "\n"
2263  << ctx << "\n";
2264  return res;
2265  }
2266  ptok2 = new Token( token );
2267  res = parseToken( ctx, expr, ptok2 );
2268  if ( res < 0 )
2269  {
2270  delete ptok2;
2271  ctx.s = t; // FIXME operator=
2272 
2273  if ( ( err == PERR_UNEXRPAREN ) && rightparen_term_allowed )
2274  {
2275  err = PERR_NONE;
2276  res = 0;
2277  done = 1;
2278  }
2279  }
2280  }
2281  else if ( token.id == TOK_ARRAY )
2282  {
2283  auto array_tkn = new Token( TOK_ARRAY, TYP_OPERAND );
2284  array_tkn->dbg_filenum = token.dbg_filenum;
2285  array_tkn->dbg_linenum = token.dbg_linenum;
2286 
2287  res = parseToken( ctx, expr, array_tkn );
2288  if ( res < 0 )
2289  return res;
2290 
2291  // expr.CA.push( array_tkn );
2292 
2293  // 'array' can be of the following forms:
2294  // var x := array; // preferred
2295  // var x := array { 2, 4, 6, 1 }; // preferred
2296  // var x := array ( 2, 4, 6, 1 ); // not preferred, looks too much like a multi-dim
2297 
2298  Token peek_token;
2299  res = peekToken( ctx, peek_token );
2300  if ( res == 0 )
2301  {
2302  if ( peek_token.id == TOK_LPAREN )
2303  {
2304  res = getArrayElements( expr, ctx );
2305  }
2306  else if ( peek_token.id == TOK_LBRACE )
2307  {
2308  // this form expects that the LBRACE has been eaten, so do so.
2309  getToken( ctx, peek_token );
2310  res = getNewArrayElements( expr, ctx );
2311  }
2312  if ( res < 0 )
2313  {
2314  INFO_PRINT << "Error getting elements for array\n";
2315  }
2316  }
2317  }
2318  else if ( token.id == TOK_LBRACE ) // a bare array declaration, like var x := { 2, 4 };
2319  {
2320  expr.CA.push( new Token( TOK_ARRAY, TYP_OPERAND ) );
2321  res = getNewArrayElements( expr, ctx );
2322  if ( res < 0 )
2323  {
2324  INFO_PRINT << "Error getting elements for array\n";
2325  }
2326  }
2327  else if ( token.id == TOK_ERROR )
2328  {
2329  auto error_tkn = new Token( TOK_ERROR, TYP_OPERAND );
2330  error_tkn->dbg_filenum = token.dbg_filenum;
2331  error_tkn->dbg_linenum = token.dbg_linenum;
2332  expr.CA.push( error_tkn );
2333  res = getStructMembers( expr, ctx );
2334  if ( res < 0 )
2335  {
2336  INFO_PRINT << "Error reading members for error\n";
2337  }
2338  }
2339  else if ( token.id == TOK_STRUCT )
2340  {
2341  auto struct_tkn = new Token( TOK_STRUCT, TYP_OPERAND );
2342  struct_tkn->dbg_filenum = token.dbg_filenum;
2343  struct_tkn->dbg_linenum = token.dbg_linenum;
2344  expr.CA.push( struct_tkn );
2345  res = getStructMembers( expr, ctx );
2346  if ( res < 0 )
2347  {
2348  INFO_PRINT << "Error reading members for struct\n";
2349  }
2350  }
2351  else if ( token.id == TOK_DICTIONARY )
2352  {
2353  auto dict_tkn = new Token( token ); // Mod_Basic, TOK_DICTIONARY, TYP_OPERAND );
2354  // struct_tkn->dbg_filenum = token.dbg_filenum;
2355  // struct_tkn->dbg_linenum = token.dbg_linenum;
2356  expr.CA.push( dict_tkn );
2357  res = getDictionaryMembers( expr, ctx );
2358  if ( res < 0 )
2359  {
2360  INFO_PRINT << "Error reading members for dictionary\n";
2361  }
2362  }
2363  else if ( token.id == TOK_FUNCREF )
2364  {
2365  auto ref_tkn = new Token( token );
2366  res = getFunctionPArgument( expr, ctx, ref_tkn );
2367  if ( res < 0 )
2368  {
2369  INFO_PRINT << "Error reading function reference argument\n";
2370  }
2371  expr.CA.push( ref_tkn );
2372  }
2373  else if ( token.id == TOK_MEMBER && callingMethod( ctx ) )
2374  {
2375  // look for something like x.foo(b);
2376  // we'll catch this on the 'foo'
2377  // so check for preceding TOK_MEMBER, and succeeding TOK_LPAREN
2378 
2379  ptok2 = new Token( token ); // this we will turn into an INS_CALL_METHOD.
2380  int _res = parseToken( ctx, expr, ptok2 );
2381  if ( _res < 0 )
2382  {
2383  delete ptok2;
2384  return _res;
2385  }
2386 
2387  // grab the method name
2388  getToken( ctx, token, &expr );
2389  std::string methodName( token.tokval() );
2390  Clib::mklower( methodName );
2391  int nargs;
2392  res = getMethodArguments( expr, ctx, nargs );
2393  // our ptok2 is now sitting in TX. Move it to CA.
2394  Token* _t = expr.TX.top();
2395  expr.TX.pop();
2396  expr.CA.push( _t );
2397 
2398  ObjMethod* objmeth = getKnownObjMethod( methodName.c_str() );
2399  if ( objmeth != NULL && compilercfg.OptimizeObjectMembers )
2400  {
2401  ptok2->id = INS_CALL_METHOD_ID;
2402  ptok2->type = TYP_METHOD;
2403  ptok2->lval = nargs;
2404  ptok2->dval = objmeth->id;
2405  }
2406  else
2407  {
2408  ptok2->id = INS_CALL_METHOD;
2409  ptok2->type = TYP_METHOD;
2410  ptok2->lval = nargs;
2411  ptok2->copyStr( methodName.c_str() );
2412  }
2413  last_type = TYP_OPERAND;
2414  }
2415  else
2416  {
2417  if ( res >= 0 )
2418  {
2419  ptok2 = new Token( token );
2420  res = parseToken( ctx, expr, ptok2 );
2421  if ( res < 0 )
2422  {
2423  delete ptok2;
2424  ctx.s = t; // FIXME operator=
2425 
2426  if ( ( err == PERR_UNEXRPAREN ) && rightparen_term_allowed )
2427  {
2428  err = PERR_NONE;
2429  res = 0;
2430  done = 1;
2431  }
2432  }
2433  }
2434  }
2435  if ( flags & EXPR_FLAG_SINGLE_ELEMENT )
2436  done = 1;
2437  }
2438 
2439  int nres = parseToken( ctx, expr, new Token ); /* end of expression */
2440  if ( res >= 0 )
2441  res = nres;
2442 
2443  if ( !comma_term_allowed && !quiet )
2444  {
2445  INFO_PRINT << "Result: " << res << "\n";
2446  }
2447  return res;
2448 }
2449 
2450 /* not used? 12/10/1998 ens
2451 int SmartParser::IP(Expression& expr, char *s)
2452 {
2453 // return Parser::IP(s);
2454 reinit(expr);
2455 int res = IIP(expr, &s, EXPR_FLAG_SEMICOLON_TERM_ALLOWED);
2456 if (res < 0 && !quiet)
2457 {
2458 cout << "Parse Error: " << ParseErrorStr[err];
2459 if (ext_err[0]) cout << " " << ext_err;
2460 cout << endl;
2461 }
2462 return res;
2463 }
2464 */
2465 
2467 {
2468  reinit( expr );
2469  return IIP( expr, ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED );
2470 }
2471 }
2472 }
unsigned char u8
Definition: rawtypes.h:25
virtual int tryBinaryOperator(Token &tok, CompilerContext &ctx)
Definition: parser.cpp:1122
const unsigned EXPR_FLAG_DICTKEY_TERM_ALLOWED
Definition: parser.h:104
virtual int recognize(Token &tok, const char *buf, const char **s)
Definition: parser.cpp:1381
const unsigned EXPR_FLAG_AUTO_TERM_ALLOWED
Definition: parser.h:102
static void write_words(std::ostream &os)
Definition: parser.cpp:925
std::queue< Token * > CA
Definition: parser.h:91
virtual bool recognize_reserved_word(Token &tok, const char *buf)
Definition: parser.cpp:1389
virtual int getToken(CompilerContext &ctx, Token &token, Expression *expr=NULL) POL_OVERRIDE
Definition: parser.cpp:1922
int getArgs(Expression &expr, CompilerContext &ctx)
Definition: parser.cpp:1951
ReservedWord reserved_words[]
Definition: parser.cpp:809
BTokenType type
Definition: token.h:40
char ext_err[50]
Definition: parser.h:116
virtual int tryOperator(Token &tok, const char *buf, const char **s, Operator *opList, int n_ops, char *opbuf)
Definition: parser.cpp:1047
std::stack< Token * > TX
Definition: parser.h:90
ParseError err
Definition: parser.h:114
const char * tokval() const
Definition: token.h:71
void reinit(Expression &ex)
Definition: parser.cpp:1431
ObjMember * getKnownObjMember(const char *token)
Definition: parser.cpp:483
BTokenId id
Definition: token.h:39
int IP(Expression &expr, char *s)
Operator binary_operators[]
Definition: parser.cpp:189
std::map< std::string, ReservedWord *, Clib::ci_cmp_pred > ReservedWords
Definition: parser.cpp:912
unsigned n_reserved
Definition: parser.cpp:910
const unsigned EXPR_FLAG_SINGLE_ELEMENT
Definition: parser.h:98
char buffer[51]
Definition: parser.h:117
virtual int recognize_binary(Token &tok, const char *buf, const char **s)
Definition: parser.cpp:1322
char * stracpy(char *dest, const char *src, size_t maxlen)
Definition: stracpy.cpp:17
int n_objmembers
Definition: parser.cpp:482
int allowed_table[8][8]
Definition: parser.cpp:111
CompilerConfig compilercfg
ObjMethod * getKnownObjMethod(const char *token)
Definition: parser.cpp:665
const unsigned EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED
Definition: parser.h:97
virtual int recognize_unary(Token &tok, const char *buf)
Definition: parser.cpp:1362
char ident_allowed[]
Definition: parser.cpp:104
int IIP(Expression &expr, CompilerContext &ctx, unsigned expr_flags)
Definition: parser.cpp:2076
void copyStr(const char *s)
Definition: token.cpp:214
const unsigned EXPR_FLAG_TO_TERM_ALLOWED
Definition: parser.h:101
virtual int parseToken(CompilerContext &ctx, Expression &expr, Token *) POL_OVERRIDE
Definition: parser.cpp:1681
Clib::UnitTest testparserdefinitions_obj(testparserdefinitions)
int n_operators
Definition: parser.cpp:243
void matchOperators(Operator *oplist, int n_ops, char *buf, int *nPartial, Operator **pTotalMatchOperator)
Definition: parser.cpp:735
virtual int tryNumeric(Token &tok, CompilerContext &ctx)
Definition: parser.cpp:1156
virtual int getToken(CompilerContext &ctx, Token &token, Expression *expr=NULL)
Definition: parser.cpp:1482
ObjMember * getObjMember(int id)
Definition: parser.cpp:501
ObjMethod * getObjMethod(int id)
Definition: parser.cpp:684
virtual int tryLiteral(Token &tok, CompilerContext &ctx)
Definition: parser.cpp:1214
void testparserdefinitions()
Definition: parser.cpp:692
Precedence precedence
Definition: operator.h:41
#define passert_always_r(exp, reason)
Definition: passert.h:84
virtual int tryUnaryOperator(Token &tok, CompilerContext &ctx)
Definition: parser.cpp:1139
const char * ParseErrorStr[PERR_NUM_ERRORS]
Definition: parser.cpp:82
char operator_brk[]
Definition: parser.cpp:102
const unsigned EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED
Definition: parser.h:100
ReservedWords reservedWordsByName
Definition: parser.cpp:913
virtual int peekToken(const CompilerContext &ctx, Token &token, Expression *expr=NULL)
Definition: parser.cpp:1561
virtual int tryLiteral(Token &tok, CompilerContext &ctx) POL_OVERRIDE
Definition: parser.cpp:1641
virtual int parseToken(CompilerContext &ctx, Expression &expr, Token *token)=0
unsigned char module
Definition: token.h:55
#define ERROR_PRINT
Definition: logfacility.h:230
void setStr(const char *s)
Definition: token.cpp:204
const unsigned EXPR_FLAG_COMMA_TERM_ALLOWED
Definition: parser.h:96
void mklower(std::string &str)
Definition: strutil.cpp:266
Precedence find_precedence(Token &token)
Definition: parser.cpp:1426
#define INFO_PRINT
Definition: logfacility.h:223
int n_objmethods
Definition: parser.cpp:664
bool callingMethod(CompilerContext &ctx)
Definition: parser.cpp:2053
Definition: berror.cpp:12
ObjMethod object_methods[]
Definition: parser.cpp:509
virtual int isOkay(const Token &token, BTokenType last_type)
Definition: parser.cpp:1590
const unsigned EXPR_FLAG_ENDENUM_TERM_ALLOWED
Definition: parser.h:99
ObjMember object_members[]
Definition: parser.cpp:256
static void init_tables()
Definition: parser.cpp:914
Operator unary_operators[]
Definition: parser.cpp:245
const unsigned EXPR_FLAG_SEMICOLON_TERM_ALLOWED
Definition: parser.h:95