Pegjs: Terapkan aturan parametrizable

Dibuat pada 25 Agu 2011  ·  29Komentar  ·  Sumber: pegjs/pegjs

Akan sangat bagus untuk dapat membuat parametrize aturan dengan variabel ;

   = '\"' parse_contents '\"' ->
   / '\'' parse_contents('\'') '\'' ->
   / '+' parse_contents('+') '+' -> /* sure why not :) */

parse_contents(terminator='\"')
    = ('\\' terminator / !terminator .)+ -> return stuff
feature

Komentar yang paling membantu

Terima kasih atas waktu Anda dalam penjelasan

Saya mungkin akan memiliki pertanyaan lain untuk Anda dalam sepuluh tahun. Selamat menjalani 2020-an

Semua 29 komentar

Apakah Anda memiliki kasus penggunaan khusus di mana ini akan menghemat banyak pekerjaan atau membuat sesuatu yang saat ini tidak mungkin menjadi mungkin?

Itu membuat parsing level indentasi lebih mudah dengan memanggil aturan yang levelnya dilewati sebagai argumen.

Juga, dalam logika KERING murni, ketika melakukan hal-hal seperti "hal-hal yang dibatasi oleh karakter ini dengan urutan pelarian seperti itu", lebih baik memanggil sesuatu seperti delimited('\'', '\\') daripada hanya melakukan aturan (dan tindakannya!) tiga kali .

Aku seharusnya lebih jelas. Dengan "spesifik" saya mencari sesuatu seperti "Saya sedang mengerjakan tata bahasa bahasa X dan ada 5 aturan di sana yang bisa digabungkan menjadi satu, ini dia:" Artinya, saya ingin melihat dunia nyata use case dan kode dunia nyata. Dari situ saya dapat menilai lebih baik dalam hal apa fitur ini akan berguna dan untuk berapa banyak orang.

Tolong jangan ambil ini karena saya menentang fitur ini. Saya biasanya tidak ingin mengimplementasikan fitur yang hanya berguna untuk sebagian kecil bahasa atau pengembang karena kerumitan dan biaya implementasi. Dan dalam hal ini biayanya relatif tinggi.

Hanya menulis parser untuk javascript, saya dapat memiliki string = delimited_by('\'') / delimited_by('\"') dan kemudian regexp = delimited_by('/') .

Akhir-akhir ini, saya telah menulis parser untuk bahasa saya sendiri. Saya memiliki sesuatu seperti ini dalam kerangka PEG yang saya tulis untuk python :

LeftAssociative(op, subrule): l:subrule rest:(op subrule)* -> a_recursive_function_that_reverses_rest_and_makes_bintrees(l, op, subrule)

Dan saya kemudian dapat menulis:

...
exp_mult = LeftAssociative(/[\*\/%]/, exp_paren)
exp_add = LeftAssociative(/[\+-]/, exp_mult)

Karena saya memiliki tingkat prioritas sebanyak dalam C++ (semua operatornya ditambah beberapa lagi), saya akan membiarkan Anda membayangkan betapa bergunanya in. Saya belum selesai dalam ekspresi parsing, namun saya sudah menggunakannya 12 kali.

Ini akan sangat bagus jika dikombinasikan dengan fitur 'impor'

require(CommaSeparatedList.pegjs)
require(StringLiteral.pegjs)
require(IntegerLiteral.pegjs)

...

Function
 = name:FuncName "(" args:CommaSeparatedList(Literal)  ")" 

Hash
= "{"   props:CommaSeparatedList(Prop)   "}"

Prop
= Key ":" Literal

Literal =
  StringLiteral / IntegerLiteral

(Ini sedikit lebih rumit daripada permintaan OP, tetapi tampaknya terlalu dekat untuk membenarkan utasnya sendiri.)

Saya sedang membangun parser Skema R5RS dengan bantuan PEG.js. Semuanya cerah kecuali untuk quasiquotations, yang membutuhkan penguraian kontekstual. Akan berguna untuk dapat membuat parameter aturan demi pembuatan aturan on-the-fly dari template, menghindari sejumlah besar pasca-pemrosesan yang canggung. Misalnya, tata bahasa quasiquotation yang disederhanakan mungkin terlihat seperti:

    quasiquotation = qq[1]
    qq[n] = "`" qq_template[n]
    qq_template[0] = expression
    qq_template[n] = simple_datum / list_qq_template[n] / unquotation[n]
    list_qq_template[n] = "(" qq_template[n]* ")" / qq[n+1]
    unquotation[n] = "," qq_template[n-1]

Saya tertarik untuk berkontribusi dalam pengembangan fitur ini jika ada minat untuk menambahkannya ke alat ini.

Alasan utama untuk melakukan ini adalah untuk mendukung tata bahasa yang sensitif terhadap konteks, yang jika saya tidak salah, bahasa yang paling populer adalah (saya tahu pasti bahwa C dan python memiliki hal-hal khusus konteks). Menurut Trevor Jim, Haskell juga tidak bebas konteks, dan menegaskan bahwa sebagian besar bahasa tidak:

http://trevorjim.com/haskell-is-not-context-free/
http://trevorjim.com/how-to-prove-that-a-programming-language-is-context-free/

Menggunakan status eksternal dalam parser yang dapat mundur (seperti kaleng PEG) berbahaya, dan dapat menghasilkan masalah seperti yang dapat dilihat di parser ini:

{   var countCs = 0;
}

start = ((x/y) ws*)* { return countCs }

x = "ab" c "d"
y = "a" bc "e"

c = "c" { countCs++; }
bc = "bc" { countCs++; }

ws = " " / "\n"

Di atas mengembalikan 2 alih-alih jawaban yang benar dari 1. Masalah seperti ini bisa sulit untuk dijelaskan, dapat membuat bug yang sulit ditemukan, dan ketika ditemukan bisa sangat sulit untuk diatasi sama sekali, apalagi melakukannya dengan elegan . Tidak jelas bagi saya bagaimana melakukan ini tanpa melakukan pasca-pemrosesan data yang dikembalikan oleh PEG. Jika entah bagaimana parser Anda sendiri membutuhkan hitungan, itu hanya kurang beruntung.

Saat ini, (berbahaya) menggunakan keadaan eksternal adalah satu-satunya cara untuk mengurai tata bahasa yang sensitif terhadap konteks. Dengan aturan berparameter, parser dapat menguraikan ini tanpa mempertaruhkan status tidak valid:

start = countCs:((x<0>/y<0>) ws*)* { return countCs.reduce(function(a,b){return a+b[0];}, 0); }

x<count> = "ab" theCount:c<count> "d" { return theCount; }
y<count> = "a" theCount:bc<count> "e" { return theCount; }

c<count> = "c" { return count++; }
bc<count> = "bc" { return count++; }

ws = " " / "\n"

David, Anda meminta situasi nyata, dan sintaks lekukan spasi putih python jelas merupakan contoh di sini. Saya ingin melakukan sintaks indentasi spasi putih serupa di Lima (bahasa pemrograman yang saya buat dengan PEG). Tetapi saya tidak ingin menerapkan hal seperti itu ketika saya secara tidak sengaja dapat membuat status tidak valid yang menghancurkan segalanya. Saya dapat memberi nama konstruk penguraian apa pun yang memerlukan konteks, seperti x* y C (apakah x kali y atau y didefinisikan sebagai penunjuk ke nilai yang diketik x?).

Perhatikan bahwa agar tata bahasa yang peka terhadap konteks dapat diuraikan, seseorang perlu meneruskan informasi yang dikembalikan dari subekspresi yang sudah dicocokkan dengan aturan berparameter - jika tidak, parser tidak dapat benar-benar menggunakan konteks apa pun. Berikut adalah contoh nyata dari tipe string yang saya pertimbangkan untuk Lima yang hanya berfungsi jika penguraian berparameter tersedia dan dapat mengakses (sebagai variabel) label dari ekspresi yang sebelumnya cocok:

literalStringWithExplicitLength = "string[" n:number ":" characters<n> "]"
number = n:[0-9]* {return parseInt(n.join(''));}
characters<n> = c:. { // base case
  if(n>0) return null; // don't match base case unless n is 0
  else return c;
}
/ c:. cs:characters<n-1> {
  ret c+cs
}

Ini akan dapat mengurai string seperti string[10:abcdefghij] . Anda tidak dapat melakukannya dengan PEG.js murni yang bagus sebagaimana adanya. Anda telah melakukan sesuatu yang buruk seperti:

{ var literalStringLengthLeft=undefined;
}
literalStringWithExplicitLength = "string[" n:specialNumber ":" characters "]"
specialNumber = n:number {
  literalStringLengthLeft = n;
  return n;
}
number = n:[0-9]* {return parseInt(n.join(''));}
characters = c:character cs:characters? {
  return c + cs
}
character = c:. {
  if(literalStringLengthLeft > 0) {
    literalStringLengthLeft--;
    return c;
  } else {
    literalStringLengthLeft = undefined;
    return null; // doesn't match
  }
}

Banyak protokol memiliki kebutuhan penguraian semacam ini - misalnya, paket IPv4 memiliki bidang yang menjelaskan panjang totalnya. Anda memerlukan konteks itu untuk mengurai sisa paket dengan benar. Hal yang sama berlaku untuk IPv6, UDP, dan mungkin protokol berbasis paket lainnya. Sebagian besar protokol yang menggunakan TCP juga akan membutuhkan sesuatu seperti ini, karena seseorang harus dapat mengirimkan beberapa objek yang benar-benar terpisah menggunakan aliran karakter konseptual yang sama.

Bagaimanapun, saya harap saya telah memberikan beberapa contoh dan alasan yang baik mengapa saya pikir ini bukan hanya fitur yang bagus, tidak hanya fitur yang kuat, tetapi benar-benar fitur penting yang banyak parser hilang (termasuk, untuk saat ini, PEG.js ).

Pegasus (proyek yang berbagi sebagian besar sintaksnya dengan peg.js) memecahkan ini dengan memiliki ekspresi #STATE{} yang diberi kemampuan untuk mengubah kamus keadaan. Kamus keadaan ini di-backtrack ketika aturan di-backtrack. Ini memungkinkannya untuk mendukung penguraian spasi putih yang signifikan (lihat entri wiki tentang Spasi Signifikan untuk detailnya).

Juga, dengan melacak kembali status bersama dengan kursor parsing, memoisasi dapat dicapai untuk aturan stateful juga.

Peg.js dapat dengan mudah melakukan hal yang sama, saya rasa.

Bagaimana cara Pegasus mengelola status mundur saat aturan mundur? Saya dapat membayangkan bahwa Anda dapat menyimpan snapshot dari seluruh status program yang berubah, dan mengembalikannya, tetapi itu akan mahal. Saya bisa membayangkan menyimpan snapshot hanya dari variabel yang berubah, tetapi itu akan mengharuskan pengguna untuk menentukannya yang akan menambah kompleksitas untuk membuat parser, atau akan mengharuskan parser untuk entah bagaimana mencari tahu semua keadaan berubah dalam beberapa bit kode. Tak satu pun dari ini terdengar ideal, jadi bagaimana Pegasus melakukannya?

Secara teoritis, pengurai dapat menghindari tindakan yang dieksekusi secara tidak valid jika tindakan A. diantrekan dalam penutupan dan hanya dieksekusi setelah pengurai selesai sepenuhnya, dan B. karena tindakan tersebut dijalankan setelah pengurai selesai, mereka tidak dapat membatalkan kecocokan aturan. Mungkin skema itu akan lebih optimal daripada keadaan mundur yang dilakukan di pegasus?

