他の文法からルールをインポートして文法を定義できると非常に便利です。
いくつかのアイデア;
<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でコード全体を再生成しないでしょう。 parse()の動作を少し変更する必要があるかもしれません;
options
問題であなたが言っていたことに従って編集します;
parse(input, startRule)
->
parse(input, { startRule: "...", startPos : 9000 })
そして最後に、 startPos != 0 && result !== null
場合、 input.length
まで行ったかどうかを確認せず、代わりに結果とendPosを返します(これをエレガントに行う方法がわかりません)。 -オプションパラメータを変更するだけかもしれませんか?)。
これにより、文法の再利用性とコードのモジュール化が可能になります。これは、一般的なコーディングの2つの非常に重要な側面だと思います。
これが重要な機能であることに同意します。バージョン1.0以降でこれを実行したいと思います。
(ところで、私はあなたが提案するPythonのような構文が好きではありません— Node.jsのrequire
似たものは、JavaScriptプログラマーに馴染みがあるので、より良いでしょう。後で出ます。)
パッチが提供されている場合、1.0より前に含めることを検討しますか?
Python構文についてのあなたの意見に同意します。
この機能の+1
@ceymardはい、私はそれを検討します。
機能の場合は+1、 require
スタイルの包含の場合は+1
@dmajda @ceymardこれを実装する方法について、すでに何か考えがありますか? 作業中のプロジェクトにこれが必要で、実装しようとします。 問題は、これが文法を複数のファイルに分割するための単なる追加、または継承のようなものである必要があるため、たとえばすべてのルールを継承してから、新しい文法の特定のルールを上書きすることができます。
@Dignifiedquire私は現在、例で最もよく説明できる構文とセマンティクスについて考えています。
static-languages.pegjs
langauges = "C" / "C++" / "Java" / "C#"
dynamic-languages.pegjs
languages = "Ruby" / "Python" / "JavaScript"
all-languages.pegjs
static = require("./static-languages")
dynamic = require("./dynamic-languages")
all = static.languages / dynamic.languages
各.pegjs
ファイルは、含まれるすべてのルールをエクスポートするモジュールを暗黙的に定義します。 <name> = require(<module>)
構造は、そのようなモジュールをインポートします。 そのルールは、名前空間内で使用できるようになります。
この設計は、意図的にNode.jsに似ています。 名前空間を使用すると、競合を回避できます。 私が見る2つの欠点があります:
<name> = require(<module>)
構造はルール定義に類似しすぎているため、混乱する可能性があります(1つのルールだけがインポートされていると思われるかもしれません)。.
構文は、「任意の文字」である.
の現在の意味と競合します。 これは、醜いハック(たとえば、空白で囲まれた.
は「任意の文字」を意味し、識別子で囲まれた.
は名前空間名とルール名を区切る)または構文を変更することで解決できます(たとえば、 any
キーワードを使用して「任意の文字」を表します)。@dmajda <identifier> = <expression>
パターンはすでにルール定義によって採用されているので、次のようなことをしてみませんか。
static := require("./static-languages")
dynamic := require("./dynamic-languages")
all = static::languages / dynamic::languages
::
は、私がPEG.jsで知っている場所では使用されておらず、名前空間と他のものを簡単に区別できます。 :=
についてはよくわかりませんが、Javascriptには非常に異質な感じがします。
また、名前空間を使用する場合は、ファイルごとに1つの名前空間のみを使用する必要があると思いますか、それとも次のように1つのファイルに複数の名前空間を作成する方法があると思いますか。
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私は基本的にすでにビルドステップでこれを行っているので、依存関係ツリー(結合された文法を実装する単一のパーサーが生成される)を使用して、文法から有用なパーサーを生成するための何かを探している場合は、私が持っているものをきれいにしてリリースし、議論がより永続的な方法でこれに対処する方法に再び焦点を合わせることができるようにします。
もう1つのこと:主に文法構文の拡張機能を設計することによってこれにアプローチすることは、重要なことを見逃します。これは、他の文法からルールを引き込むことに悩む主な理由の1つであり、共有するパーサーを作成する必要があることです。多くのロジック。 したがって、生成されたパーサーが解析時に意味のある再構成可能になることは決してないかもしれませんが、文法のツリーが1つのモノリシックパーサーではなくパーサーのツリーを生成することが重要であるように思われます。 パーサーのセットがWebUIの一部になる場合が最も重要ですが、生成されたコードの不要な肥大化を回避することは一般的に害にはなりません。
@ odonnell + 1何かをリリースするために-あなたがそれをきれいにする時間があるかどうかに関係なく
明確化のために+1。 これは、長期的な適切な解決策ではなく、迅速な回避策として扱う必要があります。
@odonnell私の見解は、 https://github.com/andreineculau/core-pegjsでオンラインになっています。もっと良いものがあれば、私を突いてください。
この機能の+1
:+1:
:+1:
:+1:
インポートを行うPEG.jsのプラグイン/拡張機能を作成しました: https :
これも+1。
私はこれを#308で一般的な方法で実装します。文法を含めることは、分解ルールを実装する1つの方法にすぎません。
素晴らしい機能:+1:
それがリリースされるのを楽しみにしています。
:+1:
素晴らしい! :+1:
@dmajdaこのパーティーに遅れますが、別のライブラリから多くのルールをインポートする必要がある頻度はどれくらいかと思います。 Url
やEmail
ようなものを作成した文法にインポートできるようにしたいと思いますが、 Url
もHierarchicalPart
ようなものが含まれていてもかまいません。 AsciiLetter
。 ノードの名前付きエクスポートのようなものは、名前空間の利点を維持しながら、直接名前空間のインポートを許可する、実行可能な方法だと思いますか?
import { SchemalessUrl, Url } from "./Urls.pegjs"
Token
= PhoneNumber
/ Url
/ SchemalessUrl
名前空間は、他の方法で構成可能な文法を書くことを試みている私にとって問題でした。 私は今、ファイルにファイルを含め、適切な名前空間を導入する前にPHP関数に名前を付けた方法で名前を付けています: UrlIpHost
、 HtmlQuotedString
など…
@dmajda @futagoza
この問題に関する進展はありますか? または現在#473で生きている主要な議論?
私の文法ファイルは非常に速く成長しています:(
それをいくつかに分割するといいでしょう
単に整理と構成のために、ファイル間で文法を分割できることを気にしません。 文法を動的に交換する方法を提供するだけでなく、テストと再利用を容易にするでしょう。 いくつかの考え。
ベースとして使用したJavaScriptの例は1,300行を超えています。 すべてがどこにあるかを学び、飛び回ってさまざまなセクションを編集するのに少し時間がかかりました。
@mikeaustin私はこの機能をある種のNode.JSとして見ていますrequired
:
cat bash.pegjs
{{
const _ = require( "whitespace");
const LB = require( "line_break");
const CodeBlock = require( "code_block");
const BoolExpr = require( "boolean_expression");
}
..。
IfStatement = "if" _ "[" BoolExpr "]" _ ";" _「それから」LB? CodeBlock "fi"
私は同意します。文法を分割してモジュール化することは素晴らしい機能ですが、これらのケースを処理することは問題になります。
1-メインの文法コードで定義されたグローバル変数に依存するサブ文法?
2-変数と文法名が重複していますか?
IMO、時間的に便利なアプローチは、インポートするキーワードを定義するPEG.js(PEG.jsから独立)の新しいアドオンを作成することです(たとえば、@ load(anotherGrammarFileLocation))キーワードはjavacsript /peg.js文法の一部であってはなりません。
reg-expまたはpeg文法を作成してそのキーワードを検出し、それを「anotherGrammarFile Location」コンテンツに置き換えて、置き換えられたコードをPEG.jsに送信します。
integers=[0-9]* {return parseInt(text())}
arrayOfInteger="["(integers ",")* integers"]"
@load("integers.pegjs")
誰かが開始文法を定義せず、「arrayOfInteger」の前に@loadを配置した場合、このメソッドを使用することに注意してください。peg.jsは、最初の文法を開始(整数文法)と見なします。
これを処理する1つのアプローチは、同じ名前のファイル名と開始文法を使用し、新しい広告に開始属性をファイル名として手動で構成させるか、ファイルの最後にあるすべてのコンテンツを置き換えることです。
構成可能性/モジュール性は、特に文法の全範囲を制御する場合に自分で達成できるものであるため、この問題は主に最適化の要求であることを強調したいと思います。
1k行の長さの文法に慣れていない場合は、それを分割し、適切と思われるように連結してから、pegjsにポンプで送ります。
最も参考になるコメント
@Dignifiedquire私は現在、例で最もよく説明できる構文とセマンティクスについて考えています。
static-languages.pegjs
dynamic-languages.pegjs
all-languages.pegjs
各
.pegjs
ファイルは、含まれるすべてのルールをエクスポートするモジュールを暗黙的に定義します。<name> = require(<module>)
構造は、そのようなモジュールをインポートします。 そのルールは、名前空間内で使用できるようになります。この設計は、意図的にNode.jsに似ています。 名前空間を使用すると、競合を回避できます。 私が見る2つの欠点があります:
<name> = require(<module>)
構造はルール定義に類似しすぎているため、混乱する可能性があります(1つのルールだけがインポートされていると思われるかもしれません)。.
構文は、「任意の文字」である.
の現在の意味と競合します。 これは、醜いハック(たとえば、空白で囲まれた.
は「任意の文字」を意味し、識別子で囲まれた.
は名前空間名とルール名を区切る)または構文を変更することで解決できます(たとえば、any
キーワードを使用して「任意の文字」を表します)。