Click here to Skip to main content
15,896,606 members
Articles / Containers / Virtual Machine

Parsing Expression Grammar Support for C# 3.0 Part 1 - PEG Lib and Parser Generator

Rate me:
Please Sign up or sign in to vote.
4.95/5 (49 votes)
7 Oct 2008CPOL40 min read 205.2K   2.1K   118  
Introduction to the parsing method PEG with library and parser generator
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Peg.Samples;
using Peg.Base;
using System.IO;
using System.Diagnostics;

namespace BERTree
{
    class BERConvertDefiniteLength : IParserPostProcessor
    {
        #region Data members
        TreeContext context_;
        Dictionary<PegNode, uint> dictLength= new Dictionary<PegNode,uint>();
        byte[] lengthBuffer = new byte[5];
        #endregion Data members
        #region Helper classes
        class TreeContext
        {
            internal PegNode root_;
            internal byte[] byteSrc_;
            internal TextWriter errOut_;
            internal ParserPostProcessParams generatorParams_;
            internal string sErrorPrefix;

            internal TreeContext(ParserPostProcessParams generatorParams)
            {
                generatorParams_ = generatorParams;
                root_ = generatorParams.root_;
                byteSrc_ = generatorParams.byteSrc_;
                errOut_ = generatorParams.errOut_;
                sErrorPrefix = "<BER_DEFINITE_CONVERTER> FILE:'" + generatorParams_.grammarFileName_ + "' ";
            }
        }
        #endregion Helper classes
        #region IParserPostProcessor Members

        string IParserPostProcessor.ShortDesc
        {
            get { return "-> Definite Length Form"; }
        }
        string IParserPostProcessor.ShortestDesc
        {
            get { return "Definite"; }
        }
        string IParserPostProcessor.DetailDesc
        {
            get
            {
                return "Convert to definite length form (usally smaller files)";
            }
        }

        void IParserPostProcessor.Postprocess(ParserPostProcessParams postProcessorParams)
        {
            context_ = new TreeContext(postProcessorParams);
            string outDir =PUtils.MakeFileName("", context_.generatorParams_.outputDirectory_, "DefiniteLengthForm");
            if (!Directory.Exists(outDir))
            {
                Directory.CreateDirectory(outDir);
            }
            string outFile= PUtils.MakeFileName(context_.generatorParams_.sourceFileTitle_,outDir);
            using (BinaryWriter rw = new BinaryWriter(File.Open(outFile, FileMode.Create)))
            {
                WriteDefinite(rw, context_.generatorParams_.root_);
                context_.generatorParams_.errOut_.WriteLine("INFO  from <BER_DEFINITE_ENCODER> {0} bytes written to '{1}'",
                                             rw.BaseStream.Position,outFile);
            }
            
        }

       
        #endregion IParserPostProcessor Members
        #region WriteDefinite method and helpers
        private void WriteDefinite(BinaryWriter rw, PegNode pegNode)
        {
            PegNode child;
           if (pegNode == null) return;
           WriteTag(rw, pegNode);
           WriteLength(rw, pegNode);
           if (IsComposite(pegNode, out child))
           {
               WriteDefinite(rw, child);
           }
           else
           {
               Debug.Assert(pegNode.child_ != null && pegNode.child_.next_ != null && pegNode.child_.next_.next_ != null);
               WriteContent(rw, pegNode.child_.next_.next_);
           }
           WriteDefinite(rw, pegNode.next_);
        }

        private bool IsComposite(PegNode pegNode,out PegNode child)
        {
            Debug.Assert(pegNode.child_ != null && pegNode.child_.next_ != null && pegNode.child_.next_.next_!=null);
            
            child = pegNode.child_.next_.next_;
            bool bIsComposite=  child.id_ == (int)EBERTree.ConstructedDelimValue ||
                                child.id_ == (int)EBERTree.ConstructedValue;
            if (bIsComposite) child = child.child_;
            return bIsComposite;
        }

        private void WriteTag(BinaryWriter rw, PegNode pegNode)
        {
            Debug.Assert(pegNode.child_ != null);
            PegNode tagNode = pegNode.child_;
            Debug.Assert(tagNode.id_== (int)EBERTree.OneOctetTag || tagNode.id_==(int)EBERTree.MultiOctetTag);
            rw.Write(context_.byteSrc_, tagNode.match_.posBeg_, tagNode.match_.posEnd_ - tagNode.match_.posBeg_);
            
        }
        private void WriteLength(BinaryWriter rw, PegNode pegNode)
        {
            Debug.Assert(pegNode.child_ != null && pegNode.child_.next_ != null);
            PegNode lengthNode = pegNode.child_.next_;
            uint length = GetLength(lengthNode.next_);
            int bytesToWrite= GetLengthEncodingLength(length);
            if (bytesToWrite == 1)
            {
                lengthBuffer[0] = (byte)length;
            }
            else
            {
                lengthBuffer[0] = (byte)((bytesToWrite - 1) | 0x80);
                int j=1;
                for (int i =  bytesToWrite - 1; i>0; i--)
                {
                    byte @byte = (byte)((length & (0xFF << 8 * (i - 1) )) >>  8 * (i - 1));
                    lengthBuffer[j++] = @byte;
                }
            }
            rw.Write(lengthBuffer, 0,bytesToWrite);
        }

        private uint GetLength(PegNode pegNode)
        {
            if (dictLength.ContainsKey(pegNode)) return dictLength[pegNode];
            if (pegNode.id_ == (int)EBERTree.ConstructedDelimValue ||
                pegNode.id_ == (int)EBERTree.ConstructedValue)
            {
                uint length = 0;
                for (PegNode child = pegNode.child_; child != null; child = child.next_)
                {
                    length += GetLength(child);
                }
                dictLength.Add(pegNode, length);
                return length;
            }
            else if (pegNode.id_ == (int)EBERTree.PrimitiveValue)
            {
                uint length= (uint)(pegNode.match_.posEnd_ - pegNode.match_.posBeg_);
                dictLength.Add(pegNode, length);
                return length;
            }
            else if (pegNode.id_ == (int)EBERTree.TLV)
            {
                Debug.Assert(pegNode.child_ != null && pegNode.child_.next_ != null);
                PegNode tag = pegNode.child_, content = tag.next_.next_;
                uint length= (uint)(pegNode.child_.match_.posEnd_ - pegNode.child_.match_.posBeg_)
                    + (uint)GetLengthEncodingLength(GetLength(content))
                    + GetLength(content);
                dictLength.Add(pegNode, length);
                return length;
            }
            else
            {
                Debug.Assert(false);
                return 0;
            }
        }

        private int GetLengthEncodingLength(uint length)
        {
            if (length < 127) return 1;
            else
            {
                if ((length & 0xFF000000) != 0) return 5;
                else if ((length & 0xFFFF0000) != 0) return 4;
                else if ((length & 0xFFFFFF00) != 0) return 3;
                else return 2;
            }
        }
        private void WriteContent(BinaryWriter rw, PegNode pegNode)
        {
            Debug.Assert(pegNode.id_ == (int)EBERTree.PrimitiveValue);
            rw.Write(context_.byteSrc_, pegNode.match_.posBeg_, pegNode.match_.posEnd_ - pegNode.match_.posBeg_);
        }
        #endregion  WriteDefinite method and helpers
    }
    class BERConvertIndefiniteLength : IParserPostProcessor
    {
        #region Data members
        TreeContext context_;
        Dictionary<PegNode, uint> dictLength = new Dictionary<PegNode, uint>();
        byte[] lengthBuffer = new byte[5];
        #endregion Data members
        #region Helper classes
        class TreeContext
        {
            internal PegNode root_;
            internal byte[] byteSrc_;
            internal TextWriter errOut_;
            internal ParserPostProcessParams generatorParams_;
            internal string sErrorPrefix;

            internal TreeContext(ParserPostProcessParams generatorParams)
            {
                generatorParams_ = generatorParams;
                root_ = generatorParams.root_;
                byteSrc_ = generatorParams.byteSrc_;
                errOut_ = generatorParams.errOut_;
                sErrorPrefix = "<BER_INDEFINITE_CONVERTER> FILE:'" + generatorParams_.grammarFileName_ + "' ";
            }
        }
        #endregion Helper classes
        #region IParserPostProcessor Members

        string IParserPostProcessor.ShortDesc
        {
            get { return "-> Indefinite Length Form"; }
        }
        string IParserPostProcessor.ShortestDesc
        {
            get { return "Indefinite"; }
        }

        string IParserPostProcessor.DetailDesc
        {
            get
            {
                return "Convert to indefinite length form (usally bigger files)";
            }
        }

        void IParserPostProcessor.Postprocess(ParserPostProcessParams postProcessorParams)
        {
            context_ = new TreeContext(postProcessorParams);
            string outDir = PUtils.MakeFileName("", context_.generatorParams_.outputDirectory_, "IndefiniteLengthForm");
            if (!Directory.Exists(outDir))
            {
                Directory.CreateDirectory(outDir);
            }
            string outFile = PUtils.MakeFileName(context_.generatorParams_.sourceFileTitle_, outDir);
            using (BinaryWriter rw = new BinaryWriter(File.Open(outFile, FileMode.Create)))
            {
                WriteInDefinite(rw, context_.generatorParams_.root_);
                context_.generatorParams_.errOut_.WriteLine("INFO  from <BER_INDEFINITE_ENCODER> {0} bytes written to '{1}'",
                                             rw.BaseStream.Position, outFile);
            }

        }


        #endregion IParserPostProcessor Members
        #region WriteDefinite method and helpers
        private void WriteInDefinite(BinaryWriter rw, PegNode pegNode)
        {
            PegNode child;
            if (pegNode == null) return;
            bool bIsComposite = IsComposite(pegNode, out child);
            WriteTag(rw, pegNode);
            WriteLength(rw, pegNode,bIsComposite);
            if (bIsComposite)
            {
                for(;child!=null;child= child.next_)
                {
                    WriteInDefinite(rw, child);
                }
                rw.Write((ushort)0x0000);
            }
            else
            {
                Debug.Assert(pegNode.child_ != null && pegNode.child_.next_ != null && pegNode.child_.next_.next_ != null);
                WriteContent(rw, pegNode.child_.next_.next_);
            }
        }

        private bool IsComposite(PegNode pegNode, out PegNode child)
        {
            Debug.Assert(pegNode.child_ != null && pegNode.child_.next_ != null && pegNode.child_.next_.next_ != null);

            child = pegNode.child_.next_.next_;
            bool bIsComposite = child.id_ == (int)EBERTree.ConstructedDelimValue ||
                                child.id_ == (int)EBERTree.ConstructedValue;
            if (bIsComposite) child = child.child_;
            return bIsComposite;
        }

        private void WriteTag(BinaryWriter rw, PegNode pegNode)
        {
            Debug.Assert(pegNode.child_ != null);
            PegNode tagNode = pegNode.child_;
            Debug.Assert(tagNode.id_ == (int)EBERTree.OneOctetTag || tagNode.id_ == (int)EBERTree.MultiOctetTag);
            rw.Write(context_.byteSrc_, tagNode.match_.posBeg_, tagNode.match_.posEnd_ - tagNode.match_.posBeg_);

        }
        private void WriteLength(BinaryWriter rw, PegNode pegNode,bool bIsComposite)
        {
            Debug.Assert(pegNode.child_ != null && pegNode.child_.next_ != null);
            if (bIsComposite)
            {
                rw.Write((byte)0x80);
            }
            else {
                PegNode lengthNode = pegNode.child_.next_;
                uint length = GetLength(lengthNode.next_);
                int bytesToWrite = GetLengthEncodingLength(length);
                if (bytesToWrite == 1)
                {
                    lengthBuffer[0] = (byte)length;
                }
                else
                {
                    lengthBuffer[0] = (byte)((bytesToWrite - 1) | 0x80);
                    int j = 1;
                    for (int i = bytesToWrite - 1; i > 0; i--)
                    {
                        byte @byte = (byte)((length & (0xFF << 8 * (i - 1))) >> 8 * (i - 1));
                        lengthBuffer[j++] = @byte;
                    }
                }
                rw.Write(lengthBuffer, 0, bytesToWrite);
            }
        }

        private uint GetLength(PegNode pegNode)
        {
            
            uint length = (uint)(pegNode.match_.posEnd_ - pegNode.match_.posBeg_);
            return length;
        }

        private int GetLengthEncodingLength(uint length)
        {
            if (length < 127) return 1;
            else
            {
                if ((length & 0xFF000000) != 0) return 5;
                else if ((length & 0xFFFF0000) != 0) return 4;
                else if ((length & 0xFFFFFF00) != 0) return 3;
                else return 2;
            }
        }
        private void WriteContent(BinaryWriter rw, PegNode pegNode)
        {
            Debug.Assert(pegNode.id_ == (int)EBERTree.PrimitiveValue);
            rw.Write(context_.byteSrc_, pegNode.match_.posBeg_, pegNode.match_.posEnd_ - pegNode.match_.posBeg_);
        }
        #endregion  WriteDefinite method and helpers
    }
}

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
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions