CocoSourcesCS 2
/*-------------------------------------------------------------------------
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() == ) {
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-) {
if (i <= cur.to + ) { // (cur.from-1) <= i <= (cur.to+1)
if (i == cur.from - ) cur.from--;
else if (i == cur.to + ) {
cur.to++;
Range next = cur.next;
if (next != null && cur.to == next.from - ) { 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 = ;
for (Range p = head; p != null; p = p.next) n += p.to - p.from + ;
return n;
} public int First() {
if (head != null) return head.from;
return -;
} 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 = -; 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) ;
int endOfStopString = ; if (stop != null) {
startCh = stop[];
endOfStopString = stop.Length - ;
} int ch = framRead();
while (ch != EOF) {
if (stop != null && ch == startCh) {
int i = ;
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(, 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 >= || 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 == ) { 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 + ];
BitArray used = new BitArray(lastStateNr + );
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 = ; // 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(, s.Length-));
int i, len = s.Length;
State state = firstState;
Action a = null;
for (i = ; 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 = ; 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(, , 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 = * 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() != ) 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 == || s.Length > ) {
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[])); gen.WriteLine("{");
if (com.stop.Length == ) {
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[]));
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[])); gen.WriteLine("{");
if (com.start.Length == )
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[])); 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 == ) {
gen.WriteLine("\t\tNextCh();");
GenComBody(com);
} else {
gen.WriteLine("\t\tNextCh();");
gen.Write ("\t\tif ({0}) ", ChCond(com.start[])); 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[])) { // 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 > ) {
gen.Write("namespace ");
gen.Write(tab.nsName);
gen.Write(" {");
}
g.CopyFramePart("-->declarations");
gen.WriteLine("\tconst int maxT = {0};", tab.terminals.Count - );
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 = ;
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() > ) { PutRange(tab.ignored); } else { gen.Write("false"); }
g.CopyFramePart("-->scan2");
if (firstComment != null) {
gen.Write("\t\tif (");
com = firstComment; comIdx = ;
while (com != null) {
gen.Write(ChCond(com.start[]));
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 > ) 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 = -;
firstState = NewState();
firstMelted = null; firstComment = null;
ignoreCase = false;
dirtyDFA = false;
hasCtxMoves = false;
} } // end DFA } // end namespace