📄 parsergen.cs
字号:
/*-------------------------------------------------------------------------
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 static Position usingPos; // "using" definitions from the attributed grammar
// CHANGES BY M.KRUEGER
public static string productionModifiers = String.Empty;
//EOC
static int errorNr; // highest parser error number
static Symbol curSy; // symbol whose production is currently generated
static FileStream fram; // parser frame file
static StreamWriter gen; // generated parser source file
static StringWriter err; // generated parser error messages
static string srcName; // name of attributed grammar file
static string srcDir; // directory of attributed grammar file
static ArrayList symSet = new ArrayList();
static void Indent (int n) {
for (int i = 1; i <= n; i++) gen.Write('\t');
}
// AW: use a switch if more than 5 alternatives and none starts with a resolver
static bool UseSwitch (Node p) {
if (p.typ != Node.alt) return false;
int nAlts = 0;
while (p != null) {
++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;
}
static void CopyFramePart (string stop) {
char startCh = stop[0];
int endOfStopString = stop.Length-1;
int ch = fram.ReadByte();
while (ch != EOF)
if (ch == startCh) {
int i = 0;
do {
if (i == endOfStopString) return; // stop[0..i] found
ch = fram.ReadByte(); i++;
} while (ch == stop[i]);
// stop[0..i-1] found; continue with last read character
gen.Write(stop.Substring(0, i));
} else {
gen.Write((char)ch); ch = fram.ReadByte();
}
Errors.Exception(" -- incomplete or corrupt parser frame file");
}
static void CopySourcePart (Position pos, int indent) {
// Copy text described by pos from atg to gen
int ch, nChars, i;
if (pos != null) {
Buffer.Pos = pos.beg; ch = Buffer.Read(); nChars = pos.len - 1;
// CHANGES BY M.KRUEGER (#line pragma generation)
gen.WriteLine();
gen.WriteLine(String.Format("#line {0} \"{1}\" ", Buffer.CountLines(pos.beg) + 1, Scanner.fileName));
// EOC
Indent(indent);
while (nChars >= 0) {
while (ch == CR || ch == LF) { // eol is either CR or CRLF or LF
gen.WriteLine(); Indent(indent);
if (ch == CR) { ch = Buffer.Read(); nChars--; } // skip CR
if (ch == LF) { ch = Buffer.Read(); nChars--; } // skip LF
for (i = 1; i <= pos.col && (ch == ' ' || ch == '\t'); i++) {
// skip blanks at beginning of line
ch = Buffer.Read(); nChars--;
}
if (i <= pos.col) pos.col = i - 1; // heading TABs => not enough blanks
if (nChars < 0) goto done;
}
gen.Write((char)ch);
ch = Buffer.Read(); nChars--;
}
done:
if (indent > 0) gen.WriteLine();
}
}
static void GenErrorMsg (int errTyp, Symbol sym) {
errorNr++;
err.Write("\t\t\tcase " + errorNr + ": s = \"");
switch (errTyp) {
case tErr:
if (sym.name[0] == '"') err.Write(DFA.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;");
}
static 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;
}
static 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"); // should never happen
else if (n <= maxTerm)
foreach (Symbol sym in Symbol.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));
/*
if (p.typ == Node.alt) {
// for { ... | IF ... | ... } or [ ... | IF ... | ... ]
// check resolvers in addition to terminal start symbols of alternatives
Node q = p;
while (q != null) {
if (q.sub.typ == Node.rslv) {
gen.Write(" || ");
CopySourcePart(q.sub.pos, 0);
}
q = q.down;
}
}
*/
}
}
static void PutCaseLabels (BitArray s) {
foreach (Symbol sym in Symbol.terminals)
if (s[sym.n]) gen.Write("case {0}: ", sym.n);
}
static 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);
// CHANGES BY M.KRUEGER
//if (isChecked[p.sym.n]) gen.WriteLine("Get();");
if (isChecked[p.sym.n]) gen.WriteLine("lexer.NextToken();");
//EOC
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);
// CHANGES BY M.KRUEGER
//gen.WriteLine("Get();");
gen.WriteLine("lexer.NextToken();");
//EOC
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(")) {");
// CHANGES BY M.KRUEGER
//gen.Write("SynErr({0}); Get();", errorNr);
gen.Write("SynErr({0}); lexer.NextToken(); ", errorNr);
//EOC
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(") {");
}
s1.Or(isChecked);
//if (p2.sub.typ == Node.rslv) GenCode(p2.sub.next, indent + 1, s1);
//else GenCode(p2.sub, indent + 1, s1);
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(Symbol.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:
//if (p.sub.typ == Node.rslv) s1 = Tab.First(p.sub.next);
//else s1 = Tab.First(p.sub);
s1 = Tab.First(p.sub);
Indent(indent);
gen.Write("if ("); GenCond(s1, p.sub); gen.WriteLine(") {");
//if (p.sub.typ == Node.rslv) GenCode(p.sub.next, indent + 1, s1);
//else GenCode(p.sub, indent + 1, s1);
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(Symbol.terminals.Count);
if (p.up) break;
p = p.next;
}
}
static void GenTokens() {
foreach (Symbol sym in Symbol.terminals) {
if (Char.IsLetter(sym.name[0]))
gen.WriteLine("\tconst int _{0} = {1};", sym.name, sym.n);
}
}
static void GenCodePragmas() {
foreach (Symbol sym in Symbol.pragmas) {
gen.WriteLine("\t\t\t\tif (la.kind == {0}) {{", sym.n);
CopySourcePart(sym.semPos, 4);
gen.WriteLine("\t\t\t\t}");
}
}
static void GenProductions() {
foreach (Symbol sym in Symbol.nonterminals) {
curSy = sym;
// CHANGES BY M.KRUEGER
gen.Write("\t" + productionModifiers + " void {0}(", sym.name);
// OLD VERSION: gen.Write("\tstatic void {0}(", sym.name);
//EOC
CopySourcePart(sym.attrPos, 0);
gen.WriteLine(") {");
CopySourcePart(sym.semPos, 2);
GenCode(sym.graph, 2, new BitArray(Symbol.terminals.Count));
gen.WriteLine("\t}"); gen.WriteLine();
}
}
static 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 Symbol.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},");
}
}
static void OpenGen(bool backUp) { /* pdt */
try {
string fn = srcDir + "Parser.cs"; /* pdt */
if (File.Exists(fn) && backUp) File.Copy(fn, fn + ".old", true);
gen = new StreamWriter(new FileStream(fn, FileMode.Create)); /* pdt */
} catch (IOException) {
Errors.Exception("-- Cannot generate parser file");
}
}
public static void WriteParser () {
symSet.Add(Tab.allSyncSets);
string fr = srcDir + "Parser.frame";
if (!File.Exists(fr)) {
if (Tab.frameDir != null) fr = Tab.frameDir.Trim() + Path.DirectorySeparatorChar + "Parser.frame";
if (!File.Exists(fr)) Errors.Exception("-- Cannot find Parser.frame");
}
try {
fram = new FileStream(fr, FileMode.Open, FileAccess.Read, FileShare.Read);
} catch (IOException) {
Errors.Exception("-- Cannot open Parser.frame.");
}
OpenGen(true); /* pdt */
err = new StringWriter();
foreach (Symbol sym in Symbol.terminals) GenErrorMsg(tErr, sym);
CopyFramePart("-->begin");
if (!srcName.ToLower().EndsWith("coco.atg")) {
gen.Close(); OpenGen(false); /* pdt */
}
if (usingPos != null) {CopySourcePart(usingPos, 0); gen.WriteLine();}
CopyFramePart("-->namespace");
/* AW open namespace, if it exists */
if (Tab.nsName != null && Tab.nsName.Length > 0) {
gen.WriteLine("namespace {0} {{", Tab.nsName);
gen.WriteLine();
}
CopyFramePart("-->constants");
GenTokens(); /* ML 2002/09/07 write the token kinds */
gen.WriteLine("\tconst int maxT = {0};", Symbol.terminals.Count-1);
CopyFramePart("-->declarations"); CopySourcePart(Tab.semDeclPos, 0);
CopyFramePart("-->pragmas"); GenCodePragmas();
CopyFramePart("-->productions"); GenProductions();
CopyFramePart("-->parseRoot"); gen.WriteLine("\t\t{0}();", Tab.gramSy.name);
CopyFramePart("-->initialization"); InitSets();
CopyFramePart("-->errors"); gen.Write(err.ToString());
CopyFramePart("$$$");
/* AW 2002-12-20 close namespace, if it exists */
if (Tab.nsName != null && Tab.nsName.Length > 0) gen.Write("}");
gen.Close();
}
public static void WriteStatistics () {
Trace.WriteLine();
Trace.WriteLine("{0} terminals", Symbol.terminals.Count);
Trace.WriteLine("{0} symbols", Symbol.terminals.Count + Symbol.pragmas.Count +
Symbol.nonterminals.Count);
Trace.WriteLine("{0} nodes", Node.nodes.Count);
Trace.WriteLine("{0} sets", symSet.Count);
}
public static void Init (string file, string dir) {
srcName = file;
srcDir = dir;
errorNr = -1;
usingPos = null;
}
} // end ParserGen
} // end namespace
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -