Amazon Best VPN GoSearch

OnWorks 网站图标

peg - 云端在线

通过 Ubuntu Online、Fedora Online、Windows 在线模拟器或 MAC OS 在线模拟器在 OnWorks 免费托管服务提供商中运行 peg

这是可以使用我们的多个免费在线工作站之一在 OnWorks 免费托管服务提供商中运行的命令挂钩,例如 Ubuntu Online、Fedora Online、Windows 在线模拟器或 MAC OS 在线模拟器

程序:

您的姓名


peg, leg - 解析器生成器

概要


[-HVV -o输出] [文件名 ...]
[-HVV -o输出] [文件名 ...]

商品描述


是用于生成递归下降解析器的工具:执行
文本模式匹配。 他们处理解析表达式语法 (PEG) [Ford 2004] 以
生成一个程序来识别该语法的合法句子。 处理 PEG
使用福特描述的原始语法编写; 处理使用编写的 PEG
略有不同的语法和约定,旨在使其具有吸引力
替换构建的解析器 (1)和 雅克(1). 不像 雅克,
支持无限回溯,提供有序选择作为消除歧义的手段,以及
可以将扫描(词法分析)和解析(句法分析)合并为一个
活性。

读取指定的 文件名s,如果没有,则为标准输入 文件名s 是给定的,对于一个
描述要生成的解析器的语法。 然后生成一个 C 源文件
定义一个函数 yyparse() 这个 C 源文件可以包含在,或者编译然后
与客户端程序相关联。 每次客户端程序调用 yy解析() 解析器
根据解析规则使用输入文本,从第一个规则开始
语法。 yy解析() 如果输入可以根据
语法; 如果无法解析输入,则返回零。

前缀“yy”或“YY”被添加到生成的所有外部可见符号之前
解析器。 这是为了降低客户端程序中命名空间污染的风险。
('yy' 的选择是历史性的;见 (1)和 雅克(1),例如。)

配置


提供以下选项:

-h 打印可用选项的摘要,然后退出。

-o输出
将生成的解析器写入文件 产量 而不是标准输出。

-v 在工作时将详细信息写入标准错误。

-V 将版本信息写入标准错误然后退出。

A 简单


下列 输入指定具有单个规则(称为“开始”)的语法,即
当输入包含字符串“用户名”时满意。

开始 <- “用户名”

(引号是 而不去 匹配文本的一部分; 它们用于指示文字
要匹配的字符串。)换句话说, yy解析() 在生成的 C 源代码中会返回
仅当从输入中读取的下八个字符拼写单词“username”时才非零。
如果输入包含其他任何内容, yy解析() 返回零并且没有输入
消耗。 (随后调用 yy解析() 也将返回零,因为解析器是
有效地阻止了查找字符串“用户名”。)为了确保进度,我们可以添加一个
'start' 规则的替代子句,如果“用户名”将匹配任何单个字符
没有找到。

开始 <- “用户名”
/。

yy解析() 现在总是返回非零值(除了在输入的最后)。 去做
一些有用的东西,我们可以向规则添加操作。 这些操作是在一个
找到完全匹配(从第一条规则开始)并根据
'path' 通过语法匹配输入。 (语言学家将这条路径称为
'短语标记'。)

开始 <- "用户名" { printf("%s\n", getlogin()); }
/ < . > { putchar(yytext[0]); }

第一行指示解析器在它看到时打印用户的登录名
输入中的“用户名”。 如果匹配失败,第二行告诉解析器回显
输入标准输出的下一个字符。 我们的解析器现在正在执行有用的
工作:它将输入复制到输出,将所有出现的“用户名”替换为
用户的帐户名。

请注意添加到第二个选项中的尖括号(“<”和“>”)。 这些
对规则的含义没有影响,但有助于划定可供使用的文本
变量中的以下操作 文本.

如果将上述语法放在文件中 用户名.peg, 运行命令

peg -o 用户名.c 用户名.peg

将相应的解析器保存在文件中 用户名.c. 创建一个完整的程序
这个解析器可以包含在 C 程序中,如下所示。

#包括/* printf(), putchar() */
#包括/* 获取登录() */

#include "username.c" /* yyparse() */

int main()
{
while (yyparse()) /* 重复直到 EOF */
;
0返回;
}

PEG 语法


文法由一组命名规则组成。

名称 <- 模式

- 模式 包含以下一个或多个元素。

姓名 该元素代表具有给定规则的整个模式 姓名.

"字符"
双引号中的字符或字符串按字面匹配。 ANSI C
转义序列在 字符.

'字符'
单引号中的字符或字符串按字面匹配,如上所述。

[字符]
用方括号括起来的一组字符匹配来自
集,转义字符识别如上。 如果集合以
uparrow (^) 然后集合被否定(元素匹配任何字符 而不去 ,在
放)。 任何以破折号 (-) 分隔的字符对表示范围
从第一个字符到第二个字符,包括在内。 单个字母字符
或下划线与以下集合匹配。

[a-zA-Z_]

类似地,以下匹配任何单个非数字字符。

[^ 0-9]

. 点匹配任何字符。 请注意,唯一失败的时间是在
文件,其中没有要匹配的字符。

( 模式 )
括号用于分组(修改运算符的优先级)
如下面所描述的)。

{ 行动 }
花括号包围动作。 动作是任意的C源代码
在匹配结束时执行。 动作中的任何大括号都必须正确
嵌套。 在操作之前匹配并以角度分隔的任何输入文本
括号(见下文)在动作中作为内容提供
字符数组 文本. 长度(字符数) 文本 is
在变量中可用 伊伦. (这些变量名称是历史性的;见
(1)。)

< 左尖括号始终匹配(不消耗输入)并导致解析器
开始积累匹配的文本。 此文本将可用于以下操作
变量 文本.

> 右尖括号始终匹配(不消耗输入)并导致解析器
停止累积文本 文本.

以上 elements 可以通过以下后缀变为可选和/或可重复的:

element ?
该元素是可选的。 如果出现在输入中,它会被消耗并且匹配
成功。 如果输入中不存在,则不消耗任何文本并且匹配成功
反正。

element +
该元素是可重复的。 如果出现在输入中,则出现一次或多次
element 被消耗并且匹配成功。 如果没有出现 element 旨在
出现在输入中,匹配失败。

element *
该元素是可选的且可重复的。 如果出现在输入中,一个或多个
的发生 element 被消耗并且匹配成功。 如果没有出现
element 出现在输入中,无论如何匹配都会成功。

上述元素和后缀可以转换为谓词(匹配任意
输入文本然后成功或失败 也完全不需要 消耗该输入)与
以下前缀:

& element
谓词仅在以下情况下成功 element 可以匹配。 输入文本扫描时
匹配 element 不会从输入中消耗并保持可用于
后续匹配。

! element
谓词仅在以下情况下成功 element 无法匹配。 输入文本扫描时
匹配 element 不会从输入中消耗并保持可用于
后续匹配。 一个流行的成语是

!.

在输入的最后一个字符之后匹配文件结尾
被消耗了。

提供了一种特殊形式的“&”谓词:

&{ 表达 }
在这个谓词中,简单的 C 表达 (而不去 语句)被立即评估
当解析器到达谓词时。 如果 表达 产生非零(真)
“匹配”成功,解析器继续处理模式中的下一个元素。
如果 表达 产生零(假)“匹配”失败并且解析器备份到
寻找输入的替代解析。

几个元素(有或没有前缀和后缀)可以组合成一个 序列
通过一个接一个地写它们。 仅当每个个体都匹配时,整个序列才匹配
其中的元素从左到右匹配。

可以通过交替运算符“/”将序列分成不相交的备选方案。

序列 1 / 序列 2 / ... / 序列-N
依次尝试每个序列,直到其中一个匹配,此时匹配
因为整体模式成功。 如果没有任何序列匹配,则匹配
整体模式失败。

最后,井号 (#) 引入了一直持续到结尾的注释(已丢弃)
行的。

总结上述内容,解析器尝试将输入文本与模式匹配
包含文字、名称(代表其他规则)和各种运算符(写成
前缀、后缀、排序和中缀交替运算符的并置),
修改模式中元素的匹配方式。 比赛是从左到
对,在遇到命名的子规则时“降序”。 如果匹配过程
失败,解析器“回溯”(在过程中适当地“回绕”输入)到
通过语法找到最近的替代“路径”。 换句话说,解析器
对第一个成功匹配的路径执行深度优先、从左到右的搜索
通过规则。 如果找到,则执行成功路径上的操作(在
他们遇到的顺序)。

请注意,谓词被评估 立即 在寻找成功匹配的过程中,
因为它们有助于搜索的成功或失败。 然而,行动是
仅在找到成功匹配后才进行评估。

PEG 语法 用于 PEG 语法


语法为 语法如下所示。 这将说明和形式化
以上说明。

语法 <- 间距定义+ EndOfFile

定义 <- 标识符 LEFTARROW 表达式
表达式 <- 序列(斜线序列)*
序列 <- 前缀*
前缀 <- AND 操作
/ ( 和 | 不是 )? 后缀
后缀 <- 主要 ( QUERY / STAR / PLUS )?
主要 <- 标识符 !LEFTARROW
/ OPEN 表达式 CLOSE
/ 文字
/课
/点
/ 行动
/ 开始
/ 结尾

标识符 <- < IdentStart IdentCont* > 间距
IdentStart <- [a-zA-Z_]
IdentCont <- IdentStart / [0-9]
文字 <- ['] < ( !['] Char )* > ['] 间距
/ ["] < ( !["] Char )* > ["] 间距
类 <- '[' < ( !']' 范围 )* > ']' 间距
范围 <- 字符 '-' 字符 / 字符
字符 <- '\\' [abefnrtv'"\[\]\\]
/ '\\' [0-3][0-7][0-7]
/ '\\' [0-7][0-7]?
/ '\\' '-'
/ !'\\' 。
左箭头 <- '<-' 间距
斜线 <- '/' 间距
AND <- '&' 间距
不是 <- '!' 间距
查询 <- '?' 间距
星号 <- '*' 间距
加号 <- '+' 间距
OPEN <- '(' 间距
CLOSE <- ')' 间距
点 <- '.' 间距
间距 <-(空格/注释)*
注释 <- '#' ( !EndOfLine . )* EndOfLine
空格 <- ' ' / '\t' / EndOfLine
EndOfLine <- '\r\n' / '\n' / '\r'
EndOfFile <- !.
操作 <- '{' < [^}]* > '}' 间距
BEGIN <- '<' 间距
END <- '>' 间距

专家组 语法


是...的变体 这增加了一些功能 (1)和 雅克(1). 它不同于
通过以下方式。

%{ 文本... %}
声明部分可以出现在需要规则定义的任何地方。 这
文本 分隔符 '%{' 和 '%}' 之间被逐字复制到生成的 C
解析器代码 before 实现解析器本身的代码。

姓名 = 模式
“赋值”运算符替换了左箭头运算符“<-”。

规则名称
连字符可以显示为规则名称中的字母。 每个连字符都转换为
生成的 C 源代码中的下划线。 单个连字符“-”是
法律规则名称。

- = [ \t\n\r]*
数字 = [0-9]+ -
名称 = [a-zA-Z_][a-zA_Z_0-9]* -
l-paren = '(' -
r-paren = ')' -

这个例子显示了在阅读语法时被忽略的空格是多么明显
并且当自由地放置在与
一个词汇元素。

序列-1 | 序列-2
交替运算符是竖线“|” 而不是正斜杠'/'。 这
排除

名称 <- 序列-1
/ 序列-2
/ 序列-3

因此写成

名称 = 序列 1
| 序列 2
| 序列 3
;

in (最后一个分号是可选的,如下所述)。

EXP ~ { 行动 }
后缀运算符 ~{ 行动 } 可以放在任何表达式之后并且会表现
就像一个正常的动作(任意的 C 代码),除了它只在 EXP
失败。 除了交替和
排序,旨在使错误处理和恢复代码更容易
写。 注意 文本伊伦 在这些操作中不可用,但是
指针变量 yy 可用于授予代码访问任何用户定义的权限
解析器状态的成员(请参阅下面的“自定义解析器”)。 还要注意的是
EXP 始终是单个表达式; 为任何失败调用错误操作
序列,必须使用括号将序列分组为单个
表达。

rule = e1 e2 e3 ~{ error("e[12] ok; e3 has failed"); }
| ...

rule = (e1 e2 e3) ~{ error("e[123] 之一失败"); }
| ...

模式 ;
分号标点符号可以选择性地终止 模式.

%% 文本...
双百分比 '%%' 终止规则(和声明)部分
语法。 全部 文本 后面的 '%%' 被逐字复制到生成的 C 解析器代码
after 解析器实现代码。

$$ = 折扣值
子规则可以返回语义 折扣值 通过将一个动作分配给
伪变量'$$'。 所有语义值必须具有相同的类型(默认为
'int')。 可以通过在声明部分定义 YYSTYPE 来更改此类型。

识别码:姓名
从子规则返回的语义值(通过分配给 '$$') 姓名 is
识别码 并可在后续操作中参考。

下面的桌面计算器示例说明了“$$”和“:”的使用。

专家组 例: A 计算器


中的扩展 上面描述的允许有用的解析器和评估器(包括
声明、语法规则和支持 C 函数(例如“main”)要保留在
单个源文件。 为了说明这一点,我们展示了一个简单的桌面计算器,支持
四种常见的算术运算符和命名变量。 中间结果
算术计算将通过将它们返回为隐式堆栈来累积
来自子规则的语义值。

%{
#包括/* 打印 () */
#包括/* atoi() */
整数变量[26];
%}

stmt = - e:Expr EOL { printf("%d\n", e); }
| ( !EOL . )* EOL { printf("error\n"); }

Expr = i:ID 分配 s:Sum { $$ = vars[i] = s; }
| s:总和 { $$ = s; }

总和 = l:乘积
( 加上 r:Product { l += r; }
| 减去 r:产品 { l -= r; }
)* { $$ = l; }

产品 = l:值
( 时间 r:Value { l *= r; }
| 除法 r: 值 { l /= r; }
)* { $$ = l; }

值 = i:NUMBER { $$ = atoi(yytext); }
| i:ID !ASSIGN { $$ = vars[i]; }
| 打开 i:Expr 关闭 { $$ = i; }

NUMBER = < [0-9]+ > - { $$ = atoi(yytext); }
ID = < [az] > - { $$ = yytext[0] - 'a'; }
赋值 = '=' -
加号 = '+' -
减号 = '-' -
时间 = '*' -
除法 = '/' -
打开 = '(' -
关闭 = ')' -

- = [ \t]*
EOL = '\n' | '\r\n' | '\r' | ';'

%%

int main()
{
而 (yyparse())
;
0返回;
}

专家组 语法 用于 专家组 语法


语法为 语法如下所示。 这将说明和形式化
以上说明。

语法 = -
(声明|定义)+
预告片? 文件尾

声明 = '%{' < ( !'%}' . )* > RPERCENT

拖车 = '%%' < .* >

定义 = 标识符 EQUAL 表达式 SEMICOLON?

表达式 = 序列(BAR 序列)*

序列=错误+

错误 = 前缀(波浪号操作)?

前缀 = AND 动作
| (和 | 不是)? 后缀

后缀 = 主要 ( QUERY | STAR | PLUS )?

主要 = 标识符 COLON 标识符 !EQUAL
| 标识符 !EQUAL
| OPEN 表达式 CLOSE
| 文字
| 班级
| 点
| 行动
| 开始
| 结尾

标识符 = < [-a-zA-Z_][-a-zA-Z_0-9]* > -

文字 = ['] < ( !['] char )* > ['] -
| ["] < ( !["] 字符 )* > ["] -

class = '[' < ( !']' 范围 )* > ']' -

范围 = 字符 '-' 字符 | 字符

char = '\\' [abefnrtv'"\[\]\\]
| '\\' [0-3][0-7][0-7]
| '\\' [0-7][0-7]?
| !'\\' 。

动作 = '{' < 大括号 * > '}' -

大括号 = '{' 大括号 * '}'
| !'}' .

等于 = '=' -
冒号 = ':' -
分号 = ';' ——
酒吧 = '|' ——
AND = '&' -
不是 = '!' ——
查询 = '?' ——
星 = '*' -
加号 = '+' -
打开 = '(' -
关闭 = ')' -
点 = '.' ——
开始 = '<' -
END = '>' -
波浪号 = '~' -
RPERCENT = '%}' -

- = ( 空格 | 评论 )*
空格 = ' ' | '\t' | 行结束
注释 = '#' ( !end-of-line . )* 行尾
行尾 = '\r\n' | '\n' | '\r'
文件结尾 = !.

定制 解析器


可以在声明部分重新定义以下符号以修改生成的
解析器代码。

YYS型
语义值类型。 伪变量“$$”和标识符“绑定”到
带有冒号运算符 ':' 的规则结果都应视为已声明
要有这种类型。 默认值为“int”。

YY解析
解析器的主入口点的名称。 默认值为“yyparse”。

yyparsfrom
解析器的替代入口点的名称。 这个函数需要一个
参数:与从中搜索匹配项的规则相对应的函数
应该开始。 默认值为“yyparsefrom”。 请注意, yyparse() 定义为

int yyparse() { 返回 yyparsefrom(yy_foo); }

其中“foo”是语法中第一条规则的名称。

YY_输入(BUF, 导致, 最大尺寸)
该宏由解析器调用以获取更多输入文本。 BUF 指向
最多可容纳的内存区域 最大尺寸 人物。 宏应该复制
输入文本到 BUF 然后分配整数变量 导致 表示
复制的字符数。 如果没有更多的输入可用,宏应该
将 0 分配给 导致. 默认情况下,YY_INPUT 宏定义如下。

#define YY_INPUT(buf, 结果, max_size) \
{\
int yyc=getchar(); \
结果=(EOF == yyc)? 0 : (*(buf)= yyc, 1); \
}

请注意,如果定义了 YY_CTX_LOCAL(见下文),则附加第一个参数,
包含解析器上下文,传递给 YY_INPUT。

YY_调试
如果定义了这个符号,那么额外的代码将包含在解析器中
在解析器时将大量神秘信息打印到标准错误
在跑。

YY_开始
调用此宏来标记将可用的输入文本的开始
在作为“yytext”的行动中。 这对应于语法中出现的“<”。
这些被转换成预期成功的谓词。 默认的
定义

#define YY_BEGIN (yybegin=yypos, 1)

因此保存当前输入位置并返回 1 ('true') 作为结果
谓词。

YY_END 这个宏对应于语法中的“>”。 同样,它是一个谓词,所以
默认定义在“成功”之前保存输入位置。

#define YY_END (yyend=yypos, 1)

YY_解析(T)
这个宏将解析器入口点(yyparse 和 yyparsefrom)声明为类型
T. 默认定义

#define YY_PARSE(T) T

使 yyparse() 和 yyparsefrom() 具有全局可见性。 如果他们不应该
在其他源文件中外部可见,可以重新定义这个宏来声明
他们“静态”。

#define YY_PARSE(T) 静态 T

YY_CTX_LOCAL
如果在编译生成的解析器期间定义了这个符号,那么全局
解析器状态将保存在可以声明的“yycontext”类型的结构中
作为局部变量。 这允许解析器的多个实例共存并
是线程安全的。 解析函数 yy解析() 将被声明为期待第一个
'yycontext *' 类型的参数,一个包含全局变量的结构实例
解析器的状态。 这个实例必须被分配并初始化为零
客户端。 一个简单但完整的例子如下。

#包括

#定义YY_CTX_LOCAL

#include "the-generated-parser.peg.c"

int main()
{
yy上下文ctx;
memset(&ctx, 0, sizeof(yycontext));
而 (yyparse(&ctx));
0返回;
}

请注意,如果此符号未定义,则编译后的解析器将静态
分配其全局状态,并且既不是可重入的也不是线程安全的。 还要注意
解析器 yycontext 结构在第一次自动初始化
yy解析() 叫做; 这种结构 必须 因此被正确初始化为零
在第一次调用之前 yy解析()。

YY_CTX_MEMBERS
如果定义了 YY_CTX_LOCAL(见上文),则可以定义宏 YY_CTX_MEMBERS
扩展到客户想要的任何其他成员字段声明
包含在 'yycontext' 结构类型的声明中。 这些额外的
否则生成的解析器会忽略成员。 'yycontext' 的实例
与当前活动的解析器关联的在操作中可用
指针变量 yy.

YY_BUFFER_SIZE
文本缓冲区的初始大小,以字节为单位。 默认为 1024 和缓冲区
在解析过程中,只要需要满足需求,大小就会加倍。 一个应用程序
通常解析更长的字符串可以增加这个以避免不必要的
缓冲区重新分配。

YY_STACK_SIZE
变量和动作堆栈的初始大小。 默认为 128,即
在解析过程中需要时加倍以满足需求。 具有的应用程序
具有许多局部变量的深层调用堆栈,或者在一个
单个成功匹配,可以增加此项以避免不必要的缓冲区
重新分配。

YY_MALLOC(YY, 尺寸)
所有解析器相关存储的内存分配器。 参数是
当前 yycontext 结构和要分配的字节数。 默认的
定义是: malloc(尺寸)

YY_REALLOC(YY, PTR, 尺寸)
用于动态增长存储的内存重新分配器(例如文本缓冲区和
变量栈)。 参数是当前的yycontext结构,
先前分配的存储,以及该存储应分配的字节数
长大。 默认定义为:realloc(PTR, 尺寸)

YY_免费(YY, PTR)
内存释放器。 参数是当前的 yycontext 结构和
要释放的存储。 默认定义为:free(PTR)

YY发布
释放 yycontext 结构持有的所有资源的函数的名称。
默认值为“yyrelease”。

可以在操作中引用以下变量。

坦克 *yybuf
此变量指向解析器的输入缓冲区,用于存储具有
尚未匹配。

INT yypos
这是要匹配和消耗的下一个字符的偏移量(以 yybuf 为单位)。

坦克 *yy文本
由“<”和“>”分隔的最近匹配的文本存储在此变量中。

INT 伊伦
此变量表示 'yytext' 中的字符数。

yy上下文 *年
此变量指向与
当前活动的解析器。

希望释放与解析器关联的所有资源的程序可以使用
以下功能。

yyrelease(yycontext*yy)
返回与关联的所有解析器分配的存储 yy 到系统。 存储
将在下一次调用时重新分配 yy解析()。

请注意,yycontext 结构本身的存储空间永远不会被分配或回收
含蓄地。 应用程序必须在自动存储中分配这些结构,或使用
卡洛克()和 免费() 来明确地管理它们。 以下部分中的示例
演示了一种资源管理方法。

专家组 例: 扩展 解析器 CONTEXT


- yy 传递给动作的变量包含解​​析器的状态以及任何额外的
YY_CTX_MEMBERS 定义的字段。 这些字段可用于存储特定于应用程序的
特定调用的全局信息 yy解析(). 一个琐碎但完整的
示例如下,其中 yycontext 结构被扩展为 的数量
到目前为止在输入中看到的换行符(否则语法会消耗并忽略
整个输入)。 的来电者 yy解析() 用途 打印行数
读取的输入。

%{
#定义YY_CTX_LOCAL 1
#定义YY_CTX_MEMBERS \
整数计数
%}

Char = ('\n' | '\r\n' | '\r') { yy->count++ }
| 。

%%

#包括
#包括

int main()
{
/* 在自动存储中创建本地解析器上下文 */
yy上下文 yy;
/* 上下文*必须*在第一次使用前初始化为零*/
memset(&yy, 0, sizeof(yy));

而 (yyparse(&yy))
;
printf("%d 换行\n", yy.count);

/* 释放与上下文关联的所有资源 */
yyrelease(&yy);

0返回;
}

诊断


在将语法转换为解析器时警告以下情况。

句法 错误
输入语法在某些方面格式不正确。 错误消息将包括
即将匹配的文本(通常从实际位置备份大量
错误)和最近考虑的字符的行号(即
通常是问题的真实位置)。

排除 '富' 用过的 但是 而不去 定义
语法引用了一个名为“foo”的规则,但没有给出它的定义。
尝试使用生成的解析器可能会导致链接器出错
由于与缺失规则相关的未定义符号。

排除 '富' 定义 但是 而不去 用过的
语法定义了一个名为“foo”的规则,然后忽略了它。 关联的代码
规则包含在生成的解析器中,这将在所有其他方面
健康。

可能 无穷 递归 in 排除 '富'
从规则“foo”引出的语法中至少存在一条路径
回到(递归调用)相同的规则而不消耗任何输入。

左递归,尤其是在标准文档中发现的,通常是“直接的”并且
意味着琐碎的重复。

#(6.7.6)
直接抽象声明符 =
LPAREN 抽象声明符 RPAREN
| 直接抽象声明符? LBRACKET 赋值表达式? 支架
| 直接抽象声明符? LBRACKET STAR RBRACKET
| 直接抽象声明符? LPAREN 参数类型列表? 帕伦

通过转换以下模式的部分,可以轻松消除递归
递归成一个可重复的后缀。

#(6.7.6)
直接抽象声明符 =
直接抽象声明头?
直接抽象声明符尾*

直接抽象声明符头 =
LPAREN 抽象声明符 RPAREN

直接抽象声明符尾 =
LBRACKET 赋值表达式? 支架
| LBRACKET 星形 RBRACKET
| LPAREN 参数类型列表? 帕伦

洞穴


接受空输入的解析器将 时刻 成功。 考虑下面的例子,
第一次尝试编写基于 PEG 的解析器并不是典型的:

程序 = 表达式*
表达式 = "随便"
%%
int main(){
而 (yyparse())
puts("成功!");
0返回;
}

无论标准输入上提供什么(如果有)输入,该程序都会永远循环。 许多
修复是可能的,最简单的方法是坚持解析器总是消耗一些
非空输入。 将第一行更改为

程序=表达式+

实现这一点。 如果预计解析器会消耗整个输入,那么明确地
还强烈建议要求文件结尾:

程序 = 表达式 + !。

这是有效的,因为解析器只会无法匹配(“!”谓词)任何字符
("." 表达式) 当它尝试读取超出输入末尾的内容时。

使用 onworks.net 服务在线使用挂钩


免费服务器和工作站

下载 Windows 和 Linux 应用程序

Linux 命令

Ad




×
广告
❤️在这里购物、预订或购买——免费,有助于保持服务免费。