Juga, memperbaiki masalah status tidak valid memang sangat bagus, tetapi itu tidak menyelesaikan masalah ekspresibilitas yang saya kemukakan terkait dengan string literal seperti string[10:abcdefghij], tapi saya pasti tertarik dengan cara kerjanya

Itu tidak mundur dari status keseluruhan program. Itu memelihara kamus negara yang tidak dapat diubah. Ini menyimpan kamus keadaan saat ini bersama dengan kursor dan setiap kali kursor mundur, kamus keadaan akan mundur dengannya. Kamus tidak dapat diubah di mana pun di luar tindakan #STATE{} , dan DISALIN sesaat sebelum setiap status berubah.

Ada penalti kinerja kecil untuk menetapkan variabel tambahan setiap kali Anda memajukan kursor, tetapi ini jauh lebih besar daripada kemampuan untuk memoize aturan stateful. Selain itu, ini tidak menyebabkan banyak alokasi memori, karena kamus status yang tidak dapat diubah memungkinkannya untuk dibagikan hingga dimutasi. Misalnya, jika Anda tidak memiliki status di parser Anda, hanya akan ada satu alokasi: satu kamus status (kosong).

JavaScript tidak (setahu saya) memiliki kemampuan untuk membuat objek tidak berubah, tetapi itu sebagian besar merupakan fitur keamanan. Peg.js hanya perlu menyalin kamus keadaan sebelum memproses setiap blok kode #STATE{} dan melacak kembali setiap kali kursor dilacak kembali.

Oh ok, jadi pengguna pada dasarnya harus menentukan status apa yang mereka ubah. Itu cukup keren. Tapi saya masih tidak berpikir itu benar-benar mencakup manfaat yang sama dengan parameterisasi. Kedengarannya seperti itu mungkin berguna dalam dirinya sendiri untuk hal-hal lain.

Saya baru saja menulis garpu yang memasok lingkungan, dapat diakses menggunakan variabel env : https://github.com/tebbi/pegjs
Ini sama dengan objek #STATE{} yang disarankan di atas.
Ini adalah peretasan cepat, menggunakan variabel global (paket-), yang dipulihkan setiap kali fungsi penguraian dibiarkan. Penyalinan env dilakukan dengan Object.create().

Berikut adalah contoh tata bahasa yang menggunakannya untuk mengurai blok yang ditentukan spasi ala Python:

{
  env.indLevel = -1
}

block =
  empty
  ind:ws* &{
    if (ind.length <= env.indLevel) return false;
    env.indLevel = ind.length;
    return true;
  }
  first:statement? rest:indStatement*
  {
    if (first) rest.unshift(first);
    return rest;
  }

indStatement =
  "\n" empty ind:ws* &{ return env.indLevel === ind.length; }
  stm:statement
  {return stm; }

statement =
    id:identifier ws* ":" ws* "\n"
    bl:block { return [id, bl]; }
  / identifier

identifier = s:[a-z]* { return s.join(""); }

empty = (ws* "\n")*

ws = [ \t\r]

Berikut adalah contoh input untuk parser yang dihasilkan:

b:
   c
   d:
       e
   f
g

Saya mendapat kesan bahwa PEG.js tidak mendukung parameter apa pun pada aturan - yang mengejutkan. Fitur ini sangat penting bagi saya.

Yang saya butuhkan lebih sederhana daripada permintaan OP - OP ingin memodifikasi tata bahasa itu sendiri tergantung pada parameternya, tetapi setidaknya saya hanya perlu memasukkan bilangan bulat ke dalam aturan. Pada dasarnya saya ingin menerjemahkan aturan LLLPG yang terlihat seperti ini (di mana PrefixExpr adalah ekspresi prioritas tinggi seperti ekspresi awalan seperti -x , atau pengenal...):

