
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) NGINX, Inc.
 */

#ifndef _NJS_LEXER_H_INCLUDED_
#define _NJS_LEXER_H_INCLUDED_


typedef enum {
    NJS_TOKEN_ERROR = -1,
    NJS_TOKEN_ILLEGAL = 0,

    NJS_TOKEN_END,
    NJS_TOKEN_SPACE,
    NJS_TOKEN_LINE_END,

    NJS_TOKEN_DOUBLE_QUOTE,
    NJS_TOKEN_SINGLE_QUOTE,

    NJS_TOKEN_OPEN_PARENTHESIS,
    NJS_TOKEN_CLOSE_PARENTHESIS,
    NJS_TOKEN_OPEN_BRACKET,
    NJS_TOKEN_CLOSE_BRACKET,
    NJS_TOKEN_OPEN_BRACE,
    NJS_TOKEN_CLOSE_BRACE,

    NJS_TOKEN_COMMA,
    NJS_TOKEN_DOT,
    NJS_TOKEN_ELLIPSIS,
    NJS_TOKEN_SEMICOLON,

    NJS_TOKEN_COLON,
    NJS_TOKEN_CONDITIONAL,

    NJS_TOKEN_COMMENT,

    NJS_TOKEN_ASSIGNMENT,
    NJS_TOKEN_ARROW,
    NJS_TOKEN_ADDITION_ASSIGNMENT,
    NJS_TOKEN_SUBTRACTION_ASSIGNMENT,
    NJS_TOKEN_MULTIPLICATION_ASSIGNMENT,
    NJS_TOKEN_EXPONENTIATION_ASSIGNMENT,
    NJS_TOKEN_DIVISION_ASSIGNMENT,
    NJS_TOKEN_REMAINDER_ASSIGNMENT,
    NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT,
    NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT,
    NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT,
    NJS_TOKEN_BITWISE_OR_ASSIGNMENT,
    NJS_TOKEN_BITWISE_XOR_ASSIGNMENT,
    NJS_TOKEN_BITWISE_AND_ASSIGNMENT,

    NJS_TOKEN_INCREMENT,
    NJS_TOKEN_DECREMENT,
    NJS_TOKEN_POST_INCREMENT,
    NJS_TOKEN_POST_DECREMENT,

#define NJS_TOKEN_LAST_ASSIGNMENT   NJS_TOKEN_POST_DECREMENT

    NJS_TOKEN_EQUAL,
    NJS_TOKEN_STRICT_EQUAL,
    NJS_TOKEN_NOT_EQUAL,
    NJS_TOKEN_STRICT_NOT_EQUAL,

    NJS_TOKEN_ADDITION,
    NJS_TOKEN_UNARY_PLUS,

    NJS_TOKEN_SUBTRACTION,
    NJS_TOKEN_UNARY_NEGATION,

    NJS_TOKEN_MULTIPLICATION,

    NJS_TOKEN_EXPONENTIATION,

    NJS_TOKEN_DIVISION,

    NJS_TOKEN_REMAINDER,

    NJS_TOKEN_LESS,
    NJS_TOKEN_LESS_OR_EQUAL,
    NJS_TOKEN_LEFT_SHIFT,

    NJS_TOKEN_GREATER,
    NJS_TOKEN_GREATER_OR_EQUAL,
    NJS_TOKEN_RIGHT_SHIFT,
    NJS_TOKEN_UNSIGNED_RIGHT_SHIFT,

    NJS_TOKEN_BITWISE_OR,
    NJS_TOKEN_LOGICAL_OR,

    NJS_TOKEN_BITWISE_XOR,

    NJS_TOKEN_BITWISE_AND,
    NJS_TOKEN_LOGICAL_AND,

    NJS_TOKEN_BITWISE_NOT,
    NJS_TOKEN_LOGICAL_NOT,

    NJS_TOKEN_COALESCE,

    NJS_TOKEN_IN,
    NJS_TOKEN_OF,
    NJS_TOKEN_INSTANCEOF,
    NJS_TOKEN_TYPEOF,
    NJS_TOKEN_VOID,
    NJS_TOKEN_NEW,
    NJS_TOKEN_DELETE,
    NJS_TOKEN_YIELD,

    NJS_TOKEN_DIGIT,
    NJS_TOKEN_LETTER,

#define NJS_TOKEN_FIRST_CONST     NJS_TOKEN_NULL

    NJS_TOKEN_NULL,
    NJS_TOKEN_NUMBER,
    NJS_TOKEN_TRUE,
    NJS_TOKEN_UNDEFINED,
    NJS_TOKEN_FALSE,
    NJS_TOKEN_STRING,

#define NJS_TOKEN_LAST_CONST      NJS_TOKEN_STRING

    NJS_TOKEN_ESCAPE_STRING,
    NJS_TOKEN_UNTERMINATED_STRING,
    NJS_TOKEN_NAME,

    NJS_TOKEN_OBJECT,
    NJS_TOKEN_OBJECT_VALUE,
    NJS_TOKEN_PROPERTY,
    NJS_TOKEN_PROPERTY_INIT,
    NJS_TOKEN_PROPERTY_DELETE,
    NJS_TOKEN_PROPERTY_GETTER,
    NJS_TOKEN_PROPERTY_SETTER,
    NJS_TOKEN_PROTO_INIT,

    NJS_TOKEN_ARRAY,

    NJS_TOKEN_GRAVE,
    NJS_TOKEN_TEMPLATE_LITERAL,

    NJS_TOKEN_FUNCTION,
    NJS_TOKEN_FUNCTION_DECLARATION,
    NJS_TOKEN_FUNCTION_EXPRESSION,
    NJS_TOKEN_FUNCTION_CALL,
    NJS_TOKEN_METHOD_CALL,
    NJS_TOKEN_ARGUMENT,
    NJS_TOKEN_RETURN,

    NJS_TOKEN_ASYNC_FUNCTION,
    NJS_TOKEN_ASYNC_FUNCTION_DECLARATION,
    NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION,

    NJS_TOKEN_REGEXP,

    NJS_TOKEN_EXTERNAL,

    NJS_TOKEN_STATEMENT,
    NJS_TOKEN_BLOCK,
    NJS_TOKEN_VAR,
    NJS_TOKEN_IF,
    NJS_TOKEN_ELSE,
    NJS_TOKEN_BRANCHING,
    NJS_TOKEN_WHILE,
    NJS_TOKEN_DO,
    NJS_TOKEN_FOR,
    NJS_TOKEN_FOR_IN,
    NJS_TOKEN_BREAK,
    NJS_TOKEN_CONTINUE,
    NJS_TOKEN_SWITCH,
    NJS_TOKEN_CASE,
    NJS_TOKEN_DEFAULT,
    NJS_TOKEN_WITH,
    NJS_TOKEN_TRY,
    NJS_TOKEN_CATCH,
    NJS_TOKEN_FINALLY,
    NJS_TOKEN_THROW,

    NJS_TOKEN_THIS,
    NJS_TOKEN_ARGUMENTS,
    NJS_TOKEN_EVAL,

    NJS_TOKEN_IMPORT,
    NJS_TOKEN_EXPORT,

    NJS_TOKEN_TARGET,

    NJS_TOKEN_FROM,

    NJS_TOKEN_META,

    NJS_TOKEN_AWAIT,
    NJS_TOKEN_ASYNC,
    NJS_TOKEN_CLASS,
    NJS_TOKEN_CONST,
    NJS_TOKEN_DEBUGGER,
    NJS_TOKEN_ENUM,
    NJS_TOKEN_EXTENDS,
    NJS_TOKEN_IMPLEMENTS,
    NJS_TOKEN_INTERFACE,
    NJS_TOKEN_LET,
    NJS_TOKEN_PACKAGE,
    NJS_TOKEN_PRIVATE,
    NJS_TOKEN_PROTECTED,
    NJS_TOKEN_PUBLIC,
    NJS_TOKEN_STATIC,
    NJS_TOKEN_SUPER,

    NJS_TOKEN_RESERVED,
} njs_token_type_t;


typedef enum {
    NJS_KEYWORD_TYPE_UNDEF    = 0,
    NJS_KEYWORD_TYPE_RESERVED = 1,
    NJS_KEYWORD_TYPE_KEYWORD  = 2
} njs_keyword_type_t;


typedef struct {
    njs_token_type_t                type;
    njs_bool_t                      reserved;
} njs_keyword_t;


typedef struct {
    const char                      *key;
    const njs_keyword_t             *value;

    size_t                          length;
    size_t                          next;
} njs_lexer_keyword_entry_t;


typedef struct {
    njs_token_type_t                type:16;
    njs_keyword_type_t              keyword_type;
    uint32_t                        line;
    uint32_t                        atom_id;
    njs_str_t                       text;
    double                          number;
    njs_queue_link_t                link;
} njs_lexer_token_t;


typedef struct {
    njs_lexer_token_t               *token;
    njs_queue_t                     preread; /*  of njs_lexer_token_t */

    u_char                          *prev_start;
    njs_token_type_t                prev_type:16;
    njs_token_type_t                last_type:16;

    uint32_t                        line;
    njs_str_t                       file;

    njs_vm_t                        *vm;

    u_char                          *start;
    u_char                          *end;

#define NJS_INITIAL_IN_STACK_SIZE 128
    uint8_t                        *in_stack;
    njs_int_t                       in_stack_ptr;
    njs_int_t                       in_stack_size;
} njs_lexer_t;


njs_int_t njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file,
    u_char *start, u_char *end);

njs_lexer_token_t *njs_lexer_token(njs_lexer_t *lexer,
    njs_bool_t with_end_line);
njs_lexer_token_t *njs_lexer_peek_token(njs_lexer_t *lexer,
    njs_lexer_token_t *current, njs_bool_t with_end_line);
void njs_lexer_consume_token(njs_lexer_t *lexer, unsigned length);
njs_int_t njs_lexer_make_token(njs_lexer_t *lexer, njs_lexer_token_t *token);
njs_int_t njs_lexer_in_stack_init(njs_lexer_t *lexer);
njs_int_t njs_lexer_in_stack_push(njs_lexer_t *lexer);
void njs_lexer_in_stack_pop(njs_lexer_t *lexer);
void njs_lexer_in_fail_set(njs_lexer_t *lexer, njs_int_t flag);
njs_int_t njs_lexer_in_fail_get(njs_lexer_t *lexer);


const njs_lexer_keyword_entry_t *njs_lexer_keyword(const u_char *key,
    size_t length);
njs_int_t njs_lexer_keywords(njs_arr_t *array);


njs_inline njs_bool_t
njs_lexer_token_is_keyword(njs_lexer_token_t *token)
{
    return token->keyword_type & NJS_KEYWORD_TYPE_KEYWORD;
}


njs_inline njs_bool_t
njs_lexer_token_is_reserved(njs_lexer_token_t *token)
{
    return token->keyword_type & NJS_KEYWORD_TYPE_RESERVED;
}


njs_inline njs_bool_t
njs_lexer_token_is_name(njs_lexer_token_t *token)
{
    return token->type == NJS_TOKEN_NAME
           || (!njs_lexer_token_is_reserved(token)
               && njs_lexer_token_is_keyword(token));
}


njs_inline njs_bool_t
njs_lexer_token_is_identifier_name(njs_lexer_token_t *token)
{
    return token->type == NJS_TOKEN_NAME || njs_lexer_token_is_keyword(token);
}


njs_inline njs_bool_t
njs_lexer_token_is_binding_identifier(njs_lexer_token_t *token)
{
    switch (token->type) {
    case NJS_TOKEN_NAME:
    case NJS_TOKEN_YIELD:
    case NJS_TOKEN_AWAIT:
        return 1;

    default:
        return (!njs_lexer_token_is_reserved(token)
                && njs_lexer_token_is_keyword(token));
    };
}


njs_inline njs_bool_t
njs_lexer_token_is_label_identifier(njs_lexer_token_t *token)
{
    return njs_lexer_token_is_binding_identifier(token);
}


njs_inline njs_bool_t
njs_lexer_token_is_identifier_reference(njs_lexer_token_t *token)
{
    return njs_lexer_token_is_binding_identifier(token);
}

#endif /* _NJS_LEXER_H_INCLUDED_ */
