# Copyright (C) 2008, The Perl Foundation.
# $Id: /mirror/trunk/languages/ecmascript/src/parser/grammar.pg 35360 2009-01-10T04:08:55.034776Z tewk  $

## ECMAscript grammar, based on the grammar as defined in the
## reference manual.
##
## See "ECMAScript Language Specification"
## Standard ECMA-262, 3rd edition, December 1999

grammar JS::Grammar is PCT::Grammar;

token TOP {
    {*}                            #= open
    <source_element>*
    [ $ || <panic: Syntax error> ]
    {*}                            #= close
}

rule source_element {
    | <function_declaration> {*}   #= function_declaration
    | <statement> {*}              #= statement
}

##
## functions
##

rule function_declaration {
    'function' <identifier> <function_common>
    {*}
}

rule function_expression {
    'function' <identifier>? <function_common>
    {*}
}

rule function_common {
    '(' <formal_parameter_list> ')'
    <block>
    {*}
}

rule formal_parameter_list {
    [ <identifier> [',' <identifier>]* ]?
    {*}
}

##
## statements
##

rule statement {
##tewk  added function_expression, don't know if it should go here though
    | <function_expression> {*}   #= function_expression
    | <expression_statement> {*}  #= expression_statement
    | <block> {*}                 #= block
    | <variable_statement> {*}    #= variable_statement
    | <empty_statement> {*}       #= empty_statement
    | <if_statement> {*}          #= if_statement
    | <while_statement> {*}       #= while_statement
    | <do_while_statement> {*}    #= do_while_statement
    | <for1_statement> {*}        #= for1_statement
    | <for2_statement> {*}        #= for2_statement
    | <for3_statement> {*}        #= for3_statement
    | <for4_statement> {*}        #= for4_statement
    | <continue_statement> {*}    #= continue_statement
    | <break_statement> {*}       #= break_statement
    | <return_statement> {*}      #= return_statement
    #| <with_statement> {*}        #= with_statement
    | <labelled_statement> {*}    #= labelled_statement
    #| <switch_statement> {*}      #= switch_statement
    | <throw_statement> {*}       #= throw_statement
    | <try_statement> {*}         #= try_statement
}


rule statements {
    <statement>*
    {*}
}

rule block {
    '{' <statements> '}'
    {*}
}

rule if_statement {
    'if' '(' <expression> ')' <statement> ['else' $<else>=<statement>]?
    {*}
}

rule do_while_statement {
    'do' <statement> 'while' '(' <expression> ')' ';'
    {*}
}

rule while_statement {
    'while' '(' <expression> ')' <statement>
    {*}
}

rule for1_statement {
    'for' '(' [$<init>=<expression>]?
    ';' [$<cond>=<expression>]?
    ';' [$<step>=<expression>]? ')'
    <statement>
    {*}
}

rule for2_statement {
    'for' '(' 'var' <variable_declaration> 'in' <expression> ')'
    <statement>
    {*}
}

rule for3_statement {
    'for' '(' <lhs_expression> 'in' <expression> ')'
    <statement>
    {*}
}

rule for4_statement {
    'for' '(' 'var' $<init>=<variable_declaration_list>
    ';' [$<cond>=<expression>]?
    ';' [$<step>=<expression>]? ')'
    <statement>
    {*}
}

rule continue_statement {
    'continue' [\N <identifier>]? ';'
    {*}
}

rule break_statement {
    'break' [\N <identifier>]? ';'
    {*}
}

rule labelled_statement {
    <identifier> ':' <statement>
    {*}
}

rule try_statement {
    'try' <block>
    [
    | <catch> <finally>
    | <finally>
    | <catch>
    ]
    {*}
}

rule catch {
    'catch' '(' <identifier> ')' <block>
    {*}
}

rule finally {
    'finally' <block>
    {*}
}

token throw_statement {
    'throw' \N <expression> ';'
    {*}
}

rule variable_statement {
    'var' <variable_declaration_list> ';'
    {*}
}

rule variable_declaration_list {
    <variable_declaration> [',' <variable_declaration>]*
    {*}
}

rule variable_declaration {
    <identifier> ['=' <assignment_expression>]?
    {*}
}

rule empty_statement {
    ';' {*}
}

rule expression_statement {
    ##<!['{'|'function']> ## is this necessary? isn't this fixed automatically by PGE?
    #<!before \w>
    #<!['{'|'function']>
    <expression> ';'
    {*}
}

token return_statement {
    'return' [\N <expression>]?  ';'
    {*}
}

rule switch_statement {
    'switch' '(' <expression> ')' <case_block>
    {*}
}

rule case_block {
    '{'
    <case_clause>*
    [ <default_clause>
      <case_clause>*
    ]?
    '}'
}

rule case_clause {
    'case' <expression> ':' <statements>
    {*}
}

rule default_clause {
    'default' ':' <statements>
    {*}
}

rule with_statement {
    'with' '(' <expression> ')' <statement>
    {*}
}


##
## expressions
##



rule arguments {
    '(' [ <assignment_expression> [',' <assignment_expression>]* ]? ')'
    {*}
}

rule primary_expression {
    | <this> {*}                #= this
    | <literal> {*}             #= literal
    | <array_literal> {*}       #= array_literal
    | <identifier> {*}          #= identifier
    | <object_literal> {*}      #= object_literal
    | '(' <expression> ')' {*}  #= expression
    | <regular_expression_literal> {*} #= regular_expression_literal
}

#RegularExpressionLiteral :: See section 7.8.5
#/ RegularExpressionBody / RegularExpressionFlags
token regular_expression_literal {
  '/' <regular_expression_body> '/' <regular_expression_flags>? {*}
}
#RegularExpressionBody :: See section 7.8.5
#RegularExpressionFirstChar RegularExpressionChars
token regular_expression_body {
  <regular_expression_first_char> <regular_expression_char>*
}

#RegularExpressionChars :: See section 7.8.5
#[empty]
#RegularExpressionChars RegularExpressionChar

#RegularExpressionFirstChar :: See section 7.8.5
#NonTerminator but not * or \ or /
#BackslashSequence
token regular_expression_first_char {
  | <-[\n*\\/]>
  | '\\' \N
}

#RegularExpressionChar :: See section 7.8.5
#NonTerminator but not \ or /
#BackslashSequence
token regular_expression_char {
  | <-[\n\\/]>
  | '\\' \N
}

#BackslashSequence :: See section 7.8.5
#\ NonTerminator
#token backslash_sequence {
#  '\\' \N
#}
#
#token NonTerminator :: See section 7.8.5
#SourceCharacter but not LineTerminator
#token non_terminator {
#  \N
#}

#RegularExpressionFlags :: See section 7.8.5
#[empty]
#RegularExpressionFlags IdentifierPart
token regular_expression_flags {
  <identifier_part>+
}

token this { 'this' {*} }

rule array_literal {
##    '[' <elision>? <element_list>? [',' <elision>]?  ']'
    '[' <assignment_expression> [',' <assignment_expression>]* ']'
    {*}
}

rule element_list {
    <elision>? <assignment_expression> [',' <elision>? <assignment_expression>]*
    {*}
}

rule elision {
    [$<comma>=',']+ {*}
}

token identifier {
    <!reserved_word>
    <alpha> [<alpha>|<digit>|_]*
    {*}
}

token <identifier_part> {
    [<alpha>|<digit>|_]
}

token literal {
    | <str_literal> {*}            #= str_literal
    | <numeric_literal> {*}        #= numeric_literal
    | <builtin_literal> {*}        #= builtin_literal
}


token builtin_literal {
    | <true> {*}   #= true
    | <false> {*}  #= false
    | <null> {*}   #= null
}

token true { 'true' {*} }
token false { 'false' {*} }
token null { 'null' {*} }

rule object_literal {
    '{' [ <property> [',' <property> ]* [',']? ]?  '}'
    {*}
}

rule property {
    <property_name> ':' <assignment_expression>
    {*}
}

token property_name {
    | <identifier> {*}       #= identifier
    | <str_literal> {*}      #= str_literal
    | <numeric_literal> {*}  #= numeric_literal
}

token numeric_literal {
    | <hex_integer_literal> {*}    #= hex_integer_literal
    | <decimal_literal> {*}        #= decimal_literal
}

token hex_integer_literal {
    0<[xX]><hex_digits>+ {*}
}

token hex_digits { <[0..9 a..f A..F]> }

token decimal_literal {
    | <floating_point_number> {*}  #= floating_point_number
    | <integer_number> {*}         #= integer_number
}

token floating_point_number {
    [
    | <decimal_integer_literal> '.' <digit>* <exponent_part>?
    | '.' <digit>+ <exponent_part>?
    ]
    {*}
}

token integer_number {
    <decimal_integer_literal> <exponent_part>?
    {*}
}

token decimal_integer_literal {
    0 | <[1..9]> <digit>*
}

token exponent_part { <[eE]> <[+\-]> <digit>+ }

token str_literal {
    [
    | '"' <string_literal: '"'> '"'
    | \' <string_literal: '\''> \'
    ]
    {*}
}


token keyword {
    [ break  | case     | catch    | continue | default  | delete
    | do     | else     | finally  | for      | function | if
    | in     | instance | new      | return   | switch   | this
    | throw  | try      | typeof   | var      | void     | while
    | with
    ] >>
}

token future_reserved_word {
    [ abstract | enum       | int       | short
    | boolean  | export     | interface | static
    | byte     | extends    | long      | super
    | char     | final      | native    | synchronized
    | class    | float      | package   | throws
    | const    | goto       | private   | transient
    | debugger | implements | protected | volatile
    ] >>
}

token reserved_word {
    <keyword> | <future_reserved_word>
}

token singlelinecomment { '//' \N* }    # end of line comment like Unix shell and C++

token multilinecomment  { '/*' .*?  '*/' }         # C-like multiline comment

token ws_all {
    | \h
    | \v
    | <singlelinecomment>
    | <multilinecomment>
}

# whitespace rule used implicity by rules
token ws {
  | <?{{  $P0 = get_global '$!ws'
          if null $P0 goto noshort
          $P1 = $P0.'to'()
          $P2 = match.'to'()
          if $P1 != $P2 goto noshort
          .return (1)
        noshort:
          set_global '$!ws', match
          .return (0)
  }}>
  | <!ww> <ws_all>+
  | <ws_all>*
}

#### whitespace
#token ws {
#    | <.whitespace>
#    | <.newline>
#    | <.comment>
#}

#token whitespace {
#    | <[\t\v\f\ ]>
#    | \nbsp
#    | \usp #unicode sp
#}

#token newline {
#    | \r?\n
#    | \LS
#    | \PS
#}

#token comment {
#    <.singlelinecomment>
#    <.multilinecomment>
#}


## comments

#token multilinecomment {
#    '/*' .*? '*/'
#}

#token singlelinecomment {
#    '//' \N* \n
#}

rule expression {
    <assignment_expression> [',' <assignment_expression>]*
    {*}
}

#rule assignment_expression {
#   [<lhs_expression> <assignop>]* <conditional_expression>
#    {*}
#}

#token assignop {
#    $<op>=['='|'*='|'/='|'%='|'+='|'-='|'<<='|'>>='|'>>>='|'&='|'^='|'|=']
#}

#rule conditional_expression {
#    <logical_or_expression>
#    ['?' $<then>=<assignment_expression>
#     ':' $<else>=<assignment_expression>]?
#    {*}
#}

rule 'assignment_expression' is optable { ... }

proto 'infix:=' is precedence('1') is pasttype('copy') is lvalue(1)     { ... }

proto 'infix:+=' is equiv('infix:=') { ... } 
proto 'infix:-=' is equiv('infix:=') { ... } 
proto 'infix:/=' is equiv('infix:=') is pirop('div')        { ... } 
proto 'infix:*=' is equiv('infix:=') is pirop('mul')        { ... } 
proto 'infix:%=' is equiv('infix:=') is pirop('mul')        { ... } 
proto 'infix:|=' is equiv('infix:=')    { ... } 
proto 'infix:&=' is equiv('infix:=')    { ... } 
proto 'infix:~=' is equiv('infix:=')    { ... } 
proto infix:«>>=» is equiv('infix:=') is pirop('rsh')       { ... } 
proto infix:«<<=» is equiv('infix:=') is pirop('lsh')       { ... } 
proto infix:«>>>=» is equiv('infix:=') is pirop('rsh')       { ... } 
proto 'ternary:? :' is tighter('infix:=') is pirop('if')      { ... } 

proto 'infix:||'  is tighter('ternary:? :') is pasttype('unless') { ... }

proto 'infix:&&'  is tighter('infix:||') is pasttype('if') { ... }

proto 'infix:|'   is tighter('infix:&&') { ... }

proto 'infix:^'   is tighter('infix:|')  { ... }

proto 'infix:&'   is tighter('infix:^')  { ... }

proto 'infix:!='  is tighter('infix:&')  { ... }
proto 'infix:=='  is equiv('infix:!=')   { ... }
proto 'infix:===' is equiv('infix:!=')   { ... }
proto 'infix:!==' is equiv('infix:!=')   { ... }

proto infix:«<»   is tighter('infix:!=') { ... }
proto infix:«>»   is equiv(infix:«<»)    { ... }
proto infix:«<=»  is equiv(infix:«<»)    { ... }
proto infix:«>=»  is equiv(infix:«<»)    { ... }
proto 'infix:instanceof' is equiv(infix:«<») { ... }
proto 'infix:in'  is equiv(infix:«<»)    { ... }

proto infix:«<<»  is tighter(infix:«<»)  { ... }
proto infix:«>>»  is equiv(infix:«<<»)   { ... }
proto infix:«>>>» is equiv(infix:«<<»)   { ... }

proto 'infix:+' is tighter(infix:«<<») is pirop('add') { ... }
proto 'infix:-' is equiv('infix:+')    is pirop('sub') { ... }

proto 'infix:*' is tighter('infix:+')  is pirop('mul') { ... }
proto 'infix:/' is equiv('infix:*')    is pirop('div') { ... }
proto 'infix:%' is equiv('infix:*')    is pirop('mod') { ... }


rule unary_expression {
    <unop>* <postfix_expression>
    {*}
}

rule unop { $<op>=['delete'|'void'|'typeof'|'++'|'--'|'+'|'-'|'~'|'!'] {*} }


proto term: is tighter(infix:<||>) is parsed(&unary_expression) { ... }

rule postfix_expression {
    # how to handle the requirement of NO linebreak? \N doesn't seem to work.
    <lhs_expression> [ $<postfixop>=['++'|'--'] ]?
    {*}
}

rule lhs_expression {
    | <call_expression> {*} #= call_expression
    | <new_expression> {*}  #= new_expression
}

rule new_expression {
    [$<sym>='new']* <member_expression> <arguments>?
    {*}
}

rule member_expression {
    <member_prefix> <member_suffix>* {*}
}

rule member_prefix {
    | <primary_expression> {*}      #= primary_expression
    | <function_expression> {*}     #= function_expression
}

rule member_suffix {
    | '[' <expression> ']' {*} #= expression
    | <identifier_field> {*}   #= identifier_field
}

rule identifier_field {
    '.' <identifier> {*}
}

rule call_expression {
    <member_expression> <arguments> <post_call_expr>* {*}
}

rule post_call_expr {
    | <arguments>     {*}   #= arguments
    | <member_suffix> {*}   #= member_suffix
}


## Grammar rules converted directly from the ECMA-script reference manual, 3rd edition.
## This makes converting the whole thing to an optable easier.
##
## These rules are handled by the operator table (bottom-up parsing method).
##
#
#rule logical_or_expression {
#    <logical_and_expression> ['||' <logical_and_expression>]*
#}
#
#rule logical_and_expression {
#    <bitwise_or_expression> ['&&' <bitwise_or_expression>]*
#}
#
#rule bitwise_or_expression {
#    <bitwise_xor_expression> ['|' <bitwise_xor_expression>]*
#}
#
#rule bitwise_xor_expression {
#    <bitwise_and_expression> ['^' <bitwise_and_expression>]*
#}
#
#rule bitwise_and_expression {
#    <equality_expression> ['&' <equality_expression>]*
#}
#
#rule equality_expression {
#    <relational_expression> [<eqop> <relational_expression>]*
#}
#
#token eqop { '==' | '!=' | '===' | '!==' }
#
#rule relational_expression {
#    <shift_expression> [<relop> <shift_expression>]*
#}
#
#token relop { '<' | '>' | '<=' | '>=' | 'instanceof' }
#
#rule shift_expression {
#    <additive_expression> [<shiftop> <additive_expression>]*
#}
#
#token shiftop { '<<' | '>>' | '>>>' }
#
#rule additive_expression {
#    <multiplicative_expression> [<addop> <multiplicative_expression>]*
#}
#
#token addop { '+' | '-' }
#
#rule multiplicative_expression {
#    <unary_expression> [<mulop> <unary_expression>]*
#}
#
#token mulop { '*' | '/' | '%' }
#
