:- use_module(library(dcg/basics)).

:- set_prolog_flag(double_quotes, chars).

/*
Stroustroup, B. The C++ Programming Language 2nd Ed. 1993.

Appendix A: Grammar Summary (p. 614)
*/

% 1 or more whitespace characters, including newline.
ws --> blank, blanks.

% Because it just looks weird having a lot of quoted commas in a
% comma-separated list.
comma --> ",".

/* Symbols that are operators: */
% Equality
greater --> ">".
greater_or_equal --> ">=".
less --> "<".
less_or_equal --> "<=".
equal --> "==".
not_equal --> "!=".
% Bit shift
shift_left --> "<<".
shift_right --> ">>".
% Arithmetic
addition --> "+".
subtraction --> "-".
mulitplication --> "*".
division --> "/".
exponentiation --> "^".
modulo --> "%".
/*
 r.17.1 Keywords
*/
class_name(N) --> identifier(N).
enum_name(N) --> identifier(N).
typedef_name(N) --> identifier(N).

keyword(K) --> blank, string(K).

% TODO: "Note that a typedef-name naming a class is also a class-name"
% (p. 615).

/*
 r.17.2 Expressions
*/
expression([E|Exps]) --> blanks, assignment_expression([E|Exps]), blanks.
expression([E1, E2]) --> expression(E1),
                        blanks, comma, blanks,
                        assignment_expression(E2), blanks.

assignment_expression([E|Exps]) --> conditional_expression([E|Exps]).
assignment_expression([E1, Operator, E2]) --> unary_expression(E1),
                                           assignment_operator(Operator),
                                           assignment_expression(E2).

conditional_expression([E|Exps]) --> logical_or_expression([E|Exps]).
conditional_expression([E1, E2, E3]) --> logical_or_expression(E1),
                                      expression(E2),
                                      conditional_expression(E3).

% DCG rules given here for logical/inclusive and/or expressions from
% p. 615 are more literal than what's in the book.
logical_and_expression([E1, E2]) --> expression(E1), "&&", expression(E2).
logical_or_expression([E1, E2]) --> expression(E1), "||", expression(E2).
inclusive_or_expression([E1, E2]) --> expression(E1), "|", expression(E2).
inclusive_and_expression([E1, E2]) --> expression(E1), "&", expression(E2).

equality_expression([E|Exps]) --> relational_expression([E|Exps]).
equality_expression([E1, E2]) -->
   equality_expression(E1), ("==" | "!="), relational_expression(E2).

relational_expression([E|Exps]) --> shift_expression([E|Exps]).
relational_expression([E1, E2]) -->
   relational_expression(E1),
   ("<" | ">" | "<=" | ">=" ),
   shift_expression.

shift_expression([E|Exps]) --> additive_expression([E|Exps]).
shift_expression([E1, E2]) -->
   shift_expression(E1), ("<<" | ">>"), additive_expression(E2).

additive_expression([E|Exps]) --> multiplicative_expression([E|Exps]).
additive_expression([E1, E2]) -->
   additive_expression(E1), ("+" | "-"), multiplicative_expression(E2).



multiplicative_expression([E|Exps]) --> pm_expression([E|Exps]).
multiplicative_expression([E1, E2]) -->
   multiplicative_expression(E1), ("*" | "/" | "%"), pm_expression(E2).

% What the fudge is a "pm_expression"?  (p. 616).  The 'm' probably
% stands for "member".
pm_expression([E|Exps]) --> cast_expression([E|Exps]).
pm_expression([E1, E2]) -->
   pm_expression(E1), (".*" | "->*"), cast_expression(E2).

cast_expression([E|Exps]) --> unary_expression([E|Exps]).
cast_expression([E1, E2]) --> type_name(E1), cast_expression(E2).


sizeof([E|Exps]) --> "sizeof", ws, unary_expression([E|Exps]).
sizeof([E|Exps]) -->
   "sizeof", blanks, "(", type_name([E|Exps]), ")".

unary_expression([E|Exps]) --> postfix_expression([E|Exps]).
unary_expression([E|Exps]) --> ("++" | "--"), unary_expression([E|Exps]).
unary_expression([E|Exps]) --> "sizeof", unary_expression([E|Exps]).
unary_expression([E|Exps]) --> sizeof([E|Exps]).
unary_expression([E1, E2]) --> unary_operator(E1), cast_expression(E2).
unary_expression([E|Exps]) -->
   ( allocation_expression([E|Exps]) | deallocation_expression([E|Exps]) ).

unary_operator --> ("*" | "&" | "+" | "-" | "!" | "~").


allocation_expression_([E1,E2,E3]) -->
   "new", ws, placement(E1), new_type_name(E2), new_initializer(E3).
allocation_expression_([E1,E2,E3]) -->"new", ws, placement(E1),
                                     blanks, "(" blanks,
                                     type_name(E2),
                                     blanks, ")" blanks,
                                     new_initializer(E3).
allocation_expression_([E1,E2]) -->
   "new", ws, new_type_name(E1), new_initializer(E2).
