#! nqp
# Copyright (C) 2010, Parrot Foundation.
=begin overview

This is the grammar for Squaak in Perl 6 rules.

=end overview

grammar Squaak::Grammar is HLL::Grammar;

token begin_TOP {
    <?>
}

token TOP {
    <.begin_TOP>
    <statementlist>
    [ $ || <.panic: "Syntax error"> ]
}

## Lexer items

# This <ws> rule treats # as "comment to eol".
token ws {
    <!ww>
    [ '#' \N* \n? | \s+ ]*
}

## Statements

rule statementlist {
    <stat_or_def>*
}

rule stat_or_def {
    | <statement>
    | <sub_definition>
}

rule sub_definition {
    'sub' <identifier> <parameters>
    <statement>*
    'end'
}

rule parameters {
   '(' [<identifier> ** ',']? ')'
}

proto rule statement { <...> }

rule statement:sym<assignment> {
    <primary> '=' <EXPR>
}

rule statement:sym<do> {
    <sym> <block> 'end'
}

rule statement:sym<for> {
    <sym> <for_init> ',' <EXPR> <step>?
    'do' <statement>* 'end'
}

rule step {
    ',' <EXPR>
}

rule for_init {
    'var' <identifier> '=' <EXPR>
}

rule statement:sym<if> {
    <sym> <EXPR> 'then' $<then>=<block>
    ['else' $<else>=<block> ]?
    'end'
}

rule statement:sym<sub_call> {
    <primary> <arguments>
}

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

rule statement:sym<throw> {
    <sym> <EXPR>
}

rule statement:sym<try> {
    <sym> $<try>=<block>
    'catch' <exception>
    $<catch>=<block>
    'end'
}

rule exception {
    <identifier>
}

rule statement:sym<var> {
    <sym> <identifier> ['=' <EXPR>]?
}

rule statement:sym<while> {
    <sym> <EXPR> 'do' <block> 'end'
}

token begin_block {
    <?>
}

rule block {
    <.begin_block>
    <statement>*
}

## Terms

rule primary {
    <identifier> <postfix_expression>*
}

proto rule postfix_expression { <...> }

rule postfix_expression:sym<index> { '[' <EXPR> ']' }

rule postfix_expression:sym<key> { '{' <EXPR> '}' }

rule postfix_expression:sym<member> { '.' <identifier> }

token identifier {
    <!keyword> <ident>
}

token keyword {
    ['and'|'catch'|'do'   |'else' |'end' |'for' |'if'
    |'not'|'or'   |'sub'  |'throw'|'try' |'var'|'while']>>
}

token term:sym<integer_constant> { <integer> }
token term:sym<string_constant> { <string_constant> }
token string_constant { <quote> }
token term:sym<float_constant_long> { # longer to work-around lack of LTM
    [
    | \d+ '.' \d*
    | \d* '.' \d+
    ]
}
token term:sym<primary> {
    <primary>
}

proto token quote { <...> }
token quote:sym<'> { <?[']> <quote_EXPR: ':q'> }
token quote:sym<"> { <?["]> <quote_EXPR: ':qq'> }

## Operators

INIT {
    Squaak::Grammar.O(':prec<w>, :assoc<unary>', '%unary-negate');
    Squaak::Grammar.O(':prec<v>, :assoc<unary>', '%unary-not');
    Squaak::Grammar.O(':prec<u>, :assoc<left>',  '%multiplicative');
    Squaak::Grammar.O(':prec<t>, :assoc<left>',  '%additive');
    Squaak::Grammar.O(':prec<s>, :assoc<left>',  '%relational');
    Squaak::Grammar.O(':prec<r>, :assoc<left>',  '%conjunction');
    Squaak::Grammar.O(':prec<q>, :assoc<left>',  '%disjunction');
}

token circumfix:sym<( )> { '(' <.ws> <EXPR> ')' }

rule circumfix:sym<[ ]> {
    '[' [<EXPR> ** ',']? ']'
}

rule circumfix:sym<{ }> {
    '{' [<named_field> ** ',']? '}'
}

rule named_field {
    <string_constant> '=>' <EXPR>
}

token prefix:sym<-> { <sym> <O('%unary-negate, :pirop<neg>')> }
token prefix:sym<not> { <sym> <O('%unary-not, :pirop<isfalse>')> }

token infix:sym<*>  { <sym> <O('%multiplicative, :pirop<mul>')> }
token infix:sym<%>  { <sym> <O('%multiplicative, :pirop<mod>')> }
token infix:sym</>  { <sym> <O('%multiplicative, :pirop<div>')> }

token infix:sym<+>  { <sym> <O('%additive, :pirop<add>')> }
token infix:sym<->  { <sym> <O('%additive, :pirop<sub>')> }
token infix:sym<..> { <sym> <O('%additive, :pirop<concat>')> }

token infix:sym«<» { <sym> <O('%relational, :pirop<islt iPP>')> }
token infix:sym«<=» { <sym> <O('%relational, :pirop<isle iPP>')> }
token infix:sym«>» { <sym> <O('%relational, :pirop<isgt iPP>')> }
token infix:sym«>=» { <sym> <O('%relational, :pirop<isge iPP>')> }
token infix:sym«==» { <sym> <O('%relational, :pirop<iseq iPP>')> }
token infix:sym«!=» { <sym> <O('%relational, :pirop<isne iPP>')> }

token infix:sym<and> { <sym> <O('%conjunction, :pasttype<if>')> }
token infix:sym<or> { <sym> <O('%disjunction, :pasttype<unless>')> }
