一个编译器的实现3——用编译原理自动化制作文本解析器

一个编译器的实现3——用编译原理自动化制作文本解析器

PS:本文PDF版在这里

 

关于编译器的概念、工作流程、算法和设计方案,可参考这里(http://www.cnblogs.com/bitzhuwei/archive/2013/06/05/CompilerDesignAndImp4Context-freeGrammar.html)。阅读本文须理解“上下文无关文法(Context-free Grammar)”是什么。

本文以加减乘除表达式和一个3D坦克游戏模型为例,说明如何自动生成解析器以及如何使用自动生成的代码。

文末附源代码。

加减乘除表达式

运行编译器代码生成器(bitzhuwei.CGCompiler.Winform.exe),默认配置文件中已经有加减乘除表达式(Expression)的文法了。

clip_image002[8]

设置好编译器名字、命名空间和代码存放的位置,点击“开始!”。

若文法没有错误,会在指定位置生成Expression解析器的代码。

clip_image004[8]

clip_image006[8]

一共生成了10个文件(其中bitzhuwei.CompilerBase.dll和使用说明.txt是直接复制的)。

三个Enum*.cs文件分别是文法的字符类型、单词类型和语法树结点类型。

LexicalAnalyzer*.cs文件是词法分析器。

LL1SyntaxParser*.cs文件是语法分析器。

SyntaxTreeNodeValue*.cs文件是语法树结点类型,稍候会用到。

使用生存的代码的方法很简单:创建一个类库项目,把生成的10个文件全部加进去,引用bitzhuwei.CompilerBase.dll文件。

clip_image007[8]

为了测试,再创建一个Console项目,用下面的代码测试。

测试Expression的代码        static void Main(string[] args)
        {
            var sourceCodes = new string[]
            {
                "37",
                "19 * 19 - 18 * 18",
                "(19 + 18) * (19 - 18)",
            };
            foreach (var sourceCode in sourceCodes)
            {
                var lex = new bitzhuwei.ExpressionCompiler.LexicalAnalyzerExpression();
                lex.SetSourceCode(sourceCode);
                var tokens = lex.Analyze();
                Console.WriteLine(tokens);
                var parser = new bitzhuwei.ExpressionCompiler.LL1SyntaxParserExpression();
                parser.SetTokenListSource(tokens);
                var tree = parser.Parse();
                Console.WriteLine(tree);
                var value = tree.GetValue();
                Console.WriteLine(value);
            }
        }

 

输入的语法树如下图所示。

clip_image009[8]

我们使用解析器,目的是为了得到数据结构后再获取有价值的结果。Expression的价值在于获取表达式的值,通过遍历语法树获取这个值是很容易的。(这个代码只能自己写,这属于语义分析阶段了,目前还无法自动生成。)

SyntaxTreeExpressionGetValue.cs/// <summary>
    /// 提供SyntaxTree&lt;EnumTokenTypeCG, EnumVTypeCG, SyntaxTreeNodeValueCG&gt;的扩展方法
    /// </summary>
    public static partial class SyntaxTreeExpression
    {
        /// <summary>
        /// 获取源代码的规范格式
        /// <para>语法分析的副产品</para>
        /// </summary>
        /// <param name="tree">语法树</param>
        /// <returns></returns>
        public static double GetValue(this SyntaxTree<EnumTokenTypeExpression, EnumVTypeExpression, TreeNodeValueExpression> tree)
        {
            if (tree == null) return double.NaN;

            var tmpTree = tree.Clone() as SyntaxTree<EnumTokenTypeExpression, EnumVTypeExpression, TreeNodeValueExpression>;
            _GetValue(tmpTree);

            return double.Parse(tmpTree.Tag.ToString());
        }

        private static void _GetValue(SyntaxTree<EnumTokenTypeExpression, EnumVTypeExpression, TreeNodeValueExpression> tree)
        {
            switch (tree.NodeValue.NodeType)
            {
                case EnumVTypeExpression.Unknown:
                    break;
                case EnumVTypeExpression.case_Expression://<Expression> ::= <Multiply> <PlusOpt>;
                    _GetValue(tree.Children[0]);
                    _GetValue(tree.Children[1]);
                    tree.Tag = double.Parse(tree.Children[0].Tag.ToString()) + double.Parse(tree.Children[1].Tag.ToString());
                    break;
                case EnumVTypeExpression.case_PlusOpt://<PlusOpt> ::= "+" <Multiply> | "-" <Multiply> | null;
                    if (tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_PlusOpt___tail_plus_Leave())
                    {
                        _GetValue(tree.Children[1]);
                        tree.Tag = tree.Children[1].Tag;
                    }
                    else if (tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_PlusOpt___tail_minus_Leave())
                    {
                        _GetValue(tree.Children[1]);
                        tree.Tag = -double.Parse(tree.Children[1].Tag.ToString());
                    }
                    else if (tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_PlusOpt___tail_rightParentheses_Leave()
                        || tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_PlusOpt___tail_startEndLeave())
                    {
                        tree.Tag = 0;
                    }
                    break;
                case EnumVTypeExpression.case_Multiply://<Multiply> ::= <Unit> <MultiplyOpt>;
                    _GetValue(tree.Children[0]);
                    _GetValue(tree.Children[1]);
                    tree.Tag = double.Parse(tree.Children[0].Tag.ToString()) * double.Parse(tree.Children[1].Tag.ToString());
                    break;
                case EnumVTypeExpression.case_MultiplyOpt://<MultiplyOpt> ::= "*" <Unit> | "/" <Unit> | null;
                    if (tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_MultiplyOpt___tail_multiply_Leave())
                    {
                        _GetValue(tree.Children[1]);
                        tree.Tag = double.Parse(tree.Children[1].Tag.ToString());
                    }
                    else if (tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_MultiplyOpt___tail_divide_Leave())
                    {
                        _GetValue(tree.Children[1]);
                        tree.Tag = 1 / (double)tree.Children[1].Tag;
                    }
                    else if (tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_MultiplyOpt___tail_plus_Leave()
                        || tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_MultiplyOpt___tail_minus_Leave()
                        || tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_MultiplyOpt___tail_rightParentheses_Leave()
                        || tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_MultiplyOpt___tail_startEndLeave())
                    {
                        tree.Tag = 1;
                    }
                    break;
                case EnumVTypeExpression.case_Unit://<Unit> ::= number | "(" <Expression> ")";
                    if (tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_Unit___numberLeave())
                    {
                        tree.Tag = double.Parse(tree.Children[0].NodeValue.NodeName);
                    }
                    else if (tree.CandidateFunc == LL1SyntaxParserExpression.GetFuncParsecase_Unit___tail_leftParentheses_Leave())
                    {
                        _GetValue(tree.Children[1]);
                        tree.Tag = tree.Children[1].Tag;
                    }
                    break;
                default:
                    break;
            }
        }


    }

 

ArmadaTank模型

坦克舰队(ArmadaTank)是我很喜欢的一款游戏,现在我正在试图用C#重写这个游戏。喜欢的同学可以自行搜索“坦克舰队”。

ArmadaTank的3D模型是用纯文本的*.dtm文件标识的。完全可以用自动生成的解析器来加载之。

步骤就不再说了,和Expression的步骤一样,这里只贴一下DTM文件的文法。

DTM的文法<ArmadaTanksModel> ::= "File" <FileContent> "endfile";

<FileContent> ::= "{" <BlockList> "}";

<BlockList> ::= <Block> <BlockList> | null;

<Block> ::= "FileDesc" <FileDesc> "endfiledesc" | "Faces" <Faces> | "MapChannel" <SignedNumber> <MapChannel> | "Frame" <SignedNumber> <Frame> "endframe";

<FileDesc> ::= "{" <FileDescItemList> "}";

<FileDescItemList> ::= <FileDescItem> <FileDescItemList> | null;

<FileDescItem> ::= "Frames" <SignedNumber> | "Vertices" <SignedNumber> | "Faces" <SignedNumber> | "Map" <SignedNumber> "TVertices" <SignedNumber>;

<Faces> ::= "{" <FaceList> "}";

<FaceList> ::= <Face> <FaceList> | null;

<Face> ::= "Face" <SignedNumber> <SignedNumber> <SignedNumber> <SignedNumber> "MatID" <SignedNumber>;

<MapChannel> ::= "{" <TextureList> "}";

<TextureList> ::= <Texture> <TextureList> | null;

<Texture> ::= "TextureVertices" <TextureVertices> | "TextureFaces" <TextureFaces>;

<TextureVertices> ::= "{" <TVertexList> "}";

<TVertexList> ::= <TVertex> <TVertexList> | null;

<TVertex> ::= "TVertex" <SignedNumber> <SignedNumber> <SignedNumber> <SignedNumber>;

<TextureFaces> ::= "{" <TFaceList> "}";

<TFaceList> ::= <TFace> <TFaceList> | null;

<TFace> ::= "TFace" <SignedNumber> <SignedNumber> <SignedNumber> <SignedNumber>;

<Frame> ::= "{" <FrameContentItemList> "}";

<FrameContentItemList> ::= <FrameContentItem> <FrameContentItemList> | null;

<FrameContentItem> ::= "Vertices" "{" <Vertices> "}";

<Vertices> ::= <Vertex> <Vertices> | null;

<Vertex> ::= "Vertex" <SignedNumber> <SignedNumber> <SignedNumber> <SignedNumber>;

<SignedNumber> ::= "+" number | "-" number | number;

 

用OpenGL来显示3D模型(语义分析及其之后的阶段),如下图所示。

clip_image011[8]

源代码在此。

http://files.cnblogs.com/bitzhuwei/bitzhuwei.CGCompiler2013-11-20_19-27-00.rar

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值