coco-cs_20110419/0000777000175000010010000000000011553276246011462 5ustar mlNonecoco-cs_20110419/build.bat0000777000175000010010000000017711466520560013253 0ustar mlNone%windir%\Microsoft.Net\Framework\v1.1.4322\csc.exe /out:Coco.exe /t:exe Coco.cs Scanner.cs Tab.cs DFA.cs ParserGen.cs Parser.cscoco-cs_20110419/coc.bat0000777000175000010010000000005011467576362012722 0ustar mlNonecoco Coco.ATG -namespace at.jku.ssw.Cocococo-cs_20110419/Coco.atg0000777000175000010010000005275211553225644013054 0ustar mlNone/*------------------------------------------------------------------------- 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 -------------------------------------------------------------------------*/ $namespace=at.jku.ssw.Coco using System.IO; COMPILER Coco const int id = 0; const int str = 1; public TextWriter trace; // other Coco objects referenced in this ATG public Tab tab; public DFA dfa; public ParserGen pgen; bool genScanner; string tokenString; // used in declarations of literal tokens 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); .) optionSym = '$' letter { letter } '=' { digit | letter | '-' | '.' | ':' }. (. tab.SetOption(la.val); .) COMMENTS FROM "/*" TO "*/" NESTED COMMENTS FROM "//" TO lf IGNORE cr + lf + tab /*-------------------------------------------------------------------------*/ PRODUCTIONS Coco (. Symbol sym; Graph g, g1, g2; string gramName; CharSet s; int beg, line; .) = [ // using statements ANY (. beg = t.pos; line = t.line; .) { ANY } (. pgen.usingPos = new Position(beg, la.pos, 0, line); .) ] "COMPILER" (. genScanner = true; tab.ignored = new CharSet(); .) ident (. gramName = t.val; beg = la.pos; line = la.line; .) { ANY } (. tab.semDeclPos = new Position(beg, la.pos, 0, line); .) [ "IGNORECASE" (. dfa.ignoreCase = true; .) ] /* pdt */ [ "CHARACTERS" { SetDecl }] [ "TOKENS" { TokenDecl }] [ "PRAGMAS" { TokenDecl }] { "COMMENTS" (. bool nested = false; .) "FROM" TokenExpr "TO" TokenExpr [ "NESTED" (. nested = true; .) ] (. dfa.NewComment(g1.l, g2.l, nested); .) } { "IGNORE" Set (. tab.ignored.Or(s); .) } SYNC "PRODUCTIONS" (. if (genScanner) dfa.MakeDeterministic(); tab.DeleteNodes(); .) { ident (. sym = tab.FindSym(t.val); bool undef = sym == null; if (undef) sym = tab.NewSym(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 ] (. if (!undef) if (noAttrs != (sym.attrPos == null)) SemErr("attribute mismatch between declaration and use of this symbol"); .) [ SemText ] WEAK '=' Expression (. sym.graph = g.l; tab.Finish(g); .) WEAK '.' } "END" ident (. if (gramName != t.val) SemErr("name does not match grammar name"); tab.gramSy = tab.FindSym(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 = tab.NewSym(Node.t, "???", 0); // noSym gets highest number tab.SetupAnys(); tab.RenumberPragmas(); if (tab.ddt[2]) tab.PrintNodes(); if (errors.count == 0) { Console.WriteLine("checking"); tab.CompSymbolSets(); if (tab.ddt[7]) tab.XRef(); if (tab.GrammarOk()) { Console.Write("parser"); pgen.WriteParser(); if (genScanner) { Console.Write(" + scanner"); dfa.WriteScanner(); if (tab.ddt[0]) dfa.PrintStates(); } Console.WriteLine(" generated"); if (tab.ddt[8]) pgen.WriteStatistics(); } } if (tab.ddt[6]) tab.PrintSymbolTable(); .) '.' . /*------------------------------------------------------------------------------------*/ SetDecl (. CharSet s; .) = ident (. string name = t.val; CharClass c = tab.FindCharClass(name); if (c != null) SemErr("name declared twice"); .) '=' Set (. if (s.Elements() == 0) SemErr("character set must not be empty"); tab.NewCharClass(name, s); .) '.' . /*------------------------------------------------------------------------------------*/ Set (. CharSet s2; .) = SimSet { '+' SimSet (. s.Or(s2); .) | '-' SimSet (. s.Subtract(s2); .) } . /*------------------------------------------------------------------------------------*/ SimSet (. int n1, n2; .) = (. s = new CharSet(); .) ( ident (. CharClass c = tab.FindCharClass(t.val); if (c == null) SemErr("undefined name"); else s.Or(c.set); .) | string (. string name = t.val; name = tab.Unescape(name.Substring(1, name.Length-2)); foreach (char ch in name) if (dfa.ignoreCase) s.Set(char.ToLower(ch)); else s.Set(ch); .) | Char (. s.Set(n1); .) [ ".." Char (. for (int i = n1; i <= n2; i++) s.Set(i); .) ] | "ANY" (. s = new CharSet(); s.Fill(); .) ) . /*--------------------------------------------------------------------------------------*/ Char = char (. string name = t.val; n = 0; name = tab.Unescape(name.Substring(1, name.Length-2)); if (name.Length == 1) n = name[0]; else SemErr("unacceptable character value"); if (dfa.ignoreCase && (char)n >= 'A' && (char)n <= 'Z') n += 32; .) . /*------------------------------------------------------------------------------------*/ TokenDecl (. string name; int kind; Symbol sym; Graph g; .) = Sym (. sym = tab.FindSym(name); if (sym != null) SemErr("name declared twice"); else { sym = tab.NewSym(typ, name, t.line); sym.tokenKind = Symbol.fixedToken; } tokenString = null; .) SYNC ( '=' TokenExpr '.' (. if (kind == str) SemErr("a literal must not be declared with a structure"); tab.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 (. if (typ != Node.pr) SemErr("semantic action not allowed here"); .) ] . /*------------------------------------------------------------------------------------*/ AttrDecl = '<' (. int beg = la.pos; int col = la.col; int line = la.line; .) { ANY | badString (. SemErr("bad string in attributes"); .) } '>' (. if (t.pos > beg) sym.attrPos = new Position(beg, t.pos, col, line); .) | "<." (. int beg = la.pos; int col = la.col; int line = la.line; .) { ANY | badString (. SemErr("bad string in attributes"); .) } ".>" (. if (t.pos > beg) sym.attrPos = new Position(beg, t.pos, col, line); .) . /*------------------------------------------------------------------------------------*/ Expression (. Graph g2; .) = Term (. bool first = true; .) { WEAK '|' Term (. if (first) { tab.MakeFirstAlt(g); first = false; } tab.MakeAlternative(g, g2); .) } . /*------------------------------------------------------------------------------------*/ Term (. Graph g2; Node rslv = null; g = null; .) = ( [ (. rslv = tab.NewNode(Node.rslv, null, la.line); .) Resolver (. g = new Graph(rslv); .) ] Factor (. if (rslv != null) tab.MakeSequence(g, g2); else g = g2; .) { Factor (. tab.MakeSequence(g, g2); .) } | (. g = new Graph(tab.NewNode(Node.eps, null, 0)); .) ) (. if (g == null) // invalid start of Term g = new Graph(tab.NewNode(Node.eps, null, 0)); .) . /*------------------------------------------------------------------------------------*/ Factor (. string name; int kind; Position pos; bool weak = false; g = null; .) = ( [ "WEAK" (. weak = true; .) ] Sym (. Symbol sym = tab.FindSym(name); if (sym == null && kind == str) sym = tab.literals[name] as Symbol; bool undef = sym == null; if (undef) { if (kind == id) sym = tab.NewSym(Node.nt, name, 0); // forward nt else if (genScanner) { sym = tab.NewSym(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 = tab.NewNode(typ, sym, t.line); g = new Graph(p); .) [ Attribs

(. 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 ')' | '[' Expression ']' (. tab.MakeOption(g); .) | '{' Expression '}' (. tab.MakeIteration(g); .) | SemText (. Node p = tab.NewNode(Node.sem, null, 0); p.pos = pos; g = new Graph(p); .) | "ANY" (. Node p = tab.NewNode(Node.any, null, 0); // p.set is set in tab.SetupAnys g = new Graph(p); .) | "SYNC" (. Node p = tab.NewNode(Node.sync, null, 0); g = new Graph(p); .) ) (. if (g == null) // invalid start of Factor g = new Graph(tab.NewNode(Node.eps, null, 0)); .) . /*------------------------------------------------------------------------------------*/ Resolver = "IF" "(" (. int beg = la.pos; int col = la.col; int line = la.line; .) Condition (. pos = new Position(beg, t.pos, col, line); .) . /*------------------------------------------------------------------------------------*/ Condition = { "(" Condition | ANY } ")" . /*------------------------------------------------------------------------------------*/ TokenExpr (. Graph g2; .) = TokenTerm (. bool first = true; .) { WEAK '|' TokenTerm (. if (first) { tab.MakeFirstAlt(g); first = false; } tab.MakeAlternative(g, g2); .) } . /*------------------------------------------------------------------------------------*/ TokenTerm (. Graph g2; .) = TokenFactor { TokenFactor (. tab.MakeSequence(g, g2); .) } [ "CONTEXT" '(' TokenExpr (. tab.SetContextTrans(g2.l); dfa.hasCtxMoves = true; tab.MakeSequence(g, g2); .) ')' ] . /*------------------------------------------------------------------------------------*/ TokenFactor (. string name; int kind; .) = (. g = null; .) ( Sym (. if (kind == id) { CharClass c = tab.FindCharClass(name); if (c == null) { SemErr("undefined name"); c = tab.NewCharClass(name, new CharSet()); } Node p = tab.NewNode(Node.clas, null, 0); p.val = c.n; g = new Graph(p); tokenString = noString; } else { // str g = tab.StrToGraph(name); if (tokenString == null) tokenString = name; else tokenString = noString; } .) | '(' TokenExpr ')' | '[' TokenExpr ']' (. tab.MakeOption(g); tokenString = noString; .) | '{' TokenExpr '}' (. tab.MakeIteration(g); tokenString = noString; .) ) (. if (g == null) // invalid start of TokenFactor g = new Graph(tab.NewNode(Node.eps, null, 0)); .) . /*------------------------------------------------------------------------------------*/ Sym = (. 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 = '<' (. int beg = la.pos; int col = la.col; int line = la.line; .) { ANY | badString (. SemErr("bad string in attributes"); .) } '>' (. if (t.pos > beg) p.pos = new Position(beg, t.pos, col, line); .) | "<." (. int beg = la.pos; int col = la.col; int line = la.line; .) { ANY | badString (. SemErr("bad string in attributes"); .) } ".>" (. if (t.pos > beg) p.pos = new Position(beg, t.pos, col, line); .) . /*------------------------------------------------------------------------------------*/ SemText = "(." (. int beg = la.pos; int col = la.col; int line = la.line; .) { ANY | badString (. SemErr("bad string in semantic action"); .) | "(." (. SemErr("missing end of previous semantic action"); .) } ".)" (. pos = new Position(beg, t.pos, col, line); .) . END Coco. coco-cs_20110419/Coco.build0000777000175000010010000000342411136713174013365 0ustar mlNone coco-cs_20110419/Coco.cs0000777000175000010010000001227211553244052012671 0ustar mlNone/*------------------------------------------------------------------------- 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. -------------------------------------------------------------------------*/ /*------------------------------------------------------------------------- Trace output options 0 | A: prints the states of the scanner automaton 1 | F: prints the First and Follow sets of all nonterminals 2 | G: prints the syntax graph of the productions 3 | I: traces the computation of the First sets 4 | J: prints the sets associated with ANYs and synchronisation sets 6 | S: prints the symbol table (terminals, nonterminals, pragmas) 7 | X: prints a cross reference list of all syntax symbols 8 | P: prints statistics about the Coco run Trace output can be switched on by the pragma $ { digit | letter } in the attributed grammar or as a command-line option -------------------------------------------------------------------------*/ using System; using System.IO; namespace at.jku.ssw.Coco { public class Coco { public static int Main (string[] arg) { Console.WriteLine("Coco/R (Apr 19, 2011)"); string srcName = null, nsName = null, frameDir = null, ddtString = null, traceFileName = null, outDir = null; bool emitLines = false; int retVal = 1; for (int i = 0; i < arg.Length; i++) { if (arg[i] == "-namespace" && i < arg.Length - 1) nsName = arg[++i].Trim(); else if (arg[i] == "-frames" && i < arg.Length - 1) frameDir = arg[++i].Trim(); else if (arg[i] == "-trace" && i < arg.Length - 1) ddtString = arg[++i].Trim(); else if (arg[i] == "-o" && i < arg.Length - 1) outDir = arg[++i].Trim(); else if (arg[i] == "-lines") emitLines = true; else srcName = arg[i]; } if (arg.Length > 0 && srcName != null) { try { string srcDir = Path.GetDirectoryName(srcName); Scanner scanner = new Scanner(srcName); Parser parser = new Parser(scanner); traceFileName = Path.Combine(srcDir, "trace.txt"); parser.trace = new StreamWriter(new FileStream(traceFileName, FileMode.Create)); parser.tab = new Tab(parser); parser.dfa = new DFA(parser); parser.pgen = new ParserGen(parser); parser.tab.srcName = srcName; parser.tab.srcDir = srcDir; parser.tab.nsName = nsName; parser.tab.frameDir = frameDir; parser.tab.outDir = (outDir != null) ? outDir : srcDir; parser.tab.emitLines = emitLines; if (ddtString != null) parser.tab.SetDDT(ddtString); parser.Parse(); parser.trace.Close(); FileInfo f = new FileInfo(traceFileName); if (f.Length == 0) f.Delete(); else Console.WriteLine("trace output is in " + traceFileName); Console.WriteLine("{0} errors detected", parser.errors.count); if (parser.errors.count == 0) { retVal = 0; } } catch (IOException) { Console.WriteLine("-- could not open " + traceFileName); } catch (FatalError e) { Console.WriteLine("-- " + e.Message); } } else { Console.WriteLine("Usage: Coco Grammar.ATG {{Option}}{0}" + "Options:{0}" + " -namespace {0}" + " -frames {0}" + " -trace {0}" + " -o {0}" + " -lines{0}" + "Valid characters in the trace string:{0}" + " A trace automaton{0}" + " F list first/follow sets{0}" + " G print syntax graph{0}" + " I trace computation of first sets{0}" + " J list ANY and SYNC sets{0}" + " P print statistics{0}" + " S list symbol table{0}" + " X list cross reference table{0}" + "Scanner.frame and Parser.frame files needed in ATG directory{0}" + "or in a directory specified in the -frames option.", Environment.NewLine); } return retVal; } } // end Coco } // end namespace coco-cs_20110419/Coco.csproj0000777000175000010010000000344611200321166013556 0ustar mlNone Coco . Exe v1.1 512 prompt 4 coco-cs_20110419/Copyright.frame0000777000175000010010000000241711467550042014446 0ustar mlNone/*---------------------------------------------------------------------- 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. -----------------------------------------------------------------------*/ coco-cs_20110419/DFA.cs0000777000175000010010000010314211467577506012416 0ustar mlNone/*------------------------------------------------------------------------- DFA.cs -- Generation of the Scanner Automaton 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. -------------------------------------------------------------------------*/ using System; using System.IO; using System.Text; using System.Collections; namespace at.jku.ssw.Coco { //----------------------------------------------------------------------------- // State //----------------------------------------------------------------------------- public class State { // state of finite automaton public int nr; // state number public Action firstAction;// to first action of this state public Symbol endOf; // recognized token if state is final public bool ctx; // true if state is reached via contextTrans public State next; public void AddAction(Action act) { Action lasta = null, a = firstAction; while (a != null && act.typ >= a.typ) {lasta = a; a = a.next;} // collecting classes at the beginning gives better performance act.next = a; if (a==firstAction) firstAction = act; else lasta.next = act; } public void DetachAction(Action act) { Action lasta = null, a = firstAction; while (a != null && a != act) {lasta = a; a = a.next;} if (a != null) if (a == firstAction) firstAction = a.next; else lasta.next = a.next; } public void MeltWith(State s) { // copy actions of s to state for (Action action = s.firstAction; action != null; action = action.next) { Action a = new Action(action.typ, action.sym, action.tc); a.AddTargets(action); AddAction(a); } } } //----------------------------------------------------------------------------- // Action //----------------------------------------------------------------------------- public class Action { // action of finite automaton public int typ; // type of action symbol: clas, chr public int sym; // action symbol public int tc; // transition code: normalTrans, contextTrans public Target target; // states reached from this action public Action next; public Action(int typ, int sym, int tc) { this.typ = typ; this.sym = sym; this.tc = tc; } public void AddTarget(Target t) { // add t to the action.targets Target last = null; Target p = target; while (p != null && t.state.nr >= p.state.nr) { if (t.state == p.state) return; last = p; p = p.next; } t.next = p; if (p == target) target = t; else last.next = t; } public void AddTargets(Action a) { // add copy of a.targets to action.targets for (Target p = a.target; p != null; p = p.next) { Target t = new Target(p.state); AddTarget(t); } if (a.tc == Node.contextTrans) tc = Node.contextTrans; } public CharSet Symbols(Tab tab) { CharSet s; if (typ == Node.clas) s = tab.CharClassSet(sym).Clone(); else { s = new CharSet(); s.Set(sym); } return s; } public void ShiftWith(CharSet s, Tab tab) { if (s.Elements() == 1) { typ = Node.chr; sym = s.First(); } else { CharClass c = tab.FindCharClass(s); if (c == null) c = tab.NewCharClass("#", s); // class with dummy name typ = Node.clas; sym = c.n; } } } //----------------------------------------------------------------------------- // Target //----------------------------------------------------------------------------- public class Target { // set of states that are reached by an action public State state; // target state public Target next; public Target (State s) { state = s; } } //----------------------------------------------------------------------------- // Melted //----------------------------------------------------------------------------- public class Melted { // info about melted states public BitArray set; // set of old states public State state; // new state public Melted next; public Melted(BitArray set, State state) { this.set = set; this.state = state; } } //----------------------------------------------------------------------------- // Comment //----------------------------------------------------------------------------- public class Comment { // info about comment syntax public string start; public string stop; public bool nested; public Comment next; public Comment(string start, string stop, bool nested) { this.start = start; this.stop = stop; this.nested = nested; } } //----------------------------------------------------------------------------- // CharSet //----------------------------------------------------------------------------- public class CharSet { public class Range { public int from, to; public Range next; public Range(int from, int to) { this.from = from; this.to = to; } } public Range head; public bool this[int i] { get { for (Range p = head; p != null; p = p.next) if (i < p.from) return false; else if (i <= p.to) return true; // p.from <= i <= p.to return false; } } public void Set(int i) { Range cur = head, prev = null; while (cur != null && i >= cur.from-1) { if (i <= cur.to + 1) { // (cur.from-1) <= i <= (cur.to+1) if (i == cur.from - 1) cur.from--; else if (i == cur.to + 1) { cur.to++; Range next = cur.next; if (next != null && cur.to == next.from - 1) { cur.to = next.to; cur.next = next.next; }; } return; } prev = cur; cur = cur.next; } Range n = new Range(i, i); n.next = cur; if (prev == null) head = n; else prev.next = n; } public CharSet Clone() { CharSet s = new CharSet(); Range prev = null; for (Range cur = head; cur != null; cur = cur.next) { Range r = new Range(cur.from, cur.to); if (prev == null) s.head = r; else prev.next = r; prev = r; } return s; } public bool Equals(CharSet s) { Range p = head, q = s.head; while (p != null && q != null) { if (p.from != q.from || p.to != q.to) return false; p = p.next; q = q.next; } return p == q; } public int Elements() { int n = 0; for (Range p = head; p != null; p = p.next) n += p.to - p.from + 1; return n; } public int First() { if (head != null) return head.from; return -1; } public void Or(CharSet s) { for (Range p = s.head; p != null; p = p.next) for (int i = p.from; i <= p.to; i++) Set(i); } public void And(CharSet s) { CharSet x = new CharSet(); for (Range p = head; p != null; p = p.next) for (int i = p.from; i <= p.to; i++) if (s[i]) x.Set(i); head = x.head; } public void Subtract(CharSet s) { CharSet x = new CharSet(); for (Range p = head; p != null; p = p.next) for (int i = p.from; i <= p.to; i++) if (!s[i]) x.Set(i); head = x.head; } public bool Includes(CharSet s) { for (Range p = s.head; p != null; p = p.next) for (int i = p.from; i <= p.to; i++) if (!this[i]) return false; return true; } public bool Intersects(CharSet s) { for (Range p = s.head; p != null; p = p.next) for (int i = p.from; i <= p.to; i++) if (this[i]) return true; return false; } public void Fill() { head = new Range(Char.MinValue, Char.MaxValue); } } //----------------------------------------------------------------------------- // Generator //----------------------------------------------------------------------------- class Generator { private const int EOF = -1; private FileStream fram; private StreamWriter gen; private readonly Tab tab; private string frameFile; public Generator(Tab tab) { this.tab = tab; } public FileStream OpenFrame(String frame) { if (tab.frameDir != null) frameFile = Path.Combine(tab.frameDir, frame); if (frameFile == null || !File.Exists(frameFile)) frameFile = Path.Combine(tab.srcDir, frame); if (frameFile == null || !File.Exists(frameFile)) throw new FatalError("Cannot find : " + frame); try { fram = new FileStream(frameFile, FileMode.Open, FileAccess.Read, FileShare.Read); } catch (FileNotFoundException) { throw new FatalError("Cannot open frame file: " + frameFile); } return fram; } public StreamWriter OpenGen(string target) { string fn = Path.Combine(tab.outDir, target); try { if (File.Exists(fn)) File.Copy(fn, fn + ".old", true); gen = new StreamWriter(new FileStream(fn, FileMode.Create)); /* pdt */ } catch (IOException) { throw new FatalError("Cannot generate file: " + fn); } return gen; } public void GenCopyright() { string copyFr = null; if (tab.frameDir != null) copyFr = Path.Combine(tab.frameDir, "Copyright.frame"); if (copyFr == null || !File.Exists(copyFr)) copyFr = Path.Combine(tab.srcDir, "Copyright.frame"); if (copyFr == null || !File.Exists(copyFr)) return; try { FileStream scannerFram = fram; fram = new FileStream(copyFr, FileMode.Open, FileAccess.Read, FileShare.Read); CopyFramePart(null); fram = scannerFram; } catch (FileNotFoundException) { throw new FatalError("Cannot open Copyright.frame"); } } public void SkipFramePart(String stop) { CopyFramePart(stop, false); } public void CopyFramePart(String stop) { CopyFramePart(stop, true); } // if stop == null, copies until end of file private void CopyFramePart(string stop, bool generateOutput) { char startCh = (char) 0; int endOfStopString = 0; if (stop != null) { startCh = stop[0]; endOfStopString = stop.Length - 1; } int ch = framRead(); while (ch != EOF) { if (stop != null && ch == startCh) { int i = 0; do { if (i == endOfStopString) return; // stop[0..i] found ch = framRead(); i++; } while (ch == stop[i]); // stop[0..i-1] found; continue with last read character if (generateOutput) gen.Write(stop.Substring(0, i)); } else { if (generateOutput) gen.Write((char) ch); ch = framRead(); } } if (stop != null) throw new FatalError("Incomplete or corrupt frame file: " + frameFile); } private int framRead() { try { return fram.ReadByte(); } catch (Exception) { throw new FatalError("Error reading frame file: " + frameFile); } } } //----------------------------------------------------------------------------- // DFA //----------------------------------------------------------------------------- public class DFA { private int maxStates; private int lastStateNr; // highest state number private State firstState; private State lastState; // last allocated state private int lastSimState; // last non melted state private FileStream fram; // scanner frame input private StreamWriter gen; // generated scanner file private Symbol curSy; // current token to be recognized (in FindTrans) private bool dirtyDFA; // DFA may become nondeterministic in MatchLiteral public bool ignoreCase; // true if input should be treated case-insensitively public bool hasCtxMoves; // DFA has context transitions // other Coco objects private Parser parser; private Tab tab; private Errors errors; private TextWriter trace; //---------- Output primitives private string Ch(int ch) { if (ch < ' ' || ch >= 127 || ch == '\'' || ch == '\\') return Convert.ToString(ch); else return String.Format("'{0}'", (char)ch); } private string ChCond(char ch) { return String.Format("ch == {0}", Ch(ch)); } private void PutRange(CharSet s) { for (CharSet.Range r = s.head; r != null; r = r.next) { if (r.from == r.to) { gen.Write("ch == " + Ch(r.from)); } else if (r.from == 0) { gen.Write("ch <= " + Ch(r.to)); } else { gen.Write("ch >= " + Ch(r.from) + " && ch <= " + Ch(r.to)); } if (r.next != null) gen.Write(" || "); } } //---------- State handling State NewState() { State s = new State(); s.nr = ++lastStateNr; if (firstState == null) firstState = s; else lastState.next = s; lastState = s; return s; } void NewTransition(State from, State to, int typ, int sym, int tc) { Target t = new Target(to); Action a = new Action(typ, sym, tc); a.target = t; from.AddAction(a); if (typ == Node.clas) curSy.tokenKind = Symbol.classToken; } void CombineShifts() { State state; Action a, b, c; CharSet seta, setb; for (state = firstState; state != null; state = state.next) { for (a = state.firstAction; a != null; a = a.next) { b = a.next; while (b != null) if (a.target.state == b.target.state && a.tc == b.tc) { seta = a.Symbols(tab); setb = b.Symbols(tab); seta.Or(setb); a.ShiftWith(seta, tab); c = b; b = b.next; state.DetachAction(c); } else b = b.next; } } } void FindUsedStates(State state, BitArray used) { if (used[state.nr]) return; used[state.nr] = true; for (Action a = state.firstAction; a != null; a = a.next) FindUsedStates(a.target.state, used); } void DeleteRedundantStates() { State[] newState = new State[lastStateNr + 1]; BitArray used = new BitArray(lastStateNr + 1); FindUsedStates(firstState, used); // combine equal final states for (State s1 = firstState.next; s1 != null; s1 = s1.next) // firstState cannot be final if (used[s1.nr] && s1.endOf != null && s1.firstAction == null && !s1.ctx) for (State s2 = s1.next; s2 != null; s2 = s2.next) if (used[s2.nr] && s1.endOf == s2.endOf && s2.firstAction == null & !s2.ctx) { used[s2.nr] = false; newState[s2.nr] = s1; } for (State state = firstState; state != null; state = state.next) if (used[state.nr]) for (Action a = state.firstAction; a != null; a = a.next) if (!used[a.target.state.nr]) a.target.state = newState[a.target.state.nr]; // delete unused states lastState = firstState; lastStateNr = 0; // firstState has number 0 for (State state = firstState.next; state != null; state = state.next) if (used[state.nr]) {state.nr = ++lastStateNr; lastState = state;} else lastState.next = state.next; } State TheState(Node p) { State state; if (p == null) {state = NewState(); state.endOf = curSy; return state;} else return p.state; } void Step(State from, Node p, BitArray stepped) { if (p == null) return; stepped[p.n] = true; switch (p.typ) { case Node.clas: case Node.chr: { NewTransition(from, TheState(p.next), p.typ, p.val, p.code); break; } case Node.alt: { Step(from, p.sub, stepped); Step(from, p.down, stepped); break; } case Node.iter: { if (Tab.DelSubGraph(p.sub)) { parser.SemErr("contents of {...} must not be deletable"); return; } if (p.next != null && !stepped[p.next.n]) Step(from, p.next, stepped); Step(from, p.sub, stepped); if (p.state != from) { Step(p.state, p, new BitArray(tab.nodes.Count)); } break; } case Node.opt: { if (p.next != null && !stepped[p.next.n]) Step(from, p.next, stepped); Step(from, p.sub, stepped); break; } } } // Assigns a state n.state to every node n. There will be a transition from // n.state to n.next.state triggered by n.val. All nodes in an alternative // chain are represented by the same state. // Numbering scheme: // - any node after a chr, clas, opt, or alt, must get a new number // - if a nested structure starts with an iteration the iter node must get a new number // - if an iteration follows an iteration, it must get a new number void NumberNodes(Node p, State state, bool renumIter) { if (p == null) return; if (p.state != null) return; // already visited; if (state == null || (p.typ == Node.iter && renumIter)) state = NewState(); p.state = state; if (Tab.DelGraph(p)) state.endOf = curSy; switch (p.typ) { case Node.clas: case Node.chr: { NumberNodes(p.next, null, false); break; } case Node.opt: { NumberNodes(p.next, null, false); NumberNodes(p.sub, state, true); break; } case Node.iter: { NumberNodes(p.next, state, true); NumberNodes(p.sub, state, true); break; } case Node.alt: { NumberNodes(p.next, null, false); NumberNodes(p.sub, state, true); NumberNodes(p.down, state, renumIter); break; } } } void FindTrans (Node p, bool start, BitArray marked) { if (p == null || marked[p.n]) return; marked[p.n] = true; if (start) Step(p.state, p, new BitArray(tab.nodes.Count)); // start of group of equally numbered nodes switch (p.typ) { case Node.clas: case Node.chr: { FindTrans(p.next, true, marked); break; } case Node.opt: { FindTrans(p.next, true, marked); FindTrans(p.sub, false, marked); break; } case Node.iter: { FindTrans(p.next, false, marked); FindTrans(p.sub, false, marked); break; } case Node.alt: { FindTrans(p.sub, false, marked); FindTrans(p.down, false, marked); break; } } } public void ConvertToStates(Node p, Symbol sym) { curSy = sym; if (Tab.DelGraph(p)) { parser.SemErr("token might be empty"); return; } NumberNodes(p, firstState, true); FindTrans(p, true, new BitArray(tab.nodes.Count)); if (p.typ == Node.iter) { Step(firstState, p, new BitArray(tab.nodes.Count)); } } // match string against current automaton; store it either as a fixedToken or as a litToken public void MatchLiteral(string s, Symbol sym) { s = tab.Unescape(s.Substring(1, s.Length-2)); int i, len = s.Length; State state = firstState; Action a = null; for (i = 0; i < len; i++) { // try to match s against existing DFA a = FindAction(state, s[i]); if (a == null) break; state = a.target.state; } // if s was not totally consumed or leads to a non-final state => make new DFA from it if (i != len || state.endOf == null) { state = firstState; i = 0; a = null; dirtyDFA = true; } for (; i < len; i++) { // make new DFA for s[i..len-1], ML: i is either 0 or len State to = NewState(); NewTransition(state, to, Node.chr, s[i], Node.normalTrans); state = to; } Symbol matchedSym = state.endOf; if (state.endOf == null) { state.endOf = sym; } else if (matchedSym.tokenKind == Symbol.fixedToken || (a != null && a.tc == Node.contextTrans)) { // s matched a token with a fixed definition or a token with an appendix that will be cut off parser.SemErr("tokens " + sym.name + " and " + matchedSym.name + " cannot be distinguished"); } else { // matchedSym == classToken || classLitToken matchedSym.tokenKind = Symbol.classLitToken; sym.tokenKind = Symbol.litToken; } } void SplitActions(State state, Action a, Action b) { Action c; CharSet seta, setb, setc; seta = a.Symbols(tab); setb = b.Symbols(tab); if (seta.Equals(setb)) { a.AddTargets(b); state.DetachAction(b); } else if (seta.Includes(setb)) { setc = seta.Clone(); setc.Subtract(setb); b.AddTargets(a); a.ShiftWith(setc, tab); } else if (setb.Includes(seta)) { setc = setb.Clone(); setc.Subtract(seta); a.AddTargets(b); b.ShiftWith(setc, tab); } else { setc = seta.Clone(); setc.And(setb); seta.Subtract(setc); setb.Subtract(setc); a.ShiftWith(seta, tab); b.ShiftWith(setb, tab); c = new Action(0, 0, Node.normalTrans); // typ and sym are set in ShiftWith c.AddTargets(a); c.AddTargets(b); c.ShiftWith(setc, tab); state.AddAction(c); } } bool Overlap(Action a, Action b) { CharSet seta, setb; if (a.typ == Node.chr) if (b.typ == Node.chr) return a.sym == b.sym; else {setb = tab.CharClassSet(b.sym); return setb[a.sym];} else { seta = tab.CharClassSet(a.sym); if (b.typ == Node.chr) return seta[b.sym]; else {setb = tab.CharClassSet(b.sym); return seta.Intersects(setb);} } } void MakeUnique(State state) { bool changed; do { changed = false; for (Action a = state.firstAction; a != null; a = a.next) for (Action b = a.next; b != null; b = b.next) if (Overlap(a, b)) { SplitActions(state, a, b); changed = true; } } while (changed); } void MeltStates(State state) { bool ctx; BitArray targets; Symbol endOf; for (Action action = state.firstAction; action != null; action = action.next) { if (action.target.next != null) { GetTargetStates(action, out targets, out endOf, out ctx); Melted melt = StateWithSet(targets); if (melt == null) { State s = NewState(); s.endOf = endOf; s.ctx = ctx; for (Target targ = action.target; targ != null; targ = targ.next) s.MeltWith(targ.state); MakeUnique(s); melt = NewMelted(targets, s); } action.target.next = null; action.target.state = melt.state; } } } void FindCtxStates() { for (State state = firstState; state != null; state = state.next) for (Action a = state.firstAction; a != null; a = a.next) if (a.tc == Node.contextTrans) a.target.state.ctx = true; } public void MakeDeterministic() { State state; lastSimState = lastState.nr; maxStates = 2 * lastSimState; // heuristic for set size in Melted.set FindCtxStates(); for (state = firstState; state != null; state = state.next) MakeUnique(state); for (state = firstState; state != null; state = state.next) MeltStates(state); DeleteRedundantStates(); CombineShifts(); } public void PrintStates() { trace.WriteLine(); trace.WriteLine("---------- states ----------"); for (State state = firstState; state != null; state = state.next) { bool first = true; if (state.endOf == null) trace.Write(" "); else trace.Write("E({0,12})", tab.Name(state.endOf.name)); trace.Write("{0,3}:", state.nr); if (state.firstAction == null) trace.WriteLine(); for (Action action = state.firstAction; action != null; action = action.next) { if (first) {trace.Write(" "); first = false;} else trace.Write(" "); if (action.typ == Node.clas) trace.Write(((CharClass)tab.classes[action.sym]).name); else trace.Write("{0, 3}", Ch(action.sym)); for (Target targ = action.target; targ != null; targ = targ.next) trace.Write(" {0, 3}", targ.state.nr); if (action.tc == Node.contextTrans) trace.WriteLine(" context"); else trace.WriteLine(); } } trace.WriteLine(); trace.WriteLine("---------- character classes ----------"); tab.WriteCharClasses(); } //---------------------------- actions -------------------------------- public Action FindAction(State state, char ch) { for (Action a = state.firstAction; a != null; a = a.next) if (a.typ == Node.chr && ch == a.sym) return a; else if (a.typ == Node.clas) { CharSet s = tab.CharClassSet(a.sym); if (s[ch]) return a; } return null; } public void GetTargetStates(Action a, out BitArray targets, out Symbol endOf, out bool ctx) { // compute the set of target states targets = new BitArray(maxStates); endOf = null; ctx = false; for (Target t = a.target; t != null; t = t.next) { int stateNr = t.state.nr; if (stateNr <= lastSimState) targets[stateNr] = true; else targets.Or(MeltedSet(stateNr)); if (t.state.endOf != null) if (endOf == null || endOf == t.state.endOf) endOf = t.state.endOf; else errors.SemErr("Tokens " + endOf.name + " and " + t.state.endOf.name + " cannot be distinguished"); if (t.state.ctx) { ctx = true; // The following check seems to be unnecessary. It reported an error // if a symbol + context was the prefix of another symbol, e.g. // s1 = "a" "b" "c". // s2 = "a" CONTEXT("b"). // But this is ok. // if (t.state.endOf != null) { // Console.WriteLine("Ambiguous context clause"); // errors.count++; // } } } } //------------------------- melted states ------------------------------ Melted firstMelted; // head of melted state list Melted NewMelted(BitArray set, State state) { Melted m = new Melted(set, state); m.next = firstMelted; firstMelted = m; return m; } BitArray MeltedSet(int nr) { Melted m = firstMelted; while (m != null) { if (m.state.nr == nr) return m.set; else m = m.next; } throw new FatalError("compiler error in Melted.Set"); } Melted StateWithSet(BitArray s) { for (Melted m = firstMelted; m != null; m = m.next) if (Sets.Equals(s, m.set)) return m; return null; } //------------------------ comments -------------------------------- public Comment firstComment; // list of comments string CommentStr(Node p) { StringBuilder s = new StringBuilder(); while (p != null) { if (p.typ == Node.chr) { s.Append((char)p.val); } else if (p.typ == Node.clas) { CharSet set = tab.CharClassSet(p.val); if (set.Elements() != 1) parser.SemErr("character set contains more than 1 character"); s.Append((char)set.First()); } else parser.SemErr("comment delimiters may not be structured"); p = p.next; } if (s.Length == 0 || s.Length > 2) { parser.SemErr("comment delimiters must be 1 or 2 characters long"); s = new StringBuilder("?"); } return s.ToString(); } public void NewComment(Node from, Node to, bool nested) { Comment c = new Comment(CommentStr(from), CommentStr(to), nested); c.next = firstComment; firstComment = c; } //------------------------ scanner generation ---------------------- void GenComBody(Comment com) { gen.WriteLine( "\t\t\tfor(;;) {"); gen.Write ( "\t\t\t\tif ({0}) ", ChCond(com.stop[0])); gen.WriteLine("{"); if (com.stop.Length == 1) { gen.WriteLine("\t\t\t\t\tlevel--;"); gen.WriteLine("\t\t\t\t\tif (level == 0) { oldEols = line - line0; NextCh(); return true; }"); gen.WriteLine("\t\t\t\t\tNextCh();"); } else { gen.WriteLine("\t\t\t\t\tNextCh();"); gen.WriteLine("\t\t\t\t\tif ({0}) {{", ChCond(com.stop[1])); gen.WriteLine("\t\t\t\t\t\tlevel--;"); gen.WriteLine("\t\t\t\t\t\tif (level == 0) { oldEols = line - line0; NextCh(); return true; }"); gen.WriteLine("\t\t\t\t\t\tNextCh();"); gen.WriteLine("\t\t\t\t\t}"); } if (com.nested) { gen.Write ("\t\t\t\t}"); gen.Write(" else if ({0}) ", ChCond(com.start[0])); gen.WriteLine("{"); if (com.start.Length == 1) gen.WriteLine("\t\t\t\t\tlevel++; NextCh();"); else { gen.WriteLine("\t\t\t\t\tNextCh();"); gen.Write ("\t\t\t\t\tif ({0}) ", ChCond(com.start[1])); gen.WriteLine("{"); gen.WriteLine("\t\t\t\t\t\tlevel++; NextCh();"); gen.WriteLine("\t\t\t\t\t}"); } } gen.WriteLine( "\t\t\t\t} else if (ch == Buffer.EOF) return false;"); gen.WriteLine( "\t\t\t\telse NextCh();"); gen.WriteLine( "\t\t\t}"); } void GenComment(Comment com, int i) { gen.WriteLine(); gen.Write ("\tbool Comment{0}() ", i); gen.WriteLine("{"); gen.WriteLine("\t\tint level = 1, pos0 = pos, line0 = line, col0 = col, charPos0 = charPos;"); if (com.start.Length == 1) { gen.WriteLine("\t\tNextCh();"); GenComBody(com); } else { gen.WriteLine("\t\tNextCh();"); gen.Write ("\t\tif ({0}) ", ChCond(com.start[1])); gen.WriteLine("{"); gen.WriteLine("\t\t\tNextCh();"); GenComBody(com); gen.WriteLine("\t\t} else {"); gen.WriteLine("\t\t\tbuffer.Pos = pos0; NextCh(); line = line0; col = col0; charPos = charPos0;"); gen.WriteLine("\t\t}"); gen.WriteLine("\t\treturn false;"); } gen.WriteLine("\t}"); } string SymName(Symbol sym) { if (Char.IsLetter(sym.name[0])) { // real name value is stored in Tab.literals foreach (DictionaryEntry e in tab.literals) if ((Symbol)e.Value == sym) return (string)e.Key; } return sym.name; } void GenLiterals () { if (ignoreCase) { gen.WriteLine("\t\tswitch (t.val.ToLower()) {"); } else { gen.WriteLine("\t\tswitch (t.val) {"); } foreach (IList ts in new IList[] { tab.terminals, tab.pragmas }) { foreach (Symbol sym in ts) { if (sym.tokenKind == Symbol.litToken) { string name = SymName(sym); if (ignoreCase) name = name.ToLower(); // sym.name stores literals with quotes, e.g. "\"Literal\"" gen.WriteLine("\t\t\tcase {0}: t.kind = {1}; break;", name, sym.n); } } } gen.WriteLine("\t\t\tdefault: break;"); gen.Write("\t\t}"); } void WriteState(State state) { Symbol endOf = state.endOf; gen.WriteLine("\t\t\tcase {0}:", state.nr); if (endOf != null && state.firstAction != null) { gen.WriteLine("\t\t\t\trecEnd = pos; recKind = {0};", endOf.n); } bool ctxEnd = state.ctx; for (Action action = state.firstAction; action != null; action = action.next) { if (action == state.firstAction) gen.Write("\t\t\t\tif ("); else gen.Write("\t\t\t\telse if ("); if (action.typ == Node.chr) gen.Write(ChCond((char)action.sym)); else PutRange(tab.CharClassSet(action.sym)); gen.Write(") {"); if (action.tc == Node.contextTrans) { gen.Write("apx++; "); ctxEnd = false; } else if (state.ctx) gen.Write("apx = 0; "); gen.Write("AddCh(); goto case {0};", action.target.state.nr); gen.WriteLine("}"); } if (state.firstAction == null) gen.Write("\t\t\t\t{"); else gen.Write("\t\t\t\telse {"); if (ctxEnd) { // final context state: cut appendix gen.WriteLine(); gen.WriteLine("\t\t\t\t\ttlen -= apx;"); gen.WriteLine("\t\t\t\t\tSetScannerBehindT();"); gen.Write("\t\t\t\t\t"); } if (endOf == null) { gen.WriteLine("goto case 0;}"); } else { gen.Write("t.kind = {0}; ", endOf.n); if (endOf.tokenKind == Symbol.classLitToken) { gen.WriteLine("t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}"); } else { gen.WriteLine("break;}"); } } } void WriteStartTab() { for (Action action = firstState.firstAction; action != null; action = action.next) { int targetState = action.target.state.nr; if (action.typ == Node.chr) { gen.WriteLine("\t\tstart[" + action.sym + "] = " + targetState + "; "); } else { CharSet s = tab.CharClassSet(action.sym); for (CharSet.Range r = s.head; r != null; r = r.next) { gen.WriteLine("\t\tfor (int i = " + r.from + "; i <= " + r.to + "; ++i) start[i] = " + targetState + ";"); } } } gen.WriteLine("\t\tstart[Buffer.EOF] = -1;"); } public void WriteScanner() { Generator g = new Generator(tab); fram = g.OpenFrame("Scanner.frame"); gen = g.OpenGen("Scanner.cs"); if (dirtyDFA) MakeDeterministic(); g.GenCopyright(); g.SkipFramePart("-->begin"); g.CopyFramePart("-->namespace"); if (tab.nsName != null && tab.nsName.Length > 0) { gen.Write("namespace "); gen.Write(tab.nsName); gen.Write(" {"); } g.CopyFramePart("-->declarations"); gen.WriteLine("\tconst int maxT = {0};", tab.terminals.Count - 1); gen.WriteLine("\tconst int noSym = {0};", tab.noSym.n); if (ignoreCase) gen.Write("\tchar valCh; // current input character (for token.val)"); g.CopyFramePart("-->initialization"); WriteStartTab(); g.CopyFramePart("-->casing1"); if (ignoreCase) { gen.WriteLine("\t\tif (ch != Buffer.EOF) {"); gen.WriteLine("\t\t\tvalCh = (char) ch;"); gen.WriteLine("\t\t\tch = char.ToLower((char) ch);"); gen.WriteLine("\t\t}"); } g.CopyFramePart("-->casing2"); gen.Write("\t\t\ttval[tlen++] = "); if (ignoreCase) gen.Write("valCh;"); else gen.Write("(char) ch;"); g.CopyFramePart("-->comments"); Comment com = firstComment; int comIdx = 0; while (com != null) { GenComment(com, comIdx); com = com.next; comIdx++; } g.CopyFramePart("-->literals"); GenLiterals(); g.CopyFramePart("-->scan1"); gen.Write("\t\t\t"); if (tab.ignored.Elements() > 0) { PutRange(tab.ignored); } else { gen.Write("false"); } g.CopyFramePart("-->scan2"); if (firstComment != null) { gen.Write("\t\tif ("); com = firstComment; comIdx = 0; while (com != null) { gen.Write(ChCond(com.start[0])); gen.Write(" && Comment{0}()", comIdx); if (com.next != null) gen.Write(" ||"); com = com.next; comIdx++; } gen.Write(") return NextToken();"); } if (hasCtxMoves) { gen.WriteLine(); gen.Write("\t\tint apx = 0;"); } /* pdt */ g.CopyFramePart("-->scan3"); for (State state = firstState.next; state != null; state = state.next) WriteState(state); g.CopyFramePart(null); if (tab.nsName != null && tab.nsName.Length > 0) gen.Write("}"); gen.Close(); } public DFA (Parser parser) { this.parser = parser; tab = parser.tab; errors = parser.errors; trace = parser.trace; firstState = null; lastState = null; lastStateNr = -1; firstState = NewState(); firstMelted = null; firstComment = null; ignoreCase = false; dirtyDFA = false; hasCtxMoves = false; } } // end DFA } // end namespace coco-cs_20110419/Parser.cs0000777000175000010010000005415111553243050013241 0ustar mlNone/*---------------------------------------------------------------------- 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. -----------------------------------------------------------------------*/ using System.IO; using System; namespace at.jku.ssw.Coco { public class Parser { public const int _EOF = 0; public const int _ident = 1; public const int _number = 2; public const int _string = 3; public const int _badString = 4; public const int _char = 5; public const int maxT = 41; public const int _ddtSym = 42; public const int _optionSym = 43; const bool T = true; const bool x = false; const int minErrDist = 2; public Scanner scanner; public Errors errors; public Token t; // last recognized token public Token la; // lookahead token int errDist = minErrDist; const int id = 0; const int str = 1; public TextWriter trace; // other Coco objects referenced in this ATG public Tab tab; public DFA dfa; public ParserGen pgen; bool genScanner; string tokenString; // used in declarations of literal tokens string noString = "-none-"; // used in declarations of literal tokens /*-------------------------------------------------------------------------*/ public Parser(Scanner scanner) { this.scanner = scanner; errors = new Errors(); } void SynErr (int n) { if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n); errDist = 0; } public void SemErr (string msg) { if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg); errDist = 0; } void Get () { for (;;) { t = la; la = scanner.Scan(); if (la.kind <= maxT) { ++errDist; break; } if (la.kind == 42) { tab.SetDDT(la.val); } if (la.kind == 43) { tab.SetOption(la.val); } la = t; } } void Expect (int n) { if (la.kind==n) Get(); else { SynErr(n); } } bool StartOf (int s) { return set[s, la.kind]; } void ExpectWeak (int n, int follow) { if (la.kind == n) Get(); else { SynErr(n); while (!StartOf(follow)) Get(); } } bool WeakSeparator(int n, int syFol, int repFol) { int kind = la.kind; if (kind == n) {Get(); return true;} else if (StartOf(repFol)) {return false;} else { SynErr(n); while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) { Get(); kind = la.kind; } return StartOf(syFol); } } void Coco() { Symbol sym; Graph g, g1, g2; string gramName; CharSet s; int beg, line; if (StartOf(1)) { Get(); beg = t.pos; line = t.line; while (StartOf(1)) { Get(); } pgen.usingPos = new Position(beg, la.pos, 0, line); } Expect(6); genScanner = true; tab.ignored = new CharSet(); Expect(1); gramName = t.val; beg = la.pos; line = la.line; while (StartOf(2)) { Get(); } tab.semDeclPos = new Position(beg, la.pos, 0, line); if (la.kind == 7) { Get(); dfa.ignoreCase = true; } if (la.kind == 8) { Get(); while (la.kind == 1) { SetDecl(); } } if (la.kind == 9) { Get(); while (la.kind == 1 || la.kind == 3 || la.kind == 5) { TokenDecl(Node.t); } } if (la.kind == 10) { Get(); while (la.kind == 1 || la.kind == 3 || la.kind == 5) { TokenDecl(Node.pr); } } while (la.kind == 11) { Get(); bool nested = false; Expect(12); TokenExpr(out g1); Expect(13); TokenExpr(out g2); if (la.kind == 14) { Get(); nested = true; } dfa.NewComment(g1.l, g2.l, nested); } while (la.kind == 15) { Get(); Set(out s); tab.ignored.Or(s); } while (!(la.kind == 0 || la.kind == 16)) {SynErr(42); Get();} Expect(16); if (genScanner) dfa.MakeDeterministic(); tab.DeleteNodes(); while (la.kind == 1) { Get(); sym = tab.FindSym(t.val); bool undef = sym == null; if (undef) sym = tab.NewSym(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; if (la.kind == 24 || la.kind == 26) { AttrDecl(sym); } if (!undef) if (noAttrs != (sym.attrPos == null)) SemErr("attribute mismatch between declaration and use of this symbol"); if (la.kind == 39) { SemText(out sym.semPos); } ExpectWeak(17, 3); Expression(out g); sym.graph = g.l; tab.Finish(g); ExpectWeak(18, 4); } Expect(19); Expect(1); if (gramName != t.val) SemErr("name does not match grammar name"); tab.gramSy = tab.FindSym(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 = tab.NewSym(Node.t, "???", 0); // noSym gets highest number tab.SetupAnys(); tab.RenumberPragmas(); if (tab.ddt[2]) tab.PrintNodes(); if (errors.count == 0) { Console.WriteLine("checking"); tab.CompSymbolSets(); if (tab.ddt[7]) tab.XRef(); if (tab.GrammarOk()) { Console.Write("parser"); pgen.WriteParser(); if (genScanner) { Console.Write(" + scanner"); dfa.WriteScanner(); if (tab.ddt[0]) dfa.PrintStates(); } Console.WriteLine(" generated"); if (tab.ddt[8]) pgen.WriteStatistics(); } } if (tab.ddt[6]) tab.PrintSymbolTable(); Expect(18); } void SetDecl() { CharSet s; Expect(1); string name = t.val; CharClass c = tab.FindCharClass(name); if (c != null) SemErr("name declared twice"); Expect(17); Set(out s); if (s.Elements() == 0) SemErr("character set must not be empty"); tab.NewCharClass(name, s); Expect(18); } void TokenDecl(int typ) { string name; int kind; Symbol sym; Graph g; Sym(out name, out kind); sym = tab.FindSym(name); if (sym != null) SemErr("name declared twice"); else { sym = tab.NewSym(typ, name, t.line); sym.tokenKind = Symbol.fixedToken; } tokenString = null; while (!(StartOf(5))) {SynErr(43); Get();} if (la.kind == 17) { Get(); TokenExpr(out g); Expect(18); if (kind == str) SemErr("a literal must not be declared with a structure"); tab.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); } } else if (StartOf(6)) { if (kind == id) genScanner = false; else dfa.MatchLiteral(sym.name, sym); } else SynErr(44); if (la.kind == 39) { SemText(out sym.semPos); if (typ != Node.pr) SemErr("semantic action not allowed here"); } } void TokenExpr(out Graph g) { Graph g2; TokenTerm(out g); bool first = true; while (WeakSeparator(28,7,8) ) { TokenTerm(out g2); if (first) { tab.MakeFirstAlt(g); first = false; } tab.MakeAlternative(g, g2); } } void Set(out CharSet s) { CharSet s2; SimSet(out s); while (la.kind == 20 || la.kind == 21) { if (la.kind == 20) { Get(); SimSet(out s2); s.Or(s2); } else { Get(); SimSet(out s2); s.Subtract(s2); } } } void AttrDecl(Symbol sym) { if (la.kind == 24) { Get(); int beg = la.pos; int col = la.col; int line = la.line; while (StartOf(9)) { if (StartOf(10)) { Get(); } else { Get(); SemErr("bad string in attributes"); } } Expect(25); if (t.pos > beg) sym.attrPos = new Position(beg, t.pos, col, line); } else if (la.kind == 26) { Get(); int beg = la.pos; int col = la.col; int line = la.line; while (StartOf(11)) { if (StartOf(12)) { Get(); } else { Get(); SemErr("bad string in attributes"); } } Expect(27); if (t.pos > beg) sym.attrPos = new Position(beg, t.pos, col, line); } else SynErr(45); } void SemText(out Position pos) { Expect(39); int beg = la.pos; int col = la.col; int line = la.line; while (StartOf(13)) { if (StartOf(14)) { Get(); } else if (la.kind == 4) { Get(); SemErr("bad string in semantic action"); } else { Get(); SemErr("missing end of previous semantic action"); } } Expect(40); pos = new Position(beg, t.pos, col, line); } void Expression(out Graph g) { Graph g2; Term(out g); bool first = true; while (WeakSeparator(28,15,16) ) { Term(out g2); if (first) { tab.MakeFirstAlt(g); first = false; } tab.MakeAlternative(g, g2); } } void SimSet(out CharSet s) { int n1, n2; s = new CharSet(); if (la.kind == 1) { Get(); CharClass c = tab.FindCharClass(t.val); if (c == null) SemErr("undefined name"); else s.Or(c.set); } else if (la.kind == 3) { Get(); string name = t.val; name = tab.Unescape(name.Substring(1, name.Length-2)); foreach (char ch in name) if (dfa.ignoreCase) s.Set(char.ToLower(ch)); else s.Set(ch); } else if (la.kind == 5) { Char(out n1); s.Set(n1); if (la.kind == 22) { Get(); Char(out n2); for (int i = n1; i <= n2; i++) s.Set(i); } } else if (la.kind == 23) { Get(); s = new CharSet(); s.Fill(); } else SynErr(46); } void Char(out int n) { Expect(5); string name = t.val; n = 0; name = tab.Unescape(name.Substring(1, name.Length-2)); if (name.Length == 1) n = name[0]; else SemErr("unacceptable character value"); if (dfa.ignoreCase && (char)n >= 'A' && (char)n <= 'Z') n += 32; } void Sym(out string name, out int kind) { name = "???"; kind = id; if (la.kind == 1) { Get(); kind = id; name = t.val; } else if (la.kind == 3 || la.kind == 5) { if (la.kind == 3) { Get(); name = t.val; } else { Get(); 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"); } else SynErr(47); } void Term(out Graph g) { Graph g2; Node rslv = null; g = null; if (StartOf(17)) { if (la.kind == 37) { rslv = tab.NewNode(Node.rslv, null, la.line); Resolver(out rslv.pos); g = new Graph(rslv); } Factor(out g2); if (rslv != null) tab.MakeSequence(g, g2); else g = g2; while (StartOf(18)) { Factor(out g2); tab.MakeSequence(g, g2); } } else if (StartOf(19)) { g = new Graph(tab.NewNode(Node.eps, null, 0)); } else SynErr(48); if (g == null) // invalid start of Term g = new Graph(tab.NewNode(Node.eps, null, 0)); } void Resolver(out Position pos) { Expect(37); Expect(30); int beg = la.pos; int col = la.col; int line = la.line; Condition(); pos = new Position(beg, t.pos, col, line); } void Factor(out Graph g) { string name; int kind; Position pos; bool weak = false; g = null; switch (la.kind) { case 1: case 3: case 5: case 29: { if (la.kind == 29) { Get(); weak = true; } Sym(out name, out kind); Symbol sym = tab.FindSym(name); if (sym == null && kind == str) sym = tab.literals[name] as Symbol; bool undef = sym == null; if (undef) { if (kind == id) sym = tab.NewSym(Node.nt, name, 0); // forward nt else if (genScanner) { sym = tab.NewSym(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 = tab.NewNode(typ, sym, t.line); g = new Graph(p); if (la.kind == 24 || la.kind == 26) { 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"); break; } case 30: { Get(); Expression(out g); Expect(31); break; } case 32: { Get(); Expression(out g); Expect(33); tab.MakeOption(g); break; } case 34: { Get(); Expression(out g); Expect(35); tab.MakeIteration(g); break; } case 39: { SemText(out pos); Node p = tab.NewNode(Node.sem, null, 0); p.pos = pos; g = new Graph(p); break; } case 23: { Get(); Node p = tab.NewNode(Node.any, null, 0); // p.set is set in tab.SetupAnys g = new Graph(p); break; } case 36: { Get(); Node p = tab.NewNode(Node.sync, null, 0); g = new Graph(p); break; } default: SynErr(49); break; } if (g == null) // invalid start of Factor g = new Graph(tab.NewNode(Node.eps, null, 0)); } void Attribs(Node p) { if (la.kind == 24) { Get(); int beg = la.pos; int col = la.col; int line = la.line; while (StartOf(9)) { if (StartOf(10)) { Get(); } else { Get(); SemErr("bad string in attributes"); } } Expect(25); if (t.pos > beg) p.pos = new Position(beg, t.pos, col, line); } else if (la.kind == 26) { Get(); int beg = la.pos; int col = la.col; int line = la.line; while (StartOf(11)) { if (StartOf(12)) { Get(); } else { Get(); SemErr("bad string in attributes"); } } Expect(27); if (t.pos > beg) p.pos = new Position(beg, t.pos, col, line); } else SynErr(50); } void Condition() { while (StartOf(20)) { if (la.kind == 30) { Get(); Condition(); } else { Get(); } } Expect(31); } void TokenTerm(out Graph g) { Graph g2; TokenFactor(out g); while (StartOf(7)) { TokenFactor(out g2); tab.MakeSequence(g, g2); } if (la.kind == 38) { Get(); Expect(30); TokenExpr(out g2); tab.SetContextTrans(g2.l); dfa.hasCtxMoves = true; tab.MakeSequence(g, g2); Expect(31); } } void TokenFactor(out Graph g) { string name; int kind; g = null; if (la.kind == 1 || la.kind == 3 || la.kind == 5) { Sym(out name, out kind); if (kind == id) { CharClass c = tab.FindCharClass(name); if (c == null) { SemErr("undefined name"); c = tab.NewCharClass(name, new CharSet()); } Node p = tab.NewNode(Node.clas, null, 0); p.val = c.n; g = new Graph(p); tokenString = noString; } else { // str g = tab.StrToGraph(name); if (tokenString == null) tokenString = name; else tokenString = noString; } } else if (la.kind == 30) { Get(); TokenExpr(out g); Expect(31); } else if (la.kind == 32) { Get(); TokenExpr(out g); Expect(33); tab.MakeOption(g); tokenString = noString; } else if (la.kind == 34) { Get(); TokenExpr(out g); Expect(35); tab.MakeIteration(g); tokenString = noString; } else SynErr(51); if (g == null) // invalid start of TokenFactor g = new Graph(tab.NewNode(Node.eps, null, 0)); } public void Parse() { la = new Token(); la.val = ""; Get(); Coco(); Expect(0); } static readonly bool[,] set = { {T,T,x,T, x,T,x,x, x,x,T,T, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x}, {x,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, {x,T,T,T, T,T,T,x, x,x,x,x, T,T,T,x, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, {T,T,x,T, x,T,x,x, x,x,T,T, x,x,x,T, T,T,T,x, x,x,x,T, x,x,x,x, T,T,T,x, T,x,T,x, T,T,x,T, x,x,x}, {T,T,x,T, x,T,x,x, x,x,T,T, x,x,x,T, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x}, {T,T,x,T, x,T,x,x, x,x,T,T, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x}, {x,T,x,T, x,T,x,x, x,x,T,T, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x}, {x,T,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, T,x,T,x, x,x,x,x, x,x,x}, {x,x,x,x, x,x,x,x, x,x,x,T, x,T,T,T, T,x,T,x, x,x,x,x, x,x,x,x, x,x,x,T, x,T,x,T, x,x,x,x, x,x,x}, {x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,x,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, {x,T,T,T, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,x,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, {x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, {x,T,T,T, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, {x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, x,T,x}, {x,T,T,T, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, x,T,x}, {x,T,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,T, x,x,x,x, T,T,T,T, T,T,T,T, T,T,x,T, x,x,x}, {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,T, x,T,x,T, x,x,x,x, x,x,x}, {x,T,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,T,T,x, T,x,T,x, T,T,x,T, x,x,x}, {x,T,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,T,T,x, T,x,T,x, T,x,x,T, x,x,x}, {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, T,x,x,T, x,T,x,T, x,x,x,x, x,x,x}, {x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, T,T,T,T, T,T,T,T, T,T,x} }; } // end Parser public class Errors { public int count = 0; // number of errors detected public System.IO.TextWriter errorStream = Console.Out; // error messages go to this stream public string errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text public virtual void SynErr (int line, int col, int n) { string s; switch (n) { case 0: s = "EOF expected"; break; case 1: s = "ident expected"; break; case 2: s = "number expected"; break; case 3: s = "string expected"; break; case 4: s = "badString expected"; break; case 5: s = "char expected"; break; case 6: s = "\"COMPILER\" expected"; break; case 7: s = "\"IGNORECASE\" expected"; break; case 8: s = "\"CHARACTERS\" expected"; break; case 9: s = "\"TOKENS\" expected"; break; case 10: s = "\"PRAGMAS\" expected"; break; case 11: s = "\"COMMENTS\" expected"; break; case 12: s = "\"FROM\" expected"; break; case 13: s = "\"TO\" expected"; break; case 14: s = "\"NESTED\" expected"; break; case 15: s = "\"IGNORE\" expected"; break; case 16: s = "\"PRODUCTIONS\" expected"; break; case 17: s = "\"=\" expected"; break; case 18: s = "\".\" expected"; break; case 19: s = "\"END\" expected"; break; case 20: s = "\"+\" expected"; break; case 21: s = "\"-\" expected"; break; case 22: s = "\"..\" expected"; break; case 23: s = "\"ANY\" expected"; break; case 24: s = "\"<\" expected"; break; case 25: s = "\">\" expected"; break; case 26: s = "\"<.\" expected"; break; case 27: s = "\".>\" expected"; break; case 28: s = "\"|\" expected"; break; case 29: s = "\"WEAK\" expected"; break; case 30: s = "\"(\" expected"; break; case 31: s = "\")\" expected"; break; case 32: s = "\"[\" expected"; break; case 33: s = "\"]\" expected"; break; case 34: s = "\"{\" expected"; break; case 35: s = "\"}\" expected"; break; case 36: s = "\"SYNC\" expected"; break; case 37: s = "\"IF\" expected"; break; case 38: s = "\"CONTEXT\" expected"; break; case 39: s = "\"(.\" expected"; break; case 40: s = "\".)\" expected"; break; case 41: s = "??? expected"; break; case 42: s = "this symbol not expected in Coco"; break; case 43: s = "this symbol not expected in TokenDecl"; break; case 44: s = "invalid TokenDecl"; break; case 45: s = "invalid AttrDecl"; break; case 46: s = "invalid SimSet"; break; case 47: s = "invalid Sym"; break; case 48: s = "invalid Term"; break; case 49: s = "invalid Factor"; break; case 50: s = "invalid Attribs"; break; case 51: s = "invalid TokenFactor"; break; default: s = "error " + n; break; } errorStream.WriteLine(errMsgFormat, line, col, s); count++; } public virtual void SemErr (int line, int col, string s) { errorStream.WriteLine(errMsgFormat, line, col, s); count++; } public virtual void SemErr (string s) { errorStream.WriteLine(s); count++; } public virtual void Warning (int line, int col, string s) { errorStream.WriteLine(errMsgFormat, line, col, s); } public virtual void Warning(string s) { errorStream.WriteLine(s); } } // Errors public class FatalError: Exception { public FatalError(string m): base(m) {} } }coco-cs_20110419/Parser.frame0000777000175000010010000000756311467577552013760 0ustar mlNone/*---------------------------------------------------------------------- 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. ----------------------------------------------------------------------*/ -->begin using System; -->namespace public class Parser { -->constants const bool T = true; const bool x = false; const int minErrDist = 2; public Scanner scanner; public Errors errors; public Token t; // last recognized token public Token la; // lookahead token int errDist = minErrDist; -->declarations public Parser(Scanner scanner) { this.scanner = scanner; errors = new Errors(); } void SynErr (int n) { if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n); errDist = 0; } public void SemErr (string msg) { if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg); errDist = 0; } void Get () { for (;;) { t = la; la = scanner.Scan(); if (la.kind <= maxT) { ++errDist; break; } -->pragmas la = t; } } void Expect (int n) { if (la.kind==n) Get(); else { SynErr(n); } } bool StartOf (int s) { return set[s, la.kind]; } void ExpectWeak (int n, int follow) { if (la.kind == n) Get(); else { SynErr(n); while (!StartOf(follow)) Get(); } } bool WeakSeparator(int n, int syFol, int repFol) { int kind = la.kind; if (kind == n) {Get(); return true;} else if (StartOf(repFol)) {return false;} else { SynErr(n); while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) { Get(); kind = la.kind; } return StartOf(syFol); } } -->productions public void Parse() { la = new Token(); la.val = ""; Get(); -->parseRoot } static readonly bool[,] set = { -->initialization }; } // end Parser public class Errors { public int count = 0; // number of errors detected public System.IO.TextWriter errorStream = Console.Out; // error messages go to this stream public string errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text public virtual void SynErr (int line, int col, int n) { string s; switch (n) { -->errors default: s = "error " + n; break; } errorStream.WriteLine(errMsgFormat, line, col, s); count++; } public virtual void SemErr (int line, int col, string s) { errorStream.WriteLine(errMsgFormat, line, col, s); count++; } public virtual void SemErr (string s) { errorStream.WriteLine(s); count++; } public virtual void Warning (int line, int col, string s) { errorStream.WriteLine(errMsgFormat, line, col, s); } public virtual void Warning(string s) { errorStream.WriteLine(s); } } // Errors public class FatalError: Exception { public FatalError(string m): base(m) {} } coco-cs_20110419/ParserGen.cs0000777000175000010010000003067111553244144013701 0ustar mlNone/*------------------------------------------------------------------------- ParserGen.cs -- Generation of the Recursive Descent Parser 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. -------------------------------------------------------------------------*/ using System; using System.IO; using System.Collections; using System.Text; namespace at.jku.ssw.Coco { public class ParserGen { const int maxTerm = 3; // sets of size < maxTerm are enumerated const char CR = '\r'; const char LF = '\n'; const int EOF = -1; const int tErr = 0; // error codes const int altErr = 1; const int syncErr = 2; public Position usingPos; // "using" definitions from the attributed grammar int errorNr; // highest parser error number Symbol curSy; // symbol whose production is currently generated FileStream fram; // parser frame file StreamWriter gen; // generated parser source file StringWriter err; // generated parser error messages ArrayList symSet = new ArrayList(); Tab tab; // other Coco objects TextWriter trace; Errors errors; Buffer buffer; void Indent (int n) { for (int i = 1; i <= n; i++) gen.Write('\t'); } bool Overlaps(BitArray s1, BitArray s2) { int len = s1.Count; for (int i = 0; i < len; ++i) { if (s1[i] && s2[i]) { return true; } } return false; } // use a switch if more than 5 alternatives and none starts with a resolver bool UseSwitch (Node p) { BitArray s1, s2; if (p.typ != Node.alt) return false; int nAlts = 0; s1 = new BitArray(tab.terminals.Count); while (p != null) { s2 = tab.Expected0(p.sub, curSy); // must not optimize with switch statement, if there are ll1 warnings if (Overlaps(s1, s2)) { return false; } s1.Or(s2); ++nAlts; // must not optimize with switch-statement, if alt uses a resolver expression if (p.sub.typ == Node.rslv) return false; p = p.down; } return nAlts > 5; } void CopySourcePart (Position pos, int indent) { // Copy text described by pos from atg to gen int ch, i; if (pos != null) { buffer.Pos = pos.beg; ch = buffer.Read(); if (tab.emitLines) { gen.WriteLine(); gen.WriteLine("#line {0} \"{1}\"", pos.line, tab.srcName); } Indent(indent); while (buffer.Pos <= pos.end) { while (ch == CR || ch == LF) { // eol is either CR or CRLF or LF gen.WriteLine(); Indent(indent); if (ch == CR) ch = buffer.Read(); // skip CR if (ch == LF) ch = buffer.Read(); // skip LF for (i = 1; i <= pos.col && (ch == ' ' || ch == '\t'); i++) { // skip blanks at beginning of line ch = buffer.Read(); } if (buffer.Pos > pos.end) goto done; } gen.Write((char)ch); ch = buffer.Read(); } done: if (indent > 0) gen.WriteLine(); } } void GenErrorMsg (int errTyp, Symbol sym) { errorNr++; err.Write("\t\t\tcase " + errorNr + ": s = \""); switch (errTyp) { case tErr: if (sym.name[0] == '"') err.Write(tab.Escape(sym.name) + " expected"); else err.Write(sym.name + " expected"); break; case altErr: err.Write("invalid " + sym.name); break; case syncErr: err.Write("this symbol not expected in " + sym.name); break; } err.WriteLine("\"; break;"); } int NewCondSet (BitArray s) { for (int i = 1; i < symSet.Count; i++) // skip symSet[0] (reserved for union of SYNC sets) if (Sets.Equals(s, (BitArray)symSet[i])) return i; symSet.Add(s.Clone()); return symSet.Count - 1; } void GenCond (BitArray s, Node p) { if (p.typ == Node.rslv) CopySourcePart(p.pos, 0); else { int n = Sets.Elements(s); if (n == 0) gen.Write("false"); // happens if an ANY set matches no symbol else if (n <= maxTerm) foreach (Symbol sym in tab.terminals) { if (s[sym.n]) { gen.Write("la.kind == {0}", sym.n); --n; if (n > 0) gen.Write(" || "); } } else gen.Write("StartOf({0})", NewCondSet(s)); } } void PutCaseLabels (BitArray s) { foreach (Symbol sym in tab.terminals) if (s[sym.n]) gen.Write("case {0}: ", sym.n); } void GenCode (Node p, int indent, BitArray isChecked) { Node p2; BitArray s1, s2; while (p != null) { switch (p.typ) { case Node.nt: { Indent(indent); gen.Write(p.sym.name + "("); CopySourcePart(p.pos, 0); gen.WriteLine(");"); break; } case Node.t: { Indent(indent); // assert: if isChecked[p.sym.n] is true, then isChecked contains only p.sym.n if (isChecked[p.sym.n]) gen.WriteLine("Get();"); else gen.WriteLine("Expect({0});", p.sym.n); break; } case Node.wt: { Indent(indent); s1 = tab.Expected(p.next, curSy); s1.Or(tab.allSyncSets); gen.WriteLine("ExpectWeak({0}, {1});", p.sym.n, NewCondSet(s1)); break; } case Node.any: { Indent(indent); int acc = Sets.Elements(p.set); if (tab.terminals.Count == (acc + 1) || (acc > 0 && Sets.Equals(p.set, isChecked))) { // either this ANY accepts any terminal (the + 1 = end of file), or exactly what's allowed here gen.WriteLine("Get();"); } else { GenErrorMsg(altErr, curSy); if (acc > 0) { gen.Write("if ("); GenCond(p.set, p); gen.WriteLine(") Get(); else SynErr({0});", errorNr); } else gen.WriteLine("SynErr({0}); // ANY node that matches no symbol", errorNr); } break; } case Node.eps: break; // nothing case Node.rslv: break; // nothing case Node.sem: { CopySourcePart(p.pos, indent); break; } case Node.sync: { Indent(indent); GenErrorMsg(syncErr, curSy); s1 = (BitArray)p.set.Clone(); gen.Write("while (!("); GenCond(s1, p); gen.Write(")) {"); gen.Write("SynErr({0}); Get();", errorNr); gen.WriteLine("}"); break; } case Node.alt: { s1 = tab.First(p); bool equal = Sets.Equals(s1, isChecked); bool useSwitch = UseSwitch(p); if (useSwitch) { Indent(indent); gen.WriteLine("switch (la.kind) {"); } p2 = p; while (p2 != null) { s1 = tab.Expected(p2.sub, curSy); Indent(indent); if (useSwitch) { PutCaseLabels(s1); gen.WriteLine("{"); } else if (p2 == p) { gen.Write("if ("); GenCond(s1, p2.sub); gen.WriteLine(") {"); } else if (p2.down == null && equal) { gen.WriteLine("} else {"); } else { gen.Write("} else if ("); GenCond(s1, p2.sub); gen.WriteLine(") {"); } GenCode(p2.sub, indent + 1, s1); if (useSwitch) { Indent(indent); gen.WriteLine("\tbreak;"); Indent(indent); gen.WriteLine("}"); } p2 = p2.down; } Indent(indent); if (equal) { gen.WriteLine("}"); } else { GenErrorMsg(altErr, curSy); if (useSwitch) { gen.WriteLine("default: SynErr({0}); break;", errorNr); Indent(indent); gen.WriteLine("}"); } else { gen.Write("} "); gen.WriteLine("else SynErr({0});", errorNr); } } break; } case Node.iter: { Indent(indent); p2 = p.sub; gen.Write("while ("); if (p2.typ == Node.wt) { s1 = tab.Expected(p2.next, curSy); s2 = tab.Expected(p.next, curSy); gen.Write("WeakSeparator({0},{1},{2}) ", p2.sym.n, NewCondSet(s1), NewCondSet(s2)); s1 = new BitArray(tab.terminals.Count); // for inner structure if (p2.up || p2.next == null) p2 = null; else p2 = p2.next; } else { s1 = tab.First(p2); GenCond(s1, p2); } gen.WriteLine(") {"); GenCode(p2, indent + 1, s1); Indent(indent); gen.WriteLine("}"); break; } case Node.opt: s1 = tab.First(p.sub); Indent(indent); gen.Write("if ("); GenCond(s1, p.sub); gen.WriteLine(") {"); GenCode(p.sub, indent + 1, s1); Indent(indent); gen.WriteLine("}"); break; } if (p.typ != Node.eps && p.typ != Node.sem && p.typ != Node.sync) isChecked.SetAll(false); // = new BitArray(tab.terminals.Count); if (p.up) break; p = p.next; } } void GenTokens() { foreach (Symbol sym in tab.terminals) { if (Char.IsLetter(sym.name[0])) gen.WriteLine("\tpublic const int _{0} = {1};", sym.name, sym.n); } } void GenPragmas() { foreach (Symbol sym in tab.pragmas) { gen.WriteLine("\tpublic const int _{0} = {1};", sym.name, sym.n); } } void GenCodePragmas() { foreach (Symbol sym in tab.pragmas) { gen.WriteLine("\t\t\t\tif (la.kind == {0}) {{", sym.n); CopySourcePart(sym.semPos, 4); gen.WriteLine("\t\t\t\t}"); } } void GenProductions() { foreach (Symbol sym in tab.nonterminals) { curSy = sym; gen.Write("\tvoid {0}(", sym.name); CopySourcePart(sym.attrPos, 0); gen.WriteLine(") {"); CopySourcePart(sym.semPos, 2); GenCode(sym.graph, 2, new BitArray(tab.terminals.Count)); gen.WriteLine("\t}"); gen.WriteLine(); } } void InitSets() { for (int i = 0; i < symSet.Count; i++) { BitArray s = (BitArray)symSet[i]; gen.Write("\t\t{"); int j = 0; foreach (Symbol sym in tab.terminals) { if (s[sym.n]) gen.Write("T,"); else gen.Write("x,"); ++j; if (j%4 == 0) gen.Write(" "); } if (i == symSet.Count-1) gen.WriteLine("x}"); else gen.WriteLine("x},"); } } public void WriteParser () { Generator g = new Generator(tab); int oldPos = buffer.Pos; // Pos is modified by CopySourcePart symSet.Add(tab.allSyncSets); fram = g.OpenFrame("Parser.frame"); gen = g.OpenGen("Parser.cs"); err = new StringWriter(); foreach (Symbol sym in tab.terminals) GenErrorMsg(tErr, sym); g.GenCopyright(); g.SkipFramePart("-->begin"); if (usingPos != null) { CopySourcePart(usingPos, 0); gen.WriteLine(); } g.CopyFramePart("-->namespace"); /* AW open namespace, if it exists */ if (tab.nsName != null && tab.nsName.Length > 0) { gen.WriteLine("namespace {0} {{", tab.nsName); gen.WriteLine(); } g.CopyFramePart("-->constants"); GenTokens(); /* ML 2002/09/07 write the token kinds */ gen.WriteLine("\tpublic const int maxT = {0};", tab.terminals.Count-1); GenPragmas(); /* ML 2005/09/23 write the pragma kinds */ g.CopyFramePart("-->declarations"); CopySourcePart(tab.semDeclPos, 0); g.CopyFramePart("-->pragmas"); GenCodePragmas(); g.CopyFramePart("-->productions"); GenProductions(); g.CopyFramePart("-->parseRoot"); gen.WriteLine("\t\t{0}();", tab.gramSy.name); if (tab.checkEOF) gen.WriteLine("\t\tExpect(0);"); g.CopyFramePart("-->initialization"); InitSets(); g.CopyFramePart("-->errors"); gen.Write(err.ToString()); g.CopyFramePart(null); /* AW 2002-12-20 close namespace, if it exists */ if (tab.nsName != null && tab.nsName.Length > 0) gen.Write("}"); gen.Close(); buffer.Pos = oldPos; } public void WriteStatistics () { trace.WriteLine(); trace.WriteLine("{0} terminals", tab.terminals.Count); trace.WriteLine("{0} symbols", tab.terminals.Count + tab.pragmas.Count + tab.nonterminals.Count); trace.WriteLine("{0} nodes", tab.nodes.Count); trace.WriteLine("{0} sets", symSet.Count); } public ParserGen (Parser parser) { tab = parser.tab; errors = parser.errors; trace = parser.trace; buffer = parser.scanner.buffer; errorNr = -1; usingPos = null; } } // end ParserGen } // end namespace coco-cs_20110419/Scanner.cs0000777000175000010010000004242311553243050013375 0ustar mlNone/*---------------------------------------------------------------------- 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. -----------------------------------------------------------------------*/ using System; using System.IO; using System.Collections; namespace at.jku.ssw.Coco { public class Token { public int kind; // token kind public int pos; // token position in bytes in the source text (starting at 0) public int charPos; // token position in characters in the source text (starting at 0) public int col; // token column (starting at 1) public int line; // token line (starting at 1) public string val; // token value public Token next; // ML 2005-03-11 Tokens are kept in linked list } //----------------------------------------------------------------------------------- // Buffer //----------------------------------------------------------------------------------- public class Buffer { // This Buffer supports the following cases: // 1) seekable stream (file) // a) whole stream in buffer // b) part of stream in buffer // 2) non seekable stream (network, console) public const int EOF = char.MaxValue + 1; const int MIN_BUFFER_LENGTH = 1024; // 1KB const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB byte[] buf; // input buffer int bufStart; // position of first byte in buffer relative to input stream int bufLen; // length of buffer int fileLen; // length of input stream (may change if the stream is no file) int bufPos; // current position in buffer Stream stream; // input stream (seekable) bool isUserStream; // was the stream opened by the user? public Buffer (Stream s, bool isUserStream) { stream = s; this.isUserStream = isUserStream; if (stream.CanSeek) { fileLen = (int) stream.Length; bufLen = Math.Min(fileLen, MAX_BUFFER_LENGTH); bufStart = Int32.MaxValue; // nothing in the buffer so far } else { fileLen = bufLen = bufStart = 0; } buf = new byte[(bufLen>0) ? bufLen : MIN_BUFFER_LENGTH]; if (fileLen > 0) Pos = 0; // setup buffer to position 0 (start) else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid if (bufLen == fileLen && stream.CanSeek) Close(); } protected Buffer(Buffer b) { // called in UTF8Buffer constructor buf = b.buf; bufStart = b.bufStart; bufLen = b.bufLen; fileLen = b.fileLen; bufPos = b.bufPos; stream = b.stream; // keep destructor from closing the stream b.stream = null; isUserStream = b.isUserStream; } ~Buffer() { Close(); } protected void Close() { if (!isUserStream && stream != null) { stream.Close(); stream = null; } } public virtual int Read () { if (bufPos < bufLen) { return buf[bufPos++]; } else if (Pos < fileLen) { Pos = Pos; // shift buffer start to Pos return buf[bufPos++]; } else if (stream != null && !stream.CanSeek && ReadNextStreamChunk() > 0) { return buf[bufPos++]; } else { return EOF; } } public int Peek () { int curPos = Pos; int ch = Read(); Pos = curPos; return ch; } // beg .. begin, zero-based, inclusive, in byte // end .. end, zero-based, exclusive, in byte public string GetString (int beg, int end) { int len = 0; char[] buf = new char[end - beg]; int oldPos = Pos; Pos = beg; while (Pos < end) buf[len++] = (char) Read(); Pos = oldPos; return new String(buf, 0, len); } public int Pos { get { return bufPos + bufStart; } set { if (value >= fileLen && stream != null && !stream.CanSeek) { // Wanted position is after buffer and the stream // is not seek-able e.g. network or console, // thus we have to read the stream manually till // the wanted position is in sight. while (value >= fileLen && ReadNextStreamChunk() > 0); } if (value < 0 || value > fileLen) { throw new FatalError("buffer out of bounds access, position: " + value); } if (value >= bufStart && value < bufStart + bufLen) { // already in buffer bufPos = value - bufStart; } else if (stream != null) { // must be swapped in stream.Seek(value, SeekOrigin.Begin); bufLen = stream.Read(buf, 0, buf.Length); bufStart = value; bufPos = 0; } else { // set the position to the end of the file, Pos will return fileLen. bufPos = fileLen - bufStart; } } } // Read the next chunk of bytes from the stream, increases the buffer // if needed and updates the fields fileLen and bufLen. // Returns the number of bytes read. private int ReadNextStreamChunk() { int free = buf.Length - bufLen; if (free == 0) { // in the case of a growing input stream // we can neither seek in the stream, nor can we // foresee the maximum length, thus we must adapt // the buffer size on demand. byte[] newBuf = new byte[bufLen * 2]; Array.Copy(buf, newBuf, bufLen); buf = newBuf; free = bufLen; } int read = stream.Read(buf, bufLen, free); if (read > 0) { fileLen = bufLen = (bufLen + read); return read; } // end of stream reached return 0; } } //----------------------------------------------------------------------------------- // UTF8Buffer //----------------------------------------------------------------------------------- public class UTF8Buffer: Buffer { public UTF8Buffer(Buffer b): base(b) {} public override int Read() { int ch; do { ch = base.Read(); // until we find a utf8 start (0xxxxxxx or 11xxxxxx) } while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EOF)); if (ch < 128 || ch == EOF) { // nothing to do, first 127 chars are the same in ascii and utf8 // 0xxxxxxx or end of file character } else if ((ch & 0xF0) == 0xF0) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx int c1 = ch & 0x07; ch = base.Read(); int c2 = ch & 0x3F; ch = base.Read(); int c3 = ch & 0x3F; ch = base.Read(); int c4 = ch & 0x3F; ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4; } else if ((ch & 0xE0) == 0xE0) { // 1110xxxx 10xxxxxx 10xxxxxx int c1 = ch & 0x0F; ch = base.Read(); int c2 = ch & 0x3F; ch = base.Read(); int c3 = ch & 0x3F; ch = (((c1 << 6) | c2) << 6) | c3; } else if ((ch & 0xC0) == 0xC0) { // 110xxxxx 10xxxxxx int c1 = ch & 0x1F; ch = base.Read(); int c2 = ch & 0x3F; ch = (c1 << 6) | c2; } return ch; } } //----------------------------------------------------------------------------------- // Scanner //----------------------------------------------------------------------------------- public class Scanner { const char EOL = '\n'; const int eofSym = 0; /* pdt */ const int maxT = 41; const int noSym = 41; public Buffer buffer; // scanner buffer Token t; // current token int ch; // current input character int pos; // byte position of current character int charPos; // position by unicode characters starting with 0 int col; // column number of current character int line; // line number of current character int oldEols; // EOLs that appeared in a comment; static readonly Hashtable start; // maps first token character to start state Token tokens; // list of tokens already peeked (first token is a dummy) Token pt; // current peek token char[] tval = new char[128]; // text of current token int tlen; // length of current token static Scanner() { start = new Hashtable(128); for (int i = 65; i <= 90; ++i) start[i] = 1; for (int i = 95; i <= 95; ++i) start[i] = 1; for (int i = 97; i <= 122; ++i) start[i] = 1; for (int i = 48; i <= 57; ++i) start[i] = 2; start[34] = 12; start[39] = 5; start[36] = 13; start[61] = 16; start[46] = 31; start[43] = 17; start[45] = 18; start[60] = 32; start[62] = 20; start[124] = 23; start[40] = 33; start[41] = 24; start[91] = 25; start[93] = 26; start[123] = 27; start[125] = 28; start[Buffer.EOF] = -1; } public Scanner (string fileName) { try { Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); buffer = new Buffer(stream, false); Init(); } catch (IOException) { throw new FatalError("Cannot open file " + fileName); } } public Scanner (Stream s) { buffer = new Buffer(s, true); Init(); } void Init() { pos = -1; line = 1; col = 0; charPos = -1; oldEols = 0; NextCh(); if (ch == 0xEF) { // check optional byte order mark for UTF-8 NextCh(); int ch1 = ch; NextCh(); int ch2 = ch; if (ch1 != 0xBB || ch2 != 0xBF) { throw new FatalError(String.Format("illegal byte order mark: EF {0,2:X} {1,2:X}", ch1, ch2)); } buffer = new UTF8Buffer(buffer); col = 0; charPos = -1; NextCh(); } pt = tokens = new Token(); // first token is a dummy } void NextCh() { if (oldEols > 0) { ch = EOL; oldEols--; } else { pos = buffer.Pos; // buffer reads unicode chars, if UTF8 has been detected ch = buffer.Read(); col++; charPos++; // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; if (ch == EOL) { line++; col = 0; } } } void AddCh() { if (tlen >= tval.Length) { char[] newBuf = new char[2 * tval.Length]; Array.Copy(tval, 0, newBuf, 0, tval.Length); tval = newBuf; } if (ch != Buffer.EOF) { tval[tlen++] = (char) ch; NextCh(); } } bool Comment0() { int level = 1, pos0 = pos, line0 = line, col0 = col, charPos0 = charPos; NextCh(); if (ch == '/') { NextCh(); for(;;) { if (ch == 10) { level--; if (level == 0) { oldEols = line - line0; NextCh(); return true; } NextCh(); } else if (ch == Buffer.EOF) return false; else NextCh(); } } else { buffer.Pos = pos0; NextCh(); line = line0; col = col0; charPos = charPos0; } return false; } bool Comment1() { int level = 1, pos0 = pos, line0 = line, col0 = col, charPos0 = charPos; NextCh(); if (ch == '*') { NextCh(); for(;;) { if (ch == '*') { NextCh(); if (ch == '/') { level--; if (level == 0) { oldEols = line - line0; NextCh(); return true; } NextCh(); } } else if (ch == '/') { NextCh(); if (ch == '*') { level++; NextCh(); } } else if (ch == Buffer.EOF) return false; else NextCh(); } } else { buffer.Pos = pos0; NextCh(); line = line0; col = col0; charPos = charPos0; } return false; } void CheckLiteral() { switch (t.val) { case "COMPILER": t.kind = 6; break; case "IGNORECASE": t.kind = 7; break; case "CHARACTERS": t.kind = 8; break; case "TOKENS": t.kind = 9; break; case "PRAGMAS": t.kind = 10; break; case "COMMENTS": t.kind = 11; break; case "FROM": t.kind = 12; break; case "TO": t.kind = 13; break; case "NESTED": t.kind = 14; break; case "IGNORE": t.kind = 15; break; case "PRODUCTIONS": t.kind = 16; break; case "END": t.kind = 19; break; case "ANY": t.kind = 23; break; case "WEAK": t.kind = 29; break; case "SYNC": t.kind = 36; break; case "IF": t.kind = 37; break; case "CONTEXT": t.kind = 38; break; default: break; } } Token NextToken() { while (ch == ' ' || ch >= 9 && ch <= 10 || ch == 13 ) NextCh(); if (ch == '/' && Comment0() ||ch == '/' && Comment1()) return NextToken(); int recKind = noSym; int recEnd = pos; t = new Token(); t.pos = pos; t.col = col; t.line = line; t.charPos = charPos; int state; if (start.ContainsKey(ch)) { state = (int) start[ch]; } else { state = 0; } tlen = 0; AddCh(); switch (state) { case -1: { t.kind = eofSym; break; } // NextCh already done case 0: { if (recKind != noSym) { tlen = recEnd - t.pos; SetScannerBehindT(); } t.kind = recKind; break; } // NextCh already done case 1: recEnd = pos; recKind = 1; if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 1;} else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;} case 2: recEnd = pos; recKind = 2; if (ch >= '0' && ch <= '9') {AddCh(); goto case 2;} else {t.kind = 2; break;} case 3: {t.kind = 3; break;} case 4: {t.kind = 4; break;} case 5: if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '&' || ch >= '(' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 6;} else if (ch == 92) {AddCh(); goto case 7;} else {goto case 0;} case 6: if (ch == 39) {AddCh(); goto case 9;} else {goto case 0;} case 7: if (ch >= ' ' && ch <= '~') {AddCh(); goto case 8;} else {goto case 0;} case 8: if (ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'f') {AddCh(); goto case 8;} else if (ch == 39) {AddCh(); goto case 9;} else {goto case 0;} case 9: {t.kind = 5; break;} case 10: recEnd = pos; recKind = 42; if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 10;} else {t.kind = 42; break;} case 11: recEnd = pos; recKind = 43; if (ch >= '-' && ch <= '.' || ch >= '0' && ch <= ':' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 11;} else {t.kind = 43; break;} case 12: if (ch <= 9 || ch >= 11 && ch <= 12 || ch >= 14 && ch <= '!' || ch >= '#' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 12;} else if (ch == 10 || ch == 13) {AddCh(); goto case 4;} else if (ch == '"') {AddCh(); goto case 3;} else if (ch == 92) {AddCh(); goto case 14;} else {goto case 0;} case 13: recEnd = pos; recKind = 42; if (ch >= '0' && ch <= '9') {AddCh(); goto case 10;} else if (ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 15;} else {t.kind = 42; break;} case 14: if (ch >= ' ' && ch <= '~') {AddCh(); goto case 12;} else {goto case 0;} case 15: recEnd = pos; recKind = 42; if (ch >= '0' && ch <= '9') {AddCh(); goto case 10;} else if (ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 15;} else if (ch == '=') {AddCh(); goto case 11;} else {t.kind = 42; break;} case 16: {t.kind = 17; break;} case 17: {t.kind = 20; break;} case 18: {t.kind = 21; break;} case 19: {t.kind = 22; break;} case 20: {t.kind = 25; break;} case 21: {t.kind = 26; break;} case 22: {t.kind = 27; break;} case 23: {t.kind = 28; break;} case 24: {t.kind = 31; break;} case 25: {t.kind = 32; break;} case 26: {t.kind = 33; break;} case 27: {t.kind = 34; break;} case 28: {t.kind = 35; break;} case 29: {t.kind = 39; break;} case 30: {t.kind = 40; break;} case 31: recEnd = pos; recKind = 18; if (ch == '.') {AddCh(); goto case 19;} else if (ch == '>') {AddCh(); goto case 22;} else if (ch == ')') {AddCh(); goto case 30;} else {t.kind = 18; break;} case 32: recEnd = pos; recKind = 24; if (ch == '.') {AddCh(); goto case 21;} else {t.kind = 24; break;} case 33: recEnd = pos; recKind = 30; if (ch == '.') {AddCh(); goto case 29;} else {t.kind = 30; break;} } t.val = new String(tval, 0, tlen); return t; } private void SetScannerBehindT() { buffer.Pos = t.pos; NextCh(); line = t.line; col = t.col; charPos = t.charPos; for (int i = 0; i < tlen; i++) NextCh(); } // get the next token (possibly a token already seen during peeking) public Token Scan () { if (tokens.next == null) { return NextToken(); } else { pt = tokens = tokens.next; return tokens; } } // peek for the next token, ignore pragmas public Token Peek () { do { if (pt.next == null) { pt.next = NextToken(); } pt = pt.next; } while (pt.kind > maxT); // skip pragmas return pt; } // make sure that peeking starts at the current scan position public void ResetPeek () { pt = tokens; } } // end Scanner }coco-cs_20110419/Scanner.frame0000777000175000010010000002670011467576504014103 0ustar mlNone/*---------------------------------------------------------------------- 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. -----------------------------------------------------------------------*/ -->begin using System; using System.IO; using System.Collections; -->namespace public class Token { public int kind; // token kind public int pos; // token position in bytes in the source text (starting at 0) public int charPos; // token position in characters in the source text (starting at 0) public int col; // token column (starting at 1) public int line; // token line (starting at 1) public string val; // token value public Token next; // ML 2005-03-11 Tokens are kept in linked list } //----------------------------------------------------------------------------------- // Buffer //----------------------------------------------------------------------------------- public class Buffer { // This Buffer supports the following cases: // 1) seekable stream (file) // a) whole stream in buffer // b) part of stream in buffer // 2) non seekable stream (network, console) public const int EOF = char.MaxValue + 1; const int MIN_BUFFER_LENGTH = 1024; // 1KB const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB byte[] buf; // input buffer int bufStart; // position of first byte in buffer relative to input stream int bufLen; // length of buffer int fileLen; // length of input stream (may change if the stream is no file) int bufPos; // current position in buffer Stream stream; // input stream (seekable) bool isUserStream; // was the stream opened by the user? public Buffer (Stream s, bool isUserStream) { stream = s; this.isUserStream = isUserStream; if (stream.CanSeek) { fileLen = (int) stream.Length; bufLen = Math.Min(fileLen, MAX_BUFFER_LENGTH); bufStart = Int32.MaxValue; // nothing in the buffer so far } else { fileLen = bufLen = bufStart = 0; } buf = new byte[(bufLen>0) ? bufLen : MIN_BUFFER_LENGTH]; if (fileLen > 0) Pos = 0; // setup buffer to position 0 (start) else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid if (bufLen == fileLen && stream.CanSeek) Close(); } protected Buffer(Buffer b) { // called in UTF8Buffer constructor buf = b.buf; bufStart = b.bufStart; bufLen = b.bufLen; fileLen = b.fileLen; bufPos = b.bufPos; stream = b.stream; // keep destructor from closing the stream b.stream = null; isUserStream = b.isUserStream; } ~Buffer() { Close(); } protected void Close() { if (!isUserStream && stream != null) { stream.Close(); stream = null; } } public virtual int Read () { if (bufPos < bufLen) { return buf[bufPos++]; } else if (Pos < fileLen) { Pos = Pos; // shift buffer start to Pos return buf[bufPos++]; } else if (stream != null && !stream.CanSeek && ReadNextStreamChunk() > 0) { return buf[bufPos++]; } else { return EOF; } } public int Peek () { int curPos = Pos; int ch = Read(); Pos = curPos; return ch; } // beg .. begin, zero-based, inclusive, in byte // end .. end, zero-based, exclusive, in byte public string GetString (int beg, int end) { int len = 0; char[] buf = new char[end - beg]; int oldPos = Pos; Pos = beg; while (Pos < end) buf[len++] = (char) Read(); Pos = oldPos; return new String(buf, 0, len); } public int Pos { get { return bufPos + bufStart; } set { if (value >= fileLen && stream != null && !stream.CanSeek) { // Wanted position is after buffer and the stream // is not seek-able e.g. network or console, // thus we have to read the stream manually till // the wanted position is in sight. while (value >= fileLen && ReadNextStreamChunk() > 0); } if (value < 0 || value > fileLen) { throw new FatalError("buffer out of bounds access, position: " + value); } if (value >= bufStart && value < bufStart + bufLen) { // already in buffer bufPos = value - bufStart; } else if (stream != null) { // must be swapped in stream.Seek(value, SeekOrigin.Begin); bufLen = stream.Read(buf, 0, buf.Length); bufStart = value; bufPos = 0; } else { // set the position to the end of the file, Pos will return fileLen. bufPos = fileLen - bufStart; } } } // Read the next chunk of bytes from the stream, increases the buffer // if needed and updates the fields fileLen and bufLen. // Returns the number of bytes read. private int ReadNextStreamChunk() { int free = buf.Length - bufLen; if (free == 0) { // in the case of a growing input stream // we can neither seek in the stream, nor can we // foresee the maximum length, thus we must adapt // the buffer size on demand. byte[] newBuf = new byte[bufLen * 2]; Array.Copy(buf, newBuf, bufLen); buf = newBuf; free = bufLen; } int read = stream.Read(buf, bufLen, free); if (read > 0) { fileLen = bufLen = (bufLen + read); return read; } // end of stream reached return 0; } } //----------------------------------------------------------------------------------- // UTF8Buffer //----------------------------------------------------------------------------------- public class UTF8Buffer: Buffer { public UTF8Buffer(Buffer b): base(b) {} public override int Read() { int ch; do { ch = base.Read(); // until we find a utf8 start (0xxxxxxx or 11xxxxxx) } while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EOF)); if (ch < 128 || ch == EOF) { // nothing to do, first 127 chars are the same in ascii and utf8 // 0xxxxxxx or end of file character } else if ((ch & 0xF0) == 0xF0) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx int c1 = ch & 0x07; ch = base.Read(); int c2 = ch & 0x3F; ch = base.Read(); int c3 = ch & 0x3F; ch = base.Read(); int c4 = ch & 0x3F; ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4; } else if ((ch & 0xE0) == 0xE0) { // 1110xxxx 10xxxxxx 10xxxxxx int c1 = ch & 0x0F; ch = base.Read(); int c2 = ch & 0x3F; ch = base.Read(); int c3 = ch & 0x3F; ch = (((c1 << 6) | c2) << 6) | c3; } else if ((ch & 0xC0) == 0xC0) { // 110xxxxx 10xxxxxx int c1 = ch & 0x1F; ch = base.Read(); int c2 = ch & 0x3F; ch = (c1 << 6) | c2; } return ch; } } //----------------------------------------------------------------------------------- // Scanner //----------------------------------------------------------------------------------- public class Scanner { const char EOL = '\n'; const int eofSym = 0; /* pdt */ -->declarations public Buffer buffer; // scanner buffer Token t; // current token int ch; // current input character int pos; // byte position of current character int charPos; // position by unicode characters starting with 0 int col; // column number of current character int line; // line number of current character int oldEols; // EOLs that appeared in a comment; static readonly Hashtable start; // maps first token character to start state Token tokens; // list of tokens already peeked (first token is a dummy) Token pt; // current peek token char[] tval = new char[128]; // text of current token int tlen; // length of current token static Scanner() { start = new Hashtable(128); -->initialization } public Scanner (string fileName) { try { Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); buffer = new Buffer(stream, false); Init(); } catch (IOException) { throw new FatalError("Cannot open file " + fileName); } } public Scanner (Stream s) { buffer = new Buffer(s, true); Init(); } void Init() { pos = -1; line = 1; col = 0; charPos = -1; oldEols = 0; NextCh(); if (ch == 0xEF) { // check optional byte order mark for UTF-8 NextCh(); int ch1 = ch; NextCh(); int ch2 = ch; if (ch1 != 0xBB || ch2 != 0xBF) { throw new FatalError(String.Format("illegal byte order mark: EF {0,2:X} {1,2:X}", ch1, ch2)); } buffer = new UTF8Buffer(buffer); col = 0; charPos = -1; NextCh(); } pt = tokens = new Token(); // first token is a dummy } void NextCh() { if (oldEols > 0) { ch = EOL; oldEols--; } else { pos = buffer.Pos; // buffer reads unicode chars, if UTF8 has been detected ch = buffer.Read(); col++; charPos++; // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; if (ch == EOL) { line++; col = 0; } } -->casing1 } void AddCh() { if (tlen >= tval.Length) { char[] newBuf = new char[2 * tval.Length]; Array.Copy(tval, 0, newBuf, 0, tval.Length); tval = newBuf; } if (ch != Buffer.EOF) { -->casing2 NextCh(); } } -->comments void CheckLiteral() { -->literals } Token NextToken() { while (ch == ' ' || -->scan1 ) NextCh(); -->scan2 int recKind = noSym; int recEnd = pos; t = new Token(); t.pos = pos; t.col = col; t.line = line; t.charPos = charPos; int state; if (start.ContainsKey(ch)) { state = (int) start[ch]; } else { state = 0; } tlen = 0; AddCh(); switch (state) { case -1: { t.kind = eofSym; break; } // NextCh already done case 0: { if (recKind != noSym) { tlen = recEnd - t.pos; SetScannerBehindT(); } t.kind = recKind; break; } // NextCh already done -->scan3 } t.val = new String(tval, 0, tlen); return t; } private void SetScannerBehindT() { buffer.Pos = t.pos; NextCh(); line = t.line; col = t.col; charPos = t.charPos; for (int i = 0; i < tlen; i++) NextCh(); } // get the next token (possibly a token already seen during peeking) public Token Scan () { if (tokens.next == null) { return NextToken(); } else { pt = tokens = tokens.next; return tokens; } } // peek for the next token, ignore pragmas public Token Peek () { do { if (pt.next == null) { pt.next = NextToken(); } pt = pt.next; } while (pt.kind > maxT); // skip pragmas return pt; } // make sure that peeking starts at the current scan position public void ResetPeek () { pt = tokens; } } // end Scanner coco-cs_20110419/Tab.cs0000777000175000010010000011276311553244232012522 0ustar mlNone/*------------------------------------------------------------------------- Tab.cs -- Symbol Table Management 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. -------------------------------------------------------------------------*/ using System; using System.IO; using System.Text; using System.Collections; namespace at.jku.ssw.Coco { public class Position { // position of source code stretch (e.g. semantic action, resolver expressions) public readonly int beg; // start relative to the beginning of the file public readonly int end; // end of stretch public readonly int col; // column number of start position public readonly int line; // line number of start position public Position(int beg, int end, int col, int line) { this.beg = beg; this.end = end; this.col = col; this.line = line; } } //===================================================================== // Symbol //===================================================================== public class Symbol { // token kinds public const int fixedToken = 0; // e.g. 'a' ('b' | 'c') (structure of literals) public const int classToken = 1; // e.g. digit {digit} (at least one char class) public const int litToken = 2; // e.g. "while" public const int classLitToken = 3; // e.g. letter {letter} but without literals that have the same structure public int n; // symbol number public int typ; // t, nt, pr, unknown, rslv /* ML 29_11_2002 slv added */ /* AW slv --> rslv */ public string name; // symbol name public Node graph; // nt: to first node of syntax graph public int tokenKind; // t: token kind (fixedToken, classToken, ...) public bool deletable; // nt: true if nonterminal is deletable public bool firstReady; // nt: true if terminal start symbols have already been computed public BitArray first; // nt: terminal start symbols public BitArray follow; // nt: terminal followers public BitArray nts; // nt: nonterminals whose followers have to be added to this sym public int line; // source text line number of item in this node public Position attrPos; // nt: position of attributes in source text (or null) public Position semPos; // pr: pos of semantic action in source text (or null) // nt: pos of local declarations in source text (or null) public Symbol(int typ, string name, int line) { this.typ = typ; this.name = name; this.line = line; } } //===================================================================== // Node //===================================================================== public class Node { // constants for node kinds public const int t = 1; // terminal symbol public const int pr = 2; // pragma public const int nt = 3; // nonterminal symbol public const int clas = 4; // character class public const int chr = 5; // character public const int wt = 6; // weak terminal symbol public const int any = 7; // public const int eps = 8; // empty public const int sync = 9; // synchronization symbol public const int sem = 10; // semantic action: (. .) public const int alt = 11; // alternative: | public const int iter = 12; // iteration: { } public const int opt = 13; // option: [ ] public const int rslv = 14; // resolver expr public const int normalTrans = 0; // transition codes public const int contextTrans = 1; public int n; // node number public int typ; // t, nt, wt, chr, clas, any, eps, sem, sync, alt, iter, opt, rslv public Node next; // to successor node public Node down; // alt: to next alternative public Node sub; // alt, iter, opt: to first node of substructure public bool up; // true: "next" leads to successor in enclosing structure public Symbol sym; // nt, t, wt: symbol represented by this node public int val; // chr: ordinal character value // clas: index of character class public int code; // chr, clas: transition code public BitArray set; // any, sync: the set represented by this node public Position pos; // nt, t, wt: pos of actual attributes // sem: pos of semantic action in source text // rslv: pos of resolver in source text public int line; // source text line number of item in this node public State state; // DFA state corresponding to this node // (only used in DFA.ConvertToStates) public Node(int typ, Symbol sym, int line) { this.typ = typ; this.sym = sym; this.line = line; } } //===================================================================== // Graph //===================================================================== public class Graph { public Node l; // left end of graph = head public Node r; // right end of graph = list of nodes to be linked to successor graph public Graph() { l = null; r = null; } public Graph(Node left, Node right) { l = left; r = right; } public Graph(Node p) { l = p; r = p; } } //===================================================================== // Sets //===================================================================== public class Sets { public static int Elements(BitArray s) { int max = s.Count; int n = 0; for (int i=0; i= 80) { trace.WriteLine(); for (col = 1; col < indent; col++) trace.Write(" "); } trace.Write("{0} ", sym.name); col += len + 1; } } if (col == indent) trace.Write("-- empty set --"); trace.WriteLine(); } //--------------------------------------------------------------------- // Syntax graph management //--------------------------------------------------------------------- public ArrayList nodes = new ArrayList(); public string[] nTyp = {" ", "t ", "pr ", "nt ", "clas", "chr ", "wt ", "any ", "eps ", "sync", "sem ", "alt ", "iter", "opt ", "rslv"}; Node dummyNode; public Node NewNode(int typ, Symbol sym, int line) { Node node = new Node(typ, sym, line); node.n = nodes.Count; nodes.Add(node); return node; } public Node NewNode(int typ, Node sub) { Node node = NewNode(typ, null, 0); node.sub = sub; return node; } public Node NewNode(int typ, int val, int line) { Node node = NewNode(typ, null, line); node.val = val; return node; } public void MakeFirstAlt(Graph g) { g.l = NewNode(Node.alt, g.l); g.l.line = g.l.sub.line; g.r.up = true; g.l.next = g.r; g.r = g.l; } // The result will be in g1 public void MakeAlternative(Graph g1, Graph g2) { g2.l = NewNode(Node.alt, g2.l); g2.l.line = g2.l.sub.line; g2.l.up = true; g2.r.up = true; Node p = g1.l; while (p.down != null) p = p.down; p.down = g2.l; p = g1.r; while (p.next != null) p = p.next; // append alternative to g1 end list p.next = g2.l; // append g2 end list to g1 end list g2.l.next = g2.r; } // The result will be in g1 public void MakeSequence(Graph g1, Graph g2) { Node p = g1.r.next; g1.r.next = g2.l; // link head node while (p != null) { // link substructure Node q = p.next; p.next = g2.l; p = q; } g1.r = g2.r; } public void MakeIteration(Graph g) { g.l = NewNode(Node.iter, g.l); g.r.up = true; Node p = g.r; g.r = g.l; while (p != null) { Node q = p.next; p.next = g.l; p = q; } } public void MakeOption(Graph g) { g.l = NewNode(Node.opt, g.l); g.r.up = true; g.l.next = g.r; g.r = g.l; } public void Finish(Graph g) { Node p = g.r; while (p != null) { Node q = p.next; p.next = null; p = q; } } public void DeleteNodes() { nodes = new ArrayList(); dummyNode = NewNode(Node.eps, null, 0); } public Graph StrToGraph(string str) { string s = Unescape(str.Substring(1, str.Length-2)); if (s.Length == 0) parser.SemErr("empty token not allowed"); Graph g = new Graph(); g.r = dummyNode; for (int i = 0; i < s.Length; i++) { Node p = NewNode(Node.chr, (int)s[i], 0); g.r.next = p; g.r = p; } g.l = dummyNode.next; dummyNode.next = null; return g; } public void SetContextTrans(Node p) { // set transition code in the graph rooted at p while (p != null) { if (p.typ == Node.chr || p.typ == Node.clas) { p.code = Node.contextTrans; } else if (p.typ == Node.opt || p.typ == Node.iter) { SetContextTrans(p.sub); } else if (p.typ == Node.alt) { SetContextTrans(p.sub); SetContextTrans(p.down); } if (p.up) break; p = p.next; } } //------------ graph deletability check ----------------- public static bool DelGraph(Node p) { return p == null || DelNode(p) && DelGraph(p.next); } public static bool DelSubGraph(Node p) { return p == null || DelNode(p) && (p.up || DelSubGraph(p.next)); } public static bool DelNode(Node p) { if (p.typ == Node.nt) return p.sym.deletable; else if (p.typ == Node.alt) return DelSubGraph(p.sub) || p.down != null && DelSubGraph(p.down); else return p.typ == Node.iter || p.typ == Node.opt || p.typ == Node.sem || p.typ == Node.eps || p.typ == Node.rslv || p.typ == Node.sync; } //----------------- graph printing ---------------------- string Ptr(Node p, bool up) { string ptr = (p == null) ? "0" : p.n.ToString(); return (up) ? ("-" + ptr) : ptr; } string Pos(Position pos) { if (pos == null) return " "; else return String.Format("{0,5}", pos.beg); } public string Name(string name) { return (name + " ").Substring(0, 12); // found no simpler way to get the first 12 characters of the name // padded with blanks on the right } public void PrintNodes() { trace.WriteLine("Graph nodes:"); trace.WriteLine("----------------------------------------------------"); trace.WriteLine(" n type name next down sub pos line"); trace.WriteLine(" val code"); trace.WriteLine("----------------------------------------------------"); foreach (Node p in nodes) { trace.Write("{0,4} {1} ", p.n, nTyp[p.typ]); if (p.sym != null) trace.Write("{0,12} ", Name(p.sym.name)); else if (p.typ == Node.clas) { CharClass c = (CharClass)classes[p.val]; trace.Write("{0,12} ", Name(c.name)); } else trace.Write(" "); trace.Write("{0,5} ", Ptr(p.next, p.up)); switch (p.typ) { case Node.t: case Node.nt: case Node.wt: trace.Write(" {0,5}", Pos(p.pos)); break; case Node.chr: trace.Write("{0,5} {1,5} ", p.val, p.code); break; case Node.clas: trace.Write(" {0,5} ", p.code); break; case Node.alt: case Node.iter: case Node.opt: trace.Write("{0,5} {1,5} ", Ptr(p.down, false), Ptr(p.sub, false)); break; case Node.sem: trace.Write(" {0,5}", Pos(p.pos)); break; case Node.eps: case Node.any: case Node.sync: trace.Write(" "); break; } trace.WriteLine("{0,5}", p.line); } trace.WriteLine(); } //--------------------------------------------------------------------- // Character class management //--------------------------------------------------------------------- public ArrayList classes = new ArrayList(); public int dummyName = 'A'; public CharClass NewCharClass(string name, CharSet s) { if (name == "#") name = "#" + (char)dummyName++; CharClass c = new CharClass(name, s); c.n = classes.Count; classes.Add(c); return c; } public CharClass FindCharClass(string name) { foreach (CharClass c in classes) if (c.name == name) return c; return null; } public CharClass FindCharClass(CharSet s) { foreach (CharClass c in classes) if (s.Equals(c.set)) return c; return null; } public CharSet CharClassSet(int i) { return ((CharClass)classes[i]).set; } //----------- character class printing string Ch(int ch) { if (ch < ' ' || ch >= 127 || ch == '\'' || ch == '\\') return ch.ToString(); else return String.Format("'{0}'", (char)ch); } void WriteCharSet(CharSet s) { for (CharSet.Range r = s.head; r != null; r = r.next) if (r.from < r.to) { trace.Write(Ch(r.from) + ".." + Ch(r.to) + " "); } else { trace.Write(Ch(r.from) + " "); } } public void WriteCharClasses () { foreach (CharClass c in classes) { trace.Write("{0,-10}: ", c.name); WriteCharSet(c.set); trace.WriteLine(); } trace.WriteLine(); } //--------------------------------------------------------------------- // Symbol set computations //--------------------------------------------------------------------- /* Computes the first set for the graph rooted at p */ BitArray First0(Node p, BitArray mark) { BitArray fs = new BitArray(terminals.Count); while (p != null && !mark[p.n]) { mark[p.n] = true; switch (p.typ) { case Node.nt: { if (p.sym.firstReady) fs.Or(p.sym.first); else fs.Or(First0(p.sym.graph, mark)); break; } case Node.t: case Node.wt: { fs[p.sym.n] = true; break; } case Node.any: { fs.Or(p.set); break; } case Node.alt: { fs.Or(First0(p.sub, mark)); fs.Or(First0(p.down, mark)); break; } case Node.iter: case Node.opt: { fs.Or(First0(p.sub, mark)); break; } } if (!DelNode(p)) break; p = p.next; } return fs; } public BitArray First(Node p) { BitArray fs = First0(p, new BitArray(nodes.Count)); if (ddt[3]) { trace.WriteLine(); if (p != null) trace.WriteLine("First: node = {0}", p.n); else trace.WriteLine("First: node = null"); PrintSet(fs, 0); } return fs; } void CompFirstSets() { foreach (Symbol sym in nonterminals) { sym.first = new BitArray(terminals.Count); sym.firstReady = false; } foreach (Symbol sym in nonterminals) { sym.first = First(sym.graph); sym.firstReady = true; } } void CompFollow(Node p) { while (p != null && !visited[p.n]) { visited[p.n] = true; if (p.typ == Node.nt) { BitArray s = First(p.next); p.sym.follow.Or(s); if (DelGraph(p.next)) p.sym.nts[curSy.n] = true; } else if (p.typ == Node.opt || p.typ == Node.iter) { CompFollow(p.sub); } else if (p.typ == Node.alt) { CompFollow(p.sub); CompFollow(p.down); } p = p.next; } } void Complete(Symbol sym) { if (!visited[sym.n]) { visited[sym.n] = true; foreach (Symbol s in nonterminals) { if (sym.nts[s.n]) { Complete(s); sym.follow.Or(s.follow); if (sym == curSy) sym.nts[s.n] = false; } } } } void CompFollowSets() { foreach (Symbol sym in nonterminals) { sym.follow = new BitArray(terminals.Count); sym.nts = new BitArray(nonterminals.Count); } gramSy.follow[eofSy.n] = true; visited = new BitArray(nodes.Count); foreach (Symbol sym in nonterminals) { // get direct successors of nonterminals curSy = sym; CompFollow(sym.graph); } foreach (Symbol sym in nonterminals) { // add indirect successors to followers visited = new BitArray(nonterminals.Count); curSy = sym; Complete(sym); } } Node LeadingAny(Node p) { if (p == null) return null; Node a = null; if (p.typ == Node.any) a = p; else if (p.typ == Node.alt) { a = LeadingAny(p.sub); if (a == null) a = LeadingAny(p.down); } else if (p.typ == Node.opt || p.typ == Node.iter) a = LeadingAny(p.sub); if (a == null && DelNode(p) && !p.up) a = LeadingAny(p.next); return a; } void FindAS(Node p) { // find ANY sets Node a; while (p != null) { if (p.typ == Node.opt || p.typ == Node.iter) { FindAS(p.sub); a = LeadingAny(p.sub); if (a != null) Sets.Subtract(a.set, First(p.next)); } else if (p.typ == Node.alt) { BitArray s1 = new BitArray(terminals.Count); Node q = p; while (q != null) { FindAS(q.sub); a = LeadingAny(q.sub); if (a != null) Sets.Subtract(a.set, First(q.down).Or(s1)); else s1.Or(First(q.sub)); q = q.down; } } // Remove alternative terminals before ANY, in the following // examples a and b must be removed from the ANY set: // [a] ANY, or {a|b} ANY, or [a][b] ANY, or (a|) ANY, or // A = [a]. A ANY if (DelNode(p)) { a = LeadingAny(p.next); if (a != null) { Node q = (p.typ == Node.nt) ? p.sym.graph : p.sub; Sets.Subtract(a.set, First(q)); } } if (p.up) break; p = p.next; } } void CompAnySets() { foreach (Symbol sym in nonterminals) FindAS(sym.graph); } public BitArray Expected(Node p, Symbol curSy) { BitArray s = First(p); if (DelGraph(p)) s.Or(curSy.follow); return s; } // does not look behind resolvers; only called during LL(1) test and in CheckRes public BitArray Expected0(Node p, Symbol curSy) { if (p.typ == Node.rslv) return new BitArray(terminals.Count); else return Expected(p, curSy); } void CompSync(Node p) { while (p != null && !visited[p.n]) { visited[p.n] = true; if (p.typ == Node.sync) { BitArray s = Expected(p.next, curSy); s[eofSy.n] = true; allSyncSets.Or(s); p.set = s; } else if (p.typ == Node.alt) { CompSync(p.sub); CompSync(p.down); } else if (p.typ == Node.opt || p.typ == Node.iter) CompSync(p.sub); p = p.next; } } void CompSyncSets() { allSyncSets = new BitArray(terminals.Count); allSyncSets[eofSy.n] = true; visited = new BitArray(nodes.Count); foreach (Symbol sym in nonterminals) { curSy = sym; CompSync(curSy.graph); } } public void SetupAnys() { foreach (Node p in nodes) if (p.typ == Node.any) { p.set = new BitArray(terminals.Count, true); p.set[eofSy.n] = false; } } public void CompDeletableSymbols() { bool changed; do { changed = false; foreach (Symbol sym in nonterminals) if (!sym.deletable && sym.graph != null && DelGraph(sym.graph)) { sym.deletable = true; changed = true; } } while (changed); foreach (Symbol sym in nonterminals) if (sym.deletable) errors.Warning(" " + sym.name + " deletable"); } public void RenumberPragmas() { int n = terminals.Count; foreach (Symbol sym in pragmas) sym.n = n++; } public void CompSymbolSets() { CompDeletableSymbols(); CompFirstSets(); CompAnySets(); CompFollowSets(); CompSyncSets(); if (ddt[1]) { trace.WriteLine(); trace.WriteLine("First & follow symbols:"); trace.WriteLine("----------------------"); trace.WriteLine(); foreach (Symbol sym in nonterminals) { trace.WriteLine(sym.name); trace.Write("first: "); PrintSet(sym.first, 10); trace.Write("follow: "); PrintSet(sym.follow, 10); trace.WriteLine(); } } if (ddt[4]) { trace.WriteLine(); trace.WriteLine("ANY and SYNC sets:"); trace.WriteLine("-----------------"); foreach (Node p in nodes) if (p.typ == Node.any || p.typ == Node.sync) { trace.Write("{0,4} {1,4}: ", p.n, nTyp[p.typ]); PrintSet(p.set, 11); } } } //--------------------------------------------------------------------- // String handling //--------------------------------------------------------------------- char Hex2Char(string s) { int val = 0; for (int i = 0; i < s.Length; i++) { char ch = s[i]; if ('0' <= ch && ch <= '9') val = 16 * val + (ch - '0'); else if ('a' <= ch && ch <= 'f') val = 16 * val + (10 + ch - 'a'); else if ('A' <= ch && ch <= 'F') val = 16 * val + (10 + ch - 'A'); else parser.SemErr("bad escape sequence in string or character"); } if (val > char.MaxValue) /* pdt */ parser.SemErr("bad escape sequence in string or character"); return (char)val; } string Char2Hex(char ch) { StringWriter w = new StringWriter(); w.Write("\\u{0:x4}", (int)ch); return w.ToString(); } public string Unescape (string s) { /* replaces escape sequences in s by their Unicode values. */ StringBuilder buf = new StringBuilder(); int i = 0; while (i < s.Length) { if (s[i] == '\\') { switch (s[i+1]) { case '\\': buf.Append('\\'); i += 2; break; case '\'': buf.Append('\''); i += 2; break; case '\"': buf.Append('\"'); i += 2; break; case 'r': buf.Append('\r'); i += 2; break; case 'n': buf.Append('\n'); i += 2; break; case 't': buf.Append('\t'); i += 2; break; case '0': buf.Append('\0'); i += 2; break; case 'a': buf.Append('\a'); i += 2; break; case 'b': buf.Append('\b'); i += 2; break; case 'f': buf.Append('\f'); i += 2; break; case 'v': buf.Append('\v'); i += 2; break; case 'u': case 'x': if (i + 6 <= s.Length) { buf.Append(Hex2Char(s.Substring(i+2, 4))); i += 6; break; } else { parser.SemErr("bad escape sequence in string or character"); i = s.Length; break; } default: parser.SemErr("bad escape sequence in string or character"); i += 2; break; } } else { buf.Append(s[i]); i++; } } return buf.ToString(); } public string Escape (string s) { StringBuilder buf = new StringBuilder(); foreach (char ch in s) { switch(ch) { case '\\': buf.Append("\\\\"); break; case '\'': buf.Append("\\'"); break; case '\"': buf.Append("\\\""); break; case '\t': buf.Append("\\t"); break; case '\r': buf.Append("\\r"); break; case '\n': buf.Append("\\n"); break; default: if (ch < ' ' || ch > '\u007f') buf.Append(Char2Hex(ch)); else buf.Append(ch); break; } } return buf.ToString(); } //--------------------------------------------------------------------- // Grammar checks //--------------------------------------------------------------------- public bool GrammarOk() { bool ok = NtsComplete() && AllNtReached() && NoCircularProductions() && AllNtToTerm(); if (ok) { CheckResolvers(); CheckLL1(); } return ok; } //--------------- check for circular productions ---------------------- class CNode { // node of list for finding circular productions public Symbol left, right; public CNode (Symbol l, Symbol r) { left = l; right = r; } } void GetSingles(Node p, ArrayList singles) { if (p == null) return; // end of graph if (p.typ == Node.nt) { if (p.up || DelGraph(p.next)) singles.Add(p.sym); } else if (p.typ == Node.alt || p.typ == Node.iter || p.typ == Node.opt) { if (p.up || DelGraph(p.next)) { GetSingles(p.sub, singles); if (p.typ == Node.alt) GetSingles(p.down, singles); } } if (!p.up && DelNode(p)) GetSingles(p.next, singles); } public bool NoCircularProductions() { bool ok, changed, onLeftSide, onRightSide; ArrayList list = new ArrayList(); foreach (Symbol sym in nonterminals) { ArrayList singles = new ArrayList(); GetSingles(sym.graph, singles); // get nonterminals s such that sym-->s foreach (Symbol s in singles) list.Add(new CNode(sym, s)); } do { changed = false; for (int i = 0; i < list.Count; i++) { CNode n = (CNode)list[i]; onLeftSide = false; onRightSide = false; foreach (CNode m in list) { if (n.left == m.right) onRightSide = true; if (n.right == m.left) onLeftSide = true; } if (!onLeftSide || !onRightSide) { list.Remove(n); i--; changed = true; } } } while(changed); ok = true; foreach (CNode n in list) { ok = false; errors.SemErr(" " + n.left.name + " --> " + n.right.name); } return ok; } //--------------- check for LL(1) errors ---------------------- void LL1Error(int cond, Symbol sym) { string s = " LL1 warning in " + curSy.name + ": "; if (sym != null) s += sym.name + " is "; switch (cond) { case 1: s += "start of several alternatives"; break; case 2: s += "start & successor of deletable structure"; break; case 3: s += "an ANY node that matches no symbol"; break; case 4: s += "contents of [...] or {...} must not be deletable"; break; } errors.Warning(s); } void CheckOverlap(BitArray s1, BitArray s2, int cond) { foreach (Symbol sym in terminals) { if (s1[sym.n] && s2[sym.n]) LL1Error(cond, sym); } } void CheckAlts(Node p) { BitArray s1, s2; while (p != null) { if (p.typ == Node.alt) { Node q = p; s1 = new BitArray(terminals.Count); while (q != null) { // for all alternatives s2 = Expected0(q.sub, curSy); CheckOverlap(s1, s2, 1); s1.Or(s2); CheckAlts(q.sub); q = q.down; } } else if (p.typ == Node.opt || p.typ == Node.iter) { if (DelSubGraph(p.sub)) LL1Error(4, null); // e.g. [[...]] else { s1 = Expected0(p.sub, curSy); s2 = Expected(p.next, curSy); CheckOverlap(s1, s2, 2); } CheckAlts(p.sub); } else if (p.typ == Node.any) { if (Sets.Elements(p.set) == 0) LL1Error(3, null); // e.g. {ANY} ANY or [ANY] ANY or ( ANY | ANY ) } if (p.up) break; p = p.next; } } public void CheckLL1() { foreach (Symbol sym in nonterminals) { curSy = sym; CheckAlts(curSy.graph); } } //------------- check if resolvers are legal -------------------- void ResErr(Node p, string msg) { errors.Warning(p.line, p.pos.col, msg); } void CheckRes(Node p, bool rslvAllowed) { while (p != null) { switch (p.typ) { case Node.alt: BitArray expected = new BitArray(terminals.Count); for (Node q = p; q != null; q = q.down) expected.Or(Expected0(q.sub, curSy)); BitArray soFar = new BitArray(terminals.Count); for (Node q = p; q != null; q = q.down) { if (q.sub.typ == Node.rslv) { BitArray fs = Expected(q.sub.next, curSy); if (Sets.Intersect(fs, soFar)) ResErr(q.sub, "Warning: Resolver will never be evaluated. " + "Place it at previous conflicting alternative."); if (!Sets.Intersect(fs, expected)) ResErr(q.sub, "Warning: Misplaced resolver: no LL(1) conflict."); } else soFar.Or(Expected(q.sub, curSy)); CheckRes(q.sub, true); } break; case Node.iter: case Node.opt: if (p.sub.typ == Node.rslv) { BitArray fs = First(p.sub.next); BitArray fsNext = Expected(p.next, curSy); if (!Sets.Intersect(fs, fsNext)) ResErr(p.sub, "Warning: Misplaced resolver: no LL(1) conflict."); } CheckRes(p.sub, true); break; case Node.rslv: if (!rslvAllowed) ResErr(p, "Warning: Misplaced resolver: no alternative."); break; } if (p.up) break; p = p.next; rslvAllowed = false; } } public void CheckResolvers() { foreach (Symbol sym in nonterminals) { curSy = sym; CheckRes(curSy.graph, false); } } //------------- check if every nts has a production -------------------- public bool NtsComplete() { bool complete = true; foreach (Symbol sym in nonterminals) { if (sym.graph == null) { complete = false; errors.SemErr(" No production for " + sym.name); } } return complete; } //-------------- check if every nts can be reached ----------------- void MarkReachedNts(Node p) { while (p != null) { if (p.typ == Node.nt && !visited[p.sym.n]) { // new nt reached visited[p.sym.n] = true; MarkReachedNts(p.sym.graph); } else if (p.typ == Node.alt || p.typ == Node.iter || p.typ == Node.opt) { MarkReachedNts(p.sub); if (p.typ == Node.alt) MarkReachedNts(p.down); } if (p.up) break; p = p.next; } } public bool AllNtReached() { bool ok = true; visited = new BitArray(nonterminals.Count); visited[gramSy.n] = true; MarkReachedNts(gramSy.graph); foreach (Symbol sym in nonterminals) { if (!visited[sym.n]) { ok = false; errors.Warning(" " + sym.name + " cannot be reached"); } } return ok; } //--------- check if every nts can be derived to terminals ------------ bool IsTerm(Node p, BitArray mark) { // true if graph can be derived to terminals while (p != null) { if (p.typ == Node.nt && !mark[p.sym.n]) return false; if (p.typ == Node.alt && !IsTerm(p.sub, mark) && (p.down == null || !IsTerm(p.down, mark))) return false; if (p.up) break; p = p.next; } return true; } public bool AllNtToTerm() { bool changed, ok = true; BitArray mark = new BitArray(nonterminals.Count); // a nonterminal is marked if it can be derived to terminal symbols do { changed = false; foreach (Symbol sym in nonterminals) if (!mark[sym.n] && IsTerm(sym.graph, mark)) { mark[sym.n] = true; changed = true; } } while (changed); foreach (Symbol sym in nonterminals) if (!mark[sym.n]) { ok = false; errors.SemErr(" " + sym.name + " cannot be derived to terminals"); } return ok; } //--------------------------------------------------------------------- // Cross reference list //--------------------------------------------------------------------- public void XRef() { SortedList xref = new SortedList(new SymbolComp()); // collect lines where symbols have been defined foreach (Symbol sym in nonterminals) { ArrayList list = (ArrayList)xref[sym]; if (list == null) {list = new ArrayList(); xref[sym] = list;} list.Add(- sym.line); } // collect lines where symbols have been referenced foreach (Node n in nodes) { if (n.typ == Node.t || n.typ == Node.wt || n.typ == Node.nt) { ArrayList list = (ArrayList)xref[n.sym]; if (list == null) {list = new ArrayList(); xref[n.sym] = list;} list.Add(n.line); } } // print cross reference list trace.WriteLine(); trace.WriteLine("Cross reference list:"); trace.WriteLine("--------------------"); trace.WriteLine(); foreach (Symbol sym in xref.Keys) { trace.Write(" {0,-12}", Name(sym.name)); ArrayList list = (ArrayList)xref[sym]; int col = 14; foreach (int line in list) { if (col + 5 > 80) { trace.WriteLine(); for (col = 1; col <= 14; col++) trace.Write(" "); } trace.Write("{0,5}", line); col += 5; } trace.WriteLine(); } trace.WriteLine(); trace.WriteLine(); } public void SetDDT(string s) { s = s.ToUpper(); foreach (char ch in s) { if ('0' <= ch && ch <= '9') ddt[ch - '0'] = true; else switch (ch) { case 'A' : ddt[0] = true; break; // trace automaton case 'F' : ddt[1] = true; break; // list first/follow sets case 'G' : ddt[2] = true; break; // print syntax graph case 'I' : ddt[3] = true; break; // trace computation of first sets case 'J' : ddt[4] = true; break; // print ANY and SYNC sets case 'P' : ddt[8] = true; break; // print statistics case 'S' : ddt[6] = true; break; // list symbol table case 'X' : ddt[7] = true; break; // list cross reference table default : break; } } } public void SetOption(string s) { string[] option = s.Split(new char[] {'='}, 2); string name = option[0], value = option[1]; if ("$namespace".Equals(name)) { if (nsName == null) nsName = value; } else if ("$checkEOF".Equals(name)) { checkEOF = "true".Equals(value); } } class SymbolComp : IComparer { public int Compare(Object x, Object y) { return ((Symbol) x).name.CompareTo(((Symbol) y).name); } } } // end Tab } // end namespace