Skip to content

[zh_CN]IParser Configuration

Codemonk edited this page May 23, 2019 · 19 revisions

词法分析配置

为什么配置是只读的?

为了保证在 Yacep 分析的结果在转换为可执行代码时尽量少出现一些问题, ReadOnlyParseOption 对象一旦创建,其内部的属性都是只读的。

可以配置哪些功能?

只读配置对象 ReadOnlyParseOptionIParser 提供了在分析解析字符串所需的全部配置, 配置大致分为三类。

第一类是禁止对一些特征字符串处理能力。

第二类是一系列的自定义功能,包含自定义一元及二元操作符,函数,字面值等。

还有个比较特殊的专门为数值处理配置。


ParseOption 为辅助构建 ReadOnlyParseOption 而添加的一个类。而 ParseOptionExtension 为辅助构建 ParseOption 而添加的一个类。

通常的用法是使用 new 操作或 ParseOption.CreateOption 方法创建一个新的 ParseOption 对象,然后通过 ParseOptionExtension 的扩展方法为 ParseOption 设置配置值,最后调用 AsReadOnly 方法来构建一个只读的配置 ReadOnlyParseOption

var option = new ParseOption()
    .NotAllowedArrayExpression() //不允许分析包含数组表达式的字符串
    .NotAllowedInExpression() //不允许分析包含In表达式的字符串
    .AddLiteralValue("one_hundred", 100) //添加字面值oneHundred,代表数字100
    .AddLiteralValue("two_thousand", 2000) //添加字面值twoThousand,代表数字2000
    .AddLiteralValue("two_thousand_and_one", 2100) //添加字面值twoThousand,代表数字2000
    .AddBinaryOperator("plus", (a, b) => (int)a + (int)b, 2) //添加二元操作符plus,优先级为2
    .AddBinaryOperator("is", (a, b) => (int)a == (int)b, 1) //添加二元操作符is,优先级为1,数字越小优先级越低
    .AsReadOnly();
Console.WriteLine("100 plus 2000 is 2100".Compile(option).EvaluateAs<bool>());//true
Console.WriteLine("one_hundred plus two_thousand is two_thousand_and_one".Compile(option).EvaluateAs<bool>());//true
Console.WriteLine("one_hundred plus two_thousand is 2100".Compile(option).EvaluateAs<bool>());//true

禁止能力

禁止能力是禁止 Yacep 对一些特征字符串处理能力。在遇到特征字符串时,Yacep 将会报错处理。

  1. 不允许对数组表达式进行分析解析

    不允许分析包含数组表达式的字符串,如果字符串中包含数组表达式将会抛出异常。

"[1, 2]".ToEvaluableExpression(new ParseOption()
    .NotAllowedArrayExpression()
    .AsReadOnly());
//TupacAmaru.Yacep.Exceptions.ParseException:"Array expression is not allowed at character 0"
  1. 不允许对条件表达式进行分析解析

    不允许分析包含条件表达式的字符串,如果字符串中包含条件表达式将会抛出异常。

"true ? 1 : 2".ToEvaluableExpression(new ParseOption()
    .NotAllowedConditionExpression()
    .AsReadOnly());
//TupacAmaru.Yacep.Exceptions.ParseException:"Condition expression is not allowed at character 0"
  1. 不允许对访问对象成员表达式进行分析解析

    不允许分析包含对象成员表达式的字符串,如果字符串中包含对象成员表达式将会抛出异常。

"m.Length".ToEvaluableExpression(new ParseOption()
    .NotAllowedMemberExpression()
    .AsReadOnly());
//TupacAmaru.Yacep.Exceptions.ParseException:"Member expression is not allowed at character 1"
  1. 不允许对索引表达式进行分析解析

    不允许分析包含索引表达式的字符串,如果字符串中包含索引表达式将会抛出异常。
"m[0]".ToEvaluableExpression(new ParseOption()
    .NotAllowedIndexerExpression()
    .AsReadOnly());
//TupacAmaru.Yacep.Exceptions.ParseException:"Indexer expression is not allowed at character 1"
"m['Length']".ToEvaluableExpression(new ParseOption()
    .NotAllowedIndexerExpression()
    .AsReadOnly());
//TupacAmaru.Yacep.Exceptions.ParseException:"Indexer expression is not allowed at character 1"
  1. 不允许对In表达式进行分析解析

不允许分析包含In表达式的字符串,如果字符串中包含In表达式将会抛出异常。

"1 in (1, 2, 3)".ToEvaluableExpression(new ParseOption()
    .NotAllowedInExpression()
    .AsReadOnly());
//TupacAmaru.Yacep.Exceptions.ParseException:"In expression is not allowed at character 5"

禁用数值转换 (不推荐禁用此特性)

