Pegjs: 导入/包含其他语法

创建于 2011-08-16  ·  32评论  ·  资料来源: pegjs/pegjs

能够通过从其他语法导入规则来定义语法可能非常有用。

几个想法;

<strong i="7">@include</strong> "expression.pegjs"
(or <strong i="8">@from</strong> "expression.pegjs" import expression)

tag_if
    = "if" space? expression space? { ... }

<strong i="9">@import</strong> "expression.pegjs" as expr

tag_if
    = "if" space? expr.expression space?

理想情况下,这不会在每个包含另一个 .pegjs 的 .pegjs 中重新生成整个代码; 也许我们必须将 parse() 的行为稍微修改为类似的东西;

根据您在options问题中所说的进行编辑;

parse(input, startRule)
->
parse(input, { startRule: "...", startPos : 9000 })

最后,如果startPos != 0 && result !== null ,我们不会检查我们是否去了input.length ,而是返回结果以及 endPos (真的不知道如何优雅地做到这一点- 也许只是修改选项参数?)。

它将允许语法的可重用性和代码的模块化,我认为这是编码的两个极其重要的方面。

feature

最有用的评论

@Dignifiedquire我目前正在考虑可能最好用一个例子来解释的语法和语义:

静态语言.pegjs

langauges  = "C" / "C++" / "Java" / "C#"

动态语言.pegjs

languages = "Ruby" / "Python" / "JavaScript"

所有语言.pegjs

static  = require("./static-languages")
dynamic = require("./dynamic-languages")

all = static.languages / dynamic.languages

每个.pegjs文件将隐式定义一个模块,该模块将导出它包含的所有规则。 <name> = require(<module>)构造将导入这样一个模块。 然后它的规则将在命名空间内可用。

这种设计故意类似于 Node.js。 使用命名空间将避免冲突。 我看到有两个缺点:

  1. <name> = require(<module>)构造与规则定义太相似,因此可能会造成混淆(人们可能认为只导入了一个规则)。
  2. .语法与.的当前含义冲突,即“任何字符”。 这可以通过丑陋的技巧来解决(例如,由空格包围的.表示“任何字符”,而由标识符包围的.将命名空间名称与规则名称分开)或通过更改语法(例如使用any关键字来表示“任何字符”)。

所有32条评论

我同意这是一个重要的特性,我想在 1.0 版本之后做这个。

(顺便说一句,我不喜欢你提出的类似 Python 的语法——类似于 Node.js 的require会更好,因为 JavaScript 程序员会更熟悉它。但这是一件可以解决的小事晚点出来。)

如果提供补丁,您会考虑将其包含在 1.0 之前吗?

我同意你对 python 语法的评论。

+1 此功能

@ceymard是的,我会考虑的。

+1 表示功能,+1 表示包含require样式

@dmajda @ceymard您是否已经对如何实现这一点有任何想法? 我在工作中需要这个项目,并将尝试实施。 问题是这是否只是将语法拆分为多个文件或继承之类的东西的补充,例如,可以继承所有规则,然后覆盖新语法中的特定规则。

@Dignifiedquire我目前正在考虑可能最好用一个例子来解释的语法和语义:

静态语言.pegjs

langauges  = "C" / "C++" / "Java" / "C#"

动态语言.pegjs

languages = "Ruby" / "Python" / "JavaScript"

所有语言.pegjs

static  = require("./static-languages")
dynamic = require("./dynamic-languages")

all = static.languages / dynamic.languages

每个.pegjs文件将隐式定义一个模块,该模块将导出它包含的所有规则。 <name> = require(<module>)构造将导入这样一个模块。 然后它的规则将在命名空间内可用。

这种设计故意类似于 Node.js。 使用命名空间将避免冲突。 我看到有两个缺点:

  1. <name> = require(<module>)构造与规则定义太相似,因此可能会造成混淆(人们可能认为只导入了一个规则)。
  2. .语法与.的当前含义冲突,即“任何字符”。 这可以通过丑陋的技巧来解决(例如,由空格包围的.表示“任何字符”,而由标识符包围的.将命名空间名称与规则名称分开)或通过更改语法(例如使用any关键字来表示“任何字符”)。

@dmajda由于规则定义已经采用了<identifier> = <expression>模式,为什么不做这样的事情:

static := require("./static-languages")
dynamic := require("./dynamic-languages")

all = static::languages / dynamic::languages

::在 PEG.js 中我所知道的任何地方都没有使用,这使得区分命名空间和其他东西变得容易。 我不确定:=它带来了这一点,但对 Javascript 来说感觉很陌生..

此外,如果您想使用命名空间,您认为每个文件应该只有一个命名空间,还是应该有一种方法可以在一个文件中创建多个命名空间,如下所示:

static := {
  languages  = "C" / "C++" / "Java" / "C#"
}

dynamic := {
  languages = "Ruby" / "Python" / "JavaScript"
}

我不太喜欢:::= ,它们在 javaScript/CoffeeScript 世界中看起来很陌生。

我还想保持简单,仅通过需要文件来隐式定义命名空间。 我不认为需要更复杂的东西。

简单地说:

<strong i="6">@require</strong> foo = "./foo"

bar = foo:languages

冒号是一种折衷方案,但它们在很多地方都被用来分隔命名空间:C++、C#、XML 等。

对于许多函数式程序员来说, :将始终与cons相关联。 我建议远离那个运营商。 ::对我来说看起来不错。 这不是用于 C++ 命名空间吗? 我也不相信.是一个糟糕的选择。

.不能在没有重大更改的情况下使用。 它在语言中会含糊不清。

::在 C++ 中用于命名空间,在 C# 中用于命名空间前缀(例如global::System )。

我正在考虑有关此主题的快速解决方法 - 仅解决简单的继承问题 - 将 pegjs 文件粘合在一起,同时将所有内容都命名为名称空间。

这可能会使语法过于冗长,并且涉及构建步骤 - 但从好的方面来看,它会迫使您拥有细粒度的 DRY&OTW 语法

关于标记,并不是说这适合这个线程,而只是一个考虑的选项,我想要一个简单的__

languages = static__languages / dynamic__languages
<static-languages.pegjs>
<dynamic-languages.pegjs>
/* alternative */
languages = STATIC__languages / DYNAMIC__languages

@andreineculau我基本上已经通过构建步骤完成了此操作,因此,如果您和其他人只是想从具有依赖关系树的语法(其中生成一个实现组合语法的单个解析器)中生成有用的解析器,我可能会清理我所拥有的并发布它,以便讨论可以重新关注如何以更持久的方式处理这个问题。

另一件事:主要通过设计语法语法的扩展来解决这个问题错过了一些重要的东西,那就是我们都渴望从其他语法中提取规则(另一个是清晰)的主要原因之一是需要编写共享的解析器很多逻辑。 因此,虽然生成的解析器可能永远不会在解析时有意义地重新组合,但语法树生成解析器树而不是单个解析器似乎很重要。 最重要的是当一组解析器将成为 Web UI 的一部分时,但避免生成代码中不必要的膨胀通常不会有什么坏处。

@odonnell +1 释放任何东西 - 无论你是否有时间清理它

和 +1 进行澄清。 这应该被视为一种快速的解决方法,而不是长期的正确解决方案。

@odonnell我对它的https://github.com/andreineculau/core-pegjs在线 - 如果你有更好的东西,请戳我。

+1 此功能

:+1:

:+1:

:+1:

我去为 PEG.js 编写了一个插件/扩展来进行导入: https :

+1为此。

我在 #308 中以通用方式实现了这一点:包含语法只是实现分解规则的一种方式。

很棒的功能:+1:

期待看到它的发布。

:+1:

惊人的! :+1:

@dmajda我参加这个聚会迟到了,但我想知道我们多久需要从另一个库中导入许多规则。 我希望能够将诸如UrlEmail导入我的组合语法中,但我不在乎Url也可能有诸如HierarchicalPartAsciiLetter 。 你认为像 Node 的命名导出这样的东西会是一个可行的前进方式,保留命名空间的好处但允许直接命名导入吗?

import { SchemalessUrl, Url } from "./Urls.pegjs"

Token
  = PhoneNumber
  / Url
  / SchemalessUrl

当我尝试探索编写其他可组合的语法时,命名空间对我来说一直是一个问题。 我现在被困在文件中的文件中,并以 PHP 函数在引入适当的命名空间之前的命名方式命名: UrlIpHostHtmlQuotedString等......

@dmajda @futagoza

这个问题有什么进展吗? 还是现在在#473 上的主要讨论?
我的语法文件增长得非常快:(
把它分成几个会很好

我不介意能够在文件之间拆分语法,仅用于组织和组合。 这将使它们更易于测试和重用,并提供一种动态交换语法的方法,也许吧? 只是一些想法。

我用作基础的 JavaScript 示例超过 1,300 行。 花了一段时间来了解所有内容的位置,并跳来跳去并编辑不同的部分。

@mikeaustin我将此功能视为某种 Node.JS required

猫bash.pegjs
{
const _ = require("空格");
const LB = require("line_break");
const CodeBlock = require("code_block");
const BoolExpr = require("boolean_expression");
}
...
IfStatement = "if" _ "[" BoolExpr "]" _ ";" _“然后”LB? 代码块“fi”

我同意,拆分语法并使它们模块化是一个很棒的功能,但是处理这些情况将是一个问题:
1- 依赖于在主语法代码中定义的全局变量的子语法?
2-重复的变量和语法名称?

IMO,一种临时方便的方法是为 PEG.js(独立于 PEG.js)创建一个新的插件,它定义了一个用于导入的关键字(例如 @load(anotherGrammarFileLocation) )关键字不应是 javacsript/peg.js 语法的一部分,
构建 reg-exp 或 peg 语法来检测该关键字并将其替换为“anotherGrammarFile Location”内容,并将替换后的代码发送到 PEG.js

例子:

整数.pegjs

integers=[0-9]* {return parseInt(text())}

main.pegjs
arrayOfInteger="["(integers ",")* integers"]"
@load("integers.pegjs")

注意使用这种方法,如果有人没有定义开始语法,并且在“arrayOfInteger”之前放置@load ,peg.js 将假设第一个语法作为开始(整数语法)

处理这个问题的一种方法是,使用相同的文件名和开始语法名称,让新插件手动配置开始属性作为文件名,或者替换文件末尾的所有内容。

用户应对任何复制负责。

我只想强调这个问题主要是一个优化请求,因为可组合性/模块化是你可以自己实现的,尤其是当你控制语法的全部范围时。

如果您对 1k 行长的语法不满意,请将其拆分,然后按照您认为合适的方式将其连接回去,然后再将其注入 pegjs。

此页面是否有帮助?
0 / 5 - 0 等级