Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / C#

Coco Custom Tool for Visual Studio.NET

Rate me:
Please Sign up or sign in to vote.
4.64/5 (34 votes)
29 Oct 2005CPOL4 min read 130.8K   699   53  
Use the award winning Coco compiler's compiler directly from within Visual Studio
/*-------------------------------------------------------------------------
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;
using VsCoco;
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 BitArray Symbols(Tab tab) {
		BitArray s;
		if (typ == Node.clas)
			s = (BitArray) tab.CharClassSet(sym).Clone();
		else {
			s = new BitArray(CharClass.charSetSize); s[sym] = true;
		}
		return s;
	}
	
	public void ShiftWith(BitArray s, Tab tab) {
		if (Sets.Elements(s) == 1) {
			typ = Node.chr; sym = Sets.First(s);
		} 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;
	}
	
}

//-----------------------------------------------------------------------------
//  DFA
//-----------------------------------------------------------------------------

public class DFA {
	public const int  EOF = -1;
	
	public int maxStates;
	public int lastStateNr;   // highest state number
	public State firstState;
	public State lastState;   // last allocated state
	public int lastSimState;  // last non melted state
	public Stream fram;   // scanner frame input

	public int framLineNo=1;    // scanner frame line number
	public string scannerFrameFileName;
	public StreamWriter gen;  // generated scanner file
	public Symbol curSy;      // current token to be recognized (in FindTrans)
	public Node curGraph;     // start of graph for current token (in FindTrans)
	public bool ignoreCase;   // true if input should be treated case-insensitively
	public bool dirtyDFA;     // DFA may become nondeterministic in MatchLiteral
	public bool hasCtxMoves;  // DFA has context transitions
	
	Parser     parser;        // other Coco objects
	Tab        tab;
	Errors     errors;
	TextWriter trace;

	//---------- Output primitives
	private string Ch(char ch) {
		if (ch < ' ' || ch >= 127 || ch == '\'' || ch == '\\') return Convert.ToString((int)ch);
		else return String.Format("'{0}'", ch);
	}
	
	private string ChCond(char ch) {
		return String.Format("ch == {0}", Ch(ch));
	}
	
	private void PutRange(BitArray s) {
		int[] lo = new int[32];
		int[] hi = new int[32];
		// fill lo and hi
		int max = CharClass.charSetSize;
		int top = -1;
		int i = 0;
		while (i < max) {
			if (s[i]) {
				top++; lo[top] = i; i++;
				while (i < max && s[i]) i++;
				hi[top] = i-1;
			} else i++;
		}
		// print ranges
		if (top == 1 && lo[0] == 0 && hi[1] == max-1 && hi[0]+2 == lo[1]) {
			BitArray s1 = new BitArray(max); s1[hi[0]+1] = true;
			gen.Write("!"); PutRange(s1); gen.Write(" && ch != Buffer.EOF");
		} else {
			gen.Write("(");
			for (i = 0; i <= top; i++) {
				if (hi[i] == lo[i]) gen.Write("ch == {0}", Ch((char)lo[i]));
				else if (lo[i] == 0) gen.Write("ch <= {0}", Ch((char)hi[i]));
				// else if (hi[i] == max-1) gen.Write("ch >= {0}", Ch((char)lo[i]));
				// Buffer.EOF == 256 ==> always check upper bound of interval
				else gen.Write("ch >= {0} && ch <= {1}", Ch((char)lo[i]), Ch((char)hi[i]));
				if (i < top) gen.Write(" || ");
			}
			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) {
		if (to == firstState) parser.SemErr("token must not start with an iteration");
		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;
		BitArray 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: case Node.opt: {
				if (p.next != null && !stepped[p.next.n]) Step(from, p.next, stepped);
				Step(from, p.sub, stepped);
				break;
			}
		}
	}
	
	void NumberNodes(Node p, State state) {
		/* 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.
		*/
		if (p == null) return;
		if (p.state != null) return; // already visited;
		if (state == null) 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);
				break;
			}
			case Node.opt: {
				NumberNodes(p.next, null); NumberNodes(p.sub, state);
				break;
			}
			case Node.iter: {
				NumberNodes(p.next, state); NumberNodes(p.sub, state);
				break;
			}
			case Node.alt: {
				NumberNodes(p.sub, state); NumberNodes(p.down, state);
				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) {
		curGraph = p; curSy = sym;
		if (tab.DelGraph(curGraph)) parser.SemErr("token might be empty");
		NumberNodes(curGraph, firstState);
		FindTrans(curGraph, true, 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]
			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; BitArray seta, setb, setc;
		seta = a.Symbols(tab); setb = b.Symbols(tab);
		if (Sets.Equals(seta, setb)) {
			a.AddTargets(b);
			state.DetachAction(b);
		} else if (Sets.Includes(seta, setb)) {
			setc = (BitArray)seta.Clone(); Sets.Subtract(setc, setb);
			b.AddTargets(a);
			a.ShiftWith(setc, tab);
		} else if (Sets.Includes(setb, seta)) {
			setc = (BitArray)setb.Clone(); Sets.Subtract(setc, seta);
			a.AddTargets(b);
			b.ShiftWith(setc, tab);
		} else {
			setc = (BitArray)seta.Clone(); setc.And(setb);
			Sets.Subtract(seta, setc);
			Sets.Subtract(setb, 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) {
		BitArray 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 Sets.Intersect(seta, setb);}
		}
	}
	
	bool MakeUnique(State state) { // return true if actions were split
		bool 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;}
		return changed;
	}
	
	void MeltStates(State state) {
		bool changed, 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);
					do {changed = MakeUnique(s);} while (changed);
					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;
		bool changed;
		lastSimState = lastState.nr;
		maxStates = 2 * lastSimState; // heuristic for set size in Melted.set
		FindCtxStates();
		for (state = firstState; state != null; state = state.next)
			do {changed = MakeUnique(state);} while (changed);
		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((char)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) {
				BitArray 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 {
					 // todo provide proper line numbers
					vsCoco.WriteError(0,0,vsCoco.ErrorLevel.error, string.Format("Tokens {0} and {1} cannot be distinguished", endOf.name, t.state.endOf.name));
					errors.count++;
				}
			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) {
				//   vsCoco.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 Exception("-- 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) {
				BitArray set = tab.CharClassSet(p.val);
				if (Sets.Elements(set) != 1) parser.SemErr("character set contains more than 1 character");
				s.Append((char)Sets.First(set));
			} 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, line0 = line, lineStart0 = lineStart;");
		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\tif (ch==EOL) {line--; lineStart = lineStart0;}");
			gen.WriteLine("\t\t\tpos = pos - 2; buffer.Pos = pos+1; NextCh();");
			gen.WriteLine("\t\t}");
			gen.WriteLine("\t\treturn false;");
		}
		gen.WriteLine("\t}");
	}
	
	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) 
					{
						if (tab.ddt[9] && scannerFrameFileName!=null) gen.Write("\r\n#line default\r\n");
						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);
				
				if (ch== '\n') genFrameLineNumber(true);
				ch = fram.ReadByte();
			}
		}
		throw new Exception(String.Format("Incomplete or corrupt scanner frame file, missing section {0}.",stop));
	}


	void genFrameLineNumber(bool IsOnNewLine)
	{
		if (!tab.ddt[9]) return;
		framLineNo++;
		if (scannerFrameFileName!=null) 
		{
			if (!IsOnNewLine) gen.WriteLine();
			gen.Write("#line {0} \"{1}\"\r\n",framLineNo,scannerFrameFileName);
		}
	}
	
	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 (Symbol sym in tab.terminals) {
			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);
		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\tpos = pos - apx - 1; line = t.line;");
			gen.WriteLine("\t\t\t\t\tbuffer.Pos = pos + 1; NextCh();");
			gen.Write(  	"\t\t\t\t\t");
		}
		if (endOf == null) {
			gen.WriteLine("t.kind = noSym; break;}");
		} 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 FillStartTab(int[] startTab) {
		for (Action action = firstState.firstAction; action != null; action = action.next) {
			int targetState = action.target.state.nr;
			if (action.typ == Node.chr) startTab[action.sym] = targetState;
			else {
				BitArray s = tab.CharClassSet(action.sym);
				for (int i = 0; i < s.Count; i++)
					if (s[i]) startTab[i] = targetState;
			}
		}
	}
	
	public void WriteScanner() {
		int i, j;
		int[] startTab = new int[CharClass.charSetSize];
		string fr = tab.srcDir + "Scanner.frame";  /* pdt */
		if (!File.Exists(fr)) {
			if (tab.frameDir != null)
				fr = tab.frameDir.Trim() + Path.DirectorySeparatorChar + "Scanner.frame";
			if (!File.Exists(fr)) 
			{
				fr=null;
			}
		}
		if (fr==null)
		{
			vsCoco.WriteError(0,0,vsCoco.ErrorLevel.info,"Using internal Scanner.Frame file.");
			System.Reflection.Assembly a = this.GetType().Assembly;
			fram = a.GetManifestResourceStream("VsCoco.Coco.Scanner.frame");
		}
		else
		{
			fram = new FileStream(fr, FileMode.Open, FileAccess.Read, FileShare.Read);
		}
		try 
		{
			if (dirtyDFA) MakeDeterministic();
			FillStartTab(startTab);
			CopyFramePart("-->begin");
			CopyFramePart("-->namespace");
				
			scannerFrameFileName=fr;	
			if (tab.nsName != null && tab.nsName.Length > 0) 
			{
				gen.Write("namespace ");
				gen.Write(tab.nsName);
				gen.Write(" {");
			}
			CopyFramePart("-->declarations");
			gen.WriteLine("\tconst int charSetSize = {0};", CharClass.charSetSize);
			gen.WriteLine("\tconst int maxT = {0};", tab.terminals.Count - 1);
			gen.WriteLine("\tconst int noSym = {0};", tab.noSym.n);
			gen.WriteLine("\tshort[] start = {");
			for (i = 0; i < CharClass.charSetSize / 16; i++) 
			{
				gen.Write("\t");
				for (j = 0; j < 16; j++)
					gen.Write("{0,3},", startTab[16*i+j]);
				gen.WriteLine();
			}
			gen.WriteLine("\t  -1};");                                   /* pdt */
			if (ignoreCase)
				gen.Write("\tchar valCh;       // current input character (for token.val)");
			CopyFramePart("-->initialization");
			gen.Write("\t\t");
			j = 0;
			for (i = 0; i < tab.ignored.Count; i++) 
			{
				if (tab.ignored[i]) 
				{
					gen.Write("ignore[{0}] = true; ", i);
					if (++j % 4 == 0) { gen.WriteLine(); gen.Write("\t\t"); }
				}
			}
			CopyFramePart("-->casing1");
			if (ignoreCase) 
			{
				gen.WriteLine("\t\tvalCh = ch;");
				gen.Write    ("\t\tif (ch != Buffer.EOF) ch = char.ToLower(ch);");
			}
			CopyFramePart("-->casing2");
			gen.Write("\t\ttval[tlen++] = ");
			if (ignoreCase) gen.Write("valCh;"); else gen.Write("ch;");
			CopyFramePart("-->comments");
			Comment com = firstComment; i = 0;
			while (com != null) 
			{
				GenComment(com, i);
				com = com.next; i++;
			}
			CopyFramePart("-->literals"); GenLiterals();
			CopyFramePart("-->scan1");
			if (firstComment != null) 
			{
				gen.Write("\t\tif (");
				com = firstComment; i = 0;
				while (com != null) 
				{
					gen.Write(ChCond(com.start[0]));
					gen.Write(" && Comment{0}()", i);
					if (com.next != null) gen.Write(" ||");
					com = com.next; i++;
				}
				gen.Write(") return NextToken();");
			}
			if (hasCtxMoves) { gen.WriteLine(); gen.Write("\t\tint apx = 0;"); } /* pdt */
			CopyFramePart("-->scan2");
			for (State state = firstState.next; state != null; state = state.next)
				WriteState(state);
			CopyFramePart("$$$");
			if (tab.nsName != null && tab.nsName.Length > 0) gen.Write("}");
		} 
		catch (FileNotFoundException ex) 
		{
			throw new Exception("Cannot open Scanner.frame.",ex);
		}
		finally
		{
			if (fram!=null) fram.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

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
France France
I am a French programmer.
These days I spend most of my time with the .NET framework, JavaScript and html.

Comments and Discussions