@[LL(1)]
rule Expr(context::Precedence)::LNode @{
    {prec::Precedence;}
    e:PrefixExpr(context)
    greedy
    (   // Infix operator
        &{context.CanParse(prec=InfixPrecedenceOf(LT($LI)))}
        t:(NormalOp|BQString|Dot|Assignment)
        rhs:Expr(prec)
        { ... }
    |   // Method_calls(with arguments), indexers[with indexes], generic!arguments
        &{context.CanParse(P.Primary)}
        e=FinishPrimaryExpr(e)
    |   // Suffix operator
        ...
    )*
    {return e;}
};
// Helper rule that parses one of the syntactically special primary expressions
@[private] rule FinishPrimaryExpr(e::LNode)::LNode @{
(   // call(function)
    "(" list:ExprList(ref endMarker) ")"
    { ... }
    |   // ! operator (generic #of)
        "!" ...
    |   // Indexer / square brackets
        {var args = (new VList!LNode { e });}
        "[" args=ExprList(args) "]"
        { ... }
    )
    {return e;}
};

Bahasa saya memiliki 25 tingkat prioritas, dan dengan aturan ini saya telah menciutkan hampir semuanya untuk diproses oleh satu aturan (Anda dapat menganggap Precedence sebagai pembungkus di sekitar beberapa bilangan bulat yang menjelaskan prioritas suatu operator). Selain itu, bahasa saya memiliki jumlah operator yang tidak terbatas (pada dasarnya semua urutan tanda baca) dan prioritas operator diputuskan setelah diuraikan. Meskipun _secara teknis_ mungkin untuk menguraikan bahasa dengan cara biasa, dengan aturan terpisah untuk masing-masing dari 25 jenis operator, itu akan menjadi cara yang mengerikan untuk melakukannya.

Anda juga dapat melihat di sini bahwa aturan dalam FinishPrimaryExpr membangun pohon sintaks yang menggabungkan parameter yang diteruskan dari aturan terlampir.

Jadi ... apakah ada cara untuk meneruskan parameter ke aturan PEG.js?

+1! Dalam kasus saya, saya hanya ingin membuat parser untuk sintaks, di mana beberapa pembatas dapat dikonfigurasi secara global. Dalam hal ini, saya dapat mencapai ini dengan mengganti literal pembatas dengan mencocokkan ekspresi apa pun yang dikombinasikan dengan predikat, tetapi akan jauh lebih elegan (dan juga lebih efisien) jika pencocokan-semuanya dapat diganti dengan variabel.

+1, adakah peluang untuk melihat ini diterapkan di masa mendatang?

Kasus penggunaan lain. Ini dari contoh javascript.pegjs Anda:

(...)

RelationalExpression
  = head:ShiftExpression
    tail:(__ RelationalOperator __ ShiftExpression)*
    { return buildBinaryExpression(head, tail); }

RelationalOperator
  = "<="
  / ">="
  / $("<" !"<")
  / $(">" !">")
  / $InstanceofToken
  / $InToken

RelationalExpressionNoIn
  = head:ShiftExpression
    tail:(__ RelationalOperatorNoIn __ ShiftExpression)*
    { return buildBinaryExpression(head, tail); }

RelationalOperatorNoIn
  = "<="
  / ">="
  / $("<" !"<")
  / $(">" !">")
  / $InstanceofToken

(...)

  (...)
  / ForToken __
    "(" __
    init:(ExpressionNoIn __)? ";" __
    test:(Expression __)? ";" __
    update:(Expression __)?
    ")" __
    body:Statement
  (...)

(...)

Semua aturan ...NoIn ini (dan ada BANYAK dari mereka) diperlukan hanya karena pernyataan for in . Bukankah pendekatan yang jauh lebih baik untuk ini adalah seperti:

(...)

RelationalExpression<allowIn>
  = head:ShiftExpression
    tail:(__ RelationalOperator<allowIn> __ ShiftExpression)*
    { return buildBinaryExpression(head, tail); }

RelationalOperator<allowIn>
  = "<="
  / ">="
  / $("<" !"<")
  / $(">" !">")
  / $InstanceofToken
  / &{ return allowIn; } InToken
    {return "in";}

(...)

  (...)
  / ForToken __
    "(" __
    init:(Expression<false> __)? ";" __
    test:(Expression<true> __)? ";" __
    update:(Expression<true> __)?
    ")" __
    body:Statement
  (...)

(...)

Ini terasa sangat mirip dengan bagaimana, misalnya, tata bahasa JavaScript ditentukan: https://tc39.github.io/ecma262/#prod -IterationStatement (perhatikan ~In )

Bahasa yang saat ini saya kembangkan memiliki masalah persis seperti ini: Saya ingin menonaktifkan/mengaktifkan beberapa aturan hanya pada titik-titik tertentu. Saya sangat ingin menahan diri untuk tidak menduplikasi setiap aturan yang terpengaruh seperti yang Anda lakukan untuk tata bahasa JavaScript.

Apakah ada cara alternatif untuk mencapai ini tanpa menduplikasi aturan?

+1, adakah peluang untuk melihat ini diterapkan di masa mendatang?

Masalah ini memiliki pencapaian yang ditetapkan (pasca-1.0.0). Versi PEG.js saat ini adalah 0.10.0. Jelas, masalah pasca-1.0.0 akan ditangani setelah merilis 1.0.0, yang akan terjadi di beberapa titik setelah merilis 0.11.0 menurut roadmap .

Ini harus menjawab pertanyaan Anda. Cara terbaik untuk mempercepat seluruh proses adalah membantu dengan masalah yang ditargetkan untuk 0.11.0 dan 1.0.0 .

Apakah ada cara alternatif untuk mencapai ini tanpa menduplikasi aturan?

Cara yang mungkin adalah melacak status secara manual dan kemudian menggunakan predikat semantik. Tetapi pendekatan ini memiliki masalah dengan pelacakan mundur dan saya tidak akan merekomendasikannya (dengan kata lain, ketika diberi pilihan antara duplikasi aturan dan pelacakan status manual, saya akan memilih duplikasi).

Ada dua jenis argumen yang dapat diteruskan ke parser:

  1. Nilai. Tata bahasa untuk bahasa seperti Python, Nim dan Haskell (dan juga Skema dengan cara yang berbeda) bergantung pada "kedalaman" ekspresi di dalam pohon. Menulis tata bahasa yang benar membutuhkan entah bagaimana melewati konteks ini.
  2. Pengurai. leftAssociative(element, separator) , escapedString(quote) dan withPosition(parser) adalah contoh yang bagus untuk itu.

Harus ada cara untuk menandai apakah argumen itu parser atau nilai. Ketika saya mencoba mencari pendekatan yang benar, saya akhirnya menggunakan variabel global untuk konteks, dan itu jelas merupakan jalan buntu. Apakah ada yang punya ide tentang itu?

Bagaimana dengan makro ?

Diberikan:

Add <Expression, Add>
  = left:Expression _ '+' _ right:Add
    { return { type: 'add', left, right } }
  / Expression

Kapan:

  = Add <MyExpression, MyAdd>

MyExpression
  = [0-9]+

Kemudian:

  = left:MyExpression _ '+' _ right:MyAdd
    { return { type: 'add', left, right } }
  / MyExpression

MyExpression
  = [0-9]+

Ini memungkinkan kita untuk membangun aturan dari bawah ke atas :smirk:

Saya setuju, saya sarankan pengembang menambahkan fitur ini :)

Saya benar-benar membutuhkan fitur ini untuk memperbarui tata bahasa JavaScript yang saya tulis, jadi ini ada di daftar keinginan saya. Akan mencobanya dan melihat bagaimana hasilnya.

@samvv Saya menemukan ini dari rute yang sangat berbeda, dan belum membaca seluruh utasnya.
Namun, di #572, yang saya rujuk di sini, saya menunjukkan teknik yang dapat digunakan untuk mensimulasikan aturan berparameter.

Artinya, pada intinya: mengembalikan fungsi sebagai hasil parse perantara.

"Trik" itu sama sekali bukan penemuan saya, dan mungkin agak kikuk untuk tujuan Anda, saya kira. Tapi itu mungkin solusi untuk Anda. Maksud saya sampai "posting v1.0"... :)

