/* Copyright (C) 2004, 2005, 2006, 2007 John E. Davis This file is part of the S-Lang Library. The S-Lang Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The S-Lang Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "slinclud.h" #include "slang.h" #include "_slang.h" static SLang_Load_Type *LLT; static void free_token (_pSLang_Token_Type *t) { register unsigned int nrefs = t->num_refs; if (nrefs == 0) return; if (nrefs == 1) { if (t->free_sval_flag) { if (t->type == BSTRING_TOKEN) SLbstring_free (t->v.b_val); else _pSLfree_hashed_string (t->v.s_val, strlen (t->v.s_val), t->hash); t->v.s_val = NULL; } } t->num_refs = nrefs - 1; } static void init_token (_pSLang_Token_Type *t) { memset ((char *) t, 0, sizeof (_pSLang_Token_Type)); #if SLANG_HAS_DEBUG_CODE t->line_number = -1; #endif } /* Allow room for one push back of a token. This is necessary for * multiple assignment. */ static unsigned int Use_Next_Token; static _pSLang_Token_Type Next_Token; #if SLANG_HAS_DEBUG_CODE static int Last_Line_Number = -1; #endif static int unget_token (_pSLang_Token_Type *ctok) { if (_pSLang_Error) return -1; if (Use_Next_Token != 0) { _pSLparse_error (SL_INTERNAL_ERROR, "unget_token failed", ctok, 0); return -1; } Use_Next_Token++; Next_Token = *ctok; init_token (ctok); return 0; } static int get_token (_pSLang_Token_Type *ctok) { if (ctok->num_refs) free_token (ctok); if (Use_Next_Token) { Use_Next_Token--; *ctok = Next_Token; return ctok->type; } return _pSLget_token (ctok); } static int compile_token (_pSLang_Token_Type *t) { #if SLANG_HAS_DEBUG_CODE if ((t->line_number != Last_Line_Number) && (t->line_number != -1)) { _pSLang_Token_Type tok; tok.type = LINE_NUM_TOKEN; tok.v.long_val = Last_Line_Number = t->line_number; (*_pSLcompile_ptr) (&tok); } #endif (*_pSLcompile_ptr) (t); return 0; } typedef struct { #define USE_PARANOID_MAGIC 0 #if USE_PARANOID_MAGIC unsigned long magic; #endif _pSLang_Token_Type *stack; unsigned int len; unsigned int size; } Token_List_Type; #define MAX_TOKEN_LISTS 256 static Token_List_Type Token_List_Stack [MAX_TOKEN_LISTS]; static unsigned int Token_List_Stack_Depth = 0; static Token_List_Type *Token_List = NULL; static void init_token_list (Token_List_Type *t) { t->size = 0; t->len = 0; t->stack = NULL; #if USE_PARANOID_MAGIC t->magic = 0xABCDEF12; #endif } static void free_token_list (Token_List_Type *t) { _pSLang_Token_Type *s; if (t == NULL) return; #if USE_PARANOID_MAGIC if (t->magic != 0xABCDEF12) { SLang_verror (SL_INTERNAL_ERROR, "Token Magic number error."); return; } #endif s = t->stack; if (s != NULL) { _pSLang_Token_Type *smax = s + t->len; while (s != smax) { if (s->num_refs) free_token (s); s++; } SLfree ((char *) t->stack); } memset ((char *) t, 0, sizeof (Token_List_Type)); } static Token_List_Type *push_token_list (void) { if (Token_List_Stack_Depth == MAX_TOKEN_LISTS) { _pSLparse_error (SL_BUILTIN_LIMIT_EXCEEDED, "Token list stack size exceeded", NULL, 0); return NULL; } Token_List = Token_List_Stack + Token_List_Stack_Depth; Token_List_Stack_Depth++; init_token_list (Token_List); return Token_List; } static int pop_token_list (int do_free) { if (Token_List_Stack_Depth == 0) { if (_pSLang_Error == 0) _pSLparse_error (SL_INTERNAL_ERROR, "Token list stack underflow", NULL, 0); return -1; } Token_List_Stack_Depth--; if (do_free) free_token_list (Token_List); if (Token_List_Stack_Depth != 0) Token_List = Token_List_Stack + (Token_List_Stack_Depth - 1); else Token_List = NULL; return 0; } static int check_token_list_space (Token_List_Type *t, unsigned int delta_size) { _pSLang_Token_Type *st; unsigned int len; #if USE_PARANOID_MAGIC if (t->magic != 0xABCDEF12) { SLang_verror (SL_INTERNAL_ERROR, "Token Magic number error."); return -1; } #endif len = t->len + delta_size; if (len <= t->size) return 0; if (delta_size < 4) { delta_size = 4; len = t->len + delta_size; } st = (_pSLang_Token_Type *) SLrealloc((char *) t->stack, len * sizeof(_pSLang_Token_Type)); if (st == NULL) { _pSLparse_error (SL_MALLOC_ERROR, "Malloc error", NULL, 0); return -1; } memset ((char *) (st + t->len), 0, delta_size); t->stack = st; t->size = len; return 0; } static int append_token (_pSLang_Token_Type *t) { if (-1 == check_token_list_space (Token_List, 1)) return -1; Token_List->stack [Token_List->len] = *t; Token_List->len += 1; t->num_refs = 0; /* stealing it */ return 0; } static int append_copy_of_string_token (_pSLang_Token_Type *t) { _pSLang_Token_Type *t1; if (-1 == check_token_list_space (Token_List, 1)) return -1; t1 = Token_List->stack + Token_List->len; *t1 = *t; if (NULL == (t1->v.s_val = SLang_create_slstring (t->v.s_val))) return -1; t1->free_sval_flag = 1; t1->num_refs = 1; Token_List->len += 1; return 0; } static int append_int_token (int n) { _pSLang_Token_Type num_tok; init_token (&num_tok); num_tok.type = INT_TOKEN; num_tok.v.long_val = n; return append_token (&num_tok); } static int append_token_of_type (unsigned char t) { _pSLang_Token_Type *tok; if (-1 == check_token_list_space (Token_List, 1)) return -1; /* The memset when the list was created ensures that the other fields * are properly initialized. */ if ((t == CHS_TOKEN) && Token_List->len) { tok = Token_List->stack + (Token_List->len-1); if (IS_INTEGER_TOKEN(tok->type)) { tok->v.long_val = -tok->v.long_val; return 0; } #ifdef HAVE_LONG_LONG if ((tok->type == LLONG_TOKEN) || (tok->type == ULLONG_TOKEN)) { tok->v.llong_val = -tok->v.llong_val; return 0; } #endif } tok = Token_List->stack + Token_List->len; init_token (tok); tok->type = t; Token_List->len += 1; return 0; } static _pSLang_Token_Type *get_last_token (void) { unsigned int len; if ((Token_List == NULL) || (0 == (len = Token_List->len))) return NULL; len--; return Token_List->stack + len; } /* This function does NOT free the list. */ static int compile_token_list_with_fun (int dir, Token_List_Type *list, int (*f)(_pSLang_Token_Type *)) { _pSLang_Token_Type *t0, *t1; if (list == NULL) return -1; if (f == NULL) f = compile_token; t0 = list->stack; t1 = t0 + list->len; if (dir < 0) { /* backwards */ while ((_pSLang_Error == 0) && (t1 > t0)) { t1--; (*f) (t1); } return 0; } /* forward */ while ((_pSLang_Error == 0) && (t0 < t1)) { (*f) (t0); t0++; } return 0; } static int compile_token_list (void) { if (Token_List == NULL) return -1; compile_token_list_with_fun (1, Token_List, NULL); pop_token_list (1); return 0; } /* Take all elements in the list from pos2 to the end and exchange them * with the elements at pos1, e.g., * ...ABCDEabc ==> ...abcABCDE * where pos1 denotes A and pos2 denotes a. */ static int token_list_element_exchange (unsigned int pos1, unsigned int pos2) { _pSLang_Token_Type *s, *s1, *s2; unsigned int len, nloops; if (Token_List == NULL) return -1; s = Token_List->stack; len = Token_List->len; if ((s == NULL) || (len == 0) || (pos2 >= len)) return -1; /* This may not be the most efficient algorithm but the number to swap * is most-likely going to be small, e.g, 3 * The algorithm is to rotate the list. The particular rotation * direction was chosen to make insert_token fast. * It works like: * @ ABCabcde --> BCabcdeA --> CabcdeAB --> abcdefAB * which is optimal for Abcdef sequence produced by function calls. * * Profiling indicates that nloops is almost always 1, whereas the inner * loop can loop many times (e.g., 9 times). */ s2 = s + (len - 1); s1 = s + pos1; nloops = pos2 - pos1; while (nloops) { _pSLang_Token_Type save; s = s1; save = *s; while (s < s2) { *s = *(s + 1); s++; } *s = save; nloops--; } return 0; } #if 0 static int insert_token (_pSLang_Token_Type *t, unsigned int pos) { if (-1 == append_token (t)) return -1; return token_list_element_exchange (pos, Token_List->len - 1); } #endif static void compile_token_of_type (unsigned char t) { _pSLang_Token_Type tok; #if SLANG_HAS_DEBUG_CODE tok.line_number = -1; #endif tok.type = t; compile_token(&tok); } #if SLANG_HAS_BOSEOS static void compile_eos (void) { /* This is commented out to ensure that if bos was compiled, the eos will be. * * if (0 == (_pSLang_Compile_Line_Num_Info & SLANG_BOSEOS_MASK)) * return; */ compile_token_of_type (EOS_TOKEN); } static int compile_bos (_pSLang_Token_Type *t, int level) { _pSLang_Token_Type tok; if (level > _pSLang_Compile_BOSEOS) return 0; tok.type = BOS_TOKEN; tok.v.long_val = t->line_number; (*_pSLcompile_ptr) (&tok); return 1; } static void append_eos (void) { /* This is commented out to ensure that if bos was compiled, the eos will be. * * if (0 == (_pSLang_Compile_Line_Num_Info & SLANG_BOSEOS_MASK)) * return; */ append_token_of_type (EOS_TOKEN); } static int append_bos (_pSLang_Token_Type *t, int level) { _pSLang_Token_Type tok; if (level > _pSLang_Compile_BOSEOS) return 0; init_token (&tok); tok.type = BOS_TOKEN; tok.v.long_val = t->line_number; append_token (&tok); free_token (&tok); return 1; } #endif static void statement (_pSLang_Token_Type *); static void compound_statement (_pSLang_Token_Type *); static void expression_with_parenthesis (_pSLang_Token_Type *); static void handle_semicolon (_pSLang_Token_Type *); static void handle_for_statement (_pSLang_Token_Type *); static void handle_foreach_statement (_pSLang_Token_Type *); static void statement_list (_pSLang_Token_Type *); static void variable_list (_pSLang_Token_Type *, unsigned char); static void struct_declaration (_pSLang_Token_Type *, int); static void define_function_args (_pSLang_Token_Type *); static void typedef_definition (_pSLang_Token_Type *); static void function_args_expression (_pSLang_Token_Type *, int, int); static void expression (_pSLang_Token_Type *); static void expression_with_commas (_pSLang_Token_Type *, int); static void simple_expression (_pSLang_Token_Type *); static void unary_expression (_pSLang_Token_Type *); static void postfix_expression (_pSLang_Token_Type *); static int check_for_lvalue (unsigned char, _pSLang_Token_Type *); /* static void primary_expression (_pSLang_Token_Type *); */ static void block (_pSLang_Token_Type *); static void inline_array_expression (_pSLang_Token_Type *); static void inline_list_expression (_pSLang_Token_Type *); static void array_index_expression (_pSLang_Token_Type *); static void do_multiple_assignment (_pSLang_Token_Type *); static void try_multiple_assignment (_pSLang_Token_Type *); static void handle_try_statement (_pSLang_Token_Type *); static void handle_throw_statement (_pSLang_Token_Type *); #if 0 static void not_implemented (char *what) { char err [256]; sprintf (err, "Expression not implemented: %s", what); _pSLparse_error (SL_NOT_IMPLEMENTED, err, NULL, 0); } #endif static void rpn_parse_line (_pSLang_Token_Type *tok) { do { /* multiple RPN tokens possible when the file looks like: * . * . */ if (tok->type != RPN_TOKEN) compile_token (tok); free_token (tok); } while (EOF_TOKEN != _pSLget_rpn_token (tok)); } static int get_identifier_token (_pSLang_Token_Type *tok) { if (IDENT_TOKEN == get_token (tok)) return IDENT_TOKEN; _pSLparse_error (SL_SYNTAX_ERROR, "Expecting identifier", tok, 0); return tok->type; } static void define_function (_pSLang_Token_Type *ctok, unsigned char type) { _pSLang_Token_Type fname; switch (type) { case STATIC_TOKEN: type = DEFINE_STATIC_TOKEN; break; case PUBLIC_TOKEN: type = DEFINE_PUBLIC_TOKEN; break; case PRIVATE_TOKEN: type = DEFINE_PRIVATE_TOKEN; } init_token (&fname); if (IDENT_TOKEN != get_identifier_token (&fname)) { free_token (&fname); return; } compile_token_of_type(OPAREN_TOKEN); get_token (ctok); define_function_args (ctok); compile_token_of_type(FARG_TOKEN); if (ctok->type == OBRACE_TOKEN) compound_statement(ctok); else if (ctok->type != SEMICOLON_TOKEN) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting {", ctok, 0); free_token (&fname); return; } fname.type = type; compile_token (&fname); free_token (&fname); } /* This is called from "statement", which is expected to return no * new token. */ static void check_for_loop_then_else (_pSLang_Token_Type *ctok) { int b = 0; get_token (ctok); while (1) { unsigned char type = ctok->type; #ifdef LOOP_ELSE_TOKEN if ((type == ELSE_TOKEN) && ((b & 1) == 0)) { get_token (ctok); block (ctok); compile_token_of_type (LOOP_ELSE_TOKEN); get_token (ctok); b |= 1; continue; } #endif if ((type == THEN_TOKEN) && ((b & 2) == 0)) { get_token (ctok); block (ctok); compile_token_of_type (LOOP_THEN_TOKEN); get_token (ctok); b |= 2; continue; } break; } unget_token (ctok); } /* statement: * compound-statement * if ( expression ) statement * if ( expression ) statement else statement * !if ( expression ) statement * loop ( expression ) statement * _for ( expression ) statement * foreach ( expression ) statement * foreach (expression ) using (expression-list) statement * while ( expression ) statement * do statement while (expression) ; * for ( expressionopt ; expressionopt ; expressionopt ) statement * ERROR_BLOCK statement * try statement rest-of-try-statement * throw exception, object * EXIT_BLOCK statement * USER_BLOCK0 statement * USER_BLOCK1 statement * USER_BLOCK2 statement * USER_BLOCK3 statement * USER_BLOCK4 statement * forever statement * break ; * continue ; * return expressionopt ; * variable variable-list ; * struct struct-decl ; * define identifier function-args ; * define identifier function-args compound-statement * switch ( expression ) statement * rpn-line * at-line * push ( expression ) * ( expression ) = expression ; * expression ; * expression : */ /* Note: This function does not return with a new token. It is up to the * calling routine to handle that. */ static void statement (_pSLang_Token_Type *ctok) { unsigned char type; #if SLANG_HAS_BOSEOS int eos; #endif if (_pSLang_Error) return; LLT->parse_level += 1; switch (ctok->type) { case OBRACE_TOKEN: compound_statement (ctok); break; case IF_TOKEN: case IFNOT_TOKEN: type = ctok->type; get_token (ctok); #if SLANG_HAS_BOSEOS eos = compile_bos (ctok, 2); #endif expression_with_parenthesis (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif block (ctok); if (ELSE_TOKEN != get_token (ctok)) { compile_token_of_type (type); unget_token (ctok); break; } get_token (ctok); block (ctok); if (type == IF_TOKEN) type = ELSE_TOKEN; else type = NOTELSE_TOKEN; compile_token_of_type (type); break; /* case IFNOT_TOKEN: */ case _FOR_TOKEN: get_token (ctok); handle_for_statement (ctok); check_for_loop_then_else (ctok); break; case LOOP_TOKEN: get_token (ctok); #if SLANG_HAS_BOSEOS eos = compile_bos (ctok, 2); #endif expression_with_parenthesis (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif block (ctok); compile_token_of_type (LOOP_TOKEN); check_for_loop_then_else (ctok); break; case FOREACH_TOKEN: get_token (ctok); handle_foreach_statement (ctok); check_for_loop_then_else (ctok); break; case WHILE_TOKEN: get_token (ctok); compile_token_of_type (OBRACE_TOKEN); #if SLANG_HAS_BOSEOS eos = compile_bos (ctok, 2); #endif expression_with_parenthesis (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif compile_token_of_type (CBRACE_TOKEN); block (ctok); compile_token_of_type (WHILE_TOKEN); check_for_loop_then_else (ctok); break; case DO_TOKEN: get_token (ctok); block (ctok); if (WHILE_TOKEN != get_token (ctok)) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting while", ctok, 0); break; } get_token (ctok); compile_token_of_type (OBRACE_TOKEN); #if SLANG_HAS_BOSEOS eos = compile_bos (ctok, 2); #endif expression_with_parenthesis (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif compile_token_of_type (CBRACE_TOKEN); compile_token_of_type (DOWHILE_TOKEN); handle_semicolon (ctok); check_for_loop_then_else (ctok); break; case FOR_TOKEN: /* Look for (exp_opt ; exp_opt ; exp_opt ) */ if (OPAREN_TOKEN != get_token (ctok)) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting (.", ctok, 0); break; } if (NULL == push_token_list ()) break; append_token_of_type (OBRACE_TOKEN); if (SEMICOLON_TOKEN != get_token (ctok)) { #if SLANG_HAS_BOSEOS eos = append_bos (ctok, 2); #endif expression (ctok); #if SLANG_HAS_BOSEOS if (eos) append_eos (); #endif if (ctok->type != SEMICOLON_TOKEN) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting ;", ctok, 0); break; } } append_token_of_type (CBRACE_TOKEN); append_token_of_type (OBRACE_TOKEN); if (SEMICOLON_TOKEN != get_token (ctok)) { #if SLANG_HAS_BOSEOS eos = append_bos (ctok, 2); #endif expression (ctok); #if SLANG_HAS_BOSEOS if (eos) append_eos (); #endif if (ctok->type != SEMICOLON_TOKEN) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting ;", ctok, 0); break; } } append_token_of_type (CBRACE_TOKEN); append_token_of_type (OBRACE_TOKEN); if (CPAREN_TOKEN != get_token (ctok)) { #if SLANG_HAS_BOSEOS eos = append_bos (ctok, 2); #endif expression (ctok); #if SLANG_HAS_BOSEOS if (eos) append_eos (); #endif if (ctok->type != CPAREN_TOKEN) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting ).", ctok, 0); break; } } append_token_of_type (CBRACE_TOKEN); compile_token_list (); get_token (ctok); block (ctok); compile_token_of_type (FOR_TOKEN); check_for_loop_then_else (ctok); break; case FOREVER_TOKEN: /* drop */ case ERRBLK_TOKEN: case EXITBLK_TOKEN: case USRBLK0_TOKEN: case USRBLK1_TOKEN: case USRBLK2_TOKEN: case USRBLK3_TOKEN: case USRBLK4_TOKEN: type = ctok->type; get_token (ctok); block (ctok); compile_token_of_type (type); if (type == FOREVER_TOKEN) check_for_loop_then_else (ctok); break; case BREAK_TOKEN: case CONT_TOKEN: #if SLANG_HAS_BOSEOS eos = compile_bos (ctok, 3); if (eos) compile_eos (); #endif compile_token_of_type (ctok->type); get_token (ctok); handle_semicolon (ctok); break; case RETURN_TOKEN: if (SEMICOLON_TOKEN != get_token (ctok)) { if (NULL == push_token_list ()) break; #if SLANG_HAS_BOSEOS eos = append_bos (ctok, 3); #endif expression (ctok); #if SLANG_HAS_BOSEOS if (eos) append_eos (); #endif if (ctok->type != SEMICOLON_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting ;", ctok, 0); break; } compile_token_list (); } compile_token_of_type (RETURN_TOKEN); handle_semicolon (ctok); break; case STATIC_TOKEN: case PRIVATE_TOKEN: case PUBLIC_TOKEN: type = ctok->type; get_token (ctok); if (ctok->type == VARIABLE_TOKEN) { get_token (ctok); variable_list (ctok, type); handle_semicolon (ctok); break; } if (ctok->type == DEFINE_TOKEN) { define_function (ctok, type); break; } _pSLparse_error (SL_SYNTAX_ERROR, "Expecting 'variable' or 'define'", ctok, 0); break; case VARIABLE_TOKEN: get_token (ctok); variable_list (ctok, OBRACKET_TOKEN); handle_semicolon (ctok); break; case TYPEDEF_TOKEN: get_token (ctok); if (NULL == push_token_list ()) break; typedef_definition (ctok); compile_token_list (); handle_semicolon (ctok); break; case DEFINE_TOKEN: define_function (ctok, DEFINE_TOKEN); break; case TRY_TOKEN: get_token (ctok); handle_try_statement (ctok); break; case THROW_TOKEN: get_token (ctok); handle_throw_statement (ctok); break; case SWITCH_TOKEN: get_token (ctok); #if SLANG_HAS_BOSEOS eos = compile_bos (ctok, 2); #endif expression_with_parenthesis (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif while ((_pSLang_Error == 0) && (OBRACE_TOKEN == ctok->type)) { compile_token_of_type (OBRACE_TOKEN); compound_statement (ctok); compile_token_of_type (CBRACE_TOKEN); get_token (ctok); } compile_token_of_type (SWITCH_TOKEN); unget_token (ctok); break; case EOF_TOKEN: break; #if 0 case PUSH_TOKEN: get_token (ctok); expression_list_with_parenthesis (ctok); handle_semicolon (ctok); break; #endif case SEMICOLON_TOKEN: #if SLANG_HAS_BOSEOS eos = compile_bos (ctok, 3); if (eos) compile_eos (); #endif handle_semicolon (ctok); break; case RPN_TOKEN: if (POUND_TOKEN == get_token (ctok)) _pSLcompile_byte_compiled (); else if (ctok->type != EOF_TOKEN) rpn_parse_line (ctok); break; case OPAREN_TOKEN: /* multiple assignment */ #if SLANG_HAS_BOSEOS eos = compile_bos(ctok, 1); #endif try_multiple_assignment (ctok); if (ctok->type == COLON_TOKEN) compile_token_of_type (COLON_TOKEN); else handle_semicolon (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos(); #endif break; default: #if SLANG_HAS_BOSEOS eos = compile_bos(ctok, ctok->type != CASE_TOKEN ? 1 : 2); #endif if (NULL == push_token_list ()) break; expression (ctok); compile_token_list (); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif if (ctok->type == COLON_TOKEN) compile_token_of_type (COLON_TOKEN); else handle_semicolon (ctok); break; } LLT->parse_level -= 1; } /* This function does not return a new token. Calling routine must do that */ static void block (_pSLang_Token_Type *ctok) { compile_token_of_type (OBRACE_TOKEN); statement (ctok); compile_token_of_type (CBRACE_TOKEN); } static void handle_for_statement (_pSLang_Token_Type *ctok) { _pSLang_Token_Type tok_buf; _pSLang_Token_Type *ident_token = NULL; #if SLANG_HAS_BOSEOS int eos; #endif if (ctok->type == IDENT_TOKEN) { tok_buf = *ctok; ident_token = &tok_buf; init_token (ctok); get_token (ctok); } #if SLANG_HAS_BOSEOS eos = compile_bos(ctok, 2); #endif expression_with_parenthesis (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif compile_token_of_type (OBRACE_TOKEN); if (ident_token != NULL) { ident_token->type = _SCALAR_ASSIGN_TOKEN; compile_token (ident_token); free_token (ident_token); } statement (ctok); compile_token_of_type (CBRACE_TOKEN); compile_token_of_type (_FOR_TOKEN); } static int init_identifier_token (_pSLang_Token_Type *t, char *name, int alloc) { unsigned int len; len = strlen (name); init_token (t); if (alloc) { if (NULL == (t->v.s_val = _pSLstring_make_hashed_string (name, len, &t->hash))) return -1; t->free_sval_flag = 1; return 0; } t->type = IDENT_TOKEN; t->v.s_val = name; t->hash = _pSLstring_hash ((unsigned char *)name, (unsigned char *)name + len); t->free_sval_flag = 0; return 0; } /* The try-statement looks like: * * TRY ev_optional catch_blocks_opt finally_block_opt * * ev_optional: * nil * ( e , v) * * catch_blocks_opt: * catch_blocks_opt catch_block * catch_block: * CATCH exception-list : block * CATCH exception-list ; * exception-list: * expression * exception-list , exception * * finally_block_opt: * FINALLY[:] block * * The above gets compiled into the form: * * {ev_block} {try-statements} * {exception-list}{catch-block} * . * . * {exception-list}{catch-block} * {finally_block} try */ static void handle_try_statement (_pSLang_Token_Type *ctok) { int num_catches; /* start ev_block */ if (NULL == push_token_list ()) return; append_token_of_type (OBRACE_TOKEN); if (ctok->type == OPAREN_TOKEN) { _pSLang_Token_Type e; if (-1 == init_identifier_token (&e, "__get_exception_info", 0)) return; append_token (&e); free_token (&e); get_token (ctok); postfix_expression (ctok); check_for_lvalue (ASSIGN_TOKEN, NULL); if (ctok->type != CPAREN_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting )", ctok, 0); return; } get_token (ctok); } append_token_of_type (CBRACE_TOKEN); compile_token_list (); /* Now the try block itself */ block (ctok); /* Now the various catch blocks */ num_catches = 0; while (CATCH_TOKEN == get_token (ctok)) { /* Expecting catch expression-list: */ compile_token_of_type (OBRACE_TOKEN); get_token (ctok); push_token_list (); while (_pSLang_Error == 0) { if (ctok->type == COLON_TOKEN) break; simple_expression (ctok); if (ctok->type != COMMA_TOKEN) break; get_token (ctok); } if (ctok->type == COLON_TOKEN) get_token (ctok); else if (ctok->type != SEMICOLON_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting a colon to end the exception list", ctok, 0); return; } compile_token_list (); compile_token_of_type (CBRACE_TOKEN); /* catch block */ block (ctok); num_catches++; } if ((num_catches == 0) && (ctok->type != FINALLY_TOKEN)) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting \"catch\" or \"finally\"", ctok, 0); return; } /* finally */ if (ctok->type == FINALLY_TOKEN) { get_token (ctok); if (ctok->type == COLON_TOKEN) get_token (ctok); block (ctok); } else { /* since this is called directly from statement, we need to get only * tokens that were used. So, since we are not using this, put it back. */ unget_token (ctok); compile_token_of_type (OBRACE_TOKEN); compile_token_of_type (CBRACE_TOKEN); } compile_token_of_type (TRY_TOKEN); } /* * throw-statement: * throw ; * throw exception_expr ; * throw exception_expr , simple_expression * throw exception_expr , simple_expression ; * throw exception_expr , simple_expression, simple_expression ; */ static void handle_throw_statement (_pSLang_Token_Type *ctok) { #if SLANG_HAS_BOSEOS int eos; #endif push_token_list (); if (ctok->type == SEMICOLON_TOKEN) append_token_of_type (ARG_TOKEN); else { #if SLANG_HAS_BOSEOS eos = append_bos (ctok, 2); #endif append_token_of_type (ARG_TOKEN); simple_expression (ctok); if (ctok->type == COMMA_TOKEN) { get_token (ctok); simple_expression (ctok); } if (ctok->type == COMMA_TOKEN) { get_token (ctok); simple_expression (ctok); } handle_semicolon (ctok); /* not-necessary -- append_token_of_type (EARG_TOKEN); */ #if SLANG_HAS_BOSEOS if (eos) append_eos (); #endif } compile_token_list (); compile_token_of_type (THROW_TOKEN); } static _pSLang_Token_Type *allocate_token (void) { _pSLang_Token_Type *v; v = (_pSLang_Token_Type *)SLmalloc (sizeof (_pSLang_Token_Type)); if (v == NULL) return NULL; init_token (v); return v; } static void handle_foreach_statement (_pSLang_Token_Type *ctok) { _pSLang_Token_Type *var_tokens = NULL; _pSLang_Token_Type *v; #if SLANG_HAS_BOSEOS int eos; #endif #if SLANG_HAS_BOSEOS eos = compile_bos (ctok, 2); #endif /* I may want to make this a separate routine */ while (ctok->type == IDENT_TOKEN) { v = (_pSLang_Token_Type *)SLmalloc (sizeof (_pSLang_Token_Type)); if (v == NULL) goto free_return; init_token (v); *v = *ctok; init_token (ctok); v->next = var_tokens; var_tokens = v; get_token (ctok); if (ctok->type != COMMA_TOKEN) break; get_token (ctok); } expression_with_parenthesis (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif if (NULL == push_token_list ()) goto free_return; append_token_of_type (ARG_TOKEN); if (ctok->type == USING_TOKEN) { if (OPAREN_TOKEN != get_token (ctok)) { _pSLparse_error (SL_SYNTAX_ERROR, "Expected 'using ('", ctok, 0); goto free_return; } get_token (ctok); function_args_expression (ctok, 0, 0); } append_token_of_type (EARG_TOKEN); compile_token_list (); compile_token_of_type (OBRACE_TOKEN); v = var_tokens; while (v != NULL) { v->type = _SCALAR_ASSIGN_TOKEN; compile_token (v); v = v->next; } statement (ctok); compile_token_of_type (CBRACE_TOKEN); compile_token_of_type (FOREACH_TOKEN); free_return: while (var_tokens != NULL) { v = var_tokens->next; free_token (var_tokens); SLfree ((char *) var_tokens); var_tokens = v; } } /* * statement-list: * statement * statement-list statement */ static void statement_list (_pSLang_Token_Type *ctok) { while ((_pSLang_Error == 0) && (ctok->type != CBRACE_TOKEN) && (ctok->type != EOF_TOKEN)) { statement(ctok); get_token (ctok); } } /* compound-statement: * { statement-list } */ static void compound_statement (_pSLang_Token_Type *ctok) { /* ctok->type is OBRACE_TOKEN here */ get_token (ctok); statement_list(ctok); if (CBRACE_TOKEN != ctok->type) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting '}'", ctok, 0); return; } } /* This function is only called from statement. */ static void expression_with_parenthesis (_pSLang_Token_Type *ctok) { if (ctok->type != OPAREN_TOKEN) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting (", ctok, 0); return; } if (NULL == push_token_list ()) return; get_token (ctok); expression (ctok); if (ctok->type != CPAREN_TOKEN) _pSLparse_error(SL_SYNTAX_ERROR, "Expecting )", ctok, 0); compile_token_list (); get_token (ctok); } static void handle_semicolon (_pSLang_Token_Type *ctok) { if ((ctok->type == SEMICOLON_TOKEN) || (ctok->type == EOF_TOKEN)) return; _pSLparse_error (SL_SYNTAX_ERROR, "Expecting ;", ctok, 0); } void _pSLparse_start (SLang_Load_Type *llt) { _pSLang_Token_Type ctok; SLang_Load_Type *save_llt; unsigned int save_use_next_token; _pSLang_Token_Type save_next_token; Token_List_Type *save_list; #if SLANG_HAS_DEBUG_CODE int save_last_line_number = Last_Line_Number; Last_Line_Number = -1; #endif save_use_next_token = Use_Next_Token; save_next_token = Next_Token; save_list = Token_List; save_llt = LLT; LLT = llt; init_token (&Next_Token); Use_Next_Token = 0; init_token (&ctok); get_token (&ctok); llt->parse_level = 0; statement_list (&ctok); if ((_pSLang_Error == 0) && (ctok.type != EOF_TOKEN)) _pSLparse_error (SL_SYNTAX_ERROR, "Parse ended prematurely", &ctok, 0); if (_pSLang_Error) { if (_pSLang_Error < 0) /* severe error */ save_list = NULL; while (Token_List != save_list) { if (-1 == pop_token_list (1)) break; /* ??? when would this happen? */ } } free_token (&ctok); LLT = save_llt; if (Use_Next_Token) free_token (&Next_Token); Use_Next_Token = save_use_next_token; Next_Token = save_next_token; #if SLANG_HAS_DEBUG_CODE Last_Line_Number = save_last_line_number; #endif } /* variable-list: * variable-decl * variable-decl variable-list * * variable-decl: * identifier * identifier = simple-expression */ static void variable_list (_pSLang_Token_Type *name_token, unsigned char variable_type) { int declaring; _pSLang_Token_Type tok; if (name_token->type != IDENT_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting a variable name", name_token, 0); return; } declaring = 0; do { if (declaring == 0) { declaring = 1; compile_token_of_type (variable_type); } compile_token (name_token); init_token (&tok); if (ASSIGN_TOKEN == get_token (&tok)) { #if SLANG_HAS_BOSEOS int eos; #endif compile_token_of_type (CBRACKET_TOKEN); declaring = 0; get_token (&tok); #if SLANG_HAS_BOSEOS eos = compile_bos (&tok, 1); #endif push_token_list (); simple_expression (&tok); compile_token_list (); name_token->type = _SCALAR_ASSIGN_TOKEN; compile_token (name_token); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif } free_token (name_token); *name_token = tok; } while ((name_token->type == COMMA_TOKEN) && (IDENT_TOKEN == get_token (name_token))); if (declaring) compile_token_of_type (CBRACKET_TOKEN); } static void free_token_linked_list (_pSLang_Token_Type *tok) { while (tok != NULL) { _pSLang_Token_Type *next = tok->next; free_token (tok); if (tok->num_refs != 0) { SLang_verror (SL_INTERNAL_ERROR, "Cannot free token in linked list"); } else SLfree ((char *) tok); tok = next; } } /* * This function parses a structure definition block. It returns the names * of the structure fields in the form of a linked list of tokens. * * If the structure contains assignments, the function parses the expressions and returns the * number of such assignments. So: * * foo = foo_expr, bar = bar_expr, ... * * generates: foo_expr_tokens "foo" bar_expr_tokens "bar" ... */ static _pSLang_Token_Type * handle_struct_assign_list (_pSLang_Token_Type *ctok, int assign_ok, unsigned int *nassignp) { _pSLang_Token_Type *name_list_root = NULL; _pSLang_Token_Type *name_list_tail = NULL; unsigned int n, m; n = m = 0; while ((_pSLang_Error == 0) && (IDENT_TOKEN == ctok->type)) { _pSLang_Token_Type *new_tok = allocate_token (); if (new_tok == NULL) break; *new_tok = *ctok; new_tok->type = STRING_TOKEN; init_token (ctok); if (name_list_root == NULL) name_list_tail = name_list_root = new_tok; else name_list_tail->next = new_tok; name_list_tail = new_tok; n++; if (COMMA_TOKEN == get_token (ctok)) { get_token (ctok); continue; } if (assign_ok && (ASSIGN_TOKEN == ctok->type)) { /* name = ... */ #if SLANG_HAS_BOSEOS int eos = compile_bos (ctok, 1); #endif get_token (ctok); simple_expression (ctok); #if SLANG_HAS_BOSEOS if (eos) compile_eos (); #endif if (-1 == append_copy_of_string_token (new_tok)) break; m++; if (ctok->type == COMMA_TOKEN) { get_token (ctok); continue; } } break; } if (_pSLang_Error) { free_token_linked_list (name_list_root); return NULL; } if (n == 0) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting an identifier", ctok, 0); return NULL; } *nassignp = m; return name_list_root; } static int handle_struct_fields (_pSLang_Token_Type *ctok, int assign_ok) { _pSLang_Token_Type *name_list, *next; unsigned int n, m; if (NULL == (name_list = handle_struct_assign_list (ctok, assign_ok, &m))) return -1; n = 0; next = name_list; while (next != NULL) { if (-1 == append_token (next)) break; next = next->next; n++; } free_token_linked_list (name_list); if (_pSLang_Error) return -1; append_int_token (n); if (m == 0) append_token_of_type (STRUCT_TOKEN); else { append_int_token (m); append_token_of_type (STRUCT_WITH_ASSIGN_TOKEN); } if (_pSLang_Error) return -1; return 0; } /* struct-declaration: * struct { struct-field-list }; * * struct-field-list: * struct-field-name [= simple_expr], struct-field-list * struct-field-name [= simple_expr] * * Generates code: * "field-name-1" ... "field-name-N" N STRUCT_TOKEN * - OR - * expr-k1 "field-name-k1" ... expr-kM "field-name-kM" * "name-1" ... "field-name-N" N M STRUCT_DEF_ASSIGN_TOKEN */ static void struct_declaration (_pSLang_Token_Type *ctok, int assign_ok) { if (ctok->type != OBRACE_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting {", ctok, 0); return; } get_token (ctok); if (-1 == handle_struct_fields (ctok, assign_ok)) return; if (ctok->type != CBRACE_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting }", ctok, 0); return; } get_token (ctok); } /* struct-declaration: * typedef struct { struct-field-list } Type_Name; * * struct-field-list: * struct-field-name , struct-field-list * struct-field-name * * Generates code: "field-name-1" ... "field-name-N" N STRUCT_TOKEN typedef */ static void typedef_definition (_pSLang_Token_Type *t) { if (t->type != STRUCT_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting `struct'", t, 0); return; } get_token (t); struct_declaration (t, 0); if (t->type != IDENT_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting identifier", t, 0); return; } t->type = STRING_TOKEN; append_token (t); append_token_of_type (TYPEDEF_TOKEN); get_token (t); } /* function-args: * ( args-dec-opt ) * * args-decl-opt: * identifier * args-decl , identifier */ static void define_function_args (_pSLang_Token_Type *ctok) { if (CPAREN_TOKEN == get_token (ctok)) { get_token (ctok); return; } compile_token_of_type(OBRACKET_TOKEN); while ((_pSLang_Error == 0) && (ctok->type == IDENT_TOKEN)) { compile_token (ctok); if (COMMA_TOKEN != get_token (ctok)) break; get_token (ctok); } if (CPAREN_TOKEN != ctok->type) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting )", ctok, 0); return; } compile_token_of_type(CBRACKET_TOKEN); get_token (ctok); } void try_multiple_assignment (_pSLang_Token_Type *ctok) { /* This is called with ctok->type == OPAREN_TOKEN. We have no idea * what follows this. There are various possibilities such as: * @ () = x; * @ ( expression ) = x; * @ ( expression ) ; * @ ( expression ) OP expression; * @ ( expression ) [expression] = expression; * and only the first two constitute a multiple assignment. The last * two forms create the difficulty. * * Here is the plan. First parse (expression) and then check next token. * If it is an equal operator, then it will be parsed as a multiple * assignment. In fact, that is the easy part. * * The hard part stems from the fact that by parsing (expression), we * have effectly truncated the parse if (expression) is part of a binary * or unary expression. Somehow, the parsing must be resumed. The trick * here is to use a dummy literal that generates no code: NO_OP_LITERAL * Using it, we just call 'expression' and proceed. */ if (NULL == push_token_list ()) return; get_token (ctok); if (ctok->type != CPAREN_TOKEN) { expression_with_commas (ctok, 1); if (ctok->type != CPAREN_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting )", ctok, 0); return; } } switch (get_token (ctok)) { case ASSIGN_TOKEN: case PLUSEQS_TOKEN: case MINUSEQS_TOKEN: case TIMESEQS_TOKEN: case DIVEQS_TOKEN: case BOREQS_TOKEN: case BANDEQS_TOKEN: do_multiple_assignment (ctok); pop_token_list (1); break; default: unget_token (ctok); ctok->type = NO_OP_LITERAL; expression (ctok); compile_token_list (); break; } } /* assignment-expression: * simple_expression * simple_expression assign-op simple_expression * simple_expression ++ * simple_expression -- * ++ simple_expression * -- simple_expression */ static void assignment_expression (_pSLang_Token_Type *ctok) { unsigned int start_pos, end_pos; unsigned char type; if (Token_List == NULL) return; type = ctok->type; if ((type == PLUSPLUS_TOKEN) || (type == MINUSMINUS_TOKEN)) { get_token (ctok); simple_expression (ctok); check_for_lvalue (type, NULL); return; } start_pos = Token_List->len; simple_expression (ctok); switch (ctok->type) { case PLUSPLUS_TOKEN: case MINUSMINUS_TOKEN: check_for_lvalue (ctok->type, NULL); get_token (ctok); break; case ASSIGN_TOKEN: case PLUSEQS_TOKEN: case MINUSEQS_TOKEN: case TIMESEQS_TOKEN: case DIVEQS_TOKEN: case BOREQS_TOKEN: case BANDEQS_TOKEN: end_pos = Token_List->len; check_for_lvalue (ctok->type, NULL); get_token (ctok); simple_expression (ctok); token_list_element_exchange (start_pos, end_pos); break; } } /* Note: expression never gets compiled directly. Rather, it gets * appended to the token list and then compiled by a calling * routine. */ /* expression: * assignment_expression * assignment_expression, expression * */ static void expression_with_commas (_pSLang_Token_Type *ctok, int save_comma) { while (_pSLang_Error == 0) { if (ctok->type != COMMA_TOKEN) { if (ctok->type == CPAREN_TOKEN) return; assignment_expression (ctok); if (ctok->type != COMMA_TOKEN) break; } if (save_comma) append_token (ctok); get_token (ctok); } } static void expression (_pSLang_Token_Type *ctok) { expression_with_commas (ctok, 0); } /* priority levels of binary operations */ static unsigned char Binop_Level[] = { /* SC_AND_TOKEN */ 10, /* SC_OR_TOKEN */ 12, /* POW_TOKEN */ 0, /* This case is handled in unary expression so that -x^2 == -(x^2) */ /* ADD_TOKEN */ 2, /* SUB_TOKEN */ 2, /* MUL_TOKEN */ 1, /* DIV_TOKEN */ 1, /* LT_TOKEN */ 4, /* LE_TOKEN */ 4, /* GT_TOKEN */ 4, /* GE_TOKEN */ 4, /* EQ_TOKEN */ 4, /* NE_TOKEN */ 4, /* AND_TOKEN */ 9, /* OR_TOKEN */ 11, /* MOD_TOKEN */ 1, /* BAND_TOKEN */ 6, /* SHL_TOKEN */ 3, /* SHR_TOKEN */ 3, /* BXOR_TOKEN */ 7, /* BOR_TOKEN */ 8, /* POUND_TOKEN */ 1 /* Matrix Multiplication */ }; static void handle_binary_sequence (_pSLang_Token_Type *, unsigned char); static void handle_sc_sequence (_pSLang_Token_Type *ctok, unsigned char level) { unsigned char type = ctok->type; while ((ctok->type == type) && (_pSLang_Error == 0)) { append_token_of_type (OBRACE_TOKEN); get_token (ctok); unary_expression (ctok); handle_binary_sequence (ctok, level); append_token_of_type (CBRACE_TOKEN); } append_token_of_type (type); } /* unary0 OP1 unary1 OP2 unary2 ... OPN unaryN * ==> unary0 unary1 ... unaryN {OP1 OP2 ... OPN} COMPARE_BLOCK */ static void handle_compare_sequence (_pSLang_Token_Type *ctok, unsigned char level) { unsigned char op_stack [64]; unsigned int op_num = 0; unsigned int i; do { if (op_num >= sizeof(op_stack)) { _pSLparse_error (SL_BUILTIN_LIMIT_EXCEEDED, "Too many comparison operators", ctok, 0); return; } op_stack[op_num++] = ctok->type; get_token (ctok); unary_expression (ctok); handle_binary_sequence (ctok, level); } while (IS_COMPARE_OP(ctok->type) && (_pSLang_Error == 0)); if (op_num == 1) { append_token_of_type (op_stack[0]); return; } append_token_of_type (OBRACE_TOKEN); for (i = 0; i < op_num; i++) append_token_of_type (op_stack[i]); append_token_of_type (CBRACE_TOKEN); append_token_of_type (_COMPARE_TOKEN); } static void handle_binary_sequence (_pSLang_Token_Type *ctok, unsigned char max_level) { unsigned char op_stack [64]; unsigned char level_stack [64]; unsigned char level; unsigned int op_num; unsigned char type; op_num = 0; type = ctok->type; while ((_pSLang_Error == 0) && (IS_BINARY_OP(type))) { level = Binop_Level[type - FIRST_BINARY_OP]; if (level >= max_level) break; while ((op_num > 0) && (level_stack [op_num - 1] <= level)) append_token_of_type (op_stack [--op_num]); if ((type == SC_AND_TOKEN) || (type == SC_OR_TOKEN)) { handle_sc_sequence (ctok, level); type = ctok->type; continue; } if (IS_COMPARE_OP(type)) { handle_compare_sequence (ctok, level); type = ctok->type; continue; } if (op_num >= sizeof (op_stack) - 1) { _pSLparse_error (SL_BUILTIN_LIMIT_EXCEEDED, "Binary op stack overflow", ctok, 0); return; } op_stack [op_num] = type; level_stack [op_num] = level; op_num++; get_token (ctok); unary_expression (ctok); type = ctok->type; } while (op_num > 0) append_token_of_type(op_stack[--op_num]); } /* % Note: simple-expression groups operators OP1 at same level. The * % actual implementation will not do this. * simple-expression: * unary-expression * binary-expression BINARY-OP unary-expression * andelse xxelse-expression-list * orelse xxelse-expression-list * * xxelse-expression-list: * { expression } * xxelse-expression-list { expression } * binary-expression: * unary-expression * unary-expression BINARY-OP binary-expression */ static void simple_expression (_pSLang_Token_Type *ctok) { unsigned char type; switch (ctok->type) { case ANDELSE_TOKEN: case ORELSE_TOKEN: type = ctok->type; if (OBRACE_TOKEN != get_token (ctok)) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting '{'", ctok, 0); return; } while (ctok->type == OBRACE_TOKEN) { append_token (ctok); get_token (ctok); expression (ctok); if (CBRACE_TOKEN != ctok->type) { _pSLparse_error(SL_SYNTAX_ERROR, "Expecting }", ctok, 0); return; } append_token (ctok); get_token (ctok); } append_token_of_type (type); return; /* avoid unary-expression if possible */ case STRING_TOKEN: append_token (ctok); get_token (ctok); break; default: unary_expression (ctok); break; } if (SEMICOLON_TOKEN == (type = ctok->type)) return; handle_binary_sequence (ctok, 0xFF); } /* unary-expression: * postfix-expression * case unary-expression * OP3 unary-expression * (OP3: + - ~ & not) * * Note: This grammar permits: case case case WHATEVER */ static void unary_expression (_pSLang_Token_Type *ctok) { unsigned char save_unary_ops [16]; unsigned int num_unary_ops; unsigned char type; #if 0 _pSLang_Token_Type *last_token; #endif num_unary_ops = 0; while (_pSLang_Error == 0) { type = ctok->type; switch (type) { #if 0 case PLUSPLUS_TOKEN: case MINUSMINUS_TOKEN: get_token (ctok); postfix_expression (ctok); check_for_lvalue (type, NULL); goto out_of_switch; #endif case ADD_TOKEN: get_token (ctok); /* skip it-- it's unary here */ break; case SUB_TOKEN: (void) get_token (ctok); #if 0 if (IS_INTEGER_TOKEN (ctok->type)) { ctok->v.long_val = -ctok->v.long_val; break; } #ifdef HAVE_LONG_LONG else if ((ctok->type == LLONG_TOKEN) || (ctok->type == ULLONG_TOKEN)) { ctok->v.llong_val = -ctok->v.llong_val; break; } #endif #endif if (num_unary_ops == 16) goto stack_overflow_error; save_unary_ops [num_unary_ops++] = CHS_TOKEN; break; #if 0 case DEREF_TOKEN: #endif case BNOT_TOKEN: case NOT_TOKEN: case CASE_TOKEN: if (num_unary_ops == 16) goto stack_overflow_error; save_unary_ops [num_unary_ops++] = type; get_token (ctok); break; /* Try to avoid ->postfix_expression->primary_expression * subroutine calls. */ case STRING_TOKEN: append_token (ctok); get_token (ctok); goto out_of_switch; default: postfix_expression (ctok); goto out_of_switch; } } out_of_switch: while (num_unary_ops) { num_unary_ops--; append_token_of_type (save_unary_ops [num_unary_ops]); } return; stack_overflow_error: _pSLparse_error (SL_BUILTIN_LIMIT_EXCEEDED, "Too many unary operators.", ctok, 0); } static int combine_namespace_tokens (_pSLang_Token_Type *a, _pSLang_Token_Type *b) { char *sa, *sb, *sc; unsigned int lena, lenb; unsigned long hash; /* This is somewhat of a hack. Combine the TWO identifier names * (NAMESPACE) and (name) into the form NAMESPACE->name. Then when the * byte compiler compiles the object it will not be found. It will then * check for this hack and make the appropriate namespace lookup. */ sa = a->v.s_val; sb = b->v.s_val; lena = strlen (sa); lenb = strlen (sb); sc = SLmalloc (lena + lenb + 3); if (sc == NULL) return -1; strcpy (sc, sa); strcpy (sc + lena, "->"); strcpy (sc + lena + 2, sb); sb = _pSLstring_make_hashed_string (sc, lena + lenb + 2, &hash); SLfree (sc); if (sb == NULL) return -1; /* I can free this string because no other token should be referencing it. * (num_refs == 1). */ _pSLfree_hashed_string (sa, lena, a->hash); a->v.s_val = sb; a->hash = hash; return 0; } static void append_identifier_token (_pSLang_Token_Type *ctok) { _pSLang_Token_Type *last_token; append_token (ctok); if (NAMESPACE_TOKEN != get_token (ctok)) return; if (IDENT_TOKEN != get_token (ctok)) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting name-space identifier", ctok, 0); return; } last_token = get_last_token (); if (-1 == combine_namespace_tokens (last_token, ctok)) return; (void) get_token (ctok); } static int get_identifier_expr_token (_pSLang_Token_Type *ctok) { _pSLang_Token_Type next_token; if (IDENT_TOKEN != get_identifier_token (ctok)) return -1; init_token (&next_token); if (NAMESPACE_TOKEN != get_token (&next_token)) { unget_token (&next_token); return IDENT_TOKEN; } if (IDENT_TOKEN != get_identifier_token (&next_token)) { free_token (&next_token); return -1; } if (-1 == combine_namespace_tokens (ctok, &next_token)) { free_token (&next_token); return -1; } free_token (&next_token); return IDENT_TOKEN; } /* postfix-expression: * @ postfix-expression * primary-expression * postfix-expression [ expression ] * postfix-expression ( function-args-expression ) * postfix-expression . identifier * postfix-expression ^ unary-expression * * Not yet supported: * postfix-expression ++ * postfix-expression -- * postfix-expression = simple-expression * postfix-expression += simple-expression * postfix-expression -= simple-expression * * primary-expression: * literal * identifier-expr * ( expression_opt ) * [ inline-array-expression ] * { inline-list-expression } * &identifier-expr * struct-definition * __tmp(identifier-expr) * * identifier-expr: * identifier * identifier->identifier */ static void postfix_expression (_pSLang_Token_Type *ctok) { unsigned int start_pos, end_pos; unsigned char type; _pSLang_Token_Type *last_token; if (Token_List == NULL) return; start_pos = Token_List->len; switch (ctok->type) { #if 1 case DEREF_TOKEN: get_token (ctok); postfix_expression (ctok); append_token_of_type (DEREF_TOKEN); break; #endif case IDENT_TOKEN: append_identifier_token (ctok); break; case CHAR_TOKEN: case SHORT_TOKEN: case INT_TOKEN: case LONG_TOKEN: case UCHAR_TOKEN: case USHORT_TOKEN: case UINT_TOKEN: case ULONG_TOKEN: case STRING_TOKEN: case BSTRING_TOKEN: #ifdef SLANG_HAS_FLOAT case DOUBLE_TOKEN: case FLOAT_TOKEN: case LDOUBLE_TOKEN: #endif #ifdef SLANG_HAS_COMPLEX case COMPLEX_TOKEN: #endif case STRING_DOLLAR_TOKEN: #ifdef HAVE_LONG_LONG case LLONG_TOKEN: case ULLONG_TOKEN: #endif append_token (ctok); get_token (ctok); break; case OPAREN_TOKEN: if (CPAREN_TOKEN != get_token (ctok)) { expression (ctok); if (ctok->type != CPAREN_TOKEN) _pSLparse_error(SL_SYNTAX_ERROR, "Expecting )", ctok, 0); } get_token (ctok); break; case BAND_TOKEN: #if 0 if (IDENT_TOKEN != get_identifier_expr_token (ctok)) break; ctok->type = _REF_TOKEN; append_token (ctok); get_token (ctok); if ((ctok->type == OBRACKET_TOKEN) || (ctok->type == DOT_TOKEN)) _pSLparse_error (SL_NOT_IMPLEMENTED, "& of an array or structure element is not currently supported", ctok, 0); #else get_token (ctok); postfix_expression (ctok); last_token = get_last_token (); switch (last_token->type) { case IDENT_TOKEN: last_token->type = _REF_TOKEN; break; case ARRAY_TOKEN: last_token->type = _ARRAY_ELEM_REF_TOKEN; break; case DOT_TOKEN: last_token->type = _STRUCT_FIELD_REF_TOKEN; break; default: _pSLparse_error (SL_NOT_IMPLEMENTED, "& not supported in this context", last_token, 0); } #endif break; case OBRACKET_TOKEN: get_token (ctok); inline_array_expression (ctok); break; case OBRACE_TOKEN: get_token (ctok); inline_list_expression (ctok); break; case NO_OP_LITERAL: /* This token was introduced by try_multiple_assignment. There, * a new token_list was pushed and (expression) was evaluated. * NO_OP_LITERAL represents the result of expression. However, * we need to tweak the start_pos variable to point to the beginning * of the token list to complete the equivalence. */ start_pos = 0; get_token (ctok); break; case STRUCT_TOKEN: get_token (ctok); struct_declaration (ctok, 1); break; case TMP_TOKEN: get_token (ctok); if (ctok->type == OPAREN_TOKEN) { if (IDENT_TOKEN == get_identifier_expr_token (ctok)) { ctok->type = TMP_TOKEN; append_token (ctok); get_token (ctok); if (ctok->type == CPAREN_TOKEN) { get_token (ctok); break; } } } _pSLparse_error (SL_SYNTAX_ERROR, "Expecting form __tmp(NAME)", ctok, 0); break; default: #ifdef IS_INTERNAL_FUNC if (IS_INTERNAL_FUNC(ctok->type)) { append_token (ctok); get_token (ctok); } else #endif _pSLparse_error(SL_SYNTAX_ERROR, "Expecting a PRIMARY", ctok, 0); } while (_pSLang_Error == 0) { end_pos = Token_List->len; type = ctok->type; switch (type) { case OBRACKET_TOKEN: /* X[args] ==> [args] X ARRAY */ get_token (ctok); append_token_of_type (ARG_TOKEN); if (ctok->type != CBRACKET_TOKEN) array_index_expression (ctok); if (ctok->type != CBRACKET_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting ']'", ctok, 0); return; } get_token (ctok); /* append_token_of_type (EARG_TOKEN); -- ARRAY_TOKEN implicitely does this */ token_list_element_exchange (start_pos, end_pos); append_token_of_type (ARRAY_TOKEN); break; case OPAREN_TOKEN: /* f(args) ==> args f */ if ((NULL != (last_token = get_last_token ())) && (last_token->type == DEREF_TOKEN)) { /* Expressions such as (@A[i])(args...) */ last_token->type = _DEREF_FUNCALL_TOKEN; append_token_of_type (ARG_TOKEN); (void) get_token (ctok); function_args_expression (ctok, 0, 1); token_list_element_exchange (start_pos, end_pos); break; } if (CPAREN_TOKEN != get_token (ctok)) { function_args_expression (ctok, 1, 1); token_list_element_exchange (start_pos, end_pos); } else get_token (ctok); break; case DOT_TOKEN: /* S.a ==> "a" S DOT * This means that if S is X[b], then X[b].a ==> a b X ARRAY DOT * and f(a).X[b].c ==> "c" b "X" a f . ARRAY . * Also, f(a).X[b] = g(x); ==> x g b "X" a f . */ if (IDENT_TOKEN != get_identifier_token (ctok)) return; ctok->type = DOT_TOKEN; append_token (ctok); get_token (ctok); #ifdef DOT_METHOD_CALL_TOKEN if (ctok->type == OPAREN_TOKEN) { if (NULL == (last_token = get_last_token ())) return; last_token->type = DOT_METHOD_CALL_TOKEN; end_pos = Token_List->len; append_token_of_type (ARG_TOKEN); get_token (ctok); function_args_expression (ctok, 0, 1); token_list_element_exchange (start_pos, end_pos); } #endif break; #if 0 case PLUSPLUS_TOKEN: case MINUSMINUS_TOKEN: check_for_lvalue (type, NULL); get_token (ctok); break; case ASSIGN_TOKEN: case PLUSEQS_TOKEN: case MINUSEQS_TOKEN: case TIMESEQS_TOKEN: case DIVEQS_TOKEN: case BOREQS_TOKEN: case BANDEQS_TOKEN: check_for_lvalue (type, NULL); get_token (ctok); simple_expression (ctok); token_list_element_exchange (start_pos, end_pos); break; #endif #if 1 case POW_TOKEN: get_token (ctok); unary_expression (ctok); append_token_of_type (POW_TOKEN); break; #endif default: return; } } } static void function_args_expression (_pSLang_Token_Type *ctok, int handle_num_args, int handle_qualifiers) { unsigned char last_type, this_type; if (handle_num_args) append_token_of_type (ARG_TOKEN); last_type = (ctok->type == COMMA_TOKEN) ? COMMA_TOKEN : 0; while (_pSLang_Error == 0) { this_type = ctok->type; switch (this_type) { case COMMA_TOKEN: if (last_type == COMMA_TOKEN) append_token_of_type (_NULL_TOKEN); get_token (ctok); break; case CPAREN_TOKEN: if (last_type == COMMA_TOKEN) append_token_of_type (_NULL_TOKEN); if (handle_num_args) append_token_of_type (EARG_TOKEN); get_token (ctok); return; case SEMICOLON_TOKEN: if (handle_qualifiers) { if (last_type == COMMA_TOKEN) append_token_of_type (_NULL_TOKEN); if (SEMICOLON_TOKEN == get_token (ctok)) { /* foo (args... ;; q) form */ if (CPAREN_TOKEN == get_token (ctok)) break; /* foo (args ;;) */ simple_expression (ctok); } else if (ctok->type == CPAREN_TOKEN) break; /* foo (args;) */ else if (-1 == handle_struct_fields (ctok, 1)) return; append_token_of_type (QUALIFIER_TOKEN); if (ctok->type != CPAREN_TOKEN) _pSLparse_error (SL_SYNTAX_ERROR, "Expecting ')'", ctok, 0); break; } /* drop */ default: simple_expression (ctok); if ((ctok->type != COMMA_TOKEN) && (ctok->type != CPAREN_TOKEN) && ((handle_qualifiers == 0) || (ctok->type != SEMICOLON_TOKEN))) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting ')'", ctok, 0); break; } } last_type = this_type; } } static int check_for_lvalue (unsigned char eqs_type, _pSLang_Token_Type *ctok) { unsigned char type; if ((ctok == NULL) && (NULL == (ctok = get_last_token ()))) type = ILLEGAL_TOKEN; else type = ctok->type; eqs_type -= ASSIGN_TOKEN; switch (type) { case IDENT_TOKEN: eqs_type += _SCALAR_ASSIGN_TOKEN; break; case ARRAY_TOKEN: eqs_type += _ARRAY_ASSIGN_TOKEN; break; case DOT_TOKEN: eqs_type += _STRUCT_ASSIGN_TOKEN; break; case DEREF_TOKEN: eqs_type += _DEREF_ASSIGN_TOKEN; break; default: _pSLparse_error (SL_SYNTAX_ERROR, "Expecting LVALUE", ctok, 0); return -1; } ctok->type = eqs_type; return 0; } /* inline_list_expression: * (nil) * simple_expression * inline_list_expression, simple_expression */ static void inline_list_expression (_pSLang_Token_Type *ctok) { append_token_of_type (ARG_TOKEN); if (ctok->type != CBRACE_TOKEN) { while (1) { simple_expression (ctok); if (ctok->type != COMMA_TOKEN) break; get_token (ctok); if (ctok->type == CBRACE_TOKEN) /* trailing comma: {a,b,} */ break; } if (ctok->type != CBRACE_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting '}' to denote list end", ctok, 0); return; } } append_token_of_type (_INLINE_LIST_TOKEN); get_token (ctok); } static void array_index_expression (_pSLang_Token_Type *ctok) { unsigned int num_commas; num_commas = 0; while (1) { switch (ctok->type) { case COLON_TOKEN: if (num_commas) _pSLparse_error (SL_SYNTAX_ERROR, "Misplaced ':'", ctok, 0); return; case TIMES_TOKEN: append_token_of_type (_INLINE_WILDCARD_ARRAY_TOKEN); get_token (ctok); break; case COMMA_TOKEN: _pSLparse_error (SL_SYNTAX_ERROR, "Misplaced ','", ctok, 0); return; default: simple_expression (ctok); } if (ctok->type != COMMA_TOKEN) return; num_commas++; get_token (ctok); if (ctok->type == CBRACKET_TOKEN) /* allow trailing comma */ return; } } /* inline-array-expression: * array_index_expression * simple_expression : simple_expression * simple_expression : simple_expression : simple_expression * simple_expression : simple_expression : # simple_expression */ static void inline_array_expression (_pSLang_Token_Type *ctok) { int num_colons = 0; int has_pound = 0; append_token_of_type (ARG_TOKEN); if (ctok->type == COLON_TOKEN) /* [:...] */ append_token_of_type (_NULL_TOKEN); else if (ctok->type != CBRACKET_TOKEN) array_index_expression (ctok); if (ctok->type == COLON_TOKEN) { num_colons++; if ((COLON_TOKEN == get_token (ctok)) || (ctok->type == CBRACKET_TOKEN)) append_token_of_type (_NULL_TOKEN); else simple_expression (ctok); if (ctok->type == COLON_TOKEN) { num_colons++; if (POUND_TOKEN == get_token (ctok)) { has_pound = 1; get_token(ctok); } simple_expression (ctok); } } if (ctok->type != CBRACKET_TOKEN) { _pSLparse_error (SL_SYNTAX_ERROR, "Expecting ']'", ctok, 0); return; } /* append_token_of_type (EARG_TOKEN); */ if (num_colons) { if (has_pound) append_token_of_type (_INLINE_IMPLICIT_ARRAYN_TOKEN); else append_token_of_type (_INLINE_IMPLICIT_ARRAY_TOKEN); } else append_token_of_type (_INLINE_ARRAY_TOKEN); get_token (ctok); } static void do_multiple_assignment (_pSLang_Token_Type *ctok) { _pSLang_Token_Type *s; unsigned int i, k, len; unsigned char assign_type; assign_type = ctok->type; /* The LHS token list has already been pushed. Here we do the RHS * so push to another token list, process it, then come back to * LHS for assignment. */ if (NULL == push_token_list ()) return; get_token (ctok); expression (ctok); compile_token_list (); if (_pSLang_Error) return; /* Finally compile the LHS of the assignment expression * that has been saved. */ s = Token_List->stack; len = Token_List->len; if (len == 0) { compile_token_of_type (POP_TOKEN); return; } while (len > 0) { /* List is of form: * a , b, c d e, f , g , , , h , * The missing expressions will be replaced by a POP * ,,a */ /* Start from back looking for a COMMA */ k = len - 1; if (s[k].type == COMMA_TOKEN) { compile_token_of_type (POP_TOKEN); len = k; continue; } if (-1 == check_for_lvalue (assign_type, s + k)) return; i = 0; while (1) { if (s[k].type == COMMA_TOKEN) { i = k + 1; break; } if (k == 0) break; k--; } while (i < len) { compile_token (s + i); i++; } len = k; } if (s[0].type == COMMA_TOKEN) compile_token_of_type (POP_TOKEN); }