allocation_expression_([E1,E2,E3]) --> "new", ws,
                                      "(", type_name(E1), ")".
                                      new_initializer(E2).
allocation_expression([E|Exps]) --> "::", allocation_expression_([E|Exps]).
allocation_expression([E|Exps]) --> allocation_expression_([E|Exps]).

placement([E|Exps]) --> blanks, "(", expression_list([E|Exps]), ")".

% TODO: new_declarator (was skipped).

new_initializer([E|Exps]) --> blanks, "(", initializer_list([E|Exps]), ")".
new_initializer([E|Exps]) --> blanks, "(", blanks, ")".

deallocation_expression_([E|Exps]) --> ws, "delete", ws, cast_expression([E|Exps]).
deallocation_expression_([E|Exps]) -->
   ws, "delete", blanks, "[", blanks, "]", ws, cast_expression([E|Exps]).
deallocation_expression([E|Exps]) --> deallocation_expression_([E|Exps]).
deallocation_expression([E|Exps]) --> "::", deallocation_expression_([E|Exps]).

postfix_expression([E|Exps]) --> primary_expression([E|Exps]).
postfix_expression([E|Exps]) --> postfix_expression([E|Exps]), ("++" | "--").
postfix_expression([E1, E2]) -->
   postfix_expression(E1), blanks, "[", expression(E2), "]".
postfix_expression([E1, E2]) -->
   postfix_expression(E1), blanks, "(", expression_list(E2), ")".
postfix_expression([E1, E2]) -->
   simple_type_name(E1), blanks, "(", expression_list(E2), ")".
postfix_expression([E1, E2]) --> postfix_expression(E1), ("." | "->"), name(E2).

expression_list([E|Exps]) --> assignment_expression([E|Exps]).
expression_list([E1, E2]) -->
   expression_list(E1), blanks, comma, blanks, assignment_expression(E2).

primary_expression([]) --> "this".
primary_expression([E]) --> literal([E]).
primary_expression([E|Exps]) --> "::", identifier([E|Exps]).
primary_expression([E|Exps]) --> "::", operator_function_name([E|Exps]).
primary_expression([E|Exps]) --> "::", qualified_name([E|Exps]).
primary_expression([E|Exps]) -->
   "(", blanks, expression([E|Exps]), blanks, ")".
primary_expression([E|Exps]) --> name([E|Exps]).

name([E|Exps]) --> identifier([E|Exps]).
name([E|Exps]) --> operator_function_name([E|Exps]).
name([E|Exps]) --> conversion_function_name([E|Exps]).
name([E|Exps]) --> "~", class_name([E|Exps]).
name([E|Exps]) --> qualified_name([E|Exps]).

qualified_name([E1, E2]) --> qualified_class_name(E1), "::", name(E2).

literal([X]) --> integer_constant([X]).
literal([X]) --> character_constant([X]).
literal([X]) --> floating_constant([X]).
literal([X]) --> string_literal([X]).

/*
  r.17.3 Declarations (p. 618)
*/

declaration([E|Exps]) --> decl_specifiers([E|Exps]), blanks, ";".
declaration([E|Exps]) --> declarator_list([E|Exps]), blanks, ";".
declaration([E1, E2]) -->
   decl_specifiers(E1), blanks, declarator_list(E2), blanks, ";".
declaration([E|Exps]) --> asm_declaration([E|Exps]).
declaration([E|Exps]) --> template_declaration([E|Exps]).
declaration([E|Exps]) --> linkage_declaration([E|Exps]).

auto --> "auto", ws.
register --> "register", ws.
static --> "static", ws.
extern --> "extern", ws.
friend --> "friend", ws.
typedef --> "typedef", ws.
inline --> "inline", ws.
virtual --> "virtual", ws.
const --> "const", ws.
volatile --> "volatile", ws.


decl_specifier([E|Exps]) --> storage_class_specifier([E|Exps]).
decl_specifier([E|Exps]) --> type_specifier([E|Exps]).
decl_specifier([E|Exps]) --> fct_specifier([E|Exps]).
decl_specifier(["friend"]) --> friend
decl_specifier(["typedef"]) --> typedef.

decl_specifiers([E|Exps]) --> decl_specifier([E|Exps]).

storage_class_specifier(["auto"]) --> auto.
storage_class_specifier(["register"]) --> register.
storage_class_specifier(["static"])   --> static
storage_class_specifier(["extern"])   --> extern

fct_specifier(["inline"]) --> inline.
fct_specifier(["virtual"]) --> virtual.

type_specifier([E|Exps]) --> simple_type_name([E|Exps]).
type_specifier([E|Exps]) --> class_specifier([E|Exps]).
type_specifier([E|Exps]) --> enum_specifier([E|Exps]).
type_specifier([E|Exps]) --> elaborated_type_specifier([E|Exps]).
type_specifier(["const"]) --> const.
type_specifier(["volatile"]) --> volatile.

simple_type_name --> [].