zoukankan      html  css  js  c++  java
  • Grammatica简介

        上个月说到了自定义语言,不过在确定自己的语法后,遇到的第一个难题便是如何解析自己定义的语言,至于如何执行,是这之后的事情。

    找一个语法解析器

        软件中有一点很重要,不要重复造轮子,等等,这话听起来有点怪,自定义语言不也是重复造轮子么?

        好吧,我承认我在重复造轮子,不过我的主要目的不是让自己的语言怎么怎么样,而是去更深入的理解那些编程语言,要是运气好,自己找的轮子能够流行起来,那自然更好了。

        因此,我要造的是语言,而不是语法解析器,所以不要重复造语法解析器,没有那个时间和精力去消耗在这上面。

        所以,随便找了个语法解析器:Grammatica

        那么为什么用这个而不用其它的?好吧,没有理由,仅仅是因为随便找了一下。哦,对了,还因为它支持我需要的LL(*)的语法结构。

    使用之前

        在使用这个开源工具的.net版本之前,先要帮它修个小bug,当然这个小bug不是必须要修,只不过我的运用场景导致了了必须要修复。

        要修复什么问题哪?

        自定义异常

        建议大家都去读一下msdn上面的建议

        尤其是这一句:

    Do make exceptions serializable. An exception must be serializable to work correctly across application domain and remoting boundaries.

        碰巧我的运行环境是在一个独立的AppDomain中,因此,脚本Parse所发生的异常会穿透域,而Grammatica的所有自定义异常都没有实现序列化,直接导致了序列化异常时出错,然后么,就杯具了,谁知道什么错了。

        因此,第一件事情,便是修复所有的自定义异常,正确实现序列化(不要以为标记了[Serializable]就已经实现了哦,异常的序列化可没这么简单,想进一步了解的话,可以看msdn,或者google,或者等我那天想到了写一下吧)。

    设计语法

        一个好的语法绝对是一门语言成败的关键,不过,我目前还没掌握如何定义出一门好的语法。这里,先用一个滥语法来做个演示。

        首先,定义语言的一些Token,例如:

    LAMBDA = "->"
    LEFT_PAREN = "("
    RIGHT_PAREN = ")"
    LEFT_CURLY_BRACKET = "{"
    RIGHT_CURLY_BRACKET = "}"
    COLON = ":"
    DOT = "."
    ARGUMENT_SEPARATOR = ","

        和一些基本的字面量和保留字:

    NUMBER = <<-?\d+(\.\d+)?>>
    STRING_LITERAL = <<'([^'\r\n\\]|\\u[0-9a-f]{4}|\\[\\"'trn])*'>>
    TRUE = "true"
    FALSE = "false"
    NULL_LITERAL = "null"
    FUNC = "func"
    NEW = "new"

        和剩下的标识符:

    IDENTIFIER = <<[a-zA-Z]\w*>>

        这些是基础,来看看如何把这些Token组和起来(productions):

    Expression = BasicExpression {MemberAccessExpression};

    表达式就是一个基础表达式,加上0-N个成员访问表达式

    MemberAccessExpression = "." MemberFunctionExpression;

    成员访问表达式就是”.”加上一个成员或方法表达式

    BasicExpression = LiteralExpression | VariableExpression | ExpressionGroup | NEWExpression;

    基础表达式就是字面量表达式、变量表达式、表达式组、对象创建表达式中的任意一个

    MemberFunctionExpression = FieldPropertyExpression | FunctionCallExpression;

    成员或方法表达式就是字段/属性表达式或方法调用表达式中的一个

    FieldPropertyExpression = IDENTIFIER;

    字段/属性表达式就是标识符

    FunctionCallExpression = IDENTIFIER "(" ArgumentList? ")";

    方法调用表达式就是标识符+“(”+可能有参数列表+“)”

    ArgumentList = Expression {"," Expression};

    参数列表就是一个表达式,后面跟0-N个表达式

    FUNCExpression = FUNC "(" ParameterList? ")" "->" Expression;

    函数表达式就是“func”+”(”+可能有参数列表+“)”+”->”+表达式

    ParameterList = IDENTIFIER {"," IDENTIFIER};

    参数列表就是一个标识符,后面跟0-N个标识符

    NEWExpression = NEW "{" PropertyValueExpression { "," PropertyValueExpression } "}";

    对象创建表达式就是”new”+”{”+1-N个属性值表达式+”}”

    PropertyValueExpression = IDENTIFIER ":" Expression;

    属性值表达式就是一个标识符+”:”+表达式

    ExpressionGroup = "(" Expression ")";

    表达式组就是”(”+表达式+”)”

    VariableExpression = IDENTIFIER;

    变量表达式就是标识符

    LiteralExpression = NUMBER | STRING_LITERAL | TRUE | FALSE | NULL_LITERAL;

    字面量表达式就是数字、字符串、true、false、null中的任意一个

        看到这里,如果没有看晕,那么恭喜你,太有天赋了,为了理解这些,我足足花了一个星期的时间。

    看看能解析什么

        为了让大家知道上面写了这么大一串,究竟发生了什么作用,我们可以从简到繁的写几个例子:

    解析:a.b.c
    结果:变量a.字段b.字段c

    解析:foo.bar(x,y)
    结果:变量foo.方法bar(参数列表:变量x,变量y)

    解析:foo.bar().foobar
    结果:变量foo.方法bar().字段foobar

    解析:new { firstName: ‘Zhenway’, lastName: ‘Yan’ }
    结果:创建对象,字段firstName的值为“Zhenway”,字段lastName的值为“Yan”

    解析:list.select(func(item)->item.abc)
    结果:变量list.方法select(参数列表:lambda:形参列表:item -> 变量item.字段abc)

        再来个复杂点的

    解析:new { firstName: ‘Zhenway’, lastName: ‘Yan’, toString: func() –> ‘Zhenway Yan’ }
    结果:创建对象,字段firstName的值为“Zhenway”,字段lastName的值为“Yan”,字段toString是个lambda:形参列表:无 -> 字符串字面量“Zhenway Yan”

    解析:x.f(x.g(x.h(1)))
    结果:变量x.方法f(参数列表:变量x.方法g(参数列表:变量x.方法h()))

        有没有发现了什么?还没?再来一个:

    new { name: new { firstName: ‘Zhenway’, lastName: ‘Yan’, toString: func() –> ‘Zhenway Yan’ }, address: new { country:’China’ province: 'Shanghai', toString: func() –> 'Shanghai China’ } }

        现在发现什么了?

        没错这个结构可以一直嵌套下去,这就是表达式带来的无穷的想象力。

    还缺什么

        有没有发现缺了什么?现在的定义是在太少了,很多东西无法表达,例如name的toString其实永远是firstName+lastName,看缺了什么,让我们无法这样定义:

    • 最明显的是“+”,+、-、*、/、%这类表达式尚未定义(当然你要想的全的话,还有and、or、xor等等)
    • 其次是没有this,无法表达当前对象(这里,我的面向对象的惯性思维又冒出来了…)

        如果,添加一个Token:THIS=”this”,并且加一个表达式:

    THISExpression = THIS;

    this表达式就是”this”

        再修改一下BasicExpression,添加上一个或THISExpression,那么我们就可以在完全兼容原有语法的基础上,添加了对this的支持,例如:

    new { item: ‘abc’, getItemLength: func() -> this.item.length }

        是不是很Cool,想象一下,当你有个新Idea,并且实现时,你的所有表达式都可能从中获益,而且对老的表达式又完全兼容。

    下集预告

        这么Cool的功能如何实现哪?Oh, no!说了半天都是理论啊,具体如何实现,请听下回分解。

  • 相关阅读:
    鼠标经过显示边框
    特殊字符
    HTML 列表
    embed 引入网上视频
    锚点定位
    盒子阴影
    Map的四种遍历方式
    Glide的 java.lang.RuntimeException: Expected instanceof GlideModule, but found:X.GlideModule@2e4554f
    Java标识符的命名规则
    django入门与实践
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/1978531.html
Copyright © 2011-2022 走看看