/// Dedicated to the public domain by Christopher Diggins /// http://creativecommons.org/licenses/publicdomain/ using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Globalization; using System.Diagnostics; using Peg; namespace Cat { /// /// The CatParser constructs a typed AST representing Cat source code. /// This library is based on the PEG parsing library /// class CatParser { public static List Parse(string s) { Peg.Parser parser = new Peg.Parser(s); try { bool bResult = parser.Parse(CatGrammar.CatProgram()); if (!bResult) throw new Exception("failed to parse input"); } catch (Exception e) { Output.WriteLine("Parsing error occured with message: " + e.Message); Output.WriteLine(parser.ParserPosition); throw e; } AstProgram tmp = new AstProgram(parser.GetAst()); return tmp.mStatements; } } /// /// Used to identify / switch on the different types of nodes /// public enum AstLabel { AstRoot, Def, Decl, Name, Param, Lambda, Quote, Char, String, Float, Int, Bin, Hex, Stack, FxnType, Arrow, TypeName, TypeVar, StackVar, MacroRule, MacroProp, MacroPattern, MacroQuote, MacroTypeVar, MacroStackVar, MacroTypeVarName, MacroStackVarName, MacroName, MetaDataContent, MetaDataLabel, MetaDataBlock, Unknown } /// /// An AstNode is used as a base class for a typed abstract syntax tree for Cat programs. /// CatAstNodes are created from a Peg.Ast. Apart from being typed, the big difference /// is the a CatAstNode can be modified. This makes rewriting algorithms much easier. /// public abstract class CatAstNode { string msText; AstLabel mLabel; public CatAstNode(PegAstNode node) { if (node.GetLabel() != null) mLabel = (AstLabel)node.GetLabel(); else mLabel = AstLabel.AstRoot; msText = node.ToString(); } public CatAstNode(AstLabel label, string sText) { mLabel = label; msText = sText; } public static CatAstNode Create(PegAstNode node) { AstLabel label = (AstLabel)node.GetLabel(); switch (label) { case AstLabel.AstRoot: return new AstRoot(node); case AstLabel.Def: return new AstDef(node); case AstLabel.Name: return new AstName(node); case AstLabel.Param: return new AstParam(node); case AstLabel.Lambda: return new AstLambda(node); case AstLabel.Quote: return new AstQuote(node); case AstLabel.Char: return new AstChar(node); case AstLabel.String: return new AstString(node); case AstLabel.Float: return new AstFloat(node); case AstLabel.Int: return new AstInt(node); case AstLabel.Bin: return new AstBin(node); case AstLabel.Hex: return new AstHex(node); case AstLabel.Stack: return new AstStack(node); case AstLabel.FxnType: return new AstFxnType(node); case AstLabel.TypeVar: return new AstTypeVar(node); case AstLabel.TypeName: return new AstSimpleType(node); case AstLabel.StackVar: return new AstStackVar(node); case AstLabel.MacroRule: return new AstMacro(node); case AstLabel.MacroProp: return new AstMacro(node); case AstLabel.MacroPattern: return new AstMacroPattern(node); case AstLabel.MacroQuote: return new AstMacroQuote(node); case AstLabel.MacroTypeVar: return new AstMacroTypeVar(node); case AstLabel.MacroStackVar: return new AstMacroStackVar(node); case AstLabel.MacroName: return new AstMacroName(node); case AstLabel.MetaDataContent: return new AstMetaDataContent(node); case AstLabel.MetaDataLabel: return new AstMetaDataLabel(node); case AstLabel.MetaDataBlock: return new AstMetaDataBlock(node); default: throw new Exception("unrecognized node type in AST tree: " + label); } } public void CheckIsLeaf(PegAstNode node) { CheckChildCount(node, 0); } public void CheckLabel(AstLabel label) { if (!GetLabel().Equals(label)) throw new Exception("Expected label " + label.ToString() + " but instead have label " + GetLabel().ToString()); } public void CheckChildCount(PegAstNode node, int n) { if (node.GetNumChildren() != n) throw new Exception("expected " + n.ToString() + " children, instead found " + node.GetNumChildren().ToString()); } public AstLabel GetLabel() { return mLabel; } public override string ToString() { return msText; } public void SetText(string s) { msText = s; } public string IndentedString(int nIndent, string s) { if (nIndent > 0) return new String('\t', nIndent) + s; else return s; } public virtual void Output(TextWriter writer, int nIndent) { writer.Write(ToString()); } } public class AstRoot : CatAstNode { public List Defs = new List(); public AstRoot(PegAstNode node) : base(node) { foreach (PegAstNode child in node.GetChildren()) Defs.Add(new AstDef(child)); } public override void Output(TextWriter writer, int nIndent) { foreach (AstDef d in Defs) d.Output(writer, nIndent); } } public class AstExpr : CatAstNode { public AstExpr(PegAstNode node) : base(node) { } public AstExpr(AstLabel label, string sText) : base(label, sText) { } public override void Output(TextWriter writer, int nIndent) { string sLine = ToString(); writer.WriteLine(IndentedString(nIndent, sLine)); } } public class AstDef : CatAstNode { public string mName; public AstFxnType mType; public AstMetaDataBlock mpMetaData; public List mParams = new List(); public List mTerms = new List(); public List mLocals = new List(); public AstDef(PegAstNode node) : base(node) { CheckLabel(AstLabel.Def); if (node.GetNumChildren() == 0) throw new Exception("invalid function definition node"); AstName name = new AstName(node.GetChild(0)); mName = name.ToString(); int n = 1; // Look to see if a type is defined if ((node.GetNumChildren() >= 2) && (node.GetChild(1).GetLabel().Equals(AstLabel.FxnType))) { mType = new AstFxnType(node.GetChild(1)); ++n; } while (n < node.GetNumChildren()) { PegAstNode child = node.GetChild(n); if (!child.GetLabel().Equals(AstLabel.Param)) break; mParams.Add(new AstParam(child)); n++; } while (n < node.GetNumChildren()) { PegAstNode child = node.GetChild(n); if (!child.GetLabel().Equals(AstLabel.Param)) break; mParams.Add(new AstParam(child)); n++; } while (n < node.GetNumChildren()) { PegAstNode child = node.GetChild(n); if (!child.GetLabel().Equals(AstLabel.MetaDataBlock)) break; mpMetaData = new AstMetaDataBlock(child); n++; } while (n < node.GetNumChildren()) { PegAstNode child = node.GetChild(n); if (!child.GetLabel().Equals(AstLabel.Def)) break; mLocals.Add(new AstDef(child)); n++; } while (n < node.GetNumChildren()) { PegAstNode child = node.GetChild(n); CatAstNode expr = Create(child); if (!(expr is AstExpr)) throw new Exception("expected expression node"); mTerms.Add(expr as AstExpr); n++; } } public override void Output(TextWriter writer, int nIndent) { string s = "define " + mName; if (mType != null) { s += " : " + mType.ToString(); } if (mParams.Count > 0) { s += " // ( "; foreach (AstParam p in mParams) s += p.ToString() + " "; s += ")"; } writer.WriteLine(IndentedString(nIndent, s)); writer.WriteLine(IndentedString(nIndent, "{")); foreach (AstExpr x in mTerms) x.Output(writer, nIndent + 1); writer.WriteLine(IndentedString(nIndent, "}")); } } public class AstName : AstExpr { public AstName(PegAstNode node) : base(node) { CheckLabel(AstLabel.Name); CheckIsLeaf(node); } public AstName(string sOp) : base(AstLabel.Name, sOp) { } } public class AstParam : CatAstNode { public AstParam(PegAstNode node) : base(node) { CheckLabel(AstLabel.Param); CheckIsLeaf(node); } public override void Output(TextWriter writer, int nIndent) { throw new Exception("The method or operation is not implemented."); } } public class AstLiteral : AstExpr { public AstLiteral(PegAstNode node) : base(node) { } public AstLiteral(AstLabel label, string sText) : base(label, sText) { } public Object value; } public class AstLambda : AstLiteral { public List mIdentifiers = new List(); public List mTerms = new List(); public AstLambda(PegAstNode node) : base(node) { CheckLabel(AstLabel.Lambda); CheckChildCount(node, 2); AstParam name = new AstParam(node.GetChild(0)); mIdentifiers.Add(name.ToString()); CatAstNode tmp = Create(node.GetChild(1)); // lambda nodes either contain quotes or other lambda nodes if (!(tmp is AstQuote)) { if (!(tmp is AstLambda)) throw new Exception("expected lambda expression or quotation"); AstLambda lambda = tmp as AstLambda; mIdentifiers.AddRange(lambda.mIdentifiers); // Take ownership of the terms from the child lambda expression mTerms = lambda.mTerms; } else { AstQuote q = tmp as AstQuote; // Take ownership of the terms from the quote mTerms = q.mTerms; } } public List GetTerms() { return mTerms; } } public class AstQuote : AstLiteral { public List mTerms = new List(); public AstQuote(PegAstNode node) : base(node) { CheckLabel(AstLabel.Quote); foreach (PegAstNode child in node.GetChildren()) { CatAstNode tmp = CatAstNode.Create(child); if (!(tmp is AstExpr)) throw new Exception("invalid child node " + child.ToString() + ", expected an expression node"); mTerms.Add(tmp as AstExpr); } } public AstQuote(AstExpr expr) : base(AstLabel.Quote, "") { mTerms.Add(expr); } public AstQuote(List expr) : base(AstLabel.Quote, "") { mTerms.AddRange(expr); } public override void Output(TextWriter writer, int nIndent) { writer.WriteLine(IndentedString(nIndent, "[")); foreach (AstExpr x in mTerms) x.Output(writer, nIndent + 1); writer.WriteLine(IndentedString(nIndent, "]")); } public List GetTerms() { return mTerms; } } public class AstInt : AstLiteral { public AstInt(PegAstNode node) : base(node) { CheckLabel(AstLabel.Int); CheckIsLeaf(node); value = int.Parse(ToString()); } public AstInt(int n) : base(AstLabel.Int, n.ToString()) { value = n; } public int GetValue() { return (int)value; } } public class AstBin : AstLiteral { public AstBin(PegAstNode node) : base(node) { CheckLabel(AstLabel.Bin); CheckIsLeaf(node); string s = ToString(); int n = 0; int place = 1; for (int i = s.Length; i > 0; --i) { if (s[i - 1] == '1') { n += place; } else { if (s[i - 1] != '0') throw new Exception("Invalid binary number"); } place *= 2; } value = n; } public int GetValue() { return (int)value; } } public class AstHex : AstLiteral { public AstHex(PegAstNode node) : base(node) { CheckLabel(AstLabel.Hex); CheckIsLeaf(node); value = int.Parse(ToString(), NumberStyles.AllowHexSpecifier); } public int GetValue() { return (int)value; } } public class AstChar : AstLiteral { public AstChar(PegAstNode node) : base(node) { CheckLabel(AstLabel.Char); CheckIsLeaf(node); string s = ToString(); s = s.Substring(1, s.Length - 2); switch (s) { case "\\t": value = '\t'; break; case "\\n": value = '\n'; break; case "\\'": value = '\''; break; case "\\\"": value = '\"'; break; case "\\r": value = '\r'; break; default: value = char.Parse(s); break; } } public char GetValue() { return (char)value; } } public class AstString : AstLiteral { public AstString(PegAstNode node) : base(node) { CheckLabel(AstLabel.String); CheckIsLeaf(node); // strip quotes string s = ToString(); value = s.Substring(1, s.Length - 2); } public string GetValue() { return (string)value; } } public class AstFloat : AstLiteral { public AstFloat(PegAstNode node) : base(node) { CheckLabel(AstLabel.Float); CheckIsLeaf(node); value = double.Parse(ToString()); } public double GetValue() { return (double)value; } } public class AstType : CatAstNode { public AstType(PegAstNode node) : base(node) { } } public class AstStack : CatAstNode { public List mTypes = new List(); public AstStack(PegAstNode node) : base(node) { CheckLabel(AstLabel.Stack); foreach (PegAstNode child in node.GetChildren()) { CatAstNode tmp = Create(child); if (!(tmp is AstType)) throw new Exception("stack AST node should only have type AST nodes as children"); mTypes.Add(tmp as AstType); } } } public class AstTypeVar : AstType { public AstTypeVar(PegAstNode node) : base(node) { CheckLabel(AstLabel.TypeVar); CheckChildCount(node, 0); } } public class AstSimpleType : AstType { public AstSimpleType(PegAstNode node) : base(node) { CheckLabel(AstLabel.TypeName); CheckChildCount(node, 0); } } public class AstStackVar : AstType { public AstStackVar(PegAstNode node) : base(node) { CheckLabel(AstLabel.StackVar); CheckChildCount(node, 0); } } public class AstFxnType : AstType { public AstStack mProd; public AstStack mCons; bool mbSideEffects; public AstFxnType(PegAstNode node) : base(node) { CheckLabel(AstLabel.FxnType); CheckChildCount(node, 3); mCons = new AstStack(node.GetChild(0)); mbSideEffects = node.GetChild(1).ToString().Equals("~>"); mProd = new AstStack(node.GetChild(2)); } public bool HasSideEffects() { return mbSideEffects; } } public class AstMacro : CatAstNode { public AstMacroPattern mSrc; public AstMacroPattern mDest; public AstMacro(PegAstNode node) : base(node) { CheckChildCount(node, 2); CheckLabel(AstLabel.MacroRule); mSrc = new AstMacroPattern(node.GetChild(0)); mDest = new AstMacroPattern(node.GetChild(1)); } } public class AstMacroProperty : AstMacro { public AstMacroProperty(PegAstNode node) : base(node) { } } public class AstMacroPattern : CatAstNode { public List mPattern = new List(); public AstMacroPattern(PegAstNode node) : base(node) { CheckLabel(AstLabel.MacroPattern); foreach (PegAstNode child in node.GetChildren()) { AstMacroTerm tmp = CatAstNode.Create(child) as AstMacroTerm; if (tmp == null) throw new Exception("invalid grammar: only macro terms can be children of an ast macro mPattern"); mPattern.Add(tmp); } } } public class AstMacroTerm : CatAstNode { public AstMacroTerm(PegAstNode node) : base(node) { } } public class AstMacroQuote : AstMacroTerm { public List mTerms = new List(); public AstMacroQuote(PegAstNode node) : base(node) { CheckLabel(AstLabel.MacroQuote); foreach (PegAstNode child in node.GetChildren()) { AstMacroTerm term = Create(child) as AstMacroTerm; if (term == null) throw new Exception("internal grammar error: macro quotations can only contain macro terms"); mTerms.Add(term); } } } public class AstMacroTypeVar : AstMacroTerm { public string msName; public AstMacroTypeVar(PegAstNode node) : base(node) { CheckChildCount(node, 1); msName = node.GetChild(0).ToString(); CheckLabel(AstLabel.MacroTypeVar); } } public class AstMacroStackVar : AstMacroTerm { public CatFxnType mType = null; public string msName; public AstMacroStackVar(PegAstNode node) : base(node) { if (node.GetNumChildren() < 1) throw new Exception("invalid macro stack variable"); if (node.GetNumChildren() > 2) throw new Exception("invalid macro stack variable"); msName = node.GetChild(0).ToString(); if (node.GetNumChildren() == 2) { AstFxnType typeNode = new AstFxnType(node.GetChild(1)); mType = CatFxnType.Create(typeNode) as CatFxnType; if (mType == null) throw new Exception("expected function type " + typeNode.ToString()); } CheckLabel(AstLabel.MacroStackVar); } } public class AstMacroName : AstMacroTerm { public AstMacroName(PegAstNode node) : base(node) { CheckChildCount(node, 0); CheckLabel(AstLabel.MacroName); } } #region AST nodes for representing meta data public class AstMetaData : CatAstNode { public List children = new List(); public AstMetaData(PegAstNode node) : base(node) { foreach (PegAstNode child in node.GetChildren()) { AstMetaData x = Create(child) as AstMetaData; if (x == null) throw new Exception("Meta data-nodes can only have meta-data nodes as children"); children.Add(x); } } } public class AstMetaDataContent : AstMetaData { public AstMetaDataContent(PegAstNode node) : base(node) { CheckLabel(AstLabel.MetaDataContent); } } public class AstMetaDataLabel : AstMetaData { public AstMetaDataLabel(PegAstNode node) : base(node) { CheckLabel(AstLabel.MetaDataLabel); CheckIsLeaf(node); Trace.Assert(children.Count == 0); } } public class AstMetaDataBlock : AstMetaData { public AstMetaDataBlock(PegAstNode node) : base(node) { CheckLabel(AstLabel.MetaDataBlock); } } /// /// A program consists of a sequence of statements /// TODO: reintroduce declarations and macros /// public class AstProgram : CatAstNode { public List mStatements = new List(); public AstProgram(Peg.PegAstNode node) : base(node) { foreach (Peg.PegAstNode child in node.GetChildren()) { CatAstNode statement = CatAstNode.Create(child); mStatements.Add(statement); } } } #endregion }