%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
%
% This is a regression test. In versions of the compiler before 7 Aug 2007,
% it used to cause this compiler abort:
%
% Software Error: code_gen.m: Unexpected: nondet model in det/semidet context
%
% when generating code for the java_identifier predicate.
%
% The bug was that the follow_code pass would push a goal binding a variable
% into a semidet disjunction. This would cause the disjunction to have an
% output, which would cause the simplify pass to change its determinism,
% and from there the determinism of the entire procedure body, to nondet.
% Since java_identifier is supposed to be semidet, this causes the abort above.
%
%-----------------------------------------------------------------------------%
% Ralph Becket <rafe@cs.mu.oz.au>
% Mon Feb 21 09:42:40 EST 2005
%
% Java grammar taken from
% http://java.sun.com/docs/books/jls/second_edition/html/syntax.doc.html
% and converted to Mercury DCGs.  And then corrected because the grammar
% at the above page contains bugs...
%
% Usage: parse_java <filename> ...
%
% This implementation only recognises Java programs; it does not construct an
% ADT or perform any kind of analysis.  However, it has been written in a
% style that should make it fairly easy to add construction of an ADT.
%
% To compile this as a packrat parser, uncomment all of the pragma memo
% lines.
%
% To compile this as a partial packrat parser, uncomment all of the
% pragma memo lines except those for literal/2, qualified_identifier/2, and
% punct/2.
%
%-----------------------------------------------------------------------------%

:- module mercury_java_parser_follow_code_bug.

:- interface.

:- import_module io.

:- pred main(io :: di, io :: uo) is det.

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- implementation.

:- import_module char.
:- import_module exception.
:- import_module int.
:- import_module list.
:- import_module std_util.
:- import_module string.

    % The parser "state".  This is just the offset into the input string,
    % which is stored in a C global.  We do this to (a) avoid memoizing the
    % input string if we are memoing any rules and (b) to obtain a fair
    % timing comparison with the Rats! Java parser
    % (www.cs.nyu.edu/rgrimm/xtc/rats.html).
    %
:- type ps == int.

%-----------------------------------------------------------------------------%

main(!IO) :-
    io.command_line_arguments(Args, !IO),
    list.foldl(parse_file, Args, !IO).

:- pred parse_file(string::in, io::di, io::uo) is det.
:- pragma promise_pure(parse_file/3).

parse_file(Filename, !IO) :-
    io.write_string(Filename, !IO),
    io.see(Filename, _, !IO),
    io.read_file_as_string(Result, !IO),
    ( if Result = ok(Str) then
        impure set_input_string(Str),
        ( if   compilation_unit(0, _)
          then io.print(" parsed successfully\n", !IO)
          else io.print(" failed to parse\n", !IO)
        )
      else
        throw(Result)
    ).

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Low-level predicates.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- pragma foreign_decl("C", "

    MR_String input_string = NULL;
    MR_Word input_length = (MR_Word)0;

").

%-----------------------------------------------------------------------------%

:- impure pred set_input_string(string::in) is det.

:- pragma foreign_proc("C",
    set_input_string(Str::in),
    [will_not_call_mercury], "

    input_string = Str;
    input_length = strlen(Str);

").

%-----------------------------------------------------------------------------%

:- semipure pred input_string_and_length(string::out, int::out) is det.

:- pragma foreign_proc("C",
    input_string_and_length(Str::out, Length::out),
    [will_not_call_mercury, promise_semipure], "

    Str = input_string;
    Length = input_length;

").

%-----------------------------------------------------------------------------%

:- pred current_offset(int::out, int::in, int::out) is det.

current_offset(Offset, Offset, Offset).

%-----------------------------------------------------------------------------%

:- pred eof(int::in, int::out) is semidet.
:- pragma promise_pure(eof/2).

eof(Offset, Offset) :-
    semipure input_string_and_length(_Str, Length),
    Offset = Length.

%-----------------------------------------------------------------------------%

    % XXX These are really semipure, but I'm being naughty and promising them
    % to be pure because I don't want to pollute my code with impurity
    % annotations.
    %
    % XXX Also, I do not check for negative offsets.  I probably should.
    %
:- pred char(char::out, int::in, int::out) is semidet.
:- pragma promise_pure(char/3).

char(Char, Offset, Offset + 1) :-
    semipure input_string_and_length(Str, Length),
    Offset < Length,
    Char = Str ^ unsafe_elem(Offset).

%-----------------------------------------------------------------------------%

:- pred input_substring(int::in, int::in, string::out,
        int::in, int::out) is semidet.
:- pragma promise_pure(input_substring/5).

input_substring(Start, End, Substring, Offset, Offset) :-
    semipure input_string_and_length(Str, Length),
    End =< Length,
    Substring = unsafe_substring(Str, Start, End - Start).

%-----------------------------------------------------------------------------%

:- pred match_string(string::in, int::in, int::out) is semidet.
:- pragma promise_pure(match_string/3).

match_string(MatchStr, Offset, Offset + N) :-
    semipure input_string_and_length(Str, Length),
    N = length(MatchStr),
    Offset + N =< Length,
    match_string_2(0, N, MatchStr, Offset, Str).

:- pred match_string_2(int::in, int::in, string::in, int::in, string::in)
        is semidet.

match_string_2(I, N, MatchStr, Offset, Str) :-
    ( if I < N then
        MatchStr ^ unsafe_elem(I) = Str ^ unsafe_elem(Offset + I),
        match_string_2(I + 1, N, MatchStr, Offset, Str)
      else
        true
    ).

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Utility predicates.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- pred optional(pred(ps, ps)::in(pred(in, out) is semidet),
            ps::in, ps::out) is semidet.

optional(P) -->
    ( if P then [] else { semidet_succeed } ).

%-----------------------------------------------------------------------------%

:- pred zero_or_more(pred(ps, ps)::in(pred(in, out) is semidet),
            ps::in, ps::out) is semidet.

zero_or_more(P) -->
    ( if P then zero_or_more(P) else { semidet_succeed } ).

%-----------------------------------------------------------------------------%

:- pred one_or_more(pred(ps, ps)::in(pred(in, out) is semidet),
            ps::in, ps::out) is semidet.

one_or_more(P) -->
    P,
    zero_or_more(P).

%-----------------------------------------------------------------------------%

:- pred brackets(string::in, pred(ps, ps)::in(pred(in, out) is semidet),
            string::in, ps::in, ps::out) is semidet.

brackets(L, P, R) -->
    punct(L),
    P,
    punct(R).

%-----------------------------------------------------------------------------%

:- pred ++(pred(ps, ps)::in(pred(in, out) is semidet),
           pred(ps, ps)::in(pred(in, out) is semidet),
           ps::in, ps::out) is semidet.

:- pragma inline((++)/4).

P ++ Q -->
    P,
    Q.

%-----------------------------------------------------------------------------%

:- pred comma_separated_list(pred(ps, ps)::in(pred(in, out) is semidet),
            ps::in, ps::out) is semidet.

comma_separated_list(P) -->
    P, zero_or_more(punct(",") ++ P).

%-----------------------------------------------------------------------------%

:- pred whitespace(ps::in, ps::out) is semidet.

whitespace -->
    ( if char(C1), { char.is_whitespace(C1) } then
        whitespace
      else if char('/'), char('/') then
        skip_to_eol,
        whitespace
      else if char('/'), char('*') then
        skip_to_end_of_trad_comment,
        whitespace
      else
        []
    ).

:- pred skip_to_eol(ps::in, ps::out) is semidet.

skip_to_eol -->
    char(C),
    ( if { C = ('\n') } then [] else skip_to_eol ).

:- pred skip_to_end_of_trad_comment(ps::in, ps::out) is semidet.

skip_to_end_of_trad_comment -->
    ( if char('*'), char('/') then
        []
      else
        char(_),
        skip_to_end_of_trad_comment
    ).

%-----------------------------------------------------------------------------%

:- pred punct(string::in, ps::in, ps::out) is semidet.

% :- pragma memo(punct/3).
punct(Punct) -->
    match_string(Punct),
    whitespace.

%-----------------------------------------------------------------------------%

:- pred keyword(string::in, ps::in, ps::out) is semidet.

% :- pragma memo(keyword/3).
keyword(Keyword) -->
    match_string(Keyword),
    not(java_identifier_part),
    whitespace.

%-----------------------------------------------------------------------------%

:- pred keyword(string::in, pred(ps, ps)::in(pred(in, out) is semidet),
        ps::in, ps::out) is semidet.

keyword(Keyword, P) -->
    match_string(Keyword),
    not(java_identifier_part),
    whitespace,
    P.

%-----------------------------------------------------------------------------%

:- pragma memo(java_identifier/2, [allow_reset, fast_loose]).

:- pred java_identifier(/*string::out,*/ ps::in, ps::out) is semidet.

% :- pragma memo(java_identifier/2).
java_identifier/*(Identifier)*/ -->
%     current_offset(Start),
    java_identifier_start,
    zero_or_more(java_identifier_part),
%     current_offset(End),
%     input_substring(Start, End, Identifier),
    whitespace.

:- pred java_identifier_start(ps::in, ps::out) is semidet.

java_identifier_start -->
    char(C),
    { char.is_alpha_or_underscore(C) ; C = ('$') }.

:- pred java_identifier_part(ps::in, ps::out) is semidet.

java_identifier_part -->
    char(C),
    { char.is_alnum_or_underscore(C) ; C = ('$') }.

%-----------------------------------------------------------------------------%

:- pred floating_point_literal(ps::in, ps::out) is semidet.

floating_point_literal -->
    ( if optional(digits(10)), char('.'), digits(10) then
        optional(exponent_part),
        optional(float_type_suffix)
      else
        digits(10),
        ( if exponent_part then optional(float_type_suffix)
                           else float_type_suffix
        )
    ).

:- pred exponent_part(ps::in, ps::out) is semidet.

exponent_part -->
    char(C), { C = ('E') ; C = ('e') },
    optional(sign),
    one_or_more(digit(10)).

:- pred sign(ps::in, ps::out) is semidet.

sign -->
    char(C), { C = ('+') ; C = ('-') }.

:- pred float_type_suffix(ps::in, ps::out) is semidet.

float_type_suffix -->
    char(C), { C = ('F') ; C = ('f') ; C = ('D') ; C = ('d') }.

%-----------------------------------------------------------------------------%

:- pred integer_literal(ps::in, ps::out) is semidet.

integer_literal -->
    ( if
        hex_literal
      then
        []
      else if
        oct_literal
      then
        []
      else
        dec_literal
    ).

:- pred hex_literal(ps::in, ps::out) is semidet.

hex_literal -->
    char('0'),
    char('x'),
    digits(16),
    optional(integer_type_suffix).

:- pred oct_literal(ps::in, ps::out) is semidet.

oct_literal -->
    char('0'),
    digits(8),
    optional(integer_type_suffix).

:- pred dec_literal(ps::in, ps::out) is semidet.

dec_literal -->
    ( if char('0') then not digit(16) else digits(10) ),
    optional(integer_type_suffix).

:- pred integer_type_suffix(ps::in, ps::out) is semidet.

integer_type_suffix -->
    char(C), { C = ('L') ; C = ('l') }.

:- pred digits(int::in, ps::in, ps::out) is semidet.

digits(Base) -->
    one_or_more(digit(Base)).

:- pred digit(int::in, ps::in, ps::out) is semidet.

digit(Base) -->
    char(C), { char.digit_to_int(C, D), D < Base }.

%-----------------------------------------------------------------------------%

:- pred character_literal(ps::in, ps::out) is semidet.

character_literal -->
    char('\''),
    not char('\''),
    possibly_escaped_char,
    char('\'').

:- pred possibly_escaped_char(ps::in, ps::out) is semidet.

possibly_escaped_char -->
    char(C1),
    ( if { C1 = ('\\') } then
        ( if digits(8) then
            []
          else if char('u'), one_or_more(digits(16)) then
            []
          else
            char(C2),
            { member(C2, [('b'), ('t'), ('n'), ('f'), ('r'), ('\"'), ('\''),
                    ('\\')]) }
        )
      else
        []
    ).

%-----------------------------------------------------------------------------%

:- pred string_literal(ps::in, ps::out) is semidet.

string_literal -->
    char('\"'),
    zero_or_more(string_char),
    char('\"').

:- pred string_char(ps::in, ps::out) is semidet.

string_char -->
    not(char('"')),
    possibly_escaped_char.

%-----------------------------------------------------------------------------%

:- pred boolean_literal(ps::in, ps::out) is semidet.

boolean_literal -->
    ( if
        keyword("true")
      then
        []
      else
        keyword("false")
    ).

%-----------------------------------------------------------------------------%

:- pred null_literal(ps::in, ps::out) is semidet.

null_literal -->
    keyword("null").

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Taken from
% http://java.sun.com/docs/books/jls/second_edition/html/syntax.doc.html

%      _________________________________________________________________
%
%    [5]Contents | [6]Prev | [7]Next | [8]Index Java Language Specification
%    Second Edition
%
%    [9]Copyright © 2000 Sun Microsystems, Inc. All rights reserved
%    Please send any comments or corrections to the [10]JLS team

%                                     Syntax
%      _________________________________________________________________
%
%    This chapter presents a grammar for the Java programming language.
%
%    The grammar presented piecemeal in the preceding chapters is much
%    better for exposition, but it is not ideally suited as a basis for a
%    parser. The grammar presented in this chapter is the basis for the
%    reference implementation.
%
%    The grammar below uses the following BNF-style conventions:
%
%      * [x] denotes zero or one occurrences of x.
%      * {x} denotes zero or more occurrences of x.
%      * x | y means one of either x or y.

% Identifier:
%         IDENTIFIER
%
% QualifiedIdentifier:
%         Identifier { . Identifier }

:- pred qualified_identifier(ps::in, ps::out) is semidet.

% :- pragma memo(qualified_identifier/2).
qualified_identifier -->
    java_identifier, zero_or_more(dot_java_identifier).

:- pred dot_java_identifier(ps::in, ps::out) is semidet.

% :- pragma memo(dot_java_identifier/2).
dot_java_identifier -->
    punct("."),
    java_identifier.

% Literal:
%         IntegerLiteral
%         FloatingPointLiteral
%         CharacterLiteral
%         StringLiteral
%         BooleanLiteral
%         NullLiteral

:- pred literal(ps::in, ps::out) is semidet.

% :- pragma memo(literal/2).
literal -->
    ( if
        floating_point_literal
      then
        []
      else if
        integer_literal
      then
        []
      else if
        character_literal
      then
        []
      else if
        string_literal
      then
        []
      else if
        boolean_literal
      then
        []
      else
        null_literal
    ),
    whitespace.

% Expression:
%         Expression1 [AssignmentOperator Expression1]]
%           XXX I think that should be
%           Expression1 {AssignmentOperator Expression1}

:- pred expression(ps::in, ps::out) is semidet.

% :- pragma memo(expression/2).
expression -->
    expression1, zero_or_more(assignment_operator_expression1).

:- pred assignment_operator_expression1(ps::in, ps::out) is semidet.

assignment_operator_expression1 -->
    assignment_operator, expression1.

% AssignmentOperator:
%         =
%         +=
%         -=
%         *=
%         /=
%         &=
%         |=
%         ^=
%         %=
%         <<=
%         >>=
%         >>>=

:- pred assignment_operator(ps::in, ps::out) is semidet.

% :- pragma memo(assignment_operator/2).
assignment_operator -->
    ( if
        punct("=")
      then
        []
      else if
        punct("+=")
      then
        []
      else if
        punct("-=")
      then
        []
      else if
        punct("*=")
      then
        []
      else if
        punct("/=")
      then
        []
      else if
        punct("&=")
      then
        []
      else if
        punct("|=")
      then
        []
      else if
        punct("^=")
      then
        []
      else if
        punct("%=")
      then
        []
      else if
        punct("<<=")
      then
        []
      else if
        punct(">>=")
      then
        []
      else
        punct(">>>=")
    ).

% Type:
%         Identifier {   .   Identifier } BracketsOpt
%         BasicType

:- pred java_type(ps::in, ps::out) is semidet.

% :- pragma memo(java_type/2).
java_type -->
    ( if
        qualified_identifier, brackets_opt
      then
        []
      else
        basic_type
    ).

% StatementExpression:
%         Expression

:- pred statement_expression(ps::in, ps::out) is semidet.

% :- pragma memo(statement_expression/2).
statement_expression -->
    expression.

% ConstantExpression:
%         Expression

:- pred constant_expression(ps::in, ps::out) is semidet.

% :- pragma memo(constant_expression/2).
constant_expression -->
    expression.

% Expression1:
%         Expression2 [Expression1Rest]

:- pred expression1(ps::in, ps::out) is semidet.

expression1 -->
    expression2, optional(expression1_rest).

% Expression1Rest:
%         [  ?   Expression   :   Expression1]

:- pred expression1_rest(ps::in, ps::out) is semidet.

% :- pragma memo(expression1_rest/2).
expression1_rest -->
    punct("?"), expression, punct(":"), expression1.

% Expression2 :
%         Expression3 [Expression2Rest]

:- pred expression2(ps::in, ps::out) is semidet.

expression2 -->
    expression3, optional(expression2_rest).

% Expression2Rest:
%         {Infixop Expression3}
%         Expression3 instanceof Type
%         XXX The Expression3 here must be wrong...
%         XXX And {Infixop Expression3} should be allowed after Type.

:- pred expression2_rest(ps::in, ps::out) is semidet.

% :- pragma memo(expression2_rest/2).
expression2_rest -->
    ( if
        keyword("instanceof"), java_type, optional(expression2_rest)
      then
        []
      else
        zero_or_more(infix_op_expression3)
    ).

:- pred infix_op_expression3(ps::in, ps::out) is semidet.

infix_op_expression3 -->
    infix_op, expression3.

% Infixop:
%         ||
%         &&
%         |
%         ^
%         &
%         ==
%         !=
%         <
%         >
%         <=
%         >=
%         <<
%         >>
%         >>>
%         +
%         -
%         *
%         /
%         %

:- pred infix_op(ps::in, ps::out) is semidet.

% :- pragma memo(infix_op/2).
infix_op -->
    ( if
        punct("||")
      then
        []
      else if
        punct("&&")
      then
        []
      else if
        punct("|")
      then
        []
      else if
        punct("^")
      then
        []
      else if
        punct("&")
      then
        []
      else if
        punct("==")
      then
        []
      else if
        punct("!=")
      then
        []
      else if
        punct("<=")
      then
        []
      else if
        punct(">=")
      then
        []
      else if
        punct("<<")
      then
        []
      else if
        punct(">>>")
      then
        []
      else if
        punct(">>")
      then
        []
      else if
        punct("<")
      then
        []
      else if
        punct(">")
      then
        []
      else if
        punct("+")
      then
        []
      else if
        punct("-")
      then
        []
      else if
        punct("*")
      then
        []
      else if
        punct("/")
      then
        []
      else
        punct("%")
    ).

% Expression3:
%         PrefixOp Expression3
%         (   Expr | Type   )   Expression3
%         Primary {Selector} {PostfixOp}

:- pred expression3(ps::in, ps::out) is semidet.

expression3 -->
    ( if
        prefix_op, expression3
      then
        []
      else if
        brackets("(", expression_or_java_type, ")"), expression3
      then
        []
      else
        primary, zero_or_more(selector), zero_or_more(postfix_op)
    ).

:- pred expression_or_java_type(ps::in, ps::out) is semidet.

% :- pragma memo(expression_or_java_type/2).
expression_or_java_type -->
    ( if
        java_type
      then
        []
      else
        expression
    ).

% Primary:
%         ( Expression )
%         this [Arguments]
%         super SuperSuffix
%         Literal
%         new Creator
%         Identifier { . Identifier }[ IdentifierSuffix]
%         BasicType BracketsOpt .class
%         void.class

:- pred primary(ps::in, ps::out) is semidet.

% :- pragma memo(primary/2).
primary -->
    ( if
        brackets("(", expression, ")")
      then
        []
      else if
        keyword("this"), optional(arguments)
      then
        []
      else if
        keyword("super"), super_suffix
      then
        []
      else if
        keyword("new"), creator
      then
        []
      else if
        keyword("void"), punct("."), keyword("class")
      then
        []
      else if
        basic_type, brackets_opt, punct("."), keyword("class")
      then
        []
      else if
        literal
      then
        []
      else
        qualified_identifier, optional(identifier_suffix)
    ).

% XXX I don't understand how to read this rule:
%
% IdentifierSuffix:
%         [ ( ] BracketsOpt   .   class | Expression ])
%         Arguments
%         .   ( class | this | super Arguments | new InnerCreator )

:- pred identifier_suffix(ps::in, ps::out) is semidet.

% :- pragma memo(identifier_suffix/2).
identifier_suffix -->
    ( if
        brackets_opt, punct("."),
        ( if
            keyword("class")
          then
            []
          else
            expression
        )
      then
        []
      else if
        arguments
      then
        []
      else
        punct("."),
        ( if
            keyword("class")
          then
            []
          else if
            keyword("this")
          then
            []
          else if
            keyword("super"), arguments
          then
            []
          else
            keyword("new"), inner_creator
        )
    ).

% PrefixOp:
%        ,
%         --
%         !
%         ~
%         +
%         -

:- pred prefix_op(ps::in, ps::out) is semidet.

% :- pragma memo(prefix_op/2).
prefix_op -->
    ( if
        punct("++")
      then
        []
      else if
        punct("--")
      then
        []
      else if
        punct("!")
      then
        []
      else if
        punct("~")
      then
        []
      else if
        punct("+")
      then
        []
      else
        punct("-")
    ).

% PostfixOp:
%        ,
%         --

:- pred postfix_op(ps::in, ps::out) is semidet.

% :- pragma memo(postfix_op/2).
postfix_op -->
    ( if
        punct("++")
      then
        []
      else
        punct("--")
    ).

% Selector:
%         . Identifier [Arguments]
%         . this
%         . super SuperSuffix
%         . new InnerCreator
%         [ Expression ]

:- pred selector(ps::in, ps::out) is semidet.

% :- pragma memo(selector/2).
selector -->
    ( if
        brackets("[", expression, "]")
      then
        []
      else
        punct("."),
        ( if
            keyword("this")
          then
            []
          else if
            keyword("super"), super_suffix
          then
            []
          else if
            keyword("new"), inner_creator
          then
            []
          else
            java_identifier, optional(arguments)
        )
    ).

% SuperSuffix:
%         Arguments
%         . Identifier [Arguments]

:- pred super_suffix(ps::in, ps::out) is semidet.

% :- pragma memo(super_suffix/2).
super_suffix -->
    ( if
        arguments
      then
        []
      else
        punct("."), java_identifier, optional(arguments)
    ).

% BasicType:
%         byte
%         short
%         char
%         int
%         long
%         float
%         double
%         boolean

:- pred basic_type(ps::in, ps::out) is semidet.

% :- pragma memo(basic_type/2).
basic_type -->
    ( if
        punct("byte")
      then
        []
      else if
        punct("short")
      then
        []
      else if
        punct("char")
      then
        []
      else if
        punct("int")
      then
        []
      else if
        punct("long")
      then
        []
      else if
        punct("float")
      then
        []
      else if
        punct("double")
      then
        []
      else
        punct("boolean")
    ).

% Arguments:
%         ( [Expression { , Expression }] )

:- pred arguments(ps::in, ps::out) is semidet.

% :- pragma memo(arguments/2).
arguments -->
    brackets("(", optional(comma_separated_list(expression)), ")").

% BracketsOpt:
%         {[]}

:- pred brackets_opt(ps::in, ps::out) is semidet.

% :- pragma memo(brackets_opt/2).
brackets_opt -->
    zero_or_more(empty_brackets).

:- pred empty_brackets(ps::in, ps::out) is semidet.

% :- pragma memo(empty_brackets/2).
empty_brackets -->
    punct("["), punct("]").

% Creator:
%         QualifiedIdentifier ( ArrayCreatorRest  | ClassCreatorRest )

:- pred creator(ps::in, ps::out) is semidet.

% :- pragma memo(creator/2).
creator -->
    qualified_identifier, creator_rest.

:- pred creator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(creator_rest/2).
creator_rest -->
    ( if
        array_creator_rest
      then
        []
      else
        class_creator_rest
    ).

% InnerCreator:
%         Identifier ClassCreatorRest

:- pred inner_creator(ps::in, ps::out) is semidet.

% :- pragma memo(inner_creator/2).
inner_creator -->
    java_identifier, class_creator_rest.

% XXX I don't understand how to read this rule:
%
% ArrayCreatorRest:
%         [ ( ] BracketsOpt ArrayInitializer | Expression ] {[ Expression ]} BracketsOpt )

:- pred array_creator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(array_creator_rest/2).
array_creator_rest -->
    one_or_more(brackets("[", optional(expression), "]")),
    optional(array_initializer).

% ClassCreatorRest:
%         Arguments [ClassBody]

:- pred class_creator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(class_creator_rest/2).
class_creator_rest -->
    arguments, optional(class_body).

% ArrayInitializer:
%         { [VariableInitializer {, VariableInitializer} [,]] }

:- pred array_initializer(ps::in, ps::out) is semidet.

% :- pragma memo(array_initializer/2).
array_initializer -->
    brackets("{", optional(array_initializer_body), "}").

:- pred array_initializer_body(ps::in, ps::out) is semidet.

% :- pragma memo(array_initializer_body/2).
array_initializer_body -->
    comma_separated_list(variable_initializer), optional(punct(",")).

% VariableInitializer:
%         ArrayInitializer
%         Expression

:- pred variable_initializer(ps::in, ps::out) is semidet.

% :- pragma memo(variable_initializer/2).
variable_initializer -->
    ( if
        array_initializer
      then
        []
      else
        expression
    ).

% ParExpression:
%         ( Expression )

:- pred par_expression(ps::in, ps::out) is semidet.

% :- pragma memo(par_expression/2).
par_expression -->
    brackets("(", expression, ")").

% Block:
%         { BlockStatements }

:- pred block(ps::in, ps::out) is semidet.

% :- pragma memo(block/2).
block -->
    brackets("{", block_statements, "}").

% BlockStatements:
%         { BlockStatement }

:- pred block_statements(ps::in, ps::out) is semidet.

% :- pragma memo(block_statements/2).
block_statements -->
    zero_or_more(block_statement).

% BlockStatement :
%         LocalVariableDeclarationStatement
%         ClassOrInterfaceDeclaration
%         [Identifier :] Statement

:- pred block_statement(ps::in, ps::out) is semidet.

% :- pragma memo(block_statement/2).
block_statement -->
    ( if
        local_variable_declaration_statement
      then
        []
      else if
        class_or_interface_declaration
      then
        []
      else
        optional(label), statement
    ).

:- pred label(ps::in, ps::out) is semidet.

% :- pragma memo(label/2).
label -->
    java_identifier, punct(":").

% LocalVariableDeclarationStatement:
%         [final] Type VariableDeclarators   ;
%         XXX I think this is wrong: [final] should be ModifiersOpt, surely?

:- pred local_variable_declaration_statement(ps::in, ps::out) is semidet.

% :- pragma memo(local_variable_declaration_statement/2).
local_variable_declaration_statement -->
    modifiers_opt,
    java_type,
    variable_declarators,
    punct(";").

% Statement:
%         Block
%         if ParExpression Statement [else if Statement]
%         for ( ForInitOpt   ;   [Expression]   ;   ForUpdateOpt ) Statement
%         while ParExpression Statement
%         do Statement while ParExpression   ;
%         try Block ( Catches | [Catches] finally Block )
%         switch ParExpression { SwitchBlockStatementGroups }
%         synchronized ParExpression Block
%         return [Expression] ;
%         throw Expression   ;
%         break [Identifier]
%         continue [Identifier]
%         ;
%         ExpressionStatement
%         Identifier   :   Statement

:- pred statement(ps::in, ps::out) is semidet.

% :- pragma memo(statement/2).
statement -->
    ( if
        block
      then
        []
      else if
        keyword("if"), par_expression, statement,
            optional(else_statement)
      then
        []
      else if
        keyword("for"),
        punct("("),
        optional(for_init),
        punct(";"),
        optional(expression),
        punct(";"),
        optional(for_update),
        punct(")"),
        statement
      then
        []
      else if
        keyword("while"), par_expression, statement
      then
        []
      else if
        keyword("do"), statement,
            keyword("while"), par_expression, punct(";")
      then
        []
      else if
        keyword("try"), block, catches_finally
      then
        []
      else if
        keyword("switch"), par_expression,
            brackets("{", switch_block_statement_groups, "}")
      then
        []
      else if
        keyword("synchronized"), par_expression, block
      then
        []
      else if
        keyword("return"), optional(expression), punct(";")
      then
        []
      else if
        keyword("throw"), expression, punct(";")
      then
        []
      else if
        keyword("break"), optional(java_identifier), punct(";")
      then
        []
      else if
        keyword("continue"), optional(java_identifier), punct(";")
      then
        []
      else if
        punct(";")
      then
        []
      else if
        expression, punct(";")
      then
        []
      else
        java_identifier, punct(":"), statement
    ).

:- pred else_statement(ps::in, ps::out) is semidet.

% :- pragma memo(else_statement/2).
else_statement -->
    keyword("else"), statement.

:- pred catches_finally(ps::in, ps::out) is semidet.

% :- pragma memo(catches_finally/2).
catches_finally -->
    ( if
        catches, optional(finally_block)
      then
        []
      else
        finally_block
    ).

:- pred finally_block(ps::in, ps::out) is semidet.

% :- pragma memo(finally_block/2).
finally_block -->
    keyword("finally"), block.

% Catches:
%         CatchClause {CatchClause}

:- pred catches(ps::in, ps::out) is semidet.

% :- pragma memo(catches/2).
catches -->
    one_or_more(catch_clause).

% CatchClause:
%         catch ( FormalParameter ) Block

:- pred catch_clause(ps::in, ps::out) is semidet.

% :- pragma memo(catch_clause/2).
catch_clause -->
    keyword("catch"), brackets("(", formal_parameter, ")"), block.

% SwitchBlockStatementGroups:
%         { SwitchBlockStatementGroup }

:- pred switch_block_statement_groups(ps::in, ps::out) is semidet.

% :- pragma memo(switch_block_statement_groups/2).
switch_block_statement_groups -->
    zero_or_more(switch_block_statement_group).

% SwitchBlockStatementGroup:
%         SwitchLabel BlockStatements

:- pred switch_block_statement_group(ps::in, ps::out) is semidet.

% :- pragma memo(switch_block_statement_group/2).
switch_block_statement_group -->
    switch_label, block_statements.

% SwitchLabel:
%         case ConstantExpression   :
%         default:

:- pred switch_label(ps::in, ps::out) is semidet.

% :- pragma memo(switch_label/2).
switch_label -->
    ( if
        keyword("case"), constant_expression, punct(":")
      then
        []
      else
        keyword("default"), punct(":")
    ).

% MoreStatementExpressions:
%         { , StatementExpression }
%
% ForInit:
%         StatementExpression MoreStatementExpressions
%         [final] Type VariableDeclarators

:- pred for_init(ps::in, ps::out) is semidet.

% :- pragma memo(for_init/2).
for_init -->
    ( if
        comma_separated_list(for_init_statement_expression)
      then
        []
      else
        optional(keyword("final")), java_type, variable_declarators
    ).

:- pred for_init_statement_expression(ps::in, ps::out) is semidet.

% :- pragma memo(for_init_statement_expression/2).
for_init_statement_expression -->
    ( if
        java_type, java_identifier, equals_variable_initializer
      then
        []
      else
        statement_expression
    ).

% ForUpdate:
%         StatementExpression MoreStatementExpressions

:- pred for_update(ps::in, ps::out) is semidet.

% :- pragma memo(for_update/2).
for_update -->
    comma_separated_list(statement_expression).

% ModifiersOpt:
%         { Modifier }

:- pred modifiers_opt(ps::in, ps::out) is semidet.

% :- pragma memo(modifiers_opt/2).
modifiers_opt -->
    zero_or_more(modifier).

% Modifier:
%         public
%         protected
%         private
%         static
%         abstract
%         final
%         native
%         synchronized
%         transient
%         volatile
%         strictfp

:- pred modifier(ps::in, ps::out) is semidet.

% :- pragma memo(modifier/2).
modifier -->
    ( if
        keyword("public")
      then
        []
      else if
        keyword("protected")
      then
        []
      else if
        keyword("private")
      then
        []
      else if
        keyword("static")
      then
        []
      else if
        keyword("abstract")
      then
        []
      else if
        keyword("final")
      then
        []
      else if
        keyword("native")
      then
        []
      else if
        keyword("synchronized")
      then
        []
      else if
        keyword("transient")
      then
        []
      else if
        keyword("volatile")
      then
        []
      else
        keyword("strictfp")
    ).

% VariableDeclarators:
%         VariableDeclarator { ,   VariableDeclarator }

:- pred variable_declarators(ps::in, ps::out) is semidet.

% :- pragma memo(variable_declarators/2).
variable_declarators -->
    comma_separated_list(variable_declarator).

% ConstantDeclaratorsRest:
%         ConstantDeclaratorRest { ,   ConstantDeclarator }

:- pred constant_declarators_rest(ps::in, ps::out) is semidet.

% :- pragma memo(constant_declarators_rest/2).
constant_declarators_rest -->
    comma_separated_list(constant_declarator_rest).

% VariableDeclarator:
%         Identifier VariableDeclaratorRest

:- pred variable_declarator(ps::in, ps::out) is semidet.

% :- pragma memo(variable_declarator/2).
variable_declarator -->
    java_identifier, variable_declarator_rest.

% ConstantDeclarator:
%         Identifier ConstantDeclaratorRest

:- pred constant_declarator(ps::in, ps::out) is semidet.

% :- pragma memo(constant_declarator/2).
constant_declarator -->
    java_identifier, constant_declarator_rest.

% VariableDeclaratorRest:
%         BracketsOpt [  =   VariableInitializer]

:- pred variable_declarator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(variable_declarator_rest/2).
variable_declarator_rest -->
    brackets_opt, optional(equals_variable_initializer).

:- pred equals_variable_initializer(ps::in, ps::out) is semidet.

% :- pragma memo(equals_variable_initializer/2).
equals_variable_initializer -->
    punct("="),
    variable_initializer.

% ConstantDeclaratorRest:
%         BracketsOpt   =   VariableInitializer

:- pred constant_declarator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(constant_declarator_rest/2).
constant_declarator_rest -->
    brackets_opt, punct("="), variable_initializer.

% VariableDeclaratorId:
%         Identifier BracketsOpt

:- pred variable_declarator_id(ps::in, ps::out) is semidet.

% :- pragma memo(variable_declarator_id/2).
variable_declarator_id -->
    java_identifier, brackets_opt.

% CompilationUnit:
%         [package QualifiedIdentifier   ;  ] {ImportDeclaration} {TypeDeclaration}

:- pred compilation_unit(ps::in, ps::out) is semidet.

% :- pragma memo(compilation_unit/2).
compilation_unit -->
    whitespace,
    optional(package_declaration),
    zero_or_more(import_declaration),
    zero_or_more(type_declaration),
    eof.

:- pred package_declaration(ps::in, ps::out) is semidet.

% :- pragma memo(package_declaration/2).
package_declaration -->
    keyword("package"), qualified_identifier, punct(";").

% ImportDeclaration:
%         import Identifier {   .   Identifier } [   .     *   ] ;

:- pred import_declaration(ps::in, ps::out) is semidet.

% :- pragma memo(import_declaration/2).
import_declaration -->
    keyword("import"),
    qualified_identifier,
    optional(dot_star),
    punct(";").

:- pred dot_star(ps::in, ps::out) is semidet.

% :- pragma memo(dot_star/2).
dot_star -->
    punct("."), punct("*").

% TypeDeclaration:
%         ClassOrInterfaceDeclaration

:- pred type_declaration(ps::in, ps::out) is semidet.

% :- pragma memo(type_declaration/2).
type_declaration -->
    class_or_interface_declaration.

% ClassOrInterfaceDeclaration:
%         ModifiersOpt (ClassDeclaration | InterfaceDeclaration)

:- pred class_or_interface_declaration(ps::in, ps::out) is semidet.

% :- pragma memo(class_or_interface_declaration/2).
class_or_interface_declaration -->
    modifiers_opt,
    ( if
        class_declaration
      then
        []
      else
        interface_declaration
    ).

% ClassDeclaration:
%         class Identifier [extends Type] [implements TypeList] ClassBody

:- pred class_declaration(ps::in, ps::out) is semidet.

% :- pragma memo(class_declaration/2).
class_declaration -->
    keyword("class"),
    java_identifier,
    optional(keyword("extends", java_type)),
    optional(keyword("implements", java_type_list)),
    class_body.

% InterfaceDeclaration:
%         interface Identifier [extends TypeList] InterfaceBody

:- pred interface_declaration(ps::in, ps::out) is semidet.

% :- pragma memo(interface_declaration/2).
interface_declaration -->
    keyword("interface"),
    java_identifier,
    optional(keyword("extends", java_type_list)),
    interface_body.

% TypeList:
%         Type {  ,   Type}

:- pred java_type_list(ps::in, ps::out) is semidet.

% :- pragma memo(java_type_list/2).
java_type_list -->
    comma_separated_list(java_type).

% ClassBody:
%         { {ClassBodyDeclaration} }

:- pred class_body(ps::in, ps::out) is semidet.

% :- pragma memo(class_body/2).
class_body -->
    brackets("{", zero_or_more(class_body_declaration), "}").

% InterfaceBody:
%         { {InterfaceBodyDeclaration} }

:- pred interface_body(ps::in, ps::out) is semidet.

% :- pragma memo(interface_body/2).
interface_body -->
    brackets("{", zero_or_more(interface_body_declaration), "}").

% ClassBodyDeclaration:
%         ;
%         [static] Block
%         ModifiersOpt MemberDecl

:- pred class_body_declaration(ps::in, ps::out) is semidet.

% :- pragma memo(class_body_declaration/2).
class_body_declaration -->
    ( if
        punct(";")
      then
        []
      else if
        optional(keyword("static")), block
      then
        []
      else
        modifiers_opt, member_decl
    ).

% MemberDecl:
%         MethodOrFieldDecl
%         void Identifier MethodDeclaratorRest
%         Identifier ConstructorDeclaratorRest
%         ClassOrInterfaceDeclaration

:- pred member_decl(ps::in, ps::out) is semidet.

% :- pragma memo(member_decl/2).
member_decl -->
    ( if
        class_or_interface_declaration
      then
        []
      else if
        method_or_field_decl
      then
        []
      else if
        keyword("void"), java_identifier, method_declarator_rest
      then
        []
      else
        java_identifier, constructor_declarator_rest
    ).

% MethodOrFieldDecl:
%         Type Identifier MethodOrFieldRest

:- pred method_or_field_decl(ps::in, ps::out) is semidet.

% :- pragma memo(method_or_field_decl/2).
method_or_field_decl -->
    java_type, java_identifier, method_or_field_rest.

% MethodOrFieldRest:
%         VariableDeclaratorRest
%         MethodDeclaratorRest
%         XXX First should be
%           VariableDeclaratorRest [',' VariableDeclarators]

:- pred method_or_field_rest(ps::in, ps::out) is semidet.

% :- pragma memo(method_or_field_rest/2).
method_or_field_rest -->
    ( if
        method_declarator_rest
      then
        []
      else
        variable_declarator_rest,
        ( if punct(",") then
            variable_declarators
          else
            []
        )
    ).

% InterfaceBodyDeclaration:
%         ;
%         ModifiersOpt InterfaceMemberDecl

:- pred interface_body_declaration(ps::in, ps::out) is semidet.

% :- pragma memo(interface_body_declaration/2).
interface_body_declaration -->
    ( if
        punct(";")
      then
        []
      else
        modifiers_opt, interface_member_decl
    ).

% InterfaceMemberDecl:
%         InterfaceMethodOrFieldDecl
%         void Identifier VoidInterfaceMethodDeclaratorRest
%         ClassOrInterfaceDeclaration

:- pred interface_member_decl(ps::in, ps::out) is semidet.

% :- pragma memo(interface_member_decl/2).
interface_member_decl -->
    ( if
        interface_method_or_field_decl
      then
        []
      else if
        keyword("void"), java_identifier, void_interface_method_declarator_rest
      then
        []
      else
        class_or_interface_declaration
    ).

% InterfaceMethodOrFieldDecl:
%         Type Identifier InterfaceMethodOrFieldRest

:- pred interface_method_or_field_decl(ps::in, ps::out) is semidet.

% :- pragma memo(interface_method_or_field_decl/2).
interface_method_or_field_decl -->
    java_type, java_identifier, interface_method_or_field_rest.

% InterfaceMethodOrFieldRest:
%         ConstantDeclaratorsRest ;
%         InterfaceMethodDeclaratorRest

:- pred interface_method_or_field_rest(ps::in, ps::out) is semidet.

% :- pragma memo(interface_method_or_field_rest/2).
interface_method_or_field_rest -->
    ( if
        constant_declarator_rest
      then
        []
      else
        interface_method_declarator_rest
    ).

% MethodDeclaratorRest:
%                 FormalParameters BracketsOpt [throws QualifiedIdentifierList] ( MethodBody |   ;  )

:- pred method_declarator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(method_declarator_rest/2).
method_declarator_rest -->
    formal_parameters,
    brackets_opt,
    optional(throws_qualified_identifier_list),
    method_body_or_semicolon.

:- pred method_body_or_semicolon(ps::in, ps::out) is semidet.

% :- pragma memo(method_body_or_semicolon/2).
method_body_or_semicolon -->
    ( if
        method_body
      then
        []
      else
        punct(";")
    ).

% VoidMethodDeclaratorRest:
%                 FormalParameters [throws QualifiedIdentifierList] ( MethodBody |   ;  )

:- pred void_method_declarator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(void_method_declarator_rest/2).
void_method_declarator_rest -->
    formal_parameters,
    optional(throws_qualified_identifier_list),
    method_body_or_semicolon.

% InterfaceMethodDeclaratorRest:
%         FormalParameters BracketsOpt [throws QualifiedIdentifierList]   ;

:- pred interface_method_declarator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(interface_method_declarator_rest/2).
interface_method_declarator_rest -->
    formal_parameters,
    brackets_opt,
    optional(throws_qualified_identifier_list),
    punct(";").

% VoidInterfaceMethodDeclaratorRest:
%         FormalParameters [throws QualifiedIdentifierList]   ;

:- pred void_interface_method_declarator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(void_interface_method_declarator_rest/2).
void_interface_method_declarator_rest -->
    formal_parameters, optional(throws_qualified_identifier_list), punct(";").

% ConstructorDeclaratorRest:
%         FormalParameters [throws QualifiedIdentifierList] MethodBody

:- pred constructor_declarator_rest(ps::in, ps::out) is semidet.

% :- pragma memo(constructor_declarator_rest/2).
constructor_declarator_rest -->
    formal_parameters, optional(throws_qualified_identifier_list), method_body.

:- pred throws_qualified_identifier_list(ps::in, ps::out) is semidet.

% :- pragma memo(throws_qualified_identifier_list/2).
throws_qualified_identifier_list -->
    keyword("throws"), qualified_identifier_list.

% QualifiedIdentifierList:
%         QualifiedIdentifier {  ,   QualifiedIdentifier}

:- pred qualified_identifier_list(ps::in, ps::out) is semidet.

% :- pragma memo(qualified_identifier_list/2).
qualified_identifier_list -->
    comma_separated_list(qualified_identifier).

% FormalParameters:
%         ( [FormalParameter { , FormalParameter}] )

:- pred formal_parameters(ps::in, ps::out) is semidet.

% :- pragma memo(formal_parameters/2).
formal_parameters -->
    brackets("(", optional(comma_separated_list(formal_parameter)), ")").

% FormalParameter:
%         [final] Type VariableDeclaratorId

:- pred formal_parameter(ps::in, ps::out) is semidet.

% :- pragma memo(formal_parameter/2).
formal_parameter -->
    optional(keyword("final")), java_type, variable_declarator_id.

% MethodBody:
%         Block

:- pred method_body(ps::in, ps::out) is semidet.

% :- pragma memo(method_body/2).
method_body -->
    block.

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
