4

私は単純な html テンプレート エンジンを (楽しみのために) 書こうとしていましたが、このような構造を解析したいと考えています。

A. 通常の行は HTML です

B.行が次で始まる場合、$それをJavaコード行として表示します

$ if (isSuper) {
    <span>Are you wearing red underwear?</span>
$ }

C.${}複数の行を折り返す場合、その中のすべてのコードは Java コードである必要があります。

D.行がで始まる場合、行$includeに何らかのトリックを実行します(別のテンプレートを呼び出します)

$include anotherTemplate(id, name)

これは の新しいインスタンスを作成し、そのメソッドanotherTemplateを呼び出しますrender()

$includeE.など、$def以外の「コマンド」がもっとあるでしょう$val

これをパーサーコンビネーターでどのように表現できますか? 事実上、それは条件付きフォークです

1. と 2. の場合、次のような結果が得られました。

'$' ~> ( '{' ~> upto('}') <~ '}' |  not('{') <~ newline )

Scalate Scamel パーサーから借用した場所upto(読み始めたばかりで、よくわかりません)

コード行とブロックnot('{')を区別していました。しかし、これは面倒で、他の「コマンド」には拡張できません。$....${...}

どうすればこれを行うことができますか?

4

1 に答える 1

6

の使用notは冗長です。メソッドは順序付き選択|を実装します。2 番目の処理は、最初の処理が失敗した場合にのみ試行されます。これでうまくいくはずです:

def directive: Parser[Directive] =
  ( '$' ~>
    ( '{' ~> javaStuff <~ '}'
    | "include" ~> includeDirective
    | "def"     ~> defDirective
    | "val"     ~> valDirective
    | javaDirective
    )
  | htmlDirective
  )

def templateFile: Parser[List[Directive]] = (directive <~ '\n').*

解析を高速化し、エラー メッセージを改善するには、パーサーをできるだけ頻繁に「コミット」する必要があります。これは、 を使用したときに取得しようとしていたものだと思いますnot('{')

現在、上記のパーサーが a に'$'続いて a'{'を認識し、次に を認識しないjavaStuff場合、バックトラックして残りの 4 つの -alternatives のそれぞれを順番に ( 、 、 、および 最後に ) 検討し'$'includebeforedefvalバックjavaDirectiveトラックして'$'を試行htmlDirectiveします。不可解なエラー メッセージで失敗する前に。しかし、 が表示された場合'{'、他の選択肢はどれも成功しない可能性があることがわかります。同様に、 で始まる行は .'$'になることはありませんhtmlDirective

私たち'{'は、後戻りのないポイントであることを望んでいます。after '{'-parser が失敗してバックトラックしたい場合は、そのトラックでそれを停止し、バックトラックの原因となった失敗をエラーとしてユーザーに直接伝達する必要があります。

これを行う方法は、commit. この関数/コンビネータは、パーサーに適用されると、 の出てくるのをp見て、元が(バックトラック信号) だった場合は (give-up-entirely 信号) に変更し、それ以外の場合は変更しません。を適切に使用すると、パーサーは次のようになります。ParseResultpErrorFailurecommitdirective

def directive: Parser[Directive] =
  ( '$' ~> commit( '{' ~> commit(javaStuff <~ '}')
                 | "include" ~> commit(includeDirective)
                 | "def"     ~> commit(defDirective)
                 | "val"     ~> commit(valDirective
                 | javaDirective
                 )
  | htmlDirective
  )

解析ライブラリの使い方を初めて学んだときParsers . これにより、このようなもののいくつかがもう少し明確になります。

(その他のヒント: appendandの目的は、一連の parse-alternatives からどの失敗をユーザーに伝播するかを決定することです。今のところ、それらは無視してください。また、 / /が完了するまでは、ParseResult#appendあまり心配しません。もう少し練習を積んだ; その時が来たら、Daniel Sobral の説明を読んでください. 最後に、私は を使用する必要はありませんでした.>>flatMapinto|||

お役に立てれば。

于 2012-05-14T21:50:15.583 に答える