@meisl Keren, terima kasih atas tipnya! Akan mencobanya ketika saya menemukan waktu.

@samvv Ooh, ah... Saya khawatir saya telah mengabaikan sesuatu yang agak penting:

Itu membuat perbedaan yang cukup besar apakah Anda ingin aturan berparameter

  • hanya dapat menghasilkan nilai , yang bergantung pada parameter
  • atau (juga) agar keputusan penguraiannya bergantung pada parameter

Apa yang saya usulkan hanya membantu dengan yang pertama - sedangkan yang terakhir adalah masalah sebenarnya dari OP ...
Maaf untuk itu.

Namun, ada solusi bahkan untuk yang terakhir, meskipun LEBIH kikuk.
Dan, bagian "keputusan dependen" tidak ada hubungannya dengan mengembalikan fungsi...

Saya menambahkan contoh untuk Anda coba di https://pegjs.org/online

Ide dasarnya adalah: gunakan keadaan global untuk mengingat "terminator" saat ini. Itu cukup hack, harus diakui, dan berulang-ulang.
Tetapi: menambahkan pembatas lagi, katakan | hanya berarti menambahkan satu alternatif lagi ke str :

  / (t:'|' {term = t}) c:conts t:.&{ return isT(t) }  { return c }

yang berbeda dari yang lain hanya dalam satu karakter itu |


{
  var term;
  function isT(ch) { return ch === term }
  function isE(ch) { return ch === '\\' }
}
start = str*

str
  = (t:'\"' {term = t}) c:conts t:.&{ return isT(t) }  { return c }
  / (t:'\'' {term = t}) c:conts t:.&{ return isT(t) }  { return c }

conts
  = c:(
        '\\' t:.&{ return isT(t) || isE(t) } { return t }
      /      t:.!{ return isT(t)           } { return t }
    )*
    { return c.join('') }

... pada masukan

  • "abc" -> ["abc"]
  • "a\"bc" -> ["a\"bc"]
  • "a\\bc" -> ["a\bc"]
  • "a\\b\"c"'a\\b\'' -> ["a\b\"c", "a\b'c"]

ps: itu BUKAN sesuatu yang ingin ditulis dengan tangan, saya tahu. Tapi hei, bayangkan itu akan dihasilkan untuk Anda... Saya pikir pada prinsipnya begitu .

@ceymard - Saya menyadari itu sepuluh tahun kemudian, tapi saya ingin tahu bagaimana ini berbeda dari #36

Wow butuh beberapa saat untuk mengingatnya. 10 tahun !

Dalam PR ini, aturan mengambil argumen dan dapat diparametrikan. Ini dimaksudkan untuk digunakan oleh tata bahasa itu sendiri untuk menghindari pengulangan aturan yang serupa namun berbeda.

Di #36, aturan ditentukan di luar tata bahasa itu sendiri. Tata bahasa dengan demikian itu sendiri parametrized.

Saya pikir ruang lingkupnya berbeda, meskipun orang dapat berargumen bahwa tata bahasa itu sendiri adalah aturan dan dengan demikian ini adalah masalah yang sama. Saya pikir tidak, karena #36 mungkin berarti sedikit perubahan API, sedangkan PR ini tidak.

Jadi, untuk menyalahgunakan terminologi C++ dengan cara yang sangat salah, yang pertama adalah statika templat, sedangkan yang terakhir adalah panggilan konstruktor?

Saya kira analogi ini agak berhasil, ya.

Terima kasih atas waktu Anda dalam penjelasan

Saya mungkin akan memiliki pertanyaan lain untuk Anda dalam sepuluh tahun. Selamat menjalani 2020-an

Ini akan sangat berguna dalam menghilangkan redundansi definisi parser saya. Saya memiliki tata bahasa khusus yang sengaja dibuat sangat santai, dan beberapa aturan perlu diterapkan dalam konteks yang sedikit berbeda.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat