Click here to Skip to main content
Click here to Skip to main content
Articles » Languages » C# » General » Downloads
 
Add your own
alternative version

Coco Custom Tool for Visual Studio.NET

, 29 Oct 2005 CPOL
Use the award winning Coco compiler's compiler directly from within Visual Studio
vscoco.zip
vsCoco
bin
VsCoco.dll
vsCoco.tlb
vsCocoRegistration.exe
Calculator
App.ico
Calc.atg
Calculator.csproj.user
Calculator.suo
VsCoCo
vsCoco.png
Coco
Coco.atg
Parser.frame
Scanner.frame
CustomToolGenerator
vsCoco
VsCoco.csproj.user
vsCocoRegistration
reg.ico
Thumbs.db
vsCocoRegistration.csproj.user
/*-------------------------------------------------------------------------
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;
using VsCoco;

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
	Stream fram;  // parser frame file
	public StreamWriter gen; // generated parser source file
	StringWriter err; // generated parser error messages
	ArrayList symSet = new ArrayList();

	
	public int framLineNo=1;    // scanner frame line number
	public string parserFrameFileName;

	public string srcName;
	
	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');
	}
	
	// use a switch if more than 5 alternatives and none starts with a resolver
	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;
	}
	
	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] && parserFrameFileName!=null) gen.WriteLine("\r\n#line default");
						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 parser frame file, missing {0} section",stop));
	}

	
	void genFrameLineNumber(bool IsOnNewLine)
	{
		if (!tab.ddt[9]) return;
		framLineNo++;
		if (parserFrameFileName!=null) 
		{
			if (!IsOnNewLine) gen.WriteLine();
			gen.WriteLine("#line {0} \"{1}\"",framLineNo,parserFrameFileName);
		}
	}

	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;

			if (tab.ddt[9]) 
			{
				if (pos.lin>0) gen.WriteLine("\r\n#line {0} \"{1}\"",pos.lin,srcName);
				else gen.WriteLine("\r\n#line hidden");
				// If we are debugging on the original grammar file 
				// we need to keep the original location;
				for (i = 1; i < pos.col; i++)  gen.Write(' ');
				while (nChars >= 0) 
				{
					gen.Write((char)ch);
					ch = buffer.Read(); nChars--;
				}
				gen.WriteLine("\r\n#line hidden");
			}
			else
			{
				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();
		}
	}

	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"); // should never happen
			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));
			/*
			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;
				}
			}
			*/
		}
	}
		
	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);
					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);
					gen.WriteLine("Get();");
					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(") {"); 
						}
						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(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:
					//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(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("\tconst int _{0} = {1};", sym.name, sym.n);
		}
	}
	
	void GenPragmas() {
		foreach (Symbol sym in tab.pragmas) {
			gen.WriteLine("\tconst 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 () {
		int oldPos = buffer.Pos;  // Pos is modified by CopySourcePart
		symSet.Add(tab.allSyncSets);
		string fr = tab.srcDir + "Parser.frame";
		if (!File.Exists(fr)) {
			if (tab.frameDir != null) fr = tab.frameDir.Trim() + Path.DirectorySeparatorChar + "Parser.frame";
			if (!File.Exists(fr)) fr=null;
		}
		if (fr==null)
		{
			vsCoco.WriteError(0,0,vsCoco.ErrorLevel.info,"Using internal Parser.Frame file.");
			System.Reflection.Assembly a = this.GetType().Assembly;
			fram = a.GetManifestResourceStream("VsCoco.Coco.Parser.frame");
		}
		else
		{
			fram = new FileStream(fr, FileMode.Open, FileAccess.Read, FileShare.Read);
		}

		try 
		{
			err = new StringWriter();
			foreach (Symbol sym in tab.terminals) GenErrorMsg(tErr, sym);
		
			CopyFramePart("-->begin");
				
			parserFrameFileName=fr;	

			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};", tab.terminals.Count-1);
			GenPragmas(); /* ML 2005/09/23 write the pragma kinds */
			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("}");
			buffer.Pos = oldPos;
		} 
		catch (IOException ex) 
		{
			throw new Exception("Cannot open Parser.frame.",ex);
		}
		finally
		{
			if (fram!=null) fram.Close();
		}
	}
	
	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

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)

Share

About the Author

Pascal Ganaye
Software Developer (Senior)
United Kingdom United Kingdom
I am a French programmer.
These days I spend most of my time with the .NET framework, JavaScript and html.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 29 Oct 2005
Article Copyright 2005 by Pascal Ganaye
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid