Kapan cukup lexing, kapan Anda membutuhkan EBNF?
EBNF benar-benar tidak menambah banyak kekuatan tata bahasa. Ini hanya notasi kenyamanan / jalan pintas / "gula sintaksis" atas aturan tata bahasa Chomsky Normal Form (CNF) standar. Misalnya, alternatif EBNF:
S --> A | B
Anda dapat mencapainya di CNF hanya dengan mendaftar setiap produksi alternatif secara terpisah:
S --> A // `S` can be `A`,
S --> B // or it can be `B`.
Elemen opsional dari EBNF:
S --> X?
Anda dapat mencapai di CNF dengan menggunakan produksi yang dapat dibatalkan , yaitu yang dapat diganti dengan string kosong (dilambangkan dengan hanya produksi kosong di sini; yang lain menggunakan epsilon atau lambda atau lingkaran silang):
S --> B // `S` can be `B`,
B --> X // and `B` can be just `X`,
B --> // or it can be empty.
Produksi dalam bentuk seperti yang terakhir di B
atas disebut "penghapusan", karena dapat menghapus apa pun kepanjangan dari produksi lain (produk string kosong bukan yang lain).
Tidak ada atau lebih banyak pengulangan dari EBNF:
S --> A*
Anda dapat memperoleh dengan menggunakan produksi rekursif , yaitu, yang menanamkan sendiri di suatu tempat di dalamnya. Itu bisa dilakukan dengan dua cara. Yang pertama adalah rekursi kiri (yang biasanya harus dihindari, karena parser Keturunan Rekursif Top-Down tidak dapat menguraikannya):
S --> S A // `S` is just itself ended with `A` (which can be done many times),
S --> // or it can begin with empty-string, which stops the recursion.
Mengetahui bahwa itu hanya menghasilkan string kosong (akhirnya) diikuti oleh nol atau lebih A
, string yang sama ( tetapi bukan bahasa yang sama! ) Dapat diekspresikan menggunakan rekursi kanan :
S --> A S // `S` can be `A` followed by itself (which can be done many times),
S --> // or it can be just empty-string end, which stops the recursion.
Dan ketika datang +
untuk satu atau lebih pengulangan dari EBNF:
S --> A+
itu dapat dilakukan dengan memfaktorkan satu A
dan menggunakan *
seperti sebelumnya:
S --> A A*
yang bisa Anda ungkapkan dalam CNF seperti itu (saya menggunakan rekursi yang benar di sini; coba cari tahu sendiri yang lain sebagai latihan):
S --> A S // `S` can be one `A` followed by `S` (which stands for more `A`s),
S --> A // or it could be just one single `A`.
Mengetahui hal itu, Anda sekarang mungkin dapat mengenali tata bahasa untuk ekspresi reguler (yaitu, tata bahasa reguler ) sebagai yang dapat diekspresikan dalam produksi EBNF tunggal yang hanya terdiri dari simbol terminal. Secara umum, Anda dapat mengenali tata bahasa reguler ketika Anda melihat produksi yang mirip dengan ini:
A --> // Empty (nullable) production (AKA erasure).
B --> x // Single terminal symbol.
C --> y D // Simple state change from `C` to `D` when seeing input `y`.
E --> F z // Simple state change from `E` to `F` when seeing input `z`.
G --> G u // Left recursion.
H --> v H // Right recursion.
Artinya, hanya menggunakan string kosong, simbol terminal, non-terminal sederhana untuk penggantian dan perubahan keadaan, dan menggunakan rekursi hanya untuk mencapai pengulangan (iterasi, yang hanya rekursi linier - yang tidak bercabang seperti pohon). Tidak ada yang lebih maju di atas ini, maka Anda yakin itu adalah sintaksis biasa dan Anda bisa menggunakan hanya lexer untuk itu.
Tetapi ketika sintaks Anda menggunakan rekursi dengan cara yang tidak sepele, untuk menghasilkan struktur bersarang seperti pohon, mirip diri, seperti yang berikut ini:
S --> a S b // `S` can be itself "parenthesized" by `a` and `b` on both sides.
S --> // or it could be (ultimately) empty, which ends recursion.
maka Anda dapat dengan mudah melihat bahwa ini tidak dapat dilakukan dengan ekspresi reguler, karena Anda tidak dapat menyelesaikannya menjadi satu produksi EBNF dengan cara apa pun; Anda akan berakhir dengan mengganti S
tanpa batas, yang akan selalu menambah a
s dan b
s di kedua sisi. Lexers (lebih khusus: Finite State Automata yang digunakan oleh lexers) tidak dapat menghitung ke angka arbitrer (mereka terbatas, ingat?), Jadi mereka tidak tahu berapa banyak a
yang ada untuk mencocokkan mereka secara merata dengan begitu banyak b
. Tata bahasa seperti ini disebut tata bahasa bebas konteks (paling tidak), dan mereka membutuhkan parser.
Tata bahasa bebas konteks dikenal untuk diuraikan, sehingga mereka banyak digunakan untuk menggambarkan sintaks bahasa pemrograman. Tapi masih ada lagi. Kadang-kadang tata bahasa yang lebih umum diperlukan - ketika Anda memiliki lebih banyak hal untuk dihitung pada saat yang sama, secara mandiri. Misalnya, ketika Anda ingin mendeskripsikan bahasa di mana orang dapat menggunakan kurung bundar dan kurung kurawal, tetapi mereka harus dipasangkan dengan benar satu sama lain (kurung kurawal dengan kurawal, bundar dengan putaran). Tata bahasa semacam ini disebut konteks-sensitif . Anda dapat mengenalinya dengan memiliki lebih dari satu simbol di sebelah kiri (sebelum panah). Sebagai contoh:
A R B --> A S B
Anda dapat menganggap simbol-simbol tambahan ini di sebelah kiri sebagai "konteks" untuk menerapkan aturan. Mungkin ada beberapa prasyarat, postconditions dll Sebagai contoh, aturan di atas akan menggantikan R
menjadi S
, tetapi hanya ketika itu dalam antara A
dan B
, meninggalkan mereka A
dan B
diri mereka sendiri tidak berubah. Sintaks semacam ini benar-benar sulit untuk diuraikan, karena membutuhkan mesin Turing yang lengkap. Ini keseluruhan cerita lain, jadi saya akan berakhir di sini.