diff --git a/.gitignore b/.gitignore index 620d3dc8a..95baf20b4 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ *.lai *.la *.a + +# Random Trash +*~ diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..fb6c1980a --- /dev/null +++ b/Makefile @@ -0,0 +1,62 @@ +# 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. + +TARGET ?= jerry + +OBJ_DIR = obj + +SOURCES = \ + $(sort \ + $(wildcard ./src/*.c)) + +INCLUDES = -I src + +OBJS = $(sort \ + $(patsubst %.c,./$(OBJ_DIR)/%.o,$(notdir $(SOURCES)))) + +#CROSS_COMPILE ?= arm-none-eabi- + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +OBJDUMP = $(CROSS_COMPILE)objdump +OBJCOPY = $(CROSS_COMPILE)objcopy +SIZE = $(CROSS_COMPILE)size + +CFLAGS ?= -Wall -std=c99 -Wextra -Wpedantic -fdiagnostics-color=always +#CFLAGS += -Werror +#CFLAGS += -Wformat-security -Wformat-nonliteral -Winit-self +#CFLAGS += -Wconversion -Wsign-conversion -Wlogical-op +#CFLAGS += -Wstrict-prototypes -Wmissing-prototypes +#CFLAGS += -Winline -Wstack-protector + +#CFLAGS += -mlittle-endian -mcpu=cortex-m4 -march=armv7e-m -mthumb +#CFLAGS += -mfpu=fpv4-sp-d16 -mfloat-abi=hard +#CFLAGS += -ffunction-sections -fdata-sections + +#DEBUG_OPTIONS = -fsanitize=address -g3 -O0 +#RELEASE_OPTIONS = -Os + +HEADERS = error.h lexer.h pretty-printer.h parser.h +#OBJS = lexer.o pretty-printer.o parser.o main.o +DEFINES = -DDEBUG + +all: + $(CC) $(INCLUDES) $(CFLAGS) $(DEBUG_OPTIONS) $(DEFINES) $(SOURCES) \ + -o $(TARGET) + +clean: + rm -f $(OBJ_DIR)/*.o *.o $(TARGET) + +test: + ./tools/jerry_test.sh diff --git a/src/error.h b/src/error.h new file mode 100644 index 000000000..e6a85202c --- /dev/null +++ b/src/error.h @@ -0,0 +1,46 @@ +/* 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. + */ + +#ifndef ERROR_H +#define ERROR_H + +#include +#include +#include + +void lexer_dump_buffer_state (); + +#define unreachable() assert(0) + +static inline void +fatal (int code) +{ + printf ("FATAL: %d\n", code); + lexer_dump_buffer_state (); + unreachable (); + exit (code); +} + +#define ERR_IO (-1) +#define ERR_BUFFER_SIZE (-2) +#define ERR_SEVERAL_FILES (-3) +#define ERR_NO_FILES (-4) +#define ERR_NON_CHAR (-5) +#define ERR_UNCLOSED (-6) +#define ERR_INT_LITERAL (-7) +#define ERR_STRING (-8) +#define ERR_PARSER (-9) + +#endif /* ERROR_H */ \ No newline at end of file diff --git a/src/lexer.c b/src/lexer.c new file mode 100644 index 000000000..31a05df90 --- /dev/null +++ b/src/lexer.c @@ -0,0 +1,751 @@ +/* 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 +#include +#include + +#include "error.h" +#include "lexer.h" + +static token saved_token; + +#ifdef DEBUG +FILE *lexer_debug_log; +#endif + +/* If TOKEN represents a keyword, return decoded keyword, + if TOKEN represents a Future Reserved Word, return KW_RESERVED, + otherwise return KW_NONE. */ +static keyword +decode_keyword (const char *tok) +{ + assert (tok); + + if (!strcmp ("break", tok)) + return KW_BREAK; + else if (!strcmp ("case", tok)) + return KW_CASE; + else if (!strcmp ("catch", tok)) + return KW_CATCH; + else if (!strcmp ("continue", tok)) + return KW_CONTINUE; + else if (!strcmp ("debugger", tok)) + return KW_DEBUGGER; + else if (!strcmp ("default", tok)) + return KW_DEFAULT; + else if (!strcmp ("delete", tok)) + return KW_DELETE; + else if (!strcmp ("do", tok)) + return KW_DO; + else if (!strcmp ("else", tok)) + return KW_ELSE; + else if (!strcmp ("finally", tok)) + return KW_FINALLY; + else if (!strcmp ("for", tok)) + return KW_FOR; + else if (!strcmp ("function", tok)) + return KW_FUNCTION; + else if (!strcmp ("if", tok)) + return KW_IF; + else if (!strcmp ("in", tok)) + return KW_IN; + else if (!strcmp ("instanceof", tok)) + return KW_INSTANCEOF; + else if (!strcmp ("new", tok)) + return KW_NEW; + else if (!strcmp ("return", tok)) + return KW_RETURN; + else if (!strcmp ("switch", tok)) + return KW_SWITCH; + else if (!strcmp ("this", tok)) + return KW_THIS; + else if (!strcmp ("throw", tok)) + return KW_THROW; + else if (!strcmp ("try", tok)) + return KW_TRY; + else if (!strcmp ("typeof", tok)) + return KW_TYPEOF; + else if (!strcmp ("var", tok)) + return KW_VAR; + else if (!strcmp ("void", tok)) + return KW_VOID; + else if (!strcmp ("while", tok)) + return KW_WHILE; + else if (!strcmp ("with", tok)) + return KW_WITH; + else if (!strcmp ("class", tok) || !strcmp ("const", tok) + || !strcmp ("enum", tok) || !strcmp ("export", tok) + || !strcmp ("extends", tok) || !strcmp ("import", tok) + || !strcmp ("super", tok) || !strcmp ("implements", tok) + || !strcmp ("interface", tok) || !strcmp ("let", tok) + || !strcmp ("package", tok) || !strcmp ("private", tok) + || !strcmp ("protected", tok) || !strcmp ("public", tok) + || !strcmp ("static", tok) || !strcmp ("yield", tok)) + return KW_RESERVED; + else + return KW_NONE; +} +static FILE *file; + +/* Represents the contents of a file. */ +static char *buffer = NULL; +static char *buffer_start; +static char *token_start; + +#define BUFFER_SIZE 1024 + +static char +get_char (int i) +{ + int error; + const int tail_size = BUFFER_SIZE - (buffer - buffer_start); + + assert (file); + + if (buffer == NULL) + { + buffer = (char *) malloc (BUFFER_SIZE); + error = fread (buffer, 1, BUFFER_SIZE, file); + if (error < 0) + fatal (ERR_IO); + if (error == 0) + return '\0'; + if (error < BUFFER_SIZE) + memset (buffer + error, '\0', BUFFER_SIZE - error); + buffer_start = buffer; + } + + if (tail_size <= i) + { + /* We are almost at the end of the buffer. */ + if (token_start) + { + const int token_size = buffer - token_start; + /* Whole buffer contains single token. */ + if (token_start == buffer_start) + fatal (ERR_BUFFER_SIZE); + /* Move parsed token and tail of buffer to head. */ + memmove (buffer_start, token_start, tail_size + token_size); + /* Adjust pointers. */ + token_start = buffer_start; + buffer = buffer_start + token_size; + /* Read more characters form input file. */ + error = fread (buffer + tail_size, 1, BUFFER_SIZE - tail_size - token_size, file); + if (error < 0) + fatal (ERR_IO); + if (error == 0) + return '\0'; + if (error < BUFFER_SIZE - tail_size - token_size) + memset (buffer + tail_size + error, '\0', + BUFFER_SIZE - tail_size - token_size - error); + } + else + { + memmove (buffer_start, buffer, tail_size); + buffer = buffer_start; + error = fread (buffer + tail_size, 1, BUFFER_SIZE - tail_size, file); + if (error < 0) + fatal (ERR_IO); + if (error == 0) + return '\0'; + if (error < BUFFER_SIZE - tail_size) + memset (buffer + tail_size + error, '\0', BUFFER_SIZE - tail_size - error); + } + } + + return *(buffer + i); +} + +#define LA(I) (get_char (I)) + +static inline void +new_token () +{ + assert (buffer); + token_start = buffer; +} + +static inline void +consume_char () +{ + assert (buffer); + buffer++; +} + +static inline const char * +current_token () +{ + assert (buffer); + assert (token_start); + int length = buffer - token_start; + char *res = (char *) malloc (length + 1); + strncpy (res, token_start, length); + res[length] = '\0'; + token_start = NULL; + return res; +} + +#define RETURN_PUNC_EX(TOK, NUM) \ + do \ + { \ + buffer += NUM; \ + return (token) { .type = TOK, .data.none = NULL }; \ + } \ + 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 () +{ + char c = LA (0); + bool every_char_islower = isalpha (c) && islower (c); + const char *tok = NULL; + + assert (isalpha (c) || c == '$' || c == '_'); + + new_token (); + consume_char (); + while (true) + { + c = LA (0); + if (c == '\0') + c = c; + if (!isalpha (c) && !isdigit (c) && c != '$' && c != '_') + break; + if (every_char_islower && (!isalpha (c) || !islower (c))) + every_char_islower = false; + consume_char (); + } + + tok = current_token (); + if (every_char_islower) + { + keyword kw = decode_keyword (tok); + if (kw != KW_NONE) + { + free ((char *) tok); + return (token) { .type = TOK_KEYWORD, .data.kw = kw }; + } + + if (!strcmp ("null", tok)) + { + free ((char *) tok); + return (token) { .type = TOK_NULL, .data.none = NULL }; + } + + if (!strcmp ("true", tok)) + { + free ((char *) tok); + return (token) { .type = TOK_BOOL, .data.is_true = true }; + } + + if (!strcmp ("false", tok)) + { + free ((char *) tok); + return (token) { .type = TOK_BOOL, .data.is_true = false }; + } + } + + return (token) { .type = TOK_NAME, .data.name = tok }; +} + +static bool +is_hex_digit (char c) +{ + return isdigit (c) || c == 'a' || c == 'A' || c == 'b' || c == 'B' + || c == 'c' || c == 'C' || c == 'd' || c == 'D' + || c == 'e' || c == 'E' || c == 'f' || c == 'F'; +} + +static int +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: unreachable (); + } +} + +/* In this function we cannot use strtol function + since there is no octal literals in ECMAscript. */ +static token +parse_number () +{ + char c = LA (0); + bool is_hex = false; + bool is_fp = false; + bool is_exp = false; + const char *tok = NULL; + int tok_length = 0; + int res = 0; + + assert (isdigit (c) || c == '.'); + + if (c == '0') + if (LA (1) == 'x' || LA (1) == 'X') + is_hex = true; + + if (c == '.') + { + 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 (!is_hex_digit (c)) + break; + consume_char (); + } + + if (isalpha (c) || c == '_' || c == '$') + fatal (ERR_INT_LITERAL); + + tok_length = buffer - token_start; + tok = current_token (); + // OK, I know that integer overflow can occur here + for (int i = 0; i < tok_length; i++) + res = (res << 4) + hex_to_int (tok[i]); + + free ((char *) tok); + return (token) { .type = TOK_INT, .data.num = res }; + } + + assert (!is_hex && !is_exp); + + new_token (); + + // Eat up '.' + if (is_fp) + consume_char (); + + while (true) + { + c = LA (0); + if (is_fp && c == '.') + fatal (ERR_INT_LITERAL); + if (is_exp && (c == 'e' || c == 'E')) + fatal (ERR_INT_LITERAL); + + if (c == '.') + { + if (isalpha (LA (1)) || LA (1) == '_' || LA (1) == '$') + fatal (ERR_INT_LITERAL); + is_fp = true; + consume_char (); + continue; + } + + if (c == 'e' || c == 'E') + { + if (LA (1) == '-' || LA (1) == '+') + consume_char (); + if (!isdigit (LA (1))) + fatal (ERR_INT_LITERAL); + is_exp = true; + consume_char (); + continue; + } + + if (isalpha (c) || c == '_' || c == '$') + fatal (ERR_INT_LITERAL); + + if (!isdigit (c)) + break; + + consume_char (); + } + + if (is_fp || is_exp) + { + tok = current_token (); + float res = strtof (tok, NULL); + free ((char *) tok); + return (token) { .type = TOK_FLOAT, .data.fp_num = res }; + } + + tok_length = buffer - token_start; + tok = current_token (); + + for (int i = 0; i < tok_length; i++) + res = res * 10 + hex_to_int (tok[i]); + + free ((char *) tok); + return (token) { .type = TOK_INT, .data.num = res }; +} + +static char +escape_char (char c) +{ + switch (c) + { + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + case '\'': + case '"': + case '\\': + default: return c; + } +} + +static token +parse_string () +{ + char c = LA (0); + bool is_double_quoted; + char *tok = NULL; + char *index = NULL; + int length; + + assert (c == '\'' || c == '"'); + + is_double_quoted = (c == '"'); + + // Eat up '"' + consume_char (); + new_token (); + + while (true) + { + c = LA (0); + if (c == '\0') + fatal (ERR_UNCLOSED); + if (c == '\n') + fatal (ERR_STRING); + if (c == '\\') + { + /* Only single escape character is allowed. */ + if (LA (1) == 'x' || LA (1) == 'u' || isdigit (LA (1))) + fatal (ERR_STRING); + 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 (); + } + + length = buffer - token_start; + tok = (char *) malloc (length); + index = tok; + + for (char *i = token_start; i < buffer; i++) + { + if (*i == '\\') + { + if (*(i+1) == '\n') + { + i++; + continue; + } + *index = escape_char (*(i+1)); + index++; + i++; + continue; + } + + *index = *i; + index++; + } + + memset (index, '\0', length - (index - tok)); + + token_start = NULL; + // Eat up '"' + consume_char (); + + return (token) { .type = TOK_STRING, .data.str = tok }; +} + +static void +grobble_whitespaces () +{ + char c = LA (0); + + while ((isspace (c) && c != '\n') || c == '\0') + { + consume_char (); + c = LA (0); + } +} + +void +lexer_set_file (FILE *ex_file) +{ + assert (ex_file); + file = ex_file; + lexer_debug_log = fopen ("lexer.log", "w"); +} + +static bool +replace_comment_by_newline () +{ + char c = LA (0); + bool multiline; + bool was_newlines = false; + + assert (LA (0) == '/'); + 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') + fatal (ERR_UNCLOSED); + consume_char (); + } +} + +token +#ifdef DEBUG +lexer_next_token_private () +#else +lexer_next_token () +#endif +{ + char c = LA (0); + + if (saved_token.type != TOK_EOF) + { + token res = saved_token; + saved_token.type = TOK_EOF; + return res; + } + + 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 (token) { .type = TOK_NEWLINE, .data.none = NULL }; + } + + if (c == '\0') + return (token) { .type = TOK_EOF, .data.none = NULL }; + + if (c == '\'' || c == '"') + return parse_string (); + + if (isspace (c)) + { + grobble_whitespaces (); + return lexer_next_token (); + } + + if (c == '/' && LA (1) == '*') + { + if (replace_comment_by_newline ()) + return (token) { .type = TOK_NEWLINE, .data.none = NULL }; + else + return lexer_next_token (); + } + + if (c == '/' && LA (1) == '/') + { + replace_comment_by_newline (); + return lexer_next_token (); + } + + switch (c) + { + case '{': RETURN_PUNC (TOK_OPEN_BRACE); + case '}': RETURN_PUNC (TOK_CLOSE_BRACE); + case '(': RETURN_PUNC (TOK_OPEN_PAREN); + case ')': RETURN_PUNC (TOK_CLOSE_PAREN); + case '[': RETURN_PUNC (TOK_OPEN_SQUARE); + case ']': RETURN_PUNC (TOK_CLOSE_SQUARE); + case '.': RETURN_PUNC (TOK_DOT); + case ';': RETURN_PUNC (TOK_SEMICOLON); + case ',': RETURN_PUNC (TOK_COMMA); + case '~': RETURN_PUNC (TOK_COMPL); + case ':': RETURN_PUNC (TOK_COLON); + case '?': RETURN_PUNC (TOK_QUERY); + + case '*': IF_LA_IS ('=', TOK_MULT_EQ, TOK_MULT); + case '/': IF_LA_IS ('=', TOK_DIV_EQ, TOK_DIV); + case '^': IF_LA_IS ('=', TOK_XOR_EQ, TOK_XOR); + case '%': IF_LA_IS ('=', TOK_MOD_EQ, TOK_MOD); + + case '+': IF_LA_IS_OR ('+', TOK_DOUBLE_PLUS, '=', TOK_PLUS_EQ, TOK_PLUS); + case '-': IF_LA_IS_OR ('-', TOK_DOUBLE_MINUS, '=', TOK_MINUS_EQ, TOK_MINUS); + case '&': IF_LA_IS_OR ('&', TOK_DOUBLE_AND, '=', TOK_AND_EQ, TOK_AND); + case '|': IF_LA_IS_OR ('|', TOK_DOUBLE_OR, '=', TOK_OR_EQ, TOK_OR); + + case '<': + switch (LA (1)) + { + case '<': IF_LA_N_IS ('=', TOK_LSHIFT_EQ, TOK_LSHIFT, 2); + case '=': RETURN_PUNC_EX (TOK_LESS_EQ, 2); + default: RETURN_PUNC (TOK_LESS); + } + + case '>': + switch (LA (1)) + { + case '>': + switch (LA (2)) + { + case '>': IF_LA_N_IS ('=', TOK_RSHIFT_EX_EQ, TOK_RSHIFT_EX, 3); + case '=': RETURN_PUNC_EX (TOK_RSHIFT_EQ, 3); + default: RETURN_PUNC_EX (TOK_RSHIFT, 2); + } + case '=': RETURN_PUNC_EX (TOK_GREATER_EQ, 2); + default: RETURN_PUNC (TOK_GREATER); + } + + case '=': + if (LA (1) == '=') + IF_LA_N_IS ('=', TOK_TRIPLE_EQ, TOK_DOUBLE_EQ, 2); + else + RETURN_PUNC (TOK_EQ); + + case '!': + if (LA (1) == '=') + IF_LA_N_IS ('=', TOK_NOT_DOUBLE_EQ, TOK_NOT_EQ, 2); + else + RETURN_PUNC (TOK_NOT); + + default: + unreachable (); + } + fatal (ERR_NON_CHAR); +} + +#ifdef DEBUG +static int i = 0; + +token +lexer_next_token () +{ + token tok = lexer_next_token_private (); + if (tok.type == TOK_NEWLINE) + return tok; + if (tok.type == TOK_CLOSE_BRACE) + { + if (i == 300) + fprintf (lexer_debug_log, "lexer_next_token(%d): type=0x%x, data=%p\n", i, tok.type, tok.data.none); + i++; + } + return tok; +} +#endif + +void +lexer_save_token (token tok) +{ + #ifdef DEBUG + if (tok.type == TOK_CLOSE_BRACE) + fprintf (lexer_debug_log, "lexer_save_token(%d): type=0x%x, data=%p\n", i, tok.type, tok.data.none); + #endif + saved_token = tok; +} + +void +lexer_dump_buffer_state () +{ + printf ("%s\n", buffer); +} \ No newline at end of file diff --git a/src/lexer.h b/src/lexer.h new file mode 100644 index 000000000..cbc802a20 --- /dev/null +++ b/src/lexer.h @@ -0,0 +1,158 @@ +/* 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. + */ + +#ifndef LEXER_H +#define LEXER_H + +#include +#include + +/* Keywords. */ +typedef enum +{ + /* Not a keyword. */ + KW_NONE = 0, + /* Future reserved keyword. */ + KW_RESERVED, + + KW_BREAK, + KW_CASE, + KW_CATCH, + KW_CONTINUE, + KW_DEBUGGER, + KW_DEFAULT, + KW_DELETE, + + KW_DO, + KW_ELSE, + KW_FINALLY, + KW_FOR, + KW_FUNCTION, + KW_IF, + KW_IN, + + KW_INSTANCEOF, + KW_NEW, + KW_RETURN, + KW_SWITCH, + KW_THIS, + KW_THROW, + KW_TRY, + + KW_TYPEOF, + KW_VAR, + KW_VOID, + KW_WHILE, + KW_WITH +} +keyword; + +/* Type of tokens. */ +typedef enum +{ + TOK_EOF = 0x0, // End of file + TOK_NAME = 0x1, // Identifier + TOK_KEYWORD = 0x2, // Keyword + TOK_INT = 0x3, + TOK_FLOAT = 0x4, + TOK_NULL = 0x5, + TOK_BOOL = 0x6, + TOK_NEWLINE = 0x7, + TOK_STRING = 0x8, + + /* Punctuators. */ + TOK_OPEN_BRACE = 0x9, // { + TOK_CLOSE_BRACE = 0xa, // } + TOK_OPEN_PAREN = 0xb, // ( + TOK_CLOSE_PAREN = 0xc, // ) + TOK_OPEN_SQUARE, // [ + TOK_CLOSE_SQUARE, // [ + + TOK_DOT, // . + TOK_SEMICOLON, // ; + TOK_COMMA, // , + TOK_LESS, // < + TOK_GREATER, // > + TOK_LESS_EQ, // <= + + TOK_GREATER_EQ, // <= + TOK_DOUBLE_EQ, // == + TOK_NOT_EQ, // != + TOK_TRIPLE_EQ, // === + TOK_NOT_DOUBLE_EQ, // !== + + TOK_PLUS, // + + TOK_MINUS, // - + TOK_MULT, // * + TOK_MOD, // % + TOK_DOUBLE_PLUS, // ++ + TOK_DOUBLE_MINUS, // -- + + TOK_LSHIFT, // << + TOK_RSHIFT, // >> + TOK_RSHIFT_EX, // >>> + TOK_AND, // & + TOK_OR, // | + TOK_XOR, // ^ + + TOK_NOT, // ! + TOK_COMPL, // ~ + TOK_DOUBLE_AND, // && + TOK_DOUBLE_OR, // || + TOK_QUERY, // ? + TOK_COLON, // : + + TOK_EQ, // = + TOK_PLUS_EQ, // += + TOK_MINUS_EQ, // -= + TOK_MULT_EQ, // *= + TOK_MOD_EQ, // %= + TOK_LSHIFT_EQ, // <<= + + TOK_RSHIFT_EQ, // >>= + TOK_RSHIFT_EX_EQ, // >>>= + TOK_AND_EQ, // &= + TOK_OR_EQ, // |= + TOK_XOR_EQ, // ^= + + TOK_DIV, // / + TOK_DIV_EQ // /= +} +token_type; + +/* Represents the contents of a token. */ +typedef struct +{ + token_type type; + + union + { + void *none; + keyword kw; + const char *name; + bool is_true; + int num; + float fp_num; + const char *str; + } + data; +} +token; + +void lexer_set_file (FILE *); +token lexer_next_token (); +void lexer_save_token (token); + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000..b447b8299 --- /dev/null +++ b/src/main.c @@ -0,0 +1,87 @@ +/* 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 +#include +#include + +#include "error.h" +#include "lexer.h" +#include "parser.h" +#include "pretty-printer.h" + +int +main (int argc, char **argv) +{ + bool dump_tokens = false; + bool dump_ast = false; + const char *file_name = NULL; + FILE *file = NULL; + + if (argc > 0) + for (int i = 1; i < argc; i++) + { + if (!strcmp ("-t", argv[i])) + dump_tokens = true; + else if (!strcmp ("-a", argv[i])) + dump_ast = true; + else if (file_name == NULL) + file_name = argv[i]; + else + fatal (ERR_SEVERAL_FILES); + } + + if (file_name == NULL) + fatal (ERR_NO_FILES); + + if (dump_tokens && dump_ast) + fatal (ERR_SEVERAL_FILES); + + file = fopen (file_name, "r"); + + if (file == NULL) + fatal (ERR_IO); + + if (dump_tokens) + { + token tok; + lexer_set_file (file); + tok = lexer_next_token (); + pp_reset (); + while (tok.type != TOK_EOF) + { + pp_token (tok); + tok = lexer_next_token (); + } + } + + if (dump_ast) + { + statement *st; + lexer_set_file (file); + parser_init (); + st = parser_parse_statement (); + assert (st); + while (st->type != STMT_EOF) + { + pp_statement (st); + st = parser_parse_statement (); + assert (st); + } + pp_finish (); + } + + return 0; +} \ No newline at end of file diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 000000000..50f1af011 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,2074 @@ +/* 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 +#include "parser.h" +#include "error.h" +#include "lexer.h" + +static token tok; + +#ifdef DEBUG +FILE *debug_file; +#endif + +static expression *parse_expression (bool); +static assignment_expression *parse_assigment_expression (bool); + +#define ALLOCATE(T) (T*) malloc (sizeof (T)) + +#define RETURN_NULL_IF_NULL(T) do { if (T == NULL) { free (res); return NULL; } } while (0) + +typedef enum +{ + SCOPE_GLOBAL = 0, + SCOPE_IF = 1 << 0, + SCOPE_BLOCK = 1 << 1, + SCOPE_DO = 1 << 2, + SCOPE_WHILE = 1 << 3, + SCOPE_FOR = 1 << 4, + SCOPE_LOOP = SCOPE_WHILE | SCOPE_FOR | SCOPE_DO, + SCOPE_WITH = 1 << 5, + SCOPE_SWITCH = 1 << 6, + SCOPE_CASE = 1 << 7, + SCOPE_ELSE = 1 << 8, + SCOPE_TRY = 1 << 9, + SCOPE_CATCH = 1 << 10, + SCOPE_FINALLY = 1 << 11, + SCOPE_FUNCTION = 1 << 12, + SCOPE_SUBEXPRESSION = 1 << 13 +} +scope_type; + +typedef struct scope +{ + scope_type type; + bool was_stmt; + + struct scope *prev; +} +scope; + +static scope *current_scope; + +static inline void +scope_must_be (int scopes) +{ + scope *sc = current_scope; + while (sc) + { + if (scopes & sc->type) + return; + sc = sc->prev; + } + fatal (ERR_PARSER); +} + +static inline void +current_scope_must_be (int scopes) +{ + if (scopes & current_scope->type) + return; + fatal (ERR_PARSER); +} + +static inline void +current_scope_must_be_global () +{ + if (current_scope->prev) + fatal (ERR_PARSER); +} + +static void +push_scope (int type) +{ +#ifdef DEBUG + fprintf (debug_file, "push_scope: 0x%x\n", type); +#endif + scope *sc = ALLOCATE (scope); + sc->type = type; + sc->prev = current_scope; + sc->was_stmt = false; + current_scope = sc; +} + +static void +pop_scope () +{ +#ifdef DEBUG + fprintf (debug_file, "pop_scope: 0x%x\n", current_scope->type); +#endif + scope *sc = current_scope->prev; + free (current_scope); + current_scope = sc; +} + +static inline void +assert_keyword (keyword kw) +{ + if (tok.type != TOK_KEYWORD || tok.data.kw != kw) + { + printf ("assert_keyword: 0x%x\n", kw); + unreachable (); + } +} + +static inline bool +is_keyword (keyword kw) +{ + return tok.type == TOK_KEYWORD && tok.data.kw == kw; +} + +static inline void +current_token_must_be(token_type tt) +{ + if (tok.type != tt) + { + printf ("current_token_must_be: 0x%x\n", tt); + fatal (ERR_PARSER); + } +} + +static inline void +skip_newlines () +{ + tok = lexer_next_token (); + while (tok.type == TOK_NEWLINE) + tok = lexer_next_token (); +} + +static inline void +next_token_must_be (token_type tt) +{ + tok = lexer_next_token (); + if (tok.type != tt) + { + printf ("next_token_must_be: 0x%x\n", tt); + fatal (ERR_PARSER); + } +} + +static inline void +token_after_newlines_must_be (token_type tt) +{ + skip_newlines (); + if (tok.type != tt) + fatal (ERR_PARSER); +} + +static inline void +token_after_newlines_must_be_keyword (keyword kw) +{ + skip_newlines (); + if (!is_keyword (kw)) + fatal (ERR_PARSER); +} + +static inline void +fatal_if_null (void * what) +{ + if (!what) + fatal (ERR_PARSER); +} + +static inline void +insert_semicolon () +{ + tok = lexer_next_token (); + if (tok.type != TOK_NEWLINE && tok.type != TOK_SEMICOLON) + fatal (ERR_PARSER); +} + +/* formal_parameter_list + : LT!* Identifier (LT!* ',' LT!* Identifier)* + ; */ +static formal_parameter_list * +parse_formal_parameter_list () +{ + formal_parameter_list *res = ALLOCATE (formal_parameter_list); + + current_token_must_be (TOK_NAME); + res->name = tok.data.name; + + skip_newlines (); + if (tok.type == TOK_COMMA) + { + skip_newlines (); + res->next = parse_formal_parameter_list (); + } + else + { + lexer_save_token (tok); + res->next = NULL; + } + + return res; +} + +/* function_declaration + : 'function' LT!* Identifier LT!* + '(' formal_parameter_list? LT!* ')' LT!* function_body + ; + + function_body + : '{' LT!* sourceElements LT!* '}' */ +static function_declaration* +parse_function_declaration () +{ + function_declaration *res = ALLOCATE (function_declaration); + + assert_keyword (KW_FUNCTION); + + token_after_newlines_must_be (TOK_NAME); + res->name = tok.data.name; + + token_after_newlines_must_be (TOK_OPEN_PAREN); + + skip_newlines (); + if (tok.type != TOK_CLOSE_PAREN) + { + res->params = parse_formal_parameter_list (); + next_token_must_be (TOK_CLOSE_PAREN); + } + else + res->params = NULL; + + return res; +} + +/* function_expression + : 'function' LT!* Identifier? LT!* '(' formal_parameter_list? LT!* ')' LT!* function_body + ; */ +static function_expression * +parse_function_expression () +{ + function_expression *res = ALLOCATE (function_expression); + + assert_keyword (KW_FUNCTION); + + skip_newlines (); + if (tok.type == TOK_NAME) + { + res->name = tok.data.name; + skip_newlines (); + } + else + res->name = NULL; + + current_token_must_be (TOK_OPEN_PAREN); + + skip_newlines (); + if (tok.type != TOK_CLOSE_PAREN) + { + res->params = parse_formal_parameter_list (); + next_token_must_be (TOK_CLOSE_PAREN); + } + else + res->params = NULL; + + push_scope (SCOPE_FUNCTION); + + return res; +} + +/* array_literal + : assignment_expression? (LT!* ',' (LT!* assignment_expression)?)* LT!* + ; */ +static array_literal * +parse_array_literal () +{ + array_literal *res = NULL; + assignment_expression *assign_expr = parse_assigment_expression (false); + if (assign_expr != NULL) + { + res = ALLOCATE (array_literal); + res->assign_expr = assign_expr; + } + skip_newlines (); + if (tok.type == TOK_COMMA) + { + if (res == NULL) + res = ALLOCATE (array_literal); + skip_newlines (); + res->next = parse_array_literal (); + } + else + lexer_save_token (tok); + + return res; +} + +/* property_name + : Identifier + | StringLiteral + | NumericLiteral + ; */ +static property_name * +parse_property_name () +{ + property_name *res = ALLOCATE (property_name); + + switch (tok.type) + { + case TOK_NAME: + res->type = PN_NAME; + res->data.name = tok.data.name; + return res; + + case TOK_STRING: + res->type = PN_STRING; + res->data.str = tok.data.str; + return res; + + case TOK_INT: + res->type = PN_NUM; + res->data.num = tok.data.num; + return res; + + default: + unreachable (); + } +} + +/* property_name_and_value + : property_name LT!* ':' LT!* assignment_expression + ; */ +static property_name_and_value * +parse_property_name_and_value () +{ + property_name_and_value *res = ALLOCATE (property_name_and_value); + + res->name = parse_property_name (); + assert (res->name); + token_after_newlines_must_be (TOK_COLON); + + skip_newlines (); + res->assign_expr = parse_assigment_expression (true); + fatal_if_null (res->assign_expr); + + return res; +} + +/* object_literal + : LT!* property_name_and_value (LT!* ',' LT!* property_name_and_value)* LT!* + ; */ +static object_literal * +parse_object_literal () +{ + object_literal *res = ALLOCATE (object_literal); + + res->nav = parse_property_name_and_value (); + assert (res->nav); + + skip_newlines (); + if (tok.type == TOK_COMMA) + { + skip_newlines (); + res->next = parse_object_literal (); + assert (res->next); + } + else + { + lexer_save_token (tok); + res->next = NULL; + } + return res; +} + +static literal * +parse_literal () +{ + literal *res = ALLOCATE (literal); + + switch (tok.type) + { + case TOK_NULL: + res->type = LIT_NULL; + res->data.data = NULL; + return res; + + case TOK_BOOL: + res->type = LIT_BOOL; + res->data.is_true = tok.data.is_true; + return res; + + case TOK_INT: + res->type = LIT_INT; + res->data.num = tok.data.num; + return res; + + case TOK_STRING: + res->type = LIT_STR; + res->data.str = tok.data.str; + return res; + + default: + unreachable (); + } +} + +/* primary_expression + : 'this' + | Identifier + | literal + | '[' LT!* array_literal LT!* ']' + | '{' LT!* object_literal LT!* '}' + | '(' LT!* expression LT!* ')' + ; */ +static primary_expression * +parse_primary_expression () +{ + primary_expression *res = ALLOCATE (primary_expression); + + if (is_keyword (KW_THIS)) + { + res->type = PE_THIS; + res->data.none = NULL; + return res; + } + else if (tok.type == TOK_NAME) + { + res->type = PE_NAME; + res->data.name = tok.data.name; + return res; + } + else if (tok.type == TOK_NULL || tok.type == TOK_BOOL + || tok.type == TOK_INT || tok.type == TOK_STRING) + { + res->type = PE_LITERAL; + res->data.lit = parse_literal (); + assert (res->data.lit); + return res; + } + else if (tok.type == TOK_OPEN_SQUARE) + { + res->type = PE_ARRAY; + skip_newlines (); + if (tok.type != TOK_CLOSE_SQUARE) + { + res->data.array_lit = parse_array_literal (); + assert (res->data.array_lit); + token_after_newlines_must_be (TOK_CLOSE_SQUARE); + } + else + res->data.array_lit = NULL; + return res; + } + else if (tok.type == TOK_OPEN_BRACE) + { + res->type = PE_OBJECT; + skip_newlines (); + if (tok.type != TOK_CLOSE_BRACE) + { + res->data.object_lit = parse_object_literal (); + assert (res->data.object_lit); + token_after_newlines_must_be (TOK_CLOSE_BRACE); + } + else + res->data.object_lit = NULL; + return res; + } + else if (tok.type == TOK_OPEN_PAREN) + { + res->type = PE_EXPR; + skip_newlines (); + if (tok.type != TOK_CLOSE_PAREN) + { + scope *old_scope; + push_scope (SCOPE_SUBEXPRESSION); + old_scope = current_scope; + + res->data.expr = parse_expression (false); + if (current_scope == old_scope) + { + next_token_must_be (TOK_CLOSE_PAREN); + pop_scope (); + } + assert (res->data.expr); + } + else + res->data.expr = NULL; + + return res; + } + else + { + free (res); + return NULL; + } +} + +/* member_expression_suffix + : index_suffix + | property_reference_suffix + ; + + index_suffix + : '[' LT!* expression LT!* ']' + ; + + property_reference_suffix + : '.' LT!* Identifier + ; */ +static member_expression_suffix * +parse_member_expression_suffix () +{ + member_expression_suffix *res = ALLOCATE (member_expression_suffix); + + if (tok.type == TOK_OPEN_SQUARE) + { + res->type = MES_INDEX; + + skip_newlines (); + res->data.index_expr = parse_expression (true); + fatal_if_null (res->data.index_expr); + token_after_newlines_must_be (TOK_CLOSE_SQUARE); + } + else if (tok.type == TOK_DOT) + { + res->type = MES_PROPERTY; + + skip_newlines (); + if (tok.type != TOK_NAME) + fatal (ERR_PARSER); + res->data.name = tok.data.name; + } + else + unreachable (); + + return res; +} + +/* member_expression_suffix_list + : member_expression_suffix + + ; */ +static member_expression_suffix_list * +parse_member_expression_suffix_list () +{ + member_expression_suffix_list *res = ALLOCATE (member_expression_suffix_list); + + if (tok.type == TOK_OPEN_SQUARE || tok.type == TOK_DOT) + { + res->suffix = parse_member_expression_suffix (); + assert (res->suffix); + } + else + unreachable (); + + skip_newlines (); + if (tok.type == TOK_OPEN_SQUARE || tok.type == TOK_DOT) + { + res->next = parse_member_expression_suffix_list (); + assert (res->next); + } + else + { + lexer_save_token (tok); + res->next = NULL; + } + + return res; +} + +/* member_expression + : (primary_expression | function_expression | 'new' LT!* member_expression (LT!* '(' LT!* arguments? LT!* ')') + (LT!* member_expression_suffix)* + ; + + arguments + : assignment_expression (LT!* ',' LT!* assignment_expression)*)? + ;*/ +static member_expression * +parse_member_expression () +{ + member_expression *res = ALLOCATE (member_expression); + + if (is_keyword (KW_FUNCTION)) + { + res->type = ME_FUNCTION; + res->data.function_expr = parse_function_expression (); + assert (res->data.function_expr); + } + else if (is_keyword (KW_NEW)) + { + member_expression_with_arguments *expr + = ALLOCATE (member_expression_with_arguments); + res->type = ME_ARGS; + + skip_newlines (); + expr->member_expr = parse_member_expression (); + fatal_if_null (expr->member_expr); + + token_after_newlines_must_be (TOK_OPEN_PAREN); + skip_newlines (); + if (tok.type != TOK_CLOSE_PAREN) + { + expr->args = parse_expression (true); + assert (expr->args); + token_after_newlines_must_be (TOK_CLOSE_PAREN); + } + else + expr->args = NULL; + + res->data.args = expr; + } + else + { + res->type = ME_PRIMARY; + res->data.primary_expr = parse_primary_expression (); + RETURN_NULL_IF_NULL (res->data.primary_expr); + } + + skip_newlines (); + if (tok.type == TOK_OPEN_SQUARE || tok.type == TOK_DOT) + { + res->suffix_list = parse_member_expression_suffix_list (); + assert (res->suffix_list); + } + else + { + lexer_save_token (tok); + res->suffix_list = NULL; + } + + return res; +} + +/* new_expression + : member_expression + | 'new' LT!* new_expression + ; */ +static new_expression * +parse_new_expression () +{ + new_expression *res = ALLOCATE (new_expression); + + if (is_keyword (KW_NEW)) + { + member_expression *expr = parse_member_expression (); + if (expr == NULL) + { + res->type = NE_NEW; + + skip_newlines (); + res->data.new_expr = parse_new_expression (); + assert (res->data.new_expr); + return res; + } + else + { + res->type = NE_MEMBER; + res->data.member_expr = expr; + return res; + } + } + else + { + res->type = NE_MEMBER; + res->data.member_expr = parse_member_expression (); + RETURN_NULL_IF_NULL (res->data.member_expr); + return res; + } +} + +/* call_expression_suffix + : arguments + | index_suffix + | property_reference_suffix + ; */ +static call_expression_suffix * +parse_call_expression_suffix () +{ + call_expression_suffix *res = ALLOCATE (call_expression_suffix); + + if (tok.type == TOK_OPEN_PAREN) + { + res->type = CAS_ARGS; + + skip_newlines (); + res->data.args = parse_expression (true); + fatal_if_null (res->data.args); + token_after_newlines_must_be (TOK_CLOSE_PAREN); + + return res; + } + else if (tok.type == TOK_OPEN_SQUARE) + { + res->type = CAS_INDEX; + + skip_newlines (); + res->data.index_expr = parse_expression (true); + fatal_if_null (res->data.index_expr); + token_after_newlines_must_be (TOK_CLOSE_SQUARE); + + return res; + } + else if (tok.type == TOK_DOT) + { + res->type = CAS_PROPERTY; + + token_after_newlines_must_be (TOK_NAME); + res->data.name = tok.data.name; + return res; + } + else + unreachable (); +} + +/* call_expression_suffix_list + : call_expression_suffix+ + ; */ +static call_expression_suffix_list * +parse_call_expression_suffix_list () +{ + call_expression_suffix_list *res = ALLOCATE (call_expression_suffix_list); + + if (tok.type == TOK_OPEN_PAREN || tok.type == TOK_OPEN_SQUARE + || tok.type == TOK_DOT) + { + res->suffix = parse_call_expression_suffix (); + assert (res->suffix); + } + else + unreachable (); + + skip_newlines (); + if (tok.type == TOK_OPEN_PAREN || tok.type == TOK_OPEN_SQUARE + || tok.type == TOK_DOT) + { + res->next = parse_call_expression_suffix_list (); + assert (res->next); + } + else + { + lexer_save_token (tok); + res->next = NULL; + } + + return res; +} + +/* call_expression + : member_expression LT!* arguments (LT!* call_expression_suffix)* + ;*/ +static call_expression * +parse_call_expression (member_expression *member_expr) +{ + call_expression *res = ALLOCATE (call_expression); + + assert (member_expr); + res->member_expr = member_expr; + + assert (tok.type == TOK_OPEN_PAREN); + skip_newlines (); + if (tok.type != TOK_CLOSE_PAREN) + { + res->args = parse_expression (true); + token_after_newlines_must_be (TOK_CLOSE_PAREN); + } + else + res->args = NULL; + + skip_newlines (); + if (tok.type == TOK_OPEN_PAREN || tok.type == TOK_OPEN_SQUARE + || tok.type == TOK_DOT) + { + res->suffix_list = parse_call_expression_suffix_list (); + assert (res->suffix_list); + } + else + lexer_save_token (tok); + + return res; +} + +/* left_hand_side_expression + : call_expression + | new_expression + ; */ +static left_hand_side_expression * +parse_left_hand_side_expression () +{ + left_hand_side_expression *res = ALLOCATE (left_hand_side_expression); + + if (is_keyword (KW_NEW)) + { + res->type = LHSE_NEW; + res->data.new_expr = parse_new_expression (); + assert (res->data.new_expr); + return res; + } + else + { + member_expression *member_expr = parse_member_expression (); + RETURN_NULL_IF_NULL (member_expr); + skip_newlines (); + if (tok.type == TOK_OPEN_PAREN) + { + res->type = LHSE_CALL; + res->data.call_expr = parse_call_expression (member_expr); + assert (res->data.call_expr); + return res; + } + else + { + lexer_save_token (tok); + res->type = LHSE_NEW; + res->data.new_expr = ALLOCATE (new_expression); + res->data.new_expr->type = NE_MEMBER; + res->data.new_expr->data.member_expr = member_expr; + return res; + } + } +} + +/* postfix_expression + : left_hand_side_expression ('++' | '--')? + ; */ +static postfix_expression * +parse_postfix_expression () +{ + postfix_expression *res = ALLOCATE (postfix_expression); + + res->expr = parse_left_hand_side_expression (); + RETURN_NULL_IF_NULL (res->expr); + + tok = lexer_next_token (); + if (tok.type == TOK_DOUBLE_PLUS) + res->type = PE_INCREMENT; + else if (tok.type == TOK_DOUBLE_MINUS) + res->type = PE_DECREMENT; + else + { + lexer_save_token (tok); + res->type = PE_NONE; + } + + return res; +} + +/* unary_expression + : postfix_expression + | ('delete' | 'void' | 'typeof' | '++' | '--' | '+' | '-' | '~' | '!') unary_expression + ; */ +static unary_expression * +parse_unary_expression () +{ + unary_expression *res = ALLOCATE (unary_expression); + + switch (tok.type) + { + case TOK_DOUBLE_PLUS: res->type = UE_INCREMENT; break; + case TOK_DOUBLE_MINUS: res->type = UE_DECREMENT; break; + case TOK_PLUS: res->type = UE_PLUS; break; + case TOK_MINUS: res->type = UE_MINUS; break; + case TOK_COMPL: res->type = UE_COMPL; break; + case TOK_NOT: res->type = UE_NOT; break; + case TOK_KEYWORD: + if (is_keyword (KW_DELETE)) { res->type = UE_DELETE; break; } + if (is_keyword (KW_VOID)) { res->type = UE_VOID; break; } + if (is_keyword (KW_TYPEOF)) { res->type = UE_TYPEOF; break; } + /* FALLTHRU. */ + + default: + /* It is postfix_expression. */ + res->type = UE_POSTFIX; + res->data.postfix_expr = parse_postfix_expression (); + RETURN_NULL_IF_NULL (res->data.postfix_expr); + return res; + } + + skip_newlines (); + res->data.unary_expr = parse_unary_expression (); + assert (res->data.unary_expr); + return res; +} + +/* multiplicative_expression_list + : unary_expression (LT!* ('*' | '/' | '%') LT!* unary_expression)* + ; */ +static multiplicative_expression_list * +parse_multiplicative_expression_list () +{ + multiplicative_expression_list *res = ALLOCATE (multiplicative_expression_list); + + res->unary_expr = parse_unary_expression (); + RETURN_NULL_IF_NULL (res->unary_expr); + + skip_newlines (); + switch (tok.type) + { + case TOK_MULT: res->type = ME_MULT; break; + case TOK_DIV: res->type = ME_DIV; break; + case TOK_MOD: res->type = ME_MOD; break; + default: + lexer_save_token (tok); + res->type = ME_NONE; + res->next = NULL; + return res; + } + + skip_newlines (); + res->next = parse_multiplicative_expression_list (); + assert (res->next); + return res; +} + +/* additive_expression_list + : multiplicative_expression_list (LT!* ('+' | '-') LT!* multiplicative_expression_list)* + ; */ +static additive_expression_list * +parse_additive_expression_list () +{ + additive_expression_list *res = ALLOCATE (additive_expression_list); + + res->mult_expr = parse_multiplicative_expression_list (); + RETURN_NULL_IF_NULL (res->mult_expr); + + skip_newlines (); + switch (tok.type) + { + case TOK_PLUS: res->type = AE_PLUS; break; + case TOK_MINUS: res->type = AE_MINUS; break; + default: + lexer_save_token (tok); + res->type = AE_NONE; + res->next = NULL; + return res; + } + + skip_newlines (); + res->next = parse_additive_expression_list (); + assert (res->next); + return res; +} + +/* shift_expression_list + : additive_expression_list (LT!* ('<<' | '>>' | '>>>') LT!* additive_expression_list)* + ; */ +static shift_expression_list * +parse_shift_expression_list () +{ + shift_expression_list *res = ALLOCATE (shift_expression_list); + + res->add_expr = parse_additive_expression_list (); + RETURN_NULL_IF_NULL (res->add_expr); + + skip_newlines (); + switch (tok.type) + { + case TOK_LSHIFT: res->type = SE_LSHIFT; break; + case TOK_RSHIFT: res->type = SE_RSHIFT; break; + case TOK_RSHIFT_EX: res->type = SE_RSHIFT_EX; break; + default: + lexer_save_token (tok); + res->type = SE_NONE; + res->next = NULL; + return res; + } + + skip_newlines (); + res->next = parse_shift_expression_list (); + assert (res->next); + return res; +} + +/* relational_expression_list + : shift_expression_list (LT!* ('<' | '>' | '<=' | '>=' | 'instanceof' | 'in') LT!* shift_expression_list)* + ; + + relational_expression_no_in + : shift_expression_list (LT!* ('<' | '>' | '<=' | '>=' | 'instanceof') LT!* shift_expression_list)* + ; */ +static relational_expression_list * +parse_relational_expression_list (bool in_allowed) +{ + relational_expression_list *res = ALLOCATE (relational_expression_list); + + res->shift_expr = parse_shift_expression_list (); + RETURN_NULL_IF_NULL (res->shift_expr); + + skip_newlines (); + switch (tok.type) + { + case TOK_LESS: res->type = RE_LESS; break; + case TOK_GREATER: res->type = RE_GREATER; break; + case TOK_LESS_EQ: res->type = RE_LESS_EQ; break; + case TOK_GREATER_EQ: res->type = RE_GREATER_EQ; break; + case TOK_KEYWORD: + if (is_keyword (KW_INSTANCEOF)) + { + res->type = RE_INSTANCEOF; + break; + } + if (is_keyword (KW_IN)) + { + if (!in_allowed) + fatal (ERR_PARSER); + res->type = RE_IN; + break; + } + default: + lexer_save_token (tok); + res->type = RE_NONE; + res->next = NULL; + return res; + } + + skip_newlines (); + res->next = parse_relational_expression_list (in_allowed); + assert (res->next); + return res; +} + +/* equality_expression_list + : relational_expression_list (LT!* ('==' | '!=' | '===' | '!==') LT!* relational_expression_list)* + ; + + equality_expression_no_in + : relational_expression_no_in (LT!* ('==' | '!=' | '===' | '!==') LT!* relational_expression_no_in)* + ; */ +static equality_expression_list * +parse_equality_expression_list (bool in_allowed) +{ + equality_expression_list *res = ALLOCATE (equality_expression_list); + + res->rel_expr = parse_relational_expression_list (in_allowed); + RETURN_NULL_IF_NULL (res->rel_expr); + + skip_newlines (); + switch (tok.type) + { + case TOK_DOUBLE_EQ: res->type = EE_DOUBLE_EQ; break; + case TOK_NOT_EQ: res->type = EE_NOT_EQ; break; + case TOK_TRIPLE_EQ: res->type = EE_TRIPLE_EQ; break; + case TOK_NOT_DOUBLE_EQ: res->type = EE_NOT_DOUBLE_EQ; break; + default: + lexer_save_token (tok); + res->type = EE_NONE; + res->next = NULL; + return res; + } + + skip_newlines (); + res->next = parse_equality_expression_list (in_allowed); + assert (res->next); + return res; +} + +/* bitwise_and_expression_list + : equality_expression_list (LT!* '&' LT!* equality_expression_list)* + ; + + bitwise_and_expression_no_in + : equality_expression_no_in (LT!* '&' LT!* equality_expression_no_in)* + ; */ +static bitwise_and_expression_list * +parse_bitwise_and_expression_list (bool in_allowed) +{ + bitwise_and_expression_list *res = ALLOCATE (bitwise_and_expression_list); + + res->eq_expr = parse_equality_expression_list (in_allowed); + RETURN_NULL_IF_NULL (res->eq_expr); + + skip_newlines (); + if (tok.type == TOK_AND) + { + skip_newlines (); + res->next = parse_bitwise_and_expression_list (in_allowed); + assert (res->next); + return res; + } + else + { + lexer_save_token (tok); + res->next = NULL; + return res; + } +} + +/* bitwise_xor_expression_list + : bitwise_and_expression_list (LT!* '^' LT!* bitwise_and_expression_list)* + ; + + bitwise_xor_expression_no_in + : bitwise_and_expression_no_in (LT!* '^' LT!* bitwise_and_expression_no_in)* + ; */ +static bitwise_xor_expression_list * +parse_bitwise_xor_expression_list (bool in_allowed) +{ + bitwise_xor_expression_list *res = ALLOCATE (bitwise_xor_expression_list); + + res->and_expr = parse_bitwise_and_expression_list (in_allowed); + RETURN_NULL_IF_NULL (res->and_expr); + + skip_newlines (); + if (tok.type == TOK_XOR) + { + skip_newlines (); + res->next = parse_bitwise_xor_expression_list (in_allowed); + assert (res->next); + return res; + } + else + { + lexer_save_token (tok); + res->next = NULL; + return res; + } +} + +/* bitwise_or_expression_list + : bitwise_xor_expression_list (LT!* '|' LT!* bitwise_xor_expression_list)* + ; + + bitwise_or_expression_no_in + : bitwise_xor_expression_no_in (LT!* '|' LT!* bitwise_xor_expression_no_in)* + ; */ +static bitwise_or_expression_list * +parse_bitwise_or_expression_list (bool in_allowed) +{ + bitwise_or_expression_list *res = ALLOCATE (bitwise_or_expression_list); + + res->xor_expr = parse_bitwise_xor_expression_list (in_allowed); + RETURN_NULL_IF_NULL (res->xor_expr); + + skip_newlines (); + if (tok.type == TOK_OR) + { + skip_newlines (); + res->next = parse_bitwise_or_expression_list (in_allowed); + assert (res->next); + return res; + } + else + { + lexer_save_token (tok); + res->next = NULL; + return res; + } +} + +/* logical_and_expression_list + : bitwise_or_expression_list (LT!* '&&' LT!* bitwise_or_expression_list)* + ; + + logical_and_expression_no_in + : bitwise_or_expression_no_in (LT!* '&&' LT!* bitwise_or_expression_no_in)* + ;*/ +static logical_and_expression_list * +parse_logical_and_expression_list (bool in_allowed) +{ + logical_and_expression_list *res = ALLOCATE (logical_and_expression_list); + + res->or_expr = parse_bitwise_or_expression_list (in_allowed); + RETURN_NULL_IF_NULL (res->or_expr); + + skip_newlines (); + if (tok.type == TOK_DOUBLE_AND) + { + skip_newlines (); + res->next = parse_logical_and_expression_list (in_allowed); + assert (res->next); + return res; + } + else + { + lexer_save_token (tok); + res->next = NULL; + return res; + } +} + +/* logical_or_expression_list + : logical_and_expression_list (LT!* '||' LT!* logical_and_expression_list)* + ; + + logical_or_expression_no_in + : logical_and_expression_no_in (LT!* '||' LT!* logical_and_expression_no_in)* + ; */ +static logical_or_expression_list * +parse_logical_or_expression_list (bool in_allowed) +{ + logical_or_expression_list *res = ALLOCATE (logical_or_expression_list); + + res->and_expr = parse_logical_and_expression_list (in_allowed); + RETURN_NULL_IF_NULL (res->and_expr); + + skip_newlines (); + if (tok.type == TOK_DOUBLE_OR) + { + skip_newlines (); + res->next = parse_logical_or_expression_list (in_allowed); + assert (res->next); + return res; + } + else + { + lexer_save_token (tok); + res->next = NULL; + return res; + } +} + +/* conditional_expression + : logical_or_expression_list (LT!* '?' LT!* assignment_expression LT!* ':' LT!* assignment_expression)? + ; + + conditional_expression_no_in + : logical_or_expression_no_in (LT!* '?' LT!* assignment_expression_no_in LT!* ':' LT!* assignment_expression_no_in)? + ; */ +static conditional_expression * +parse_conditional_expression (bool in_allowed) +{ + conditional_expression *res = ALLOCATE (conditional_expression); + + res->or_expr = parse_logical_or_expression_list (in_allowed); + RETURN_NULL_IF_NULL (res->or_expr); + + skip_newlines (); + if (tok.type == TOK_QUERY) + { + skip_newlines (); + res->then_expr = parse_assigment_expression (in_allowed); + assert (res->then_expr); + token_after_newlines_must_be (TOK_COLON); + + skip_newlines (); + res->else_expr = parse_assigment_expression (in_allowed); + assert (res->else_expr); + return res; + } + else + { + lexer_save_token (tok); + res->then_expr = res->else_expr = NULL; + return res; + } +} + +/* assignment_expression + : conditional_expression + | left_hand_side_expression LT!* assignment_operator LT!* assignment_expression + ; + + assignment_expression_no_in + : conditional_expression_no_in + | left_hand_side_expression LT!* assignment_operator LT!* assignment_expression_no_in + ; + + assignmentOperator + : '=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '>>>=' | '&=' | '^=' | '|=' + ;*/ +static assignment_expression * +parse_assigment_expression (bool in_allowed) +{ + assignment_expression *res = ALLOCATE (assignment_expression); + + /* OK, assignment_expression contains conditional_expression, + conditional_expression contains logical_or_expression_list, + logical_or_expression_list contains logical_and_expression_list, + logical_and_expression_list contains bitwise_or_expression_list, + bitwise_or_expression_list contains bitwise_xor_expression_list, + bitwise_xor_expression_list contains bitwise_and_expression_list, + bitwise_and_expression_list contains equality_expression_list, + equality_expression_list contains relational_expression_list, + relational_expression_list contains shift_expression_list, + shift_expression_list contains additive_expression_list, + additive_expression_list contains multiplicative_expression_list, + multiplicative_expression_list contains unary_expression, + unary_expression contains postfix_expression, + postfix_expression contains left_hand_side_expression. + + So, we can try parse conditional_expression and check if there is only left_hand_side_expression. */ + conditional_expression *cond_expr = parse_conditional_expression (in_allowed); + const logical_or_expression_list *lor_expr; + const logical_and_expression_list *land_expr; + const bitwise_or_expression_list *bor_expr; + const bitwise_xor_expression_list *xor_expr; + const bitwise_and_expression_list *band_expr; + const equality_expression_list *eq_expr; + const relational_expression_list *rel_expr; + const shift_expression_list *shift_expr; + const additive_expression_list *add_expr; + const multiplicative_expression_list *mult_expr; + const unary_expression *unary_expr; + const postfix_expression *postfix_expr; + left_hand_side_expression *left_hand_expr; + + RETURN_NULL_IF_NULL (cond_expr); + + if (cond_expr->then_expr) + goto cond; + assert (cond_expr->else_expr == NULL); + assert (cond_expr->or_expr); + + lor_expr = cond_expr->or_expr; + if (lor_expr->next) + goto cond; + assert (lor_expr->and_expr); + + land_expr = lor_expr->and_expr; + if (land_expr->next) + goto cond; + assert (land_expr->or_expr); + + bor_expr = land_expr->or_expr; + if (bor_expr->next) + goto cond; + assert (bor_expr->xor_expr); + + xor_expr = bor_expr->xor_expr; + if (xor_expr->next) + goto cond; + assert (xor_expr->and_expr); + + band_expr = xor_expr->and_expr; + if (band_expr->next) + goto cond; + assert (band_expr->eq_expr); + + eq_expr = band_expr->eq_expr; + if (eq_expr->next) + goto cond; + assert (eq_expr->type == EE_NONE); + assert (eq_expr->rel_expr); + + rel_expr = eq_expr->rel_expr; + if (rel_expr->next) + goto cond; + assert (rel_expr->type == RE_NONE); + assert (rel_expr->shift_expr); + + shift_expr = rel_expr->shift_expr; + if (shift_expr->next) + goto cond; + assert (shift_expr->type == SE_NONE); + assert (shift_expr->add_expr); + + add_expr = shift_expr->add_expr; + if (add_expr->next) + goto cond; + assert (add_expr->type == AE_NONE); + assert (add_expr->mult_expr); + + mult_expr = add_expr->mult_expr; + if (mult_expr->next) + goto cond; + assert (mult_expr->type == ME_NONE); + assert (mult_expr->unary_expr); + + unary_expr = mult_expr->unary_expr; + if (unary_expr->type != UE_POSTFIX) + goto cond; + assert (unary_expr->data.postfix_expr); + + postfix_expr = unary_expr->data.postfix_expr; + if (postfix_expr->type != PE_NONE) + goto cond; + assert (postfix_expr->expr); + + left_hand_expr = postfix_expr->expr; + + skip_newlines (); + switch (tok.type) + { + case TOK_EQ: res->type = AE_EQ; break; + case TOK_MULT_EQ: res->type = AE_MULT_EQ; break; + case TOK_DIV_EQ: res->type = AE_DIV_EQ; break; + case TOK_MOD_EQ: res->type = AE_MOD_EQ; break; + case TOK_PLUS_EQ: res->type = AE_PLUS_EQ; break; + case TOK_MINUS_EQ: res->type = AE_MINUS_EQ; break; + case TOK_LSHIFT_EQ: res->type = AE_LSHIFT_EQ; break; + case TOK_RSHIFT_EQ: res->type = AE_RSHIFT_EQ; break; + case TOK_RSHIFT_EX_EQ: res->type = AE_RSHIFT_EX_EQ; break; + case TOK_AND_EQ: res->type = AE_AND_EQ; break; + case TOK_OR_EQ: res->type = AE_OR_EQ; break; + case TOK_XOR_EQ: res->type = AE_XOR_EQ; break; + default: + lexer_save_token (tok); + goto cond; + } + res->data.s.left_hand_expr = left_hand_expr; + + skip_newlines (); + res->data.s.assign_expr = parse_assigment_expression (in_allowed); + assert (res->data.s.assign_expr); + return res; + +cond: + res->type = AE_COND; + res->data.cond_expr = cond_expr; + return res; +} + +/* expression + : assignment_expression (LT!* ',' LT!* assignment_expression)* + ; + + expression_no_in + : assignment_expression_no_in (LT!* ',' LT!* assignment_expression_no_in)* + ; */ +static expression * +parse_expression (bool in_allowed) +{ + expression *res = ALLOCATE (expression); + + res->assign_expr = parse_assigment_expression (in_allowed); + RETURN_NULL_IF_NULL (res->assign_expr); + + skip_newlines (); + if (tok.type == TOK_COMMA) + { + skip_newlines (); + res->next = parse_expression (in_allowed); + } + else + { + lexer_save_token (tok); + res->next = NULL; + } + + return res; +} + +/* variable_declaration(_no_in) + : Identifier LT!* initialiser(_no_in)? + ; + initialiser(_no_in) + : '=' LT!* assignment_expression + ; */ +static variable_declaration* +parse_variable_declaration (bool in_allowed) +{ + variable_declaration *res = ALLOCATE (variable_declaration); + + current_token_must_be (TOK_NAME); + res->name = tok.data.name; + + skip_newlines (); + if (tok.type == TOK_EQ) + { + skip_newlines (); + res->ass_expr = parse_assigment_expression (in_allowed); + } + else + { + lexer_save_token (tok); + res->ass_expr = NULL; + } + + return res; +} + +/* variable_declaration_list(_no_in) + : variable_declaration(_no_in) + (LT!* ',' LT!* variable_declaration(_no_in))* + ; */ +static variable_declaration_list * +parse_variable_declaration_list (bool in_allowed) +{ + variable_declaration_list *res = ALLOCATE (variable_declaration_list);; + variable_declaration *var_decl = parse_variable_declaration (in_allowed); + + fatal_if_null (var_decl); + res->var_decl = var_decl; + skip_newlines (); + if (tok.type == TOK_COMMA) + { + skip_newlines (); + res->next = parse_variable_declaration_list (in_allowed); + assert (res->next); + } + else + { + lexer_save_token (tok); + res->next = NULL; + } + + return res; +} + +/* for_statement + : 'for' LT!* '(' (LT!* for_statement_initialiser_part)? LT!* ';' + (LT!* expression)? LT!* ';' (LT!* expression)? LT!* ')' LT!* statement + ; + + for_statement_initialiser_part + : expression_no_in + | 'var' LT!* variable_declaration_list_no_in + ; + + for_in_statement + : 'for' LT!* '(' LT!* for_in_statement_initialiser_part LT!* 'in' + LT!* expression LT!* ')' LT!* statement + ; + + for_in_statement_initialiser_part + : left_hand_side_expression + | 'var' LT!* variable_declaration_no_in + ;*/ + +static for_or_for_in_statement * +parse_for_or_for_in_statement () +{ + for_or_for_in_statement *res = ALLOCATE (for_or_for_in_statement); + variable_declaration_list *list = NULL; + expression *expr = NULL; + + assert_keyword (KW_FOR); + token_after_newlines_must_be (TOK_OPEN_PAREN); + + skip_newlines (); + if (tok.type == TOK_SEMICOLON) + goto plain_for; + /* Both for_statement_initialiser_part and for_in_statement_initialiser_part + contains 'var'. Chekc it first. */ + if (is_keyword (KW_VAR)) + { + skip_newlines (); + list = parse_variable_declaration_list (false); + assert (list); + if (list->next) + { + token_after_newlines_must_be (TOK_SEMICOLON); + goto plain_for; + } + else + { + skip_newlines (); + if (tok.type == TOK_SEMICOLON) + goto plain_for; + else if (is_keyword (KW_IN)) + goto for_in; + else + fatal (ERR_PARSER); + } + } + assert (list == NULL); + + /* expression contains left_hand_side_expression. */ + expr = parse_expression (false); + + skip_newlines (); + if (tok.type == TOK_SEMICOLON) + goto plain_for; + else if (is_keyword (KW_IN)) + goto for_in; + else + fatal (ERR_PARSER); + + unreachable (); + +plain_for: + res->is_for_in = false; + res->data.for_stmt = ALLOCATE (for_statement); + if (list) + { + assert (expr == NULL); + res->data.for_stmt->init = ALLOCATE (for_statement_initialiser_part); + res->data.for_stmt->init->is_decl = true; + res->data.for_stmt->init->data.decl_list = list; + } + if (expr) + { + assert (list == NULL); + res->data.for_stmt->init = ALLOCATE (for_statement_initialiser_part); + res->data.for_stmt->init->is_decl = false; + res->data.for_stmt->init->data.expr = expr; + } + + skip_newlines (); + if (tok.type == TOK_SEMICOLON) + res->data.for_stmt->limit = NULL; + else + { + res->data.for_stmt->limit = parse_expression (true); + assert (res->data.for_stmt->limit); + next_token_must_be (TOK_SEMICOLON); + } + + skip_newlines (); + if (tok.type == TOK_CLOSE_PAREN) + res->data.for_stmt->incr = NULL; + else + { + res->data.for_stmt->incr = parse_expression (true); + assert (res->data.for_stmt->incr); + next_token_must_be (TOK_CLOSE_PAREN); + } + return res; + +for_in: + res->is_for_in = true; + res->data.for_in_stmt = ALLOCATE (for_in_statement); + if (list) + { + assert (expr == NULL); + assert (list->next == NULL); + res->data.for_in_stmt->init = ALLOCATE (for_in_statement_initializer_part); + res->data.for_in_stmt->init->is_decl = true; + res->data.for_in_stmt->init->data.decl = list->var_decl; + } + if (expr) + { + assignment_expression *assign_expr = NULL; + conditional_expression *cond_expr = NULL; + logical_or_expression_list *lor_expr = NULL; + logical_and_expression_list *land_expr = NULL; + bitwise_or_expression_list *bor_expr = NULL; + bitwise_xor_expression_list *xor_expr = NULL; + bitwise_and_expression_list *band_expr = NULL; + equality_expression_list *eq_expr = NULL; + relational_expression_list *rel_expr = NULL; + shift_expression_list *shift_expr = NULL; + additive_expression_list *add_expr = NULL; + multiplicative_expression_list *mult_expr = NULL; + unary_expression *unary_expr = NULL; + postfix_expression *postfix_expr = NULL; + + assert (list == NULL); + assert (expr->next == NULL); + + assign_expr = expr->assign_expr; + assert (assign_expr->type == AE_COND); + assert (assign_expr->data.cond_expr); + + cond_expr = assign_expr->data.cond_expr; + assert (cond_expr->then_expr == NULL); + assert (cond_expr->else_expr == NULL); + assert (cond_expr->or_expr); + + lor_expr = cond_expr->or_expr; + assert (lor_expr->next == NULL); + assert (lor_expr->and_expr); + + land_expr = lor_expr->and_expr; + assert (land_expr->next == NULL); + assert (land_expr->or_expr); + + bor_expr = land_expr->or_expr; + assert (bor_expr->next == NULL); + assert (bor_expr->xor_expr); + + xor_expr = bor_expr->xor_expr; + assert (xor_expr->next == NULL); + assert (xor_expr->and_expr); + + band_expr = xor_expr->and_expr; + assert (band_expr->next == NULL); + assert (band_expr->eq_expr); + + eq_expr = band_expr->eq_expr; + assert (eq_expr->next == NULL); + assert (eq_expr->type == EE_NONE); + assert (eq_expr->rel_expr); + + rel_expr = eq_expr->rel_expr; + assert (rel_expr->next == NULL); + assert (rel_expr->type == RE_NONE); + assert (rel_expr->shift_expr); + + shift_expr = rel_expr->shift_expr; + assert (shift_expr->next == NULL); + assert (shift_expr->type == SE_NONE); + assert (shift_expr->add_expr); + + add_expr = shift_expr->add_expr; + assert (add_expr->next == NULL); + assert (add_expr->type == AE_NONE); + assert (add_expr->mult_expr); + + mult_expr = add_expr->mult_expr; + assert (mult_expr->next == NULL); + assert (mult_expr->type == ME_NONE); + assert (mult_expr->unary_expr); + + unary_expr = mult_expr->unary_expr; + assert (unary_expr->type == UE_POSTFIX); + assert (unary_expr->data.postfix_expr); + + postfix_expr = unary_expr->data.postfix_expr; + assert (postfix_expr->type == PE_NONE); + assert (postfix_expr->expr); + + res->data.for_in_stmt->init = ALLOCATE (for_in_statement_initializer_part); + res->data.for_in_stmt->init->is_decl = false; + res->data.for_in_stmt->init->data.left_hand_expr = postfix_expr->expr; + } + + assert (res->data.for_in_stmt->init); + skip_newlines (); + res->data.for_in_stmt->list_expr = parse_expression (true); + assert (res->data.for_in_stmt->list_expr); + token_after_newlines_must_be (TOK_CLOSE_PAREN); + + return res; +} + +static inline void +parse_expression_inside_parens (statement *res) +{ + token_after_newlines_must_be (TOK_OPEN_PAREN); + skip_newlines (); + res->data.expr = parse_expression (true); + assert (res->data.expr); + token_after_newlines_must_be (TOK_CLOSE_PAREN); +} + +/* statement + : statement_block + | variable_statement + | empty_statement + | if_statement + | iteration_statement + | continue_statement + | break_statement + | return_statement + | with_statement + | labelled_statement + | switch_statement + | throw_statement + | try_statement + | expression_statement + ; + + statement_block + : '{' LT!* statement_list? LT!* '}' + ; + + variable_statement + : 'var' LT!* variable_declaration_list (LT | ';')! + ; + + empty_statement + : ';' + ; + + expression_statement + : expression (LT | ';')! + ; + + iteration_statement + : do_while_statement + | while_statement + | for_statement + | for_in_statement + ; + + continue_statement + : 'continue' Identifier? (LT | ';')! + ; + + break_statement + : 'break' Identifier? (LT | ';')! + ; + + return_statement + : 'return' expression? (LT | ';')! + ; + + switchStatement + : 'switch' LT!* '(' LT!* expression LT!* ')' LT!* caseBlock + ; + + throw_statement + : 'throw' expression (LT | ';')! + ; + + try_statement + : 'try' LT!* '{' LT!* statement_list LT!* '}' LT!* (finally_clause | catch_clause (LT!* finally_clause)?) + ;*/ +statement * +parser_parse_statement () +{ + statement *res = ALLOCATE (statement); + res->data.none = NULL; + + skip_newlines (); + + if (is_keyword (KW_FINALLY)) + { + res->type = STMT_FINALLY; + current_scope_must_be (SCOPE_TRY | SCOPE_CATCH); + pop_scope (); + push_scope (SCOPE_FINALLY); + return res; + } + + if (current_scope->was_stmt + && (current_scope->type & (SCOPE_IF | SCOPE_DO | SCOPE_WITH | SCOPE_SWITCH | SCOPE_ELSE + | SCOPE_CATCH | SCOPE_FINALLY | SCOPE_FUNCTION | SCOPE_WHILE + | SCOPE_FOR))) + pop_scope (); + + current_scope->was_stmt = true; + + if (tok.type == TOK_EOF) + { + current_scope_must_be_global (); + res->type = STMT_EOF; + return res; + } + + if (current_scope->type == SCOPE_SUBEXPRESSION) + { + if (tok.type == TOK_CLOSE_PAREN) + { + res->type = STMT_SUBEXPRESSION_END; + pop_scope (); + return res; + } + res->type = STMT_EXPRESSION; + res->data.expr = parse_expression (true); + assert (res->data.expr); + return res; + } + if (tok.type == TOK_OPEN_BRACE) + { + res->type = STMT_BLOCK_START; + push_scope (SCOPE_BLOCK); + return res; + } + if (tok.type == TOK_CLOSE_BRACE) + { + current_scope_must_be (SCOPE_BLOCK); + res->type = STMT_BLOCK_END; + pop_scope (); + current_scope->was_stmt = true; + return res; + } + if (is_keyword (KW_ELSE)) + { + current_scope_must_be (SCOPE_IF); + skip_newlines (); + if (is_keyword (KW_IF)) + { + res->type = STMT_ELSE_IF; + parse_expression_inside_parens (res); + return res; + } + else + { + lexer_save_token (tok); + res->type = STMT_ELSE; + pop_scope (); + push_scope (SCOPE_ELSE); + return res; + } + } + if (is_keyword (KW_WHILE)) + { + res->type = STMT_WHILE; + parse_expression_inside_parens (res); + if (current_scope->type == SCOPE_DO) + { + insert_semicolon (); + pop_scope (); + } + else + push_scope (SCOPE_WHILE); + return res; + } + if (is_keyword (KW_CATCH)) + { + res->type = STMT_CATCH; + current_scope_must_be (SCOPE_TRY); + parse_expression_inside_parens (res); + pop_scope (); + push_scope (SCOPE_CATCH); + return res; + } + if (is_keyword (KW_FUNCTION)) + { + res->type = STMT_FUNCTION; + res->data.fun_decl = parse_function_declaration (); + push_scope (SCOPE_FUNCTION); + return res; + } + if (is_keyword (KW_VAR)) + { + res->type = STMT_VARIABLE; + + skip_newlines (); + res->data.var_stmt = parse_variable_declaration_list (true); + return res; + } + if (tok.type == TOK_SEMICOLON) + { + res->type = STMT_EMPTY; + return res; + } + if (is_keyword (KW_IF)) + { + res->type = STMT_IF; + parse_expression_inside_parens (res); + push_scope (SCOPE_IF); + return res; + } + if (is_keyword (KW_DO)) + { + res->type = STMT_DO; + push_scope (SCOPE_DO); + return res; + } + if (is_keyword (KW_FOR)) + { + res->type = STMT_FOR_OR_FOR_IN; + res->data.for_stmt = parse_for_or_for_in_statement (); + assert (res->data.for_stmt); + push_scope (SCOPE_FOR); + return res; + } + if (is_keyword (KW_CONTINUE)) + { + scope_must_be (SCOPE_LOOP); + res->type = STMT_CONTINUE; + tok = lexer_next_token (); + if (tok.type == TOK_NAME) + res->data.name = tok.data.name; + else + lexer_save_token (tok); + insert_semicolon (); + return res; + } + if (is_keyword (KW_BREAK)) + { + scope_must_be (SCOPE_LOOP | SCOPE_CASE); + res->type = STMT_BREAK; + tok = lexer_next_token (); + if (tok.type == TOK_NAME) + { + if (current_scope->type == SCOPE_CASE) + fatal (ERR_PARSER); + res->data.name = tok.data.name; + } + else + lexer_save_token (tok); + insert_semicolon (); + return res; + } + if (is_keyword (KW_RETURN)) + { + scope_must_be (SCOPE_FUNCTION); + res->type = STMT_RETURN; + tok = lexer_next_token (); + if (tok.type != TOK_SEMICOLON && tok.type != TOK_NEWLINE) + { + scope *old_scope = current_scope; + res->data.expr = parse_expression (false); + assert (res->data.expr); + if (old_scope == current_scope) + insert_semicolon (); + } + // Eat TOK_SEMICOLON + return res; + } + if (is_keyword (KW_WITH)) + { + res->type = STMT_WITH; + parse_expression_inside_parens (res); + push_scope (SCOPE_WITH); + return res; + } + if (is_keyword (KW_SWITCH)) + { + res->type = STMT_SWITCH; + parse_expression_inside_parens (res); + push_scope (SCOPE_SWITCH); + return res; + } + if (is_keyword (KW_THROW)) + { + res->type = STMT_THROW; + tok = lexer_next_token (); + res->data.expr = parse_expression (true); + assert (res->data.expr); + insert_semicolon (); + return res; + } + if (is_keyword (KW_TRY)) + { + res->type = STMT_TRY; + push_scope (SCOPE_TRY); + return res; + } + if (is_keyword (KW_CASE)) + { + if (current_scope->type == SCOPE_CASE) + pop_scope (); + current_scope_must_be (SCOPE_SWITCH); + skip_newlines (); + res->data.expr = parse_expression (true); + assert (res->data.expr); + token_after_newlines_must_be (TOK_SEMICOLON); + push_scope (SCOPE_CASE); + return res; + } + if (is_keyword (KW_DEFAULT)) + { + if (current_scope->type == SCOPE_CASE) + pop_scope (); + current_scope_must_be (SCOPE_SWITCH); + token_after_newlines_must_be (TOK_SEMICOLON); + push_scope (SCOPE_CASE); + return res; + } + if (tok.type == TOK_NAME) + { + token saved = tok; + skip_newlines (); + if (tok.type == TOK_COLON) + { + res->type = STMT_LABELLED; + res->data.name = saved.data.name; + return res; + } + else + { + lexer_save_token (tok); + tok = saved; + expression *expr = parse_expression (true); + fatal_if_null (expr); + res->type = STMT_EXPRESSION; + res->data.expr = expr; + return res; + } + } + + expression *expr = parse_expression (true); + if (expr) + { + res->type = STMT_EXPRESSION; + res->data.expr = expr; + return res; + } + else + { + lexer_save_token (tok); + free (res); + return NULL; + } +} + +void +parser_init () +{ + current_scope = ALLOCATE (struct scope); + current_scope->type = SCOPE_GLOBAL; +#ifdef DEBUG + debug_file = fopen ("parser.log", "w"); +#endif +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 000000000..7f88b1c64 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,738 @@ +/* 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. + */ + +#ifndef PARSER_H +#define PARSER_H + +#include +#include + +struct source_element_list; +struct statement_list; +struct statement; +struct assignment_expression; +struct member_expression; + +/** Represents list of parameters. */ +typedef struct formal_parameter_list +{ + /** Identifier of a parameter. Cannot be NULL. */ + const char *name; + /** Next parameter: can be NULL. */ + struct formal_parameter_list *next; +} +formal_parameter_list; + +/** @function_declaration represents both declaration and expression of a function. + After this parser must return a block of statements. */ +typedef struct +{ + /** Identifier: name of a function. Can be NULL for anonimous functions. */ + const char *name; + /** List of parameter of a function. Can be NULL. */ + formal_parameter_list *params; +} +function_declaration; + +typedef function_declaration function_expression; + +/** Represents expression, array literal and list of argument. */ +typedef struct expression_list +{ + /** Single assignment expression. Cannot be NULL for expression and list of arguments. + But can be NULL for array literal. */ + struct assignment_expression *assign_expr; + /** Next expression. Can be NULL. */ + struct expression_list *next; +} +expression_list; + +typedef expression_list expression; +typedef expression_list array_literal; +typedef expression_list argument_list; + +/** Types of literals: null, bool, decimal and string. + Decimal type is represented by LIT_INT and supports only double-word sized integers. */ +typedef enum +{ + LIT_NULL, + LIT_BOOL, + LIT_INT, + LIT_STR +} +literal_type; + +/** Represents different literals, contains a data of them. */ +typedef struct +{ + /** Type of a literal. */ + literal_type type; + + /** Value of a literal. */ + union + { + /** Used by null literal, always NULL. */ + void *data; + /** String literal value. */ + const char *str; + /** Number value. */ + int num; + /** Boolean value. */ + bool is_true; + } + data; +} +literal; + +/** type of PropertyName. Can be integer, identifier of string literal. */ +typedef enum +{ + PN_NAME, + PN_STRING, + PN_NUM +} +property_name_type; + +/** Represents name of property. */ +typedef struct +{ + /** Type of property name. */ + property_name_type type; + + /** Value of property name. */ + union + { + /** Identifier. */ + const char *name; + /** Value of string literal. */ + const char *str; + /** Numeric value. */ + int num; + } + data; +} +property_name; + +/** Represents a single property. */ +typedef struct +{ + /** Name of property. */ + property_name *name; + /** Value of property. */ + struct assignment_expression *assign_expr; +} +property_name_and_value; + +/** List of properties. Represents ObjectLiteral. */ +typedef struct property_name_and_value_list +{ + /** Current property. */ + property_name_and_value *nav; + + /** Next property. */ + struct property_name_and_value_list *next; +} +property_name_and_value_list; + +typedef property_name_and_value_list object_literal; + +/** Type of PrimaryExpression. Can be ThisLiteral, Identifier, Literal, ArrayLiteral, + ObjectLiteral or expression. */ +typedef enum +{ + PE_THIS, + PE_NAME, + PE_LITERAL, + PE_ARRAY, + PE_OBJECT, + PE_EXPR +} +primary_expression_type; + +/** PrimaryExpression. */ +typedef struct +{ + /** Type of PrimaryExpression. */ + primary_expression_type type; + + /** Value of PrimaryExpression. */ + union + { + /** Used for ThisLiteral. Always NULL. */ + void *none; + /** Identifier. */ + const char *name; + /** Literal. */ + literal *lit; + /** ArrayLiteral. */ + array_literal *array_lit; + /** ObjectLiteral. */ + object_literal *object_lit; + /** Expression. */ + expression *expr; + } + data; +} +primary_expression; + +/** Type of suffix of MemberExpression. Can be either index-like ([]) or property-like (.). */ +typedef enum +{ + MES_INDEX, + MES_PROPERTY +} +member_expression_suffix_type; + +/** Suffix of MemberExpression. */ +typedef struct +{ + /** Type of suffix. */ + member_expression_suffix_type type; + + /** Value of suffix. */ + union + { + /** Used by index-like suffix. */ + expression *index_expr; + /** Used by property-like suffix. */ + const char *name; + } + data; +} +member_expression_suffix; + +/** List of MemberExpression's suffixes. */ +typedef struct member_expression_suffix_list +{ + /** Current suffix. */ + member_expression_suffix *suffix; + + /** Next suffix. */ + struct member_expression_suffix_list *next; +} +member_expression_suffix_list; + +/** Represents MemberExpression Arguments grammar production. */ +typedef struct +{ + /** MemberExpression. */ + struct member_expression *member_expr; + /** Arguments. */ + argument_list *args; +} +member_expression_with_arguments; + +/** Types of MemberExpression. Can be PrimaryExpression, + FunctionExpression or MemberExpression Arguments. */ +typedef enum +{ + ME_PRIMARY, + ME_FUNCTION, + ME_ARGS +} +member_expression_type; + +/** Represents MemberExpression. */ +typedef struct member_expression +{ + /** Type of MemberExpression. */ + member_expression_type type; + + /** Value of MemberExpression. */ + union + { + /** PrimaryExpression. */ + primary_expression *primary_expr; + /** FunctionExpression. */ + function_expression *function_expr; + /** MemberExpression Arguments. */ + member_expression_with_arguments *args; + } + data; + + member_expression_suffix_list *suffix_list; +} +member_expression; + +/** Types of NewExpression. Can be either MemberExpression or NewExpression. */ +typedef enum +{ + NE_MEMBER, + NE_NEW +} +new_expression_type; + +/** Represents NewExpression. */ +typedef struct new_expression +{ + /** Type of NewExpression. */ + new_expression_type type; + + /** Value of NewExpression. */ + union + { + /** MemberExpression. */ + member_expression *member_expr; + /** NewExpression. */ + struct new_expression *new_expr; + } + data; +} +new_expression; + +/** Types of CallExpression' suffix. Can be Arguments, index-like access ([]) or + property-like access (.). */ +typedef enum +{ + CAS_ARGS, + CAS_INDEX, + CAS_PROPERTY +} +call_expression_suffix_type; + +/** Suffix of CallExpression. */ +typedef struct +{ + /** Type of suffix. */ + call_expression_suffix_type type; + + /** Value of suffix. */ + union + { + /** Arguments. */ + argument_list *args; + /** index-like access expression. */ + expression *index_expr; + /** Identifier of property. */ + const char *name; + } + data; +} +call_expression_suffix; + +/** List of CallExpression's suffixes. */ +typedef struct call_expression_suffix_list +{ + /** Current suffix. */ + call_expression_suffix *suffix; + + /** Next suffix. */ + struct call_expression_suffix_list *next; +} +call_expression_suffix_list; + +/** CallExpression. */ +typedef struct +{ + /** Callee. Cannot be NULL. */ + member_expression *member_expr; + /** List of arguments. Can be NULL. */ + argument_list *args; + /** Suffixes of CallExpression. Can be NULL. */ + call_expression_suffix_list *suffix_list; +} +call_expression; + +/** Types of LeftHandSideExpression. Can be either CallExpression or NewExpression. */ +typedef enum +{ + LHSE_CALL, + LHSE_NEW +} +left_hand_side_expression_type; + +/** LeftHandSideExpression. */ +typedef struct +{ + /** Type of LeftHandSideExpression. */ + left_hand_side_expression_type type; + + /** Value of LeftHandSideExpression. */ + union + { + /** Value of CallExpression. */ + call_expression *call_expr; + /** Value of NewExpression. */ + new_expression *new_expr; + } + data; +} +left_hand_side_expression; + +/** Type of PostfixExpression. Unlike ECMA, it can contain no postfix operator in addition to + increment and decrement. */ +typedef enum +{ + PE_NONE, + PE_INCREMENT, + PE_DECREMENT +} +postfix_expression_type; + +/** PostfixExpression. */ +typedef struct +{ + /** Type of PostfixExpression. */ + postfix_expression_type type; + /** LeftHandSideExpression. */ + left_hand_side_expression *expr; +} +postfix_expression; + +/** Types of UnaryExpression. Can be PostfixExpression, delete UnaryExpression, + void UnaryExpression, typeof UnaryExpression, ++ UnaryExpression, -- UnaryExpression, + + UnaryExpression, - UnaryExpression, ~ UnaryExpression, ! UnaryExpression. */ +typedef enum +{ + UE_POSTFIX, + UE_DELETE, + UE_VOID, + UE_TYPEOF, + UE_INCREMENT, + UE_DECREMENT, + UE_PLUS, + UE_MINUS, + UE_COMPL, + UE_NOT +} +unary_expression_type; + +/** UnaryExpression. */ +typedef struct unary_expression +{ + /** Type of UnaryExpression. */ + unary_expression_type type; + + /** Data of UnaryExpression. */ + union + { + /** PostfixExpression. Exists only when type of UE_POSTFIX. */ + postfix_expression *postfix_expr; + /** UnaryExpression after an operator. Exists otherwise. */ + struct unary_expression *unary_expr; + } + data; +} +unary_expression; + +/** Type of MultiplicativeExpression. In addition to ECMA if there is only one operand, + we use ME_NONE. */ +typedef enum +{ + ME_NONE, + ME_MULT, + ME_DIV, + ME_MOD +} +multiplicative_expression_type; + +/** List of MultiplicativeExpressions. It can contain 1..n operands. */ +typedef struct multiplicative_expression_list +{ + /** Type of current MultiplicativeExpression. */ + multiplicative_expression_type type; + /** Current operand. */ + unary_expression *unary_expr; + + /** Next operand. */ + struct multiplicative_expression_list *next; +} +multiplicative_expression_list; + +typedef enum +{ + AE_NONE, + AE_PLUS, + AE_MINUS +} +additive_expression_type; + +typedef struct additive_expression_list +{ + additive_expression_type type; + multiplicative_expression_list *mult_expr; + + struct additive_expression_list *next; +} +additive_expression_list; + +typedef enum +{ + SE_NONE, + SE_LSHIFT, + SE_RSHIFT, + SE_RSHIFT_EX +} +shift_expression_type; + +typedef struct shift_expression_list +{ + shift_expression_type type; + additive_expression_list *add_expr; + + struct shift_expression_list *next; +} +shift_expression_list; + +typedef enum +{ + RE_NONE, + RE_LESS, + RE_GREATER, + RE_LESS_EQ, + RE_GREATER_EQ, + RE_INSTANCEOF, + RE_IN +} +relational_expression_type; + +typedef struct relational_expression_list +{ + relational_expression_type type; + shift_expression_list *shift_expr; + + struct relational_expression_list *next; +} +relational_expression_list; + +typedef enum +{ + EE_NONE, + EE_DOUBLE_EQ, + EE_NOT_EQ, + EE_TRIPLE_EQ, + EE_NOT_DOUBLE_EQ +} +equality_expression_type; + +typedef struct equality_expression_list +{ + equality_expression_type type; + relational_expression_list *rel_expr; + + struct equality_expression_list *next; +} +equality_expression_list; + +typedef struct bitwise_and_expression_list +{ + equality_expression_list *eq_expr; + + struct bitwise_and_expression_list *next; +} +bitwise_and_expression_list; + +typedef struct bitwise_xor_expression_list +{ + bitwise_and_expression_list *and_expr; + + struct bitwise_xor_expression_list *next; +} +bitwise_xor_expression_list; + +typedef struct bitwise_or_expression_list +{ + bitwise_xor_expression_list *xor_expr; + + struct bitwise_or_expression_list *next; +} +bitwise_or_expression_list; + +typedef struct logical_and_expression_list +{ + bitwise_or_expression_list *or_expr; + + struct logical_and_expression_list *next; +} +logical_and_expression_list; + +typedef struct logical_or_expression_list +{ + logical_and_expression_list *and_expr; + + struct logical_or_expression_list *next; +} +logical_or_expression_list; + +typedef struct +{ + logical_or_expression_list *or_expr; + struct assignment_expression *then_expr, *else_expr; +} +conditional_expression; + +typedef enum +{ + AE_COND, + AE_EQ, + AE_MULT_EQ, + AE_DIV_EQ, + AE_MOD_EQ, + AE_PLUS_EQ, + AE_MINUS_EQ, + AE_LSHIFT_EQ, + AE_RSHIFT_EQ, + AE_RSHIFT_EX_EQ, + AE_AND_EQ, + AE_OR_EQ, + AE_XOR_EQ +} +assignment_expression_type; + +typedef struct +{ + left_hand_side_expression *left_hand_expr; + struct assignment_expression *assign_expr; +} +left_hand_and_assignment_expression; + +typedef struct assignment_expression +{ + assignment_expression_type type; + + union + { + conditional_expression *cond_expr; + left_hand_and_assignment_expression s; + } + data; +} +assignment_expression; + +/* Statements. */ + +typedef struct +{ + const char *name; + + assignment_expression *ass_expr; +} +variable_declaration; + +typedef struct variable_declaration_list +{ + variable_declaration *var_decl; + + struct variable_declaration_list *next; +} +variable_declaration_list; + +typedef struct +{ + bool is_decl; + + union + { + expression *expr; + variable_declaration_list *decl_list; + } + data; +} +for_statement_initialiser_part; + +typedef struct +{ + for_statement_initialiser_part *init; + expression *limit, *incr; +} +for_statement; + +typedef struct +{ + bool is_decl; + + union + { + left_hand_side_expression *left_hand_expr; + variable_declaration *decl; + } + data; +} +for_in_statement_initializer_part; + +typedef struct +{ + for_in_statement_initializer_part *init; + expression *list_expr; +} +for_in_statement; + +typedef struct +{ + bool is_for_in; + + union + { + for_statement *for_stmt; + for_in_statement *for_in_stmt; + } + data; +} +for_or_for_in_statement; + +typedef enum +{ + STMT_BLOCK_START, + STMT_BLOCK_END, + STMT_VARIABLE, + STMT_EMPTY, + STMT_IF, + STMT_ELSE, + STMT_ELSE_IF, + STMT_DO, + + STMT_WHILE, + STMT_FOR_OR_FOR_IN, + STMT_CONTINUE, + STMT_BREAK, + + STMT_RETURN, + STMT_WITH, + STMT_LABELLED, + STMT_SWITCH, + STMT_CASE, + STMT_THROW, + + STMT_TRY, + STMT_CATCH, + STMT_FINALLY, + STMT_EXPRESSION, + STMT_SUBEXPRESSION_END, + STMT_FUNCTION, + STMT_EOF +} +statement_type; + +typedef struct statement +{ + statement_type type; + + union + { + void *none; + variable_declaration_list *var_stmt; + expression *expr; + for_or_for_in_statement *for_stmt; + const char *name; + function_declaration *fun_decl; + } + data; +} +statement; + +void parser_init (); +statement *parser_parse_statement (); + +#endif diff --git a/src/pretty-printer.c b/src/pretty-printer.c new file mode 100644 index 000000000..ddf69ceb1 --- /dev/null +++ b/src/pretty-printer.c @@ -0,0 +1,1475 @@ +/* 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 "pretty-printer.h" +#include "error.h" + +#include + +static int intendation; +static bool was_function_expression; +static bool was_subexpression; + +static statement_type prev_stmt; + +static void pp_expression (expression *); +static void pp_assignment_expression (assignment_expression *); +static void pp_member_expression (member_expression *); + +void +pp_reset () +{ + prev_stmt = STMT_EOF; + intendation = 0; +} + +void +pp_token (token tok) +{ + switch (tok.type) + { + case TOK_NAME: + printf ("IDENTIFIER (%s)\n", tok.data.name); + break; + + case TOK_STRING: + printf ("STRING (%s)\n", tok.data.str); + break; + + case TOK_KEYWORD: + pp_keyword (tok.data.kw); + break; + + case TOK_INT: + printf ("INTEGER (%d)\n", tok.data.num); + break; + + case TOK_FLOAT: + printf ("FLOAT (%f)\n", tok.data.fp_num); + break; + + case TOK_NULL: + printf ("NULL (null)\n"); + break; + + case TOK_BOOL: + if (tok.data.is_true) + printf ("BOOL (true)\n"); + else + printf ("BOOL (false)\n"); + break; + + + case TOK_OPEN_BRACE: + printf ("PUNC ({)\n"); + break; + + case TOK_CLOSE_BRACE: + printf ("PUNC (})\n"); + break; + + case TOK_OPEN_PAREN: + printf ("PUNC (()\n"); + break; + + case TOK_CLOSE_PAREN: + printf ("PUNC ())\n"); + break; + + case TOK_OPEN_SQUARE: + printf ("PUNC ([)\n"); + break; + + case TOK_CLOSE_SQUARE: + printf ("PUNC (])\n"); + break; + + + case TOK_DOT: + printf ("PUNC (.)\n"); + break; + + case TOK_SEMICOLON: + printf ("PUNC (;)\n"); + break; + + case TOK_COMMA: + printf ("PUNC (,)\n"); + break; + + case TOK_LESS: + printf ("PUNC (<)\n"); + break; + + case TOK_GREATER: + printf ("PUNC (>)\n"); + break; + + case TOK_LESS_EQ: + printf ("PUNC (<=)\n"); + break; + + + case TOK_GREATER_EQ: + printf ("PUNC (>=)\n"); + break; + + case TOK_DOUBLE_EQ: + printf ("PUNC (==)\n"); + break; + + case TOK_NOT_EQ: + printf ("PUNC (!=)\n"); + break; + + case TOK_TRIPLE_EQ: + printf ("PUNC (===)\n"); + break; + + case TOK_NOT_DOUBLE_EQ: + printf ("PUNC (!==)\n"); + break; + + + case TOK_PLUS: + printf ("PUNC (+)\n"); + break; + + case TOK_MINUS: + printf ("PUNC (-)\n"); + break; + + case TOK_MULT: + printf ("PUNC (*)\n"); + break; + + case TOK_MOD: + printf ("PUNC (%%)\n"); + break; + + case TOK_DOUBLE_PLUS: + printf ("PUNC (++)\n"); + break; + + case TOK_DOUBLE_MINUS: + printf ("PUNC (--)\n"); + break; + + + case TOK_LSHIFT: + printf ("PUNC (<<)\n"); + break; + + case TOK_RSHIFT: + printf ("PUNC (>>)\n"); + break; + + case TOK_RSHIFT_EX: + printf ("PUNC (>>>)\n"); + break; + + case TOK_AND: + printf ("PUNC (&)\n"); + break; + + case TOK_OR: + printf ("PUNC (|)\n"); + break; + + case TOK_XOR: + printf ("PUNC (^)\n"); + break; + + + case TOK_NOT: + printf ("PUNC (!)\n"); + break; + + case TOK_COMPL: + printf ("PUNC (~)\n"); + break; + + case TOK_DOUBLE_AND: + printf ("PUNC (&&)\n"); + break; + + case TOK_DOUBLE_OR: + printf ("PUNC (||)\n"); + break; + + case TOK_QUERY: + printf ("PUNC (?)\n"); + break; + + case TOK_COLON: + printf ("PUNC (:)\n"); + break; + + + case TOK_EQ: + printf ("PUNC (=)\n"); + break; + + case TOK_PLUS_EQ: + printf ("PUNC (+=)\n"); + break; + + case TOK_MINUS_EQ: + printf ("PUNC (-=)\n"); + break; + + case TOK_MULT_EQ: + printf ("PUNC (*=)\n"); + break; + + case TOK_MOD_EQ: + printf ("PUNC (%%=)\n"); + break; + + case TOK_LSHIFT_EQ: + printf ("PUNC (<<=)\n"); + break; + + + case TOK_RSHIFT_EQ: + printf ("PUNC (>>=)\n"); + break; + + case TOK_RSHIFT_EX_EQ: + printf ("PUNC (>>>=)\n"); + break; + + case TOK_AND_EQ: + printf ("PUNC (&=)\n"); + break; + + case TOK_OR_EQ: + printf ("PUNC (|=)\n"); + break; + + case TOK_XOR_EQ: + printf ("PUNC (^=)\n"); + break; + + + case TOK_DIV: + printf ("PUNC (/)\n"); + break; + + case TOK_DIV_EQ: + printf ("PUNC (/=)\n"); + break; + + case TOK_NEWLINE: + printf ("NEWLINE\n"); + break; + + default: + unreachable (); + + } +} + +void +pp_keyword (keyword kw) +{ + switch (kw) + { + case KW_NONE: + unreachable (); + break; + + case KW_RESERVED: + printf ("KEYWORD RESERVED\n"); + break; + + + case KW_BREAK: + printf ("KEYWORD (break)\n"); + break; + + case KW_CASE: + printf ("KEYWORD (case)\n"); + break; + + case KW_CATCH: + printf ("KEYWORD (catch)\n"); + break; + + case KW_CONTINUE: + printf ("KEYWORD (continue)\n"); + break; + + case KW_DEBUGGER: + printf ("KEYWORD (debugger)\n"); + break; + + case KW_DEFAULT: + printf ("KEYWORD (default)\n"); + break; + + case KW_DELETE: + printf ("KEYWORD (delete)\n"); + break; + + + case KW_DO: + printf ("KEYWORD (do)\n"); + break; + + case KW_ELSE: + printf ("KEYWORD (else)\n"); + break; + + case KW_FINALLY: + printf ("KEYWORD (finally)\n"); + break; + + case KW_FOR: + printf ("KEYWORD (for)\n"); + break; + + case KW_FUNCTION: + printf ("KEYWORD (function)\n"); + break; + + case KW_IF: + printf ("KEYWORD (if)\n"); + break; + + case KW_IN: + printf ("KEYWORD (in)\n"); + break; + + + case KW_INSTANCEOF: + printf ("KEYWORD (instanceof)\n"); + break; + + case KW_NEW: + printf ("KEYWORD (new)\n"); + break; + + case KW_RETURN: + printf ("KEYWORD (return)\n"); + break; + + case KW_SWITCH: + printf ("KEYWORD (switch)\n"); + break; + + case KW_THIS: + printf ("KEYWORD (this)\n"); + break; + + case KW_THROW: + printf ("KEYWORD (throw)\n"); + break; + + case KW_TRY: + printf ("KEYWORD (try)\n"); + break; + + + case KW_TYPEOF: + printf ("KEYWORD (typeof)\n"); + break; + + case KW_VAR: + printf ("KEYWORD (var)\n"); + break; + + case KW_VOID: + printf ("KEYWORD (void)\n"); + break; + + case KW_WHILE: + printf ("KEYWORD (while)\n"); + break; + + case KW_WITH: + printf ("KEYWORD (with)\n"); + break; + + default: + unreachable (); + + } +} + +static void +intend () +{ + for (int i = 0; i < intendation; i++) + putchar (' '); +} + +static void +pp_formal_parameter_list (formal_parameter_list *param_list) +{ + formal_parameter_list *list = param_list; + assert (param_list); + while (list) + { + printf ("%s", list->name); + if (list->next) + printf (", "); + list = list->next; + } +} + +static void +pp_function_declaration (function_declaration *func_decl) +{ + assert (func_decl); + printf ("function "); + if (func_decl->name) + printf ("%s ", func_decl->name); + putchar ('('); + if (func_decl->params) + pp_formal_parameter_list (func_decl->params); + printf (") "); + was_function_expression = true; +} + +static void +pp_literal (literal *lit) +{ + assert (lit); + switch (lit->type) + { + case LIT_NULL: + printf ("null"); + break; + + case LIT_BOOL: + printf ("%s", lit->data.is_true ? "true" : "false"); + break; + + case LIT_INT: + printf ("%d", lit->data.num); + break; + + case LIT_STR: + printf ("\"%s\"", lit->data.str); + break; + + default: + unreachable (); + } +} + +static void +pp_property_name (property_name *name) +{ + assert (name); + switch (name->type) + { + case PN_NAME: + printf ("%s", name->data.name); + break; + + case PN_STRING: + printf ("%s", name->data.str); + break; + + case PN_NUM: + printf ("%d", name->data.num); + break; + + default: + unreachable (); + } +} + +static void +pp_property_name_and_value (property_name_and_value *nav) +{ + assert (nav); + pp_property_name (nav->name); + printf (" : "); + pp_assignment_expression (nav->assign_expr); +} + +static void +pp_property_name_and_value_list (property_name_and_value_list *nav_list) +{ + property_name_and_value_list *list = nav_list; + assert (nav_list); + while (list) + { + pp_property_name_and_value (list->nav); + if (list->next) + printf (", "); + list = list->next; + } +} + +static void +pp_primary_expression (primary_expression *primary_expr) +{ + assert (primary_expr); + switch (primary_expr->type) + { + case PE_THIS: + printf ("this"); + break; + + case PE_NAME: + printf ("%s", primary_expr->data.name); + break; + + case PE_LITERAL: + pp_literal (primary_expr->data.lit); + break; + + case PE_ARRAY: + putchar ('['); + if (primary_expr->data.array_lit) + pp_expression (primary_expr->data.array_lit); + putchar (']'); + break; + + case PE_OBJECT: + putchar ('{'); + if (primary_expr->data.object_lit) + pp_property_name_and_value_list (primary_expr->data.object_lit); + putchar ('}'); + break; + + case PE_EXPR: + putchar ('('); + if (primary_expr->data.expr) + pp_expression (primary_expr->data.expr); + was_subexpression = true; + break; + + default: + unreachable (); + } +} + +static void +pp_member_expression_with_arguments (member_expression_with_arguments *member_expr) +{ + assert (member_expr); + printf ("new "); + pp_member_expression (member_expr->member_expr); + if (member_expr->args) + { + putchar (' '); + pp_expression (member_expr->args); + } +} + +static void +pp_member_expression_suffix (member_expression_suffix *suffix) +{ + assert (suffix); + switch (suffix->type) + { + case MES_INDEX: + putchar ('['); + pp_expression (suffix->data.index_expr); + putchar (']'); + break; + + case MES_PROPERTY: + putchar ('.'); + printf ("%s", suffix->data.name); + break; + + default: + unreachable (); + } +} + +static void +pp_member_expression_suffix_list (member_expression_suffix_list *suffix_list) +{ + member_expression_suffix_list *list = suffix_list; + assert (suffix_list); + while (list) + { + pp_member_expression_suffix (list->suffix); + list = list->next; + } +} + +static void +pp_member_expression (member_expression *member_expr) +{ + assert (member_expr); + switch (member_expr->type) + { + case ME_PRIMARY: + pp_primary_expression (member_expr->data.primary_expr); + break; + + case ME_FUNCTION: + pp_function_declaration (member_expr->data.function_expr); + break; + + case ME_ARGS: + pp_member_expression_with_arguments (member_expr->data.args); + break; + + default: + unreachable (); + } + if (member_expr->suffix_list) + pp_member_expression_suffix_list (member_expr->suffix_list); +} + +static void +pp_call_expression_suffix (call_expression_suffix *suffix) +{ + assert (suffix); + switch (suffix->type) + { + case CAS_ARGS: + putchar ('('); + pp_expression (suffix->data.args); + putchar (')'); + break; + + case CAS_INDEX: + putchar ('['); + pp_expression (suffix->data.index_expr); + putchar (']'); + break; + + case CAS_PROPERTY: + putchar ('.'); + printf ("%s", suffix->data.name); + break; + + default: + unreachable (); + } +} + +static void +pp_call_expression_suffix_list (call_expression_suffix_list *suffix_list) +{ + call_expression_suffix_list *list = suffix_list; + assert (suffix_list); + while (list) + { + pp_call_expression_suffix (list->suffix); + list = list->next; + } +} + +static void +pp_call_expression (call_expression *call_expr) +{ + assert (call_expr); + pp_member_expression (call_expr->member_expr); + printf (" ("); + if (call_expr->args) + pp_expression (call_expr->args); + printf (")"); + if (call_expr->suffix_list) + pp_call_expression_suffix_list (call_expr->suffix_list); +} + +static void +pp_new_expression (new_expression *new_expr) +{ + assert (new_expr); + switch (new_expr->type) + { + case NE_MEMBER: + pp_member_expression (new_expr->data.member_expr); + break; + + case NE_NEW: + printf ("new "); + pp_new_expression (new_expr->data.new_expr); + break; + + unreachable (); + } +} + +static void +pp_left_hand_side_expression (left_hand_side_expression *left_expr) +{ + assert (left_expr); + switch (left_expr->type) + { + case LHSE_CALL: + pp_call_expression (left_expr->data.call_expr); + break; + + case LHSE_NEW: + pp_new_expression (left_expr->data.new_expr); + break; + + default: + unreachable (); + } +} + +static void +pp_postfix_expression (postfix_expression *postfix_expr) +{ + assert (postfix_expr); + pp_left_hand_side_expression (postfix_expr->expr); + switch (postfix_expr->type) + { + case PE_INCREMENT: + printf ("++"); + break; + + case PE_DECREMENT: + printf ("--"); + break; + + default: + break; + } +} + +static void +pp_unary_expression (unary_expression *unary_expr) +{ + assert (unary_expr); + if (unary_expr->type != UE_POSTFIX) + putchar ('('); + switch (unary_expr->type) + { + case UE_POSTFIX: + pp_postfix_expression (unary_expr->data.postfix_expr); + break; + + case UE_DELETE: + printf ("delete "); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + case UE_VOID: + printf ("void "); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + case UE_TYPEOF: + printf ("typeof "); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + case UE_INCREMENT: + printf ("++"); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + case UE_DECREMENT: + printf ("--"); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + case UE_PLUS: + printf ("+"); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + case UE_MINUS: + printf ("-"); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + case UE_COMPL: + printf ("~"); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + case UE_NOT: + printf ("!"); + pp_unary_expression (unary_expr->data.unary_expr); + break; + + default: + unreachable (); + } + if (unary_expr->type != UE_POSTFIX) + putchar (')'); +} + +static void +pp_multiplicative_expression_list (multiplicative_expression_list *expr_list) +{ + multiplicative_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_unary_expression (list->unary_expr); + if (list->next) + { + switch (list->type) + { + case ME_MULT: + printf (" * "); + break; + + case ME_DIV: + printf (" / "); + break; + + case ME_MOD: + printf (" %% "); + break; + + default: + unreachable (); + } + } + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_additive_expression_list (additive_expression_list *expr_list) +{ + additive_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_multiplicative_expression_list (list->mult_expr); + if (list->next) + { + switch (list->type) + { + case AE_PLUS: + printf (" + "); + break; + + case AE_MINUS: + printf (" - "); + break; + + default: + unreachable (); + } + } + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_shift_expression_list (shift_expression_list *expr_list) +{ + shift_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_additive_expression_list (list->add_expr); + if (list->next) + { + switch (list->type) + { + case SE_LSHIFT: + printf (" << "); + break; + + case SE_RSHIFT: + printf (" >> "); + break; + + case SE_RSHIFT_EX: + printf (" >>> "); + break; + + default: + unreachable (); + } + } + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_relational_expression_list (relational_expression_list *expr_list) +{ + relational_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_shift_expression_list (list->shift_expr); + if (list->next) + { + switch (list->type) + { + case RE_LESS: + printf (" < "); + break; + + case RE_GREATER: + printf (" > "); + break; + + case RE_LESS_EQ: + printf (" <= "); + break; + + case RE_GREATER_EQ: + printf (" >= "); + break; + + case RE_INSTANCEOF: + printf (" instanceof "); + break; + + case RE_IN: + printf (" in "); + break; + + default: + unreachable (); + } + } + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_equality_expression_list (equality_expression_list *expr_list) +{ + equality_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_relational_expression_list (list->rel_expr); + if (list->next) + { + switch (list->type) + { + case EE_DOUBLE_EQ: + printf (" == "); + break; + + case EE_NOT_EQ: + printf (" != "); + break; + + case EE_TRIPLE_EQ: + printf (" === "); + break; + + case EE_NOT_DOUBLE_EQ: + printf (" !== "); + break; + + default: + unreachable (); + } + } + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_bitwise_and_expression_list (bitwise_and_expression_list *expr_list) +{ + bitwise_and_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_equality_expression_list (list->eq_expr); + if (list->next) + printf (" & "); + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_bitwise_xor_expression_list (bitwise_xor_expression_list *expr_list) +{ + bitwise_xor_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_bitwise_and_expression_list (list->and_expr); + if (list->next) + printf (" ^ "); + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_bitwise_or_expression_list (bitwise_or_expression_list *expr_list) +{ + bitwise_or_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_bitwise_xor_expression_list (list->xor_expr); + if (list->next) + printf (" | "); + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_logical_and_expression_list (logical_and_expression_list *expr_list) +{ + logical_and_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_bitwise_or_expression_list (list->or_expr); + if (list->next) + printf (" && "); + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_logical_or_expression_list (logical_or_expression_list *expr_list) +{ + logical_or_expression_list *list = expr_list; + assert (expr_list); + if (expr_list->next) + putchar ('('); + while (list) + { + pp_logical_and_expression_list (list->and_expr); + if (list->next) + printf (" || "); + list = list->next; + } + if (expr_list->next) + putchar (')'); +} + +static void +pp_conditional_expression (conditional_expression *cond_expr) +{ + assert (cond_expr); + pp_logical_or_expression_list (cond_expr->or_expr); + if (cond_expr->then_expr) + { + printf (" ? "); + pp_assignment_expression (cond_expr->then_expr); + } + if (cond_expr->else_expr) + { + printf (" : "); + pp_assignment_expression (cond_expr->else_expr); + } +} + +static void +pp_assignment_expression (assignment_expression *assign_expr) +{ + assert (assign_expr); + switch (assign_expr->type) + { + case AE_COND: + pp_conditional_expression (assign_expr->data.cond_expr); + return; + + case AE_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" = "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_MULT_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" *= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_DIV_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" /= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_MOD_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" %%= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_PLUS_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" += "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_MINUS_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" -= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_LSHIFT_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" <<= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_RSHIFT_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" >>= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_RSHIFT_EX_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" >>>= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_AND_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" &= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_OR_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" |= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + case AE_XOR_EQ: + pp_left_hand_side_expression (assign_expr->data.s.left_hand_expr); + printf (" ^= "); + pp_assignment_expression (assign_expr->data.s.assign_expr); + return; + + default: + unreachable (); + } +} + +static void +pp_expression (expression_list *expr_list) +{ + expression_list *list = expr_list; + assert (expr_list); + while (list) + { + pp_assignment_expression (list->assign_expr); + if (list->next) + printf (", "); + list = list->next; + } + + if (was_subexpression && !was_function_expression) + { + putchar (')'); + was_subexpression = false; + } +} + +static void +pp_variable_declaration (variable_declaration *var_decl) +{ + assert (var_decl); + printf ("%s", var_decl->name); + if (var_decl->ass_expr) + { + printf (" = "); + pp_assignment_expression (var_decl->ass_expr); + } +} + +static void +pp_variable_declaration_list (variable_declaration_list *decl_list) +{ + variable_declaration_list *list = decl_list; + assert (decl_list); + printf ("var "); + while (list) + { + pp_variable_declaration (list->var_decl); + if (list->next) + printf (", "); + list = list->next; + } +} + +static void +pp_for_in_statement_initializer_part (for_in_statement_initializer_part *init) +{ + assert (init); + if (init->is_decl) + { + printf ("var "); + pp_variable_declaration (init->data.decl); + } + else + pp_left_hand_side_expression (init->data.left_hand_expr); +} + +static void +pp_for_in_statement (for_in_statement *for_in_stmt) +{ + assert (for_in_stmt); + printf ("for ("); + pp_for_in_statement_initializer_part (for_in_stmt->init); + printf (" in "); + pp_expression (for_in_stmt->list_expr); + printf (") "); +} + +static void +pp_for_statement_initialiser_part (for_statement_initialiser_part *init) +{ + assert (init); + if (init->is_decl) + pp_variable_declaration_list (init->data.decl_list); + else + pp_expression (init->data.expr); +} + +static void +pp_for_statement (for_statement *for_stmt) +{ + assert (for_stmt); + printf ("for ("); + if (for_stmt->init) + pp_for_statement_initialiser_part (for_stmt->init); + printf ("; "); + if (for_stmt->limit) + pp_expression (for_stmt->limit); + printf ("; "); + if (for_stmt->incr) + pp_expression (for_stmt->incr); + printf (") "); +} + +static void +pp_for_or_for_in_statement (for_or_for_in_statement *for_or_for_in_stmt) +{ + assert (for_or_for_in_stmt); + if (for_or_for_in_stmt->is_for_in) + pp_for_in_statement (for_or_for_in_stmt->data.for_in_stmt); + else + pp_for_statement (for_or_for_in_stmt->data.for_stmt); +} + +void +pp_statement (statement *stmt) +{ + was_function_expression = false; + was_subexpression = false; + assert (stmt); + + if (prev_stmt == STMT_BLOCK_END) + { + if (stmt->type == STMT_EMPTY) + { + printf (";\n"); + prev_stmt = stmt->type; + return; + } + else + putchar ('\n'); + } + + switch (stmt->type) + { + case STMT_BLOCK_START: + printf ("{\n"); + intendation += 2; + break; + + case STMT_BLOCK_END: + intendation -= 2; + intend (); + printf ("}"); + break; + + case STMT_VARIABLE: + intend (); + pp_variable_declaration_list (stmt->data.var_stmt); + break; + + case STMT_EMPTY: + printf (";\n"); + break; + + case STMT_IF: + intend (); + printf ("if ("); + pp_expression (stmt->data.expr); + printf (") "); + break; + + case STMT_ELSE: + intend (); + printf ("else "); + break; + + case STMT_ELSE_IF: + intend (); + printf ("else if("); + pp_expression (stmt->data.expr); + printf (") "); + break; + + case STMT_DO: + intend (); + printf ("do "); + break; + + case STMT_WHILE: + intend (); + printf ("while ("); + pp_expression (stmt->data.expr); + printf (") "); + break; + + case STMT_FOR_OR_FOR_IN: + intend (); + pp_for_or_for_in_statement (stmt->data.for_stmt); + break; + + case STMT_CONTINUE: + intend (); + printf ("continue\n"); + break; + + case STMT_BREAK: + intend (); + printf ("break\n"); + break; + + case STMT_RETURN: + intend (); + printf ("return "); + if (stmt->data.expr) + pp_expression (stmt->data.expr); + if (!was_function_expression) + printf (";\n"); + break; + + case STMT_WITH: + intend (); + printf ("with ("); + pp_expression (stmt->data.expr); + printf (") "); + break; + + case STMT_LABELLED: + intend (); + printf ("%s:\n", stmt->data.name); + break; + + case STMT_SWITCH: + intend (); + printf ("switch ("); + pp_expression (stmt->data.expr); + printf (") "); + break; + + case STMT_CASE: + intend (); + printf ("case "); + pp_expression (stmt->data.expr); + printf (":\n"); + break; + + case STMT_THROW: + intend (); + printf ("throw "); + pp_expression (stmt->data.expr); + printf (";\n"); + break; + + case STMT_TRY: + intend (); + printf ("try "); + break; + + case STMT_CATCH: + intend (); + printf ("catch ("); + pp_expression (stmt->data.expr); + printf (") "); + break; + + case STMT_FINALLY: + intend (); + printf ("finally "); + break; + + case STMT_EXPRESSION: + intend (); + pp_expression (stmt->data.expr); + break; + + case STMT_SUBEXPRESSION_END: + putchar (')'); + break; + + case STMT_FUNCTION: + intend (); + pp_function_declaration (stmt->data.fun_decl); + break; + + default: + unreachable (); + } + + prev_stmt = stmt->type; +} + +void pp_finish () +{ + if (prev_stmt == STMT_BLOCK_END) + putchar ('\n'); +} diff --git a/src/pretty-printer.h b/src/pretty-printer.h new file mode 100644 index 000000000..b3cb5199e --- /dev/null +++ b/src/pretty-printer.h @@ -0,0 +1,28 @@ +/* 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. + */ + +#ifndef PRETTY_PRINTER_H +#define PRETTY_PRINTER_H + +#include "lexer.h" +#include "parser.h" + +void pp_reset (); +void pp_finish (); +void pp_token (token); +void pp_keyword (keyword); +void pp_statement (statement *); + +#endif \ No newline at end of file