Yacep 中是没有带符号数字的,因为符号 - 可以被重定义为其他意义. 但是为了方便广大C#开发者的使用习惯,内部会按照值的大小在读取的时候动态转换类型. 如果一个整数大于 4294967295 会被读取为 ulong 类型,如果还大于 2147483648 就会被读取为 integer 类型,否则就会读取为 int 类型. 如果禁用数值转换,Yacep会将读取到数值统一按照ulong类型处理。

数值类型 C#值类型 描述
ulong UInt64 如果一个整数大于 4294967295 会被读取为 ulong 类型.
integer UInt32 如果还大于 2147483648 就会被读取为 integer
int Int32 小于 2147483648 的数值
decimal Decimal 十进制浮点数

此功能的实现代码:Parser.cs#L66

Console.WriteLine("111".Compile().EvaluateAs<object>().GetType()); //System.Int32
Console.WriteLine("111".Compile(new ParseOption()
                                 .NotAllowedConvertUnsignedInteger()
                                 .AsReadOnly())
                       .EvaluateAs<object>()
                       .GetType()); //System.UInt64 

自定义字面值

内置的字面值

Yacep 内置了三个字面值( 实现代码 )

字面值 释义
true Bool值true
false Bool值false
null null

清除内置的字面量

Yacep 支持将内置的三个字面量清除掉 使用new方法创建的ParseOption会不带任何内置字面量, 在调用 ParseOption.CreateOption 方法的时候设置参数 addDefaultLiteralValuesfalse 则不会添加任何内置字面量

自定义字面量 代码

重要!!! 字面量不能包含空格,字面量的值不能是委托类型. 在使用字面量时, Yacep 区分大小写. 所以字面量trueTrue可以定义为两个不同字面量.

  • 添加符号为字面量
Console.WriteLine("π".Compile(new ParseOption()
                                .AddLiteralValue("π", Math.PI) //数值π
                                .AsReadOnly())
                    .EvaluateAs<double>()); //3.14159265358979
  • 添加字符为字面量
Console.WriteLine("T".Compile(new ParseOption()
                             .AddLiteralValue("T", "T is a very long string") //长字符串
                            .AsReadOnly())
                    .EvaluateAs<string>()); //T is a very long string
  • 添加字符串为字面量
Console.WriteLine("os_version".Compile(new ParseOption()
                                .AddLiteralValue("os_version", Environment.OSVersion.Version) //操作系统版本
                                .AsReadOnly())
                    .EvaluateAs<Version>()); //10.0.18362.0

自定义一元操作符

内置的一元操作符

Yacep 内置了3个一元操作符( 实现代码 )

符号 释义 操作
+ 正号 表示数学中的正号 +
- 负号 表示数学中的负号 -
! 表示一个非操作,返回一个bool值

注意Yacep 中,小于等于0的数,空字符串以及为null的对象在转换为bool值的时候等于false。大于0的数,不为空的字符串,不为null的对象,在转换为bool值的时候等于true

  • 示例代码:使用默认配置获取抽象语法树 代码
//代码使用了Yacep内的扩展方法,Yacep所有的扩展方法都在命名空间TupacAmaru.Yacep.Extensions下
Console.WriteLine("+m".Compile().Evaluate(new { m = 12 }));//12
Console.WriteLine("-m".Compile().Evaluate(new { m = 12 }));//-12
Console.WriteLine("!m".Compile().Evaluate(new { m = 12 }));//False

清除内置的一元操作符

Yacep 支持将内置的3个一元操作符清除掉 使用new方法创建的ParseOption会不带任何内置一元操作符, 在调用 ParseOption.CreateOption 方法的时候设置参数 addDefaultUnaryOperatorsfalse 则不会添加任何内置一元操作符

自定义一元操作符 代码

重要!!! 一元操作符不能包含空格. 在使用一元操作符时, Yacep 区分大小写.

  • 添加符号为一元操作符
var option = new ParseOption()
    .AddUnaryOperator("#", value => (value as string)?.Length ?? 0) //获取字符串长度
    .AsReadOnly();
Console.WriteLine("#'hello'".Compile(option).EvaluateAs<int>()); //5
  • 添加字符为一元操作符
var option = new ParseOption()
    .AddUnaryOperator("L", value => (value as string)?.Length ?? 0) //获取字符串长度
    .AsReadOnly();
Console.WriteLine("L'hello'".Compile(option).EvaluateAs<int>()); //5
  • 添加字符串为一元操作符(不推荐)

    不推荐添加字符串为一元操作符,这种情况会降低表达式的可读性,推荐使用自定义函数

var option = new ParseOption()
    .AddUnaryOperator("LengthOf", value => (value as string)?.Length ?? 0) //获取字符串长度
    .AsReadOnly();
Console.WriteLine("LengthOf'hello'".Compile(option).EvaluateAs<int>()); //5

自定义二元操作符

内置的二元操作符

Yacep 内置了13个二元操作符( 实现代码 )

重要!!! Yacep 中,小于等于0的数,空字符串以及为null的对象在转换为bool值的时候等于false。大于0的数,不为空的字符串,不为null的对象,在转换为bool值的时候等于true,以下二元操作符中的或和与操作,都会按照这个规则转换。

符号 释义 操作 优先级
|| 或, 或操作 1
&& 与操作 2
== 相等判定 相等运算符 3
!= 不相等判定 不相等运算符 3
< 小于判定 小于运算符 4
> 大于判定 大于运算符 4
<= 小于等于判定 小于等于运算符 4
>= 大于等于判定 小于等于运算符 4
+ 加运算符,支持数值与数值相加
也支持字符串相加
加运算符 5
- 减运算符 减运算符 5
* 乘运算符,支持数值与数值相乘
也支持字符串与数值相乘(表重复字符串的次数)
乘运算符 6
/ 除运算符 除运算符 6
% 求余运算符 求余运算符 6

清除内置的二元操作符

Yacep 支持将内置的13个二元操作符清除掉 使用new方法创建的ParseOption会不带任何内置一元操作符, 在调用 ParseOption.CreateOption 方法的时候设置参数 addDefaultBinaryOperatorsfalse 则不会添加任何内置一元操作符

自定义二元操作符 代码

重要!!! 二元操作符不能包含空格, 且in作为内置的表达式类型不能被重新定义为二元操作符. 在使用二元操作符时, Yacep 区分大小写.

  • 添加符号为二元操作符
Console.WriteLine("'b'@'abc'".Compile(new ParseOption()
                       .AddBinaryOperator("@", (a, b) => (b as string)?.IndexOf(a as string) ?? 0, 10) //获取子串位置
                       .AsReadOnly())
                   .EvaluateAs<double>()); //1
  • 添加字符为二元操作符
Console.WriteLine("'b'I'abc'".Compile(new ParseOption()
                       .AddBinaryOperator("I", (a, b) => (b as string)?.IndexOf(a as string) ?? 0, 10) //获取子串位置
                       .AsReadOnly())
                   .EvaluateAs<double>()); //1
  • 添加字符串为二元操作符
Console.WriteLine("'hello' concat 'world'".Compile(new ParseOption()
           .AddBinaryOperator("concat", (a, b) => $"{a}, {b}", 10) //连接字符串
           .AsReadOnly())
       .EvaluateAs<string>()); //hello, world

自定义函数

内置的函数

Yacep 内置了两类函数, 一类是统计类函数( 实现代码 ), 一类是日期类函数( 实现代码 )

5个统计类函数 实现代码

函数 释义 操作
len 取长度 获取集合的长度
max 最大值 求一个数值数组中最大的值
min 最小值 求一个数值数组中最小的值
sum 求和 求一个数值数组的和
avg 均值 求一个数值数组的均值

9个日期类函数 实现代码

函数 释义 操作 返回值类型
now 获取当前日期及时间 默认的格式为yyyy/MM/dd HH:mm:ss 字符串
today 获取当前日期 默认的格式为yyyy/MM/dd 字符串
time 获取当前时间 默认的格式为HH:mm:ss 字符串
year 获取当前年的数值 等同于DateTime.Now.Year 数值
month 获取当前月的数值 等同于DateTime.Now.Month 数值
day 获取当前日的数值 等同于DateTime.Now.Day 数值
hour 获取当前小时的数值 等同于DateTime.Now.Hour 数值
minute 获取当前分钟的数值 等同于DateTime.Now.Minute 数值
second 获取当前秒的数值 等同于DateTime.Now.Second 数值

清除内置的函数

Yacep 支持将内置的14个二元操作符清除掉 使用new方法创建的ParseOption会不带任何内置一元操作符, 在调用 ParseOption.CreateOption 方法的时候设置参数 addStatisticsFunctionsfalse 则不会添加任何内置的统计类函数,设置参数 addDateAndTimeFunctionsfalse 则不会添加任何内置的日期类函数

自定义函数 代码

重要!!! 函数名不能包含空格, 且in作为内置的表达式类型不能被重新定义为函数. 在调用函数时, Yacep 区分大小写.

  • 添加符号为函数
Console.WriteLine("$('body')".Compile(new ParseOption()
           .AddNakedFunction("$", values => $"find {values[0]} in dom") //拼接字符串
           .AsReadOnly())
       .EvaluateAs<string>()); //find body in dom
  • 添加字符为二元操作符
Console.WriteLine("J('body')".Compile(new ParseOption()
          .AddNakedFunction("J", values => $"find {values[0]} in dom") //拼接字符串
          .AsReadOnly())
      .EvaluateAs<string>()); //find body in dom
  • 添加字符串为二元操作符
Console.WriteLine("jQuery('body')".Compile(new ParseOption()
           .AddNakedFunction("jQuery", values => $"find {values[0]} in dom") //拼接字符串
           .AsReadOnly())
       .EvaluateAs<string>()); //find body in dom
Clone this wiki locally