/* Copyright 2014 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mem-allocator.h" #include "jerry-libc.h" #include "lexer.h" #include "parser.h" #include "stack.h" #include "opcodes.h" #include "parse-error.h" #include "parser.h" #include "ecma-helpers.h" static token saved_token, prev_token, sent_token; static token empty_token = { .type = TOK_EMPTY, .uid = 0, .loc = 0 }; static bool allow_dump_lines = false; static size_t buffer_size = 0; /* Represents the contents of a script. */ static const char *buffer_start = NULL; static const char *buffer = NULL; static const char *token_start; #define LA(I) (get_char (I)) enum { strings_global_size }; STATIC_STACK (strings, uint8_t, lp_string) enum { numbers_global_size }; STATIC_STACK (numbers, uint8_t, ecma_number_t) enum { num_ids_global_size }; STATIC_STACK (num_ids, uint8_t, idx_t) static bool is_empty (token tok) { return tok.type == TOK_EMPTY; } static locus current_locus (void) { if (token_start == NULL) { return (locus) (buffer - buffer_start - 1); } else { return (locus) (token_start - buffer_start); } } static char get_char (size_t i) { if ((buffer + i) >= (buffer_start + buffer_size)) { return '\0'; } return *(buffer + i); } static void dump_current_line (void) { const char *i; if (!allow_dump_lines) { return; } __printf ("// "); for (i = buffer; *i != '\n' && *i != 0; i++) { __putchar (*i); } __putchar ('\n'); } static token create_token (token_type type, uint8_t uid) { return (token) { .type = type, .loc = current_locus (), .uid = uid }; } static bool current_token_equals_to (const char *str) { if (__strlen (str) != (size_t) (buffer - token_start)) { return false; } if (!__strncmp (str, token_start, (size_t) (buffer - token_start))) { return true; } return false; } static bool current_token_equals_to_lp (lp_string str) { if (str.length != (ecma_length_t) (buffer - token_start)) { return false; } if (!__strncmp ((const char *) str.str, token_start, str.length)) { return true; } return false; } static token convert_current_token_to_token (token_type tt) { JERRY_ASSERT (token_start); for (uint8_t i = 0; i < STACK_SIZE (strings); i++) { if (current_token_equals_to_lp (STACK_ELEMENT (strings, i))) { if (tt == TOK_STRING) { // locus must point to the first '"' return (token) { .type = tt, .loc = current_locus () - 1, .uid = i }; } else { return create_token (tt, i); } } } const lp_string str = (lp_string) { .length = (uint8_t) (buffer - token_start), .str = (ecma_char_t *) token_start }; STACK_PUSH (strings, str); return create_token (tt, (idx_t) (STACK_SIZE (strings) - 1)); } /* If TOKEN represents a keyword, return decoded keyword, if TOKEN represents a Future Reserved Word, return KW_RESERVED, otherwise return KW_NONE. */ static token decode_keyword (void) { if (current_token_equals_to ("break")) { return create_token (TOK_KEYWORD, KW_BREAK); } if (current_token_equals_to ("case")) { return create_token (TOK_KEYWORD, KW_CASE); } if (current_token_equals_to ("catch")) { return create_token (TOK_KEYWORD, KW_CATCH); } if (current_token_equals_to ("class")) { PARSE_SORRY ("'class' is reseved keyword and not supported yet", current_locus ()); } if (current_token_equals_to ("const")) { PARSE_SORRY ("'const' is reseved keyword and not supported yet", current_locus ()); } if (current_token_equals_to ("continue")) { return create_token (TOK_KEYWORD, KW_CONTINUE); } if (current_token_equals_to ("debugger")) { return create_token (TOK_KEYWORD, KW_DEBUGGER); } if (current_token_equals_to ("default")) { return create_token (TOK_KEYWORD, KW_DEFAULT); } if (current_token_equals_to ("delete")) { return create_token (TOK_KEYWORD, KW_DELETE); } if (current_token_equals_to ("do")) { return create_token (TOK_KEYWORD, KW_DO); } if (current_token_equals_to ("else")) { return create_token (TOK_KEYWORD, KW_ELSE); } if (current_token_equals_to ("enum")) { PARSE_SORRY ("'enum' is reseved keyword and not supported yet", current_locus ()); } if (current_token_equals_to ("export")) { PARSE_SORRY ("'export' is reseved keyword and not supported yet", current_locus ()); } if (current_token_equals_to ("extends")) { PARSE_SORRY ("'extends' is reseved keyword and not supported yet", current_locus ()); } if (current_token_equals_to ("false")) { return create_token (TOK_BOOL, false); } if (current_token_equals_to ("finally")) { return create_token (TOK_KEYWORD, KW_FINALLY); } if (current_token_equals_to ("for")) { return create_token (TOK_KEYWORD, KW_FOR); } if (current_token_equals_to ("function")) { return create_token (TOK_KEYWORD, KW_FUNCTION); } if (current_token_equals_to ("if")) { return create_token (TOK_KEYWORD, KW_IF); } if (current_token_equals_to ("instanceof")) { return create_token (TOK_KEYWORD, KW_INSTANCEOF); } if (current_token_equals_to ("interface")) { if (parser_strict_mode ()) { PARSE_ERROR ("'interface' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("in")) { return create_token (TOK_KEYWORD, KW_IN); } if (current_token_equals_to ("import")) { PARSE_SORRY ("'import' is reseved keyword and not supported yet", current_locus ()); } if (current_token_equals_to ("implements")) { if (parser_strict_mode ()) { PARSE_ERROR ("'implements' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("let")) { if (parser_strict_mode ()) { PARSE_ERROR ("'let' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("new")) { return create_token (TOK_KEYWORD, KW_NEW); } if (current_token_equals_to ("null")) { return create_token (TOK_NULL, 0); } if (current_token_equals_to ("package")) { if (parser_strict_mode ()) { PARSE_ERROR ("'package' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("private")) { if (parser_strict_mode ()) { PARSE_ERROR ("'private' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("protected")) { if (parser_strict_mode ()) { PARSE_ERROR ("'protected' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("public")) { if (parser_strict_mode ()) { PARSE_ERROR ("'public' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("return")) { return create_token (TOK_KEYWORD, KW_RETURN); } if (current_token_equals_to ("static")) { if (parser_strict_mode ()) { PARSE_ERROR ("'static' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("super")) { PARSE_SORRY ("'super' is reseved keyword and not supported yet", current_locus ()); } if (current_token_equals_to ("switch")) { return create_token (TOK_KEYWORD, KW_SWITCH); } if (current_token_equals_to ("this")) { return create_token (TOK_KEYWORD, KW_THIS); } if (current_token_equals_to ("throw")) { return create_token (TOK_KEYWORD, KW_THROW); } if (current_token_equals_to ("true")) { return create_token (TOK_BOOL, true); } if (current_token_equals_to ("try")) { return create_token (TOK_KEYWORD, KW_TRY); } if (current_token_equals_to ("typeof")) { return create_token (TOK_KEYWORD, KW_TYPEOF); } if (current_token_equals_to ("var")) { return create_token (TOK_KEYWORD, KW_VAR); } if (current_token_equals_to ("void")) { return create_token (TOK_KEYWORD, KW_VOID); } if (current_token_equals_to ("while")) { return create_token (TOK_KEYWORD, KW_WHILE); } if (current_token_equals_to ("with")) { return create_token (TOK_KEYWORD, KW_WITH); } if (current_token_equals_to ("yield")) { if (parser_strict_mode ()) { PARSE_ERROR ("'yield' is reseved keyword and not allowed in strict mode", current_locus ()); } else { return convert_current_token_to_token (TOK_NAME); } } if (current_token_equals_to ("undefined")) { return create_token (TOK_UNDEFINED, 0); } return empty_token; } static token convert_seen_num_to_token (ecma_number_t num) { uint8_t num_id; JERRY_ASSERT (STACK_SIZE (num_ids) == STACK_SIZE (numbers)); for (uint8_t i = 0; i < STACK_SIZE (numbers); i++) { if (STACK_ELEMENT (numbers, i) == num) { return create_token (TOK_NUMBER, STACK_ELEMENT (num_ids, i)); } } num_id = STACK_SIZE (num_ids); STACK_PUSH (num_ids, num_id); STACK_PUSH (numbers, num); return create_token (TOK_NUMBER, num_id); } const lp_string * lexer_get_strings (void) { lp_string *data = NULL; STACK_CONVERT_TO_RAW_DATA (strings, data); return data; } uint8_t lexer_get_strings_count (void) { return STACK_SIZE (strings); } uint8_t lexer_get_reserved_ids_count (void) { return (uint8_t) (STACK_SIZE (strings) + STACK_SIZE (numbers)); } lp_string lexer_get_string_by_id (uint8_t id) { JERRY_ASSERT (id < STACK_SIZE (strings)); return STACK_ELEMENT (strings, id); } const ecma_number_t * lexer_get_nums (void) { ecma_number_t *data = NULL; STACK_CONVERT_TO_RAW_DATA (numbers, data); return data; } uint8_t lexer_get_nums_count (void) { return STACK_SIZE (numbers); } void lexer_adjust_num_ids (void) { JERRY_ASSERT (STACK_SIZE (numbers) == STACK_SIZE (num_ids)); for (uint8_t i = 0; i < STACK_SIZE (numbers); i++) { STACK_SET_ELEMENT (num_ids, i, (uint8_t) (STACK_ELEMENT (num_ids, i) + STACK_SIZE (strings))); } } ecma_number_t lexer_get_num_by_id (uint8_t id) { JERRY_ASSERT (id >= lexer_get_strings_count () && id < lexer_get_reserved_ids_count ()); JERRY_ASSERT (STACK_ELEMENT (num_ids, id - lexer_get_strings_count ()) == id); return STACK_ELEMENT (numbers, id - lexer_get_strings_count ()); } static void new_token (void) { JERRY_ASSERT (buffer); token_start = buffer; } static void consume_char (void) { JERRY_ASSERT (buffer); buffer++; } #define RETURN_PUNC_EX(TOK, NUM) \ do \ { \ buffer += NUM; \ return create_token (TOK, 0); \ } \ while (0) #define RETURN_PUNC(TOK) RETURN_PUNC_EX(TOK, 1) #define IF_LA_N_IS(CHAR, THEN_TOK, ELSE_TOK, NUM) \ do \ { \ if (LA (NUM) == CHAR) \ { \ RETURN_PUNC_EX (THEN_TOK, NUM + 1); \ } \ else \ { \ RETURN_PUNC_EX (ELSE_TOK, NUM); \ } \ } \ while (0) #define IF_LA_IS(CHAR, THEN_TOK, ELSE_TOK) \ IF_LA_N_IS (CHAR, THEN_TOK, ELSE_TOK, 1) #define IF_LA_IS_OR(CHAR1, THEN1_TOK, CHAR2, THEN2_TOK, ELSE_TOK) \ do \ { \ if (LA (1) == CHAR1) \ { \ RETURN_PUNC_EX (THEN1_TOK, 2); \ } \ else if (LA (1) == CHAR2) \ { \ RETURN_PUNC_EX (THEN2_TOK, 2); \ } \ else \ { \ RETURN_PUNC (ELSE_TOK); \ } \ } \ while (0) static token parse_name (void) { char c = LA (0); bool every_char_islower = __islower (c); token known_token = empty_token; JERRY_ASSERT (__isalpha (c) || c == '$' || c == '_'); new_token (); consume_char (); while (true) { c = LA (0); if (c == '\0') { break; } if (!__isalpha (c) && !__isdigit (c) && c != '$' && c != '_') { break; } if (every_char_islower && (!__islower (c))) { every_char_islower = false; } consume_char (); } if (every_char_islower) { known_token = decode_keyword (); if (!is_empty (known_token)) { goto end; } } known_token = convert_current_token_to_token (TOK_NAME); end: token_start = NULL; return known_token; } static uint32_t hex_to_int (char hex) { switch (hex) { case '0': return 0x0; case '1': return 0x1; case '2': return 0x2; case '3': return 0x3; case '4': return 0x4; case '5': return 0x5; case '6': return 0x6; case '7': return 0x7; case '8': return 0x8; case '9': return 0x9; case 'a': case 'A': return 0xA; case 'b': case 'B': return 0xB; case 'c': case 'C': return 0xC; case 'd': case 'D': return 0xD; case 'e': case 'E': return 0xE; case 'f': case 'F': return 0xF; default: JERRY_UNREACHABLE (); } } /* In this function we cannot use strtol function since there is no octal literals in ECMAscript. */ static token parse_number (void) { char c = LA (0); bool is_hex = false; bool is_fp = false; bool is_exp = false; bool is_overflow = false; ecma_number_t fp_res = .0; size_t tok_length = 0, i; uint32_t res = 0; token known_token; JERRY_ASSERT (__isdigit (c) || c == '.'); if (c == '0') { if (LA (1) == 'x' || LA (1) == 'X') { is_hex = true; } } if (c == '.') { JERRY_ASSERT (!__isalpha (LA (1))); is_fp = true; } if (is_hex) { // Eat up '0x' consume_char (); consume_char (); new_token (); while (true) { c = LA (0); if (!__isxdigit (c)) { break; } consume_char (); } if (__isalpha (c) || c == '_' || c == '$') { PARSE_ERROR ("Integer literal shall not contain non-digit characters", buffer - buffer_start); } tok_length = (size_t) (buffer - token_start); for (i = 0; i < tok_length; i++) { if (!is_overflow) { res = (res << 4) + hex_to_int (token_start[i]); } else { fp_res = fp_res * 16 + (ecma_number_t) hex_to_int (token_start[i]); } if (res > 255) { fp_res = (ecma_number_t) res; is_overflow = true; res = 0; } } if (is_overflow) { known_token = convert_seen_num_to_token (fp_res); token_start = NULL; return known_token; } else { known_token = create_token (TOK_SMALL_INT, (uint8_t) res); token_start = NULL; return known_token; } } JERRY_ASSERT (!is_hex && !is_exp); new_token (); // Eat up '.' if (is_fp) { consume_char (); } while (true) { c = LA (0); if (is_fp && c == '.') { PARSE_ERROR ("Integer literal shall not contain more than one dot character", buffer - buffer_start); } if (is_exp && (c == 'e' || c == 'E')) { PARSE_ERROR ("Integer literal shall not contain more than exponential marker ('e' or 'E')", buffer - buffer_start); } if (c == '.') { if (__isalpha (LA (1)) || LA (1) == '_' || LA (1) == '$') { PARSE_ERROR ("Integer literal shall not contain non-digit character after got character", buffer - buffer_start); } is_fp = true; consume_char (); continue; } if (c == 'e' || c == 'E') { if (LA (1) == '-' || LA (1) == '+') { consume_char (); } if (!__isdigit (LA (1))) { PARSE_ERROR ("Integer literal shall not contain non-digit character after exponential marker ('e' or 'E')", buffer - buffer_start); } is_exp = true; consume_char (); continue; } if (__isalpha (c) || c == '_' || c == '$') { PARSE_ERROR ("Integer literal shall not contain non-digit characters", buffer - buffer_start); } if (!__isdigit (c)) { break; } consume_char (); } tok_length = (size_t) (buffer - token_start);; if (is_fp || is_exp) { ecma_char_t *temp = mem_heap_alloc_block ((size_t) (tok_length + 1), MEM_HEAP_ALLOC_SHORT_TERM); __strncpy ((char *) temp, token_start, (size_t) (tok_length)); temp[tok_length] = '\0'; ecma_number_t res = ecma_zt_string_to_number (temp); mem_heap_free_block (temp); known_token = convert_seen_num_to_token (res); token_start = NULL; return known_token; } if (*token_start == '0' && tok_length != 1) { if (parser_strict_mode ()) { PARSE_ERROR ("Octal tnteger literals are not allowed in strict mode", token_start - buffer_start); } for (i = 0; i < tok_length; i++) { if (!is_overflow) { res = res * 8 + hex_to_int (token_start[i]); } else { fp_res = fp_res * 8 + (ecma_number_t) hex_to_int (token_start[i]); } if (res > 255) { fp_res = (ecma_number_t) res; is_overflow = true; res = 0; } } } else { for (i = 0; i < tok_length; i++) { if (!is_overflow) { res = res * 10 + hex_to_int (token_start[i]); } else { fp_res = fp_res * 10 + (ecma_number_t) hex_to_int (token_start[i]); } if (res > 255) { fp_res = (ecma_number_t) res; is_overflow = true; res = 0; } } } if (is_overflow) { known_token = convert_seen_num_to_token (fp_res); token_start = NULL; return known_token; } else { known_token = create_token (TOK_SMALL_INT, (uint8_t) res); token_start = NULL; return known_token; } } static token parse_string (void) { char c = LA (0); bool is_double_quoted; token result; JERRY_ASSERT (c == '\'' || c == '"'); is_double_quoted = (c == '"'); // Eat up '"' consume_char (); new_token (); while (true) { c = LA (0); if (c == '\0') { PARSE_ERROR ("Unclosed string", token_start - buffer_start); } if (c == '\n') { PARSE_ERROR ("String literal shall not contain newline character", token_start - buffer_start); } if (c == '\\') { /* Only single escape character is allowed. */ if (LA (1) == 'x' || LA (1) == 'u' || __isdigit (LA (1))) { // PARSE_WARN ("Escape sequences are ignored yet", token_start - buffer_start); consume_char (); consume_char (); continue; } if ((LA (1) == '\'' && !is_double_quoted) || (LA (1) == '"' && is_double_quoted) || LA (1) == '\n') { consume_char (); consume_char (); continue; } } else if ((c == '\'' && !is_double_quoted) || (c == '"' && is_double_quoted)) { break; } consume_char (); } result = convert_current_token_to_token (TOK_STRING); // Eat up '"' consume_char (); token_start = NULL; return result; } static void grobble_whitespaces (void) { char c = LA (0); while ((__isspace (c) && c != '\n')) { consume_char (); c = LA (0); } } static void lexer_set_source (const char * source) { buffer_start = source; buffer = buffer_start; } static void lexer_rewind (void) { JERRY_ASSERT (buffer_start != NULL); buffer = buffer_start; } static bool replace_comment_by_newline (void) { char c = LA (0); bool multiline; bool was_newlines = false; JERRY_ASSERT (LA (0) == '/'); JERRY_ASSERT (LA (1) == '/' || LA (1) == '*'); multiline = (LA (1) == '*'); consume_char (); consume_char (); while (true) { c = LA (0); if (!multiline && (c == '\n' || c == '\0')) { return false; } if (multiline && c == '*' && LA (1) == '/') { consume_char (); consume_char (); if (was_newlines) { return true; } else { return false; } } if (multiline && c == '\n') { was_newlines = true; } if (multiline && c == '\0') { PARSE_ERROR ("Unclosed multiline comment", buffer - buffer_start); } consume_char (); } } static token lexer_next_token_private (void) { char c = LA (0); JERRY_ASSERT (token_start == NULL); if (__isalpha (c) || c == '$' || c == '_') { return parse_name (); } if (__isdigit (c) || (c == '.' && __isdigit (LA (1)))) { return parse_number (); } if (c == '\n') { consume_char (); return create_token (TOK_NEWLINE, 0); } if (c == '\0') { return create_token (TOK_EOF, 0); } if (c == '\'' || c == '"') { return parse_string (); } if (__isspace (c)) { grobble_whitespaces (); return lexer_next_token_private (); } if (c == '/' && LA (1) == '*') { if (replace_comment_by_newline ()) { return (token) { .type = TOK_NEWLINE, .uid = 0 }; } else { return lexer_next_token_private (); } } if (c == '/' && LA (1) == '/') { replace_comment_by_newline (); return lexer_next_token_private (); } switch (c) { case '{': RETURN_PUNC (TOK_OPEN_BRACE); break; case '}': RETURN_PUNC (TOK_CLOSE_BRACE); break; case '(': RETURN_PUNC (TOK_OPEN_PAREN); break; case ')': RETURN_PUNC (TOK_CLOSE_PAREN); break; case '[': RETURN_PUNC (TOK_OPEN_SQUARE); break; case ']': RETURN_PUNC (TOK_CLOSE_SQUARE); break; case '.': RETURN_PUNC (TOK_DOT); break; case ';': RETURN_PUNC (TOK_SEMICOLON); break; case ',': RETURN_PUNC (TOK_COMMA); break; case '~': RETURN_PUNC (TOK_COMPL); break; case ':': RETURN_PUNC (TOK_COLON); break; case '?': RETURN_PUNC (TOK_QUERY); break; case '*': IF_LA_IS ('=', TOK_MULT_EQ, TOK_MULT); break; case '/': IF_LA_IS ('=', TOK_DIV_EQ, TOK_DIV); break; case '^': IF_LA_IS ('=', TOK_XOR_EQ, TOK_XOR); break; case '%': IF_LA_IS ('=', TOK_MOD_EQ, TOK_MOD); break; case '+': IF_LA_IS_OR ('+', TOK_DOUBLE_PLUS, '=', TOK_PLUS_EQ, TOK_PLUS); break; case '-': IF_LA_IS_OR ('-', TOK_DOUBLE_MINUS, '=', TOK_MINUS_EQ, TOK_MINUS); break; case '&': IF_LA_IS_OR ('&', TOK_DOUBLE_AND, '=', TOK_AND_EQ, TOK_AND); break; case '|': IF_LA_IS_OR ('|', TOK_DOUBLE_OR, '=', TOK_OR_EQ, TOK_OR); break; case '<': { switch (LA (1)) { case '<': IF_LA_N_IS ('=', TOK_LSHIFT_EQ, TOK_LSHIFT, 2); break; case '=': RETURN_PUNC_EX (TOK_LESS_EQ, 2); break; default: RETURN_PUNC (TOK_LESS); } break; } case '>': { switch (LA (1)) { case '>': { switch (LA (2)) { case '>': IF_LA_N_IS ('=', TOK_RSHIFT_EX_EQ, TOK_RSHIFT_EX, 3); break; case '=': RETURN_PUNC_EX (TOK_RSHIFT_EQ, 3); break; default: RETURN_PUNC_EX (TOK_RSHIFT, 2); } break; } case '=': RETURN_PUNC_EX (TOK_GREATER_EQ, 2); break; default: RETURN_PUNC (TOK_GREATER); } break; } case '=': { if (LA (1) == '=') { IF_LA_N_IS ('=', TOK_TRIPLE_EQ, TOK_DOUBLE_EQ, 2); } else { RETURN_PUNC (TOK_EQ); } break; } case '!': { if (LA (1) == '=') { IF_LA_N_IS ('=', TOK_NOT_DOUBLE_EQ, TOK_NOT_EQ, 2); } else { RETURN_PUNC (TOK_NOT); } break; } default: PARSE_SORRY ("Unknown character", buffer - buffer_start); } PARSE_SORRY ("Unknown character", buffer - buffer_start); } token lexer_next_token (void) { if (buffer == buffer_start) { dump_current_line (); } if (!is_empty (saved_token)) { sent_token = saved_token; saved_token = empty_token; goto end; } prev_token = sent_token; sent_token = lexer_next_token_private (); if (sent_token.type == TOK_NEWLINE) { dump_current_line (); } end: return sent_token; } void lexer_save_token (token tok) { JERRY_ASSERT (is_empty (saved_token)); saved_token = tok; } token lexer_prev_token (void) { return prev_token; } void lexer_run_first_pass (void) { token tok = lexer_next_token (); while (tok.type != TOK_EOF) { tok = lexer_next_token (); } lexer_rewind (); } void lexer_seek (size_t locus) { JERRY_ASSERT (locus < buffer_size); JERRY_ASSERT (token_start == NULL); buffer = buffer_start + locus; saved_token = empty_token; } void lexer_locus_to_line_and_column (size_t locus, size_t *line, size_t *column) { JERRY_ASSERT (locus <= buffer_size); const char *buf; size_t l = 0, c = 0; for (buf = buffer_start; (size_t) (buf - buffer_start) < locus; buf++) { if (*buf == '\n') { c = 0; l++; continue; } c++; } if (line) { *line = l; } if (column) { *column = c; } } void lexer_dump_line (size_t line) { size_t l = 0; for (const char *buf = buffer_start; *buf != '\0'; buf++) { if (l == line) { for (; *buf != '\n' && *buf != '\0'; buf++) { __putchar (*buf); } return; } if (*buf == '\n') { l++; } } } const char * lexer_keyword_to_string (keyword kw) { switch (kw) { case KW_BREAK: return "break"; case KW_CASE: return "case"; case KW_CATCH: return "catch"; case KW_CONTINUE: return "continue"; case KW_DEBUGGER: return "debugger"; case KW_DEFAULT: return "default"; case KW_DELETE: return "delete"; case KW_DO: return "do"; case KW_ELSE: return "else"; case KW_FINALLY: return "finally"; case KW_FOR: return "for"; case KW_FUNCTION: return "function"; case KW_IF: return "if"; case KW_IN: return "in"; case KW_INSTANCEOF: return "instanceof"; case KW_NEW: return "new"; case KW_RETURN: return "return"; case KW_SWITCH: return "switch"; case KW_THIS: return "this"; case KW_THROW: return "throw"; case KW_TRY: return "try"; case KW_TYPEOF: return "typeof"; case KW_VAR: return "var"; case KW_VOID: return "void"; case KW_WHILE: return "while"; case KW_WITH: return "with"; default: JERRY_UNREACHABLE (); } } const char * lexer_token_type_to_string (token_type tt) { switch (tt) { case TOK_EOF: return "End of file"; case TOK_NAME: return "Identifier"; case TOK_KEYWORD: return "Keyword"; case TOK_SMALL_INT: /* FALLTHRU */ case TOK_NUMBER: return "Number"; case TOK_NULL: return "null"; case TOK_BOOL: return "bool"; case TOK_NEWLINE: return "newline"; case TOK_STRING: return "string"; case TOK_OPEN_BRACE: return "{"; case TOK_CLOSE_BRACE: return "}"; case TOK_OPEN_PAREN: return "("; case TOK_CLOSE_PAREN: return ")"; case TOK_OPEN_SQUARE: return "["; case TOK_CLOSE_SQUARE: return "]"; case TOK_DOT: return "."; case TOK_SEMICOLON: return ";"; case TOK_COMMA: return ","; case TOK_LESS: return "<"; case TOK_GREATER: return ">"; case TOK_LESS_EQ: return "<="; case TOK_GREATER_EQ: return "<="; case TOK_DOUBLE_EQ: return "=="; case TOK_NOT_EQ: return "!="; case TOK_TRIPLE_EQ: return "==="; case TOK_NOT_DOUBLE_EQ: return "!=="; case TOK_PLUS: return "+"; case TOK_MINUS: return "-"; case TOK_MULT: return "*"; case TOK_MOD: return "%%"; case TOK_DOUBLE_PLUS: return "++"; case TOK_DOUBLE_MINUS: return "--"; case TOK_LSHIFT: return "<<"; case TOK_RSHIFT: return ">>"; case TOK_RSHIFT_EX: return ">>>"; case TOK_AND: return "&"; case TOK_OR: return "|"; case TOK_XOR: return "^"; case TOK_NOT: return "!"; case TOK_COMPL: return "~"; case TOK_DOUBLE_AND: return "&&"; case TOK_DOUBLE_OR: return "||"; case TOK_QUERY: return "?"; case TOK_COLON: return ":"; case TOK_EQ: return "="; case TOK_PLUS_EQ: return "+="; case TOK_MINUS_EQ: return "-="; case TOK_MULT_EQ: return "*="; case TOK_MOD_EQ: return "%%="; case TOK_LSHIFT_EQ: return "<<="; case TOK_RSHIFT_EQ: return ">>="; case TOK_RSHIFT_EX_EQ: return ">>>="; case TOK_AND_EQ: return "&="; case TOK_OR_EQ: return "|="; case TOK_XOR_EQ: return "^="; case TOK_DIV: return "/"; case TOK_DIV_EQ: return "/="; case TOK_UNDEFINED: return "undefined"; default: JERRY_UNREACHABLE (); } } void lexer_init (const char *source, size_t source_size, bool show_opcodes) { saved_token = prev_token = sent_token = empty_token; allow_dump_lines = show_opcodes; buffer_size = source_size; lexer_set_source (source); STACK_INIT (strings); STACK_INIT (numbers); STACK_INIT (num_ids); } void lexer_free (void) { STACK_FREE (strings); STACK_FREE (numbers); STACK_FREE (num_ids); }