/*-------------------------------------------------------------------------
Coco.ATG -- Attributed Grammar
Compiler Generator Coco/R,
Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz
extended by M. Loeberbauer & A. Woess, Univ. of Linz
with improvements by Pat Terry, Rhodes University

This program is free software; you can redistribute it and/or modify it 
under the terms of the GNU General Public License as published by the 
Free Software Foundation; either version 2, or (at your option) any 
later version.

This program is distributed in the hope that it will be useful, but 
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
for more details.

You should have received a copy of the GNU General Public License along 
with this program; if not, write to the Free Software Foundation, Inc., 
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

As an exception, it is allowed to write an extension of Coco/R that is
used as a plugin in non-free software.

If not otherwise stated, any source code generated by Coco/R (other than 
Coco/R itself) does not fall under the GNU General Public License.
-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------
 compile with:
   Coco Coco.ATG -namespace at.jku.ssw.Coco
-------------------------------------------------------------------------*/

using System.Collections;

COMPILER Coco

	const int id = 0;
	const int str = 1;
	
	static bool genScanner;
	static string tokenString;         // used in declarations of literal tokens
	static string noString = "-none-"; // used in declarations of literal tokens

/*-------------------------------------------------------------------------*/

CHARACTERS
	letter    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
	digit     = "0123456789".
	cr        = '\r'.
	lf        = '\n'.
	tab       = '\t'.
	stringCh  = ANY - '"' - '\\' - cr - lf.
	charCh    = ANY - '\'' - '\\' - cr - lf.
	printable = '\u0020' .. '\u007e'.
	hex       = "0123456789abcdef".

TOKENS
	ident     = letter { letter | digit }.
	number    = digit { digit }.
	string    = '"' { stringCh | '\\' printable } '"'.
	badString = '"' { stringCh | '\\' printable } (cr | lf).
	char      = '\'' ( charCh | '\\' printable { hex } ) '\''.

PRAGMAS
	ddtSym    = '$' { digit | letter }.  (. Tab.SetDDT(la.val); .)

COMMENTS FROM "/*" TO "*/" NESTED

IGNORE cr + lf + tab

/*-------------------------------------------------------------------------*/

PRODUCTIONS

Coco                            (. Symbol sym; Graph g, g1, g2; string gramName; .)
=
  [ UsingDecl<out ParserGen.usingPos> ]

  "COMPILER"                    (. genScanner = true; Tab.ignored = null; .) 
  ident                         (. gramName = t.val;
                                   int beg = la.pos; 
                                 .)
  { ANY }                       (. Tab.semDeclPos = new Position(beg, la.pos-beg, 0); .)
  [ "IGNORECASE"                (. DFA.ignoreCase = true; .) ]   /* pdt */
  [ "CHARACTERS" { SetDecl }]
  [ "TOKENS"  { TokenDecl<Node.t> }]
  [ "PRAGMAS" { TokenDecl<Node.pr> }]
  { "COMMENTS"                  (. bool nested = false; .)
    "FROM" TokenExpr<out g1> 
    "TO" TokenExpr<out g2>
    [ "NESTED"                  (. nested = true; .)
    ]                           (. new Comment(g1.l, g2.l, nested); .)
  }
  { "IGNORE" 
    Set<out Tab.ignored>        (. Tab.ignored[' '] = true; /* ' ' is always ignored */ .)
  }

  SYNC 
  "PRODUCTIONS"                 (. if (genScanner) DFA.MakeDeterministic();
                                   Graph.DeleteNodes();
                                 .)
  { ident                       (. sym = Symbol.Find(t.val);
                                   bool undef = sym == null;
                                   if (undef) sym = new Symbol(Node.nt, t.val, t.line);
                                   else {
                                     if (sym.typ == Node.nt) {
                                       if (sym.graph != null) SemErr("name declared twice");
                                   	 } else SemErr("this symbol kind not allowed on left side of production");
                                   	 sym.line = t.line;
                                   }
                                   bool noAttrs = sym.attrPos == null;
                                   sym.attrPos = null;
                                 .)
    [ AttrDecl<sym> ]           (. if (!undef)
                                     if (noAttrs != (sym.attrPos == null))
                                       SemErr("attribute mismatch between declaration and use of this symbol");
                                 .)
    [ SemText<out sym.semPos> ] WEAK
    '='
    Expression<out g>           (. sym.graph = g.l;
                                   Graph.Finish(g);
                                 .)
                                WEAK
    '.'
  }
  "END" ident                   (. if (gramName != t.val)
                                     SemErr("name does not match grammar name");
                                   Tab.gramSy = Symbol.Find(gramName);
                                   if (Tab.gramSy == null)
                                     SemErr("missing production for grammar name");
                                   else {
                                     sym = Tab.gramSy;
                                     if (sym.attrPos != null)
                                       SemErr("grammar symbol must not have attributes");
                                   }
                                   Tab.noSym = new Symbol(Node.t, "???", 0); // noSym gets highest number
                                   Tab.SetupAnys();
                                   Tab.RenumberPragmas();
                                   if (Tab.ddt[2]) Node.PrintNodes();
                                   if (Errors.count == 0) {
                                     Console.WriteLine("checking");
                                     Tab.CompSymbolSets();
                                     if (Tab.ddt[7]) Tab.XRef();
                                     if (Tab.GrammarOk()) {
                                       Console.Write("parser");
                                       ParserGen.WriteParser();
                                       if (genScanner) {
                                         Console.Write(" + scanner");
                                         DFA.WriteScanner();
                                         if (Tab.ddt[0]) DFA.PrintStates();
                                       }
                                       Console.WriteLine(" generated");
                                       if (Tab.ddt[8]) ParserGen.WriteStatistics();
                                     }
                                   }
                                   if (Tab.ddt[6]) Tab.PrintSymbolTable();
                                 .)
  '.'
.

/*------------------------------------------------------------------------------------*/

SetDecl                         (. BitArray s; .)
=
  ident                         (. string name = t.val;
                                   CharClass c = CharClass.Find(name);
                                   if (c != null) SemErr("name declared twice");
                                 .)
  '=' Set<out s>                (. if (Sets.Elements(s) == 0) SemErr("character set must not be empty");
                                   c = new CharClass(name, s);
                                 .)
  '.'
.

/*------------------------------------------------------------------------------------*/

Set<out BitArray s>             (. BitArray s2; .)
=
  SimSet<out s>
  { '+' SimSet<out s2>          (. s.Or(s2); .)
  | '-' SimSet<out s2>          (. Sets.Subtract(s, s2); .)
  }
.

/*------------------------------------------------------------------------------------*/

SimSet<out BitArray s>          (. int n1, n2; .)
=                               (. s = new BitArray(CharClass.charSetSize); .)
( ident                         (. CharClass c = CharClass.Find(t.val);
                                   if (c == null) SemErr("undefined name"); else s.Or(c.set);
                                 .)
| string                        (. string name = t.val;
                                   name = DFA.Unescape(name.Substring(1, name.Length-2));
                                   foreach (char ch in name)
                                     if (DFA.ignoreCase) s[char.ToLower(ch)] = true;
                                     else s[ch] = true; .)
| Char<out n1>                  (. s[n1] = true; .)
  [ ".." Char<out n2>           (. for (int i = n1; i <= n2; i++) s[i] = true; .)
  ]
| "ANY"                         (. s = new BitArray(CharClass.charSetSize, true); .)
)
.

/*--------------------------------------------------------------------------------------*/

Char<out int n>
=
  char                          (. string name = t.val; n = 0;
                                   name = DFA.Unescape(name.Substring(1, name.Length-2));
                                   int max = CharClass.charSetSize;
                                   if (name.Length == 1 && name[0] <= max-1) n = name[0] % max;
                                   else SemErr("unacceptable character value");
                                   if (DFA.ignoreCase && (char)n >= 'A' && (char)n <= 'Z') n += 32;
                                 .)
.

/*------------------------------------------------------------------------------------*/

TokenDecl<int typ>              (. string name; int kind; Symbol sym; Graph g; .)
=
  Sym<out name, out kind>       (. sym = Symbol.Find(name);
                                   if (sym != null) SemErr("name declared twice");
                                   else {
                                     sym = new Symbol(typ, name, t.line);
                                     sym.tokenKind = Symbol.fixedToken;
                                   }
                                   tokenString = null;
                                 .)
  SYNC
  ( '=' TokenExpr<out g> '.'    (. if (kind == str) SemErr("a literal must not be declared with a structure");
                                   Graph.Finish(g);
                                   if (tokenString == null || tokenString.Equals(noString))
                                     DFA.ConvertToStates(g.l, sym);
                                   else { // TokenExpr is a single string
                                     if (Tab.literals[tokenString] != null)
                                       SemErr("token string declared twice");
                                     Tab.literals[tokenString] = sym;
                                     DFA.MatchLiteral(tokenString, sym);
                                   }
                                 .)
  |                             (. if (kind == id) genScanner = false;
                                   else DFA.MatchLiteral(sym.name, sym);
                                 .)
  )
  [ SemText<out sym.semPos>     (. if (typ != Node.pr) SemErr("semantic action not allowed here"); .)
  ]
.

/*------------------------------------------------------------------------------------*/

AttrDecl<Symbol sym>
=
  '<'                           (. int beg = la.pos; int col = la.col; .)
  { ANY
  | badString                   (. SemErr("bad string in attributes"); .)
  }
  '>'                           (. if (t.pos > beg)
                                     sym.attrPos = new Position(beg, t.pos - beg, col); .)
.

/*------------------------------------------------------------------------------------*/

Expression<out Graph g>         (. Graph g2; .)
= 
  Term<out g>                   (. bool first = true; .)
  {                             WEAK
    '|'
    Term<out g2>                (. if (first) { Graph.MakeFirstAlt(g); first = false; }
                                   Graph.MakeAlternative(g, g2);
                                 .)
  }
.

/*------------------------------------------------------------------------------------*/

Term<out Graph g>               (. Graph g2; Node rslv = null; g = null; .)
=
( [                             (. rslv = new Node(Node.rslv, null, la.line); .)
    Resolver<out rslv.pos>      (. g = new Graph(rslv); .)
  ]
  Factor<out g2>                (. if (rslv != null) Graph.MakeSequence(g, g2);
                                   else g = g2;
                                 .)
  { Factor<out g2>              (. Graph.MakeSequence(g, g2); .)
  }
|                               (. g = new Graph(new Node(Node.eps, null, 0)); .)
)                               (. if (g == null) // invalid start of Term
                                     g = new Graph(new Node(Node.eps, null, 0));
                                 .)
.

/*------------------------------------------------------------------------------------*/

Factor<out Graph g>             (. string name; int kind; Position pos; bool weak = false; 
                                   g = null;
                                 .)
=
( [ "WEAK"                      (. weak = true; .)
  ]
  Sym<out name, out kind>       (. Symbol sym = Symbol.Find(name);
                                   if (sym == null && kind == str)
                                     sym = Tab.literals[name] as Symbol;
                                   bool undef = sym == null;
                                   if (undef) {
                                     if (kind == id)
                                       sym = new Symbol(Node.nt, name, 0);  // forward nt
                                     else if (genScanner) { 
                                       sym = new Symbol(Node.t, name, t.line);
                                       DFA.MatchLiteral(sym.name, sym);
                                     } else {  // undefined string in production
                                       SemErr("undefined string in production");
                                       sym = Tab.eofSy;  // dummy
                                     }
                                   }
                                   int typ = sym.typ;
                                   if (typ != Node.t && typ != Node.nt)
                                     SemErr("this symbol kind is not allowed in a production");
                                   if (weak)
                                     if (typ == Node.t) typ = Node.wt;
                                     else SemErr("only terminals may be weak");
                                   Node p = new Node(typ, sym, t.line);
                                   g = new Graph(p);
                                 .)
  [ Attribs<p>                  (. if (kind != id) SemErr("a literal must not have attributes"); .)
  ]                             (. if (undef)
                                     sym.attrPos = p.pos;  // dummy
                                   else if ((p.pos == null) != (sym.attrPos == null))
                                     SemErr("attribute mismatch between declaration and use of this symbol");
                                 .)
| '(' Expression<out g> ')'
| '[' Expression<out g> ']'     (. Graph.MakeOption(g); .)
| '{' Expression<out g> '}'     (. Graph.MakeIteration(g); .)
| SemText<out pos>              (. Node p = new Node(Node.sem, null, 0);
                                   p.pos = pos;
                                   g = new Graph(p);
                                 .)
| "ANY"                         (. Node p = new Node(Node.any, null, 0);  // p.set is set in Tab.SetupAnys
                                   g = new Graph(p);
                                 .)
| "SYNC"                        (. Node p = new Node(Node.sync, null, 0);
                                   g = new Graph(p);
                                 .)
)                               (. if (g == null) // invalid start of Factor
                                     g = new Graph(new Node(Node.eps, null, 0));
                                 .)
.

/*------------------------------------------------------------------------------------*/

Resolver<out Position pos>   
=
  "IF" "("                       (. int beg = la.pos; int col = la.col; .)
  Condition                      (. pos = new Position(beg, t.pos - beg, col); .)
.

/*------------------------------------------------------------------------------------*/

Condition = { "(" Condition | ANY } ")" .

/*------------------------------------------------------------------------------------*/

TokenExpr<out Graph g>          (. Graph g2; .)
=
  TokenTerm<out g>              (. bool first = true; .)
  {                             WEAK
    '|'
    TokenTerm<out g2>           (. if (first) { Graph.MakeFirstAlt(g); first = false; }
                                   Graph.MakeAlternative(g, g2);
                                 .)
  }
.

/*------------------------------------------------------------------------------------*/

TokenTerm<out Graph g>          (. Graph g2; .)
=
  TokenFactor<out g>
  { TokenFactor<out g2>         (. Graph.MakeSequence(g, g2); .)
  }
  [ "CONTEXT"
    '(' TokenExpr<out g2>       (. Graph.SetContextTrans(g2.l); Graph.MakeSequence(g, g2); .)
    ')'
  ]
.

/*------------------------------------------------------------------------------------*/

TokenFactor<out Graph g>        (. string name; int kind; .)
=
                                (. g = null; .)
( Sym<out name, out kind>       (. if (kind == id) {
                                     CharClass c = CharClass.Find(name);
                                     if (c == null) {
                                       SemErr("undefined name");
                                       c = new CharClass(name, new BitArray(CharClass.charSetSize));
                                     }
                                     Node p = new Node(Node.clas, null, 0); p.val = c.n;
                                     g = new Graph(p);
                                     tokenString = noString;
                                   } else { // str
                                     g = Graph.StrToGraph(name);
                                     if (tokenString == null) tokenString = name;
                                     else tokenString = noString;
                                   }
                                 .)
| '(' TokenExpr<out g> ')'
| '[' TokenExpr<out g> ']'      (. Graph.MakeOption(g); .)
| '{' TokenExpr<out g> '}'      (. Graph.MakeIteration(g); .)
)                               (. if (g == null) // invalid start of TokenFactor
                                     g = new Graph(new Node(Node.eps, null, 0)); .)
.

/*------------------------------------------------------------------------------------*/

Sym<out string name, out int kind>
=                               (. name = "???"; kind = id; .)
( ident                         (. kind = id; name = t.val; .)
| (string                       (. name = t.val; .)
  | char                        (. name = "\"" + t.val.Substring(1, t.val.Length-2) + "\""; .)
  )                             (. kind = str;
                                   if (DFA.ignoreCase) name = name.ToLower();
                                   if (name.IndexOf(' ') >= 0)
                                     SemErr("literal tokens must not contain blanks"); .)
)
.

/*------------------------------------------------------------------------------------*/

Attribs<Node p>
=
  '<'                           (. int beg = la.pos; int col = la.col; .)
  { Attribs<p> 
  |ANY 
  | badString                   (. SemErr("bad string in attributes"); .)
  }
  '>'                           (. if (t.pos > beg) p.pos = new Position(beg, t.pos - beg, col); .)
.

/*------------------------------------------------------------------------------------*/

SemText<out Position pos>
=
  "(."                          (. int beg = la.pos; int col = la.col; .)
  { ANY
  | badString                   (. SemErr("bad string in semantic action"); .)
  | "(."                        (. SemErr("missing end of previous semantic action"); .)
  }
  ".)"                          (. pos = new Position(beg, t.pos - beg, col); .)
.

/*------------------------------------------------------------------------------------*/

UsingDecl<out Position pos>
=
  "using"                       (. int beg = t.pos; .)
  { ANY } ';'                   (. int end = t.pos; .)
  { "using" { ANY } ';'         (. end = t.pos; .)
  }                             (. pos = new Position(beg, end - beg + 1, 0); .)
.
  
END Coco.
