@license{
  Copyright (c) 2009-2015 CWI
  All rights reserved. This program and the accompanying materials
  are made available under the terms of the Eclipse Public License v1.0
  which accompanies this distribution, and is available at
  http://www.eclipse.org/legal/epl-v10.html
}
@contributor{Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI}
@contributor{Arnold Lankamp - Arnold.Lankamp@cwi.nl}
@synopsis{This module provides functionality for merging the Rascal grammar and arbitrary user-defined grammars}
module lang::rascal::grammar::ConcreteSyntax

import ValueIO;
import ParseTree;
import Grammar;
import lang::rascal::\syntax::Rascal;
import lang::rascal::grammar::definition::Productions;
import lang::rascal::grammar::definition::Literals;
import lang::rascal::grammar::definition::Regular;
import lang::rascal::grammar::definition::Symbols;

public Grammar addHoles(Grammar object) = compose(object, grammar({}, holes(object)));

@synopsis{For every non-terminal in the grammar we create a rule that can recognize its hole syntax. Each hole
  is specific for a non-terminal (using its name as a literal), such that no ambiguity can arise. When parsing
  a fragment, the Rascal evaluator will generate a string that matches this generated syntax. It does so based on
  knowing the type of the variable, which is either declared internally in the '<...>' syntax, or derived by the 
  type checker.
  
  List types are treated differently. The are added as alternatives to the element type, such that an 
  arbitrary number of list variables can be elements of list patterns. To retrieve their original type, we store
  the list symbol as one of the attributes of the generated production.
  
  Another exception is made for lexicals. Since currently the type checker is not inline yet, we can not see at parse time
  which nonterminal names are lexical and which are not. Since nonterminal names are unique between lex and normal nonterminals
  we make sure that the literal that is generated is first normalized to remove all lex names.}
public set[Production] holes(Grammar object) {
  // syntax N = @holeType=<N> [-1] "N" ":" [0-9]+ [-1];
  return  { regular(iter(\char-class([range(48,57)]))), 
            prod(label("$MetaHole",getTargetSymbol(nont)),
                 [ \char-class([range(0,0)]),
                   lit("<removeConditionals(denormalize(nont))>"),lit(":"),iter(\char-class([range(48,57)])),
                   \char-class([range(0,0)])
                 ],{Attr::\tag("holeType"(nont))})  // TODO: added qualifier to help compiler
          | Symbol nont <- object.rules, quotable(nont)
          };
}

@synopsis{This function is called by the Rascal interpreter to generate a string that can be parsed by the rules generated by the
  holes function in this module.}
public str createHole(ConcretePart hole, int idx) = createHole(hole.hole, idx);
public str createHole(ConcreteHole hole, int idx) = "\u0000<denormalize(sym2symbol(hole.symbol))>:<idx>\u0000";


@synopsis{In Rascal programs with type literals, it's hard to see easily if it is a lex or sort, so we "denormalize" here.
  The same goes for the introduction of layout non-terminals in lists. We do not know which non-terminal is introduced,
  so we remove this here to create a canonical 'source-level' type.}
private Symbol denormalize(Symbol s) = visit (s) { 
  case \lex(n)                                      => \sort(n)
  case \iter-seps(u,[layouts(_),t,layouts(_)])      => \iter-seps(u,[t])
  case \iter-star-seps(u,[layouts(_),t,layouts(_)]) => \iter-star-seps(u,[t])
  case \iter-seps(u,[layouts(_)])                   => \iter(u)
  case \iter-star-seps(u,[layouts(_)])              => \iter-star(u)
  case \seq(ss)                                     => seq([t | t <- ss, !(t is layouts)])
};

private Symbol removeConditionals(Symbol sym) = visit(sym) {
  case conditional(Symbol s, _) => s
};

@synopsis{This is needed such that list variables can be repeatedly used as elements of the same list}
private Symbol getTargetSymbol(Symbol sym) {
  switch(sym) {
    case Symbol::\iter(s) : return s;
    case Symbol::\iter-star(s) : return s;  
    case Symbol::\iter-seps(s, _) : return s; 
    case Symbol::\iter-star-seps(s, _) : return s; 
    case Symbol::\opt(s) : return s; 
    default: return sym;
  } 
}

// TODO, rewritten quotable for the benefit of the compiler
// Offending case:
//data Symbol
//        =  \lit()   
//        | \parameter() 
//        | \parameterized-lex(list[Symbol] parameters)
//        ;
//     
//private bool quotable(Symbol x) { 
//    return 
//       \lit() := x 
//       &&
//       \parameterized-lex([\parameter()]) !:= x
//       ;
//}

@synopsis{This decides for which part of the grammar we can write anti-quotes}
private bool quotable(Symbol x) { 
    switch(x){ 
        case \lit(_): return false;
        case \empty(): return false;
        case \cilit(_): return false;
        case \char-class(_): return false;
        case \layouts(_): return false;
        case \keywords(_): return false;
        case \start(_): return false;
        case \parameterized-sort(_,[\parameter(_,_),*_]): return false;
        case \parameterized-lex(_,[\parameter(_,_),*_]): return false;
        default: return true;
     };
}
