Genel olarak öğretildikleri gibi normal ayrıştırıcılar, ayrıştırıcı girdiye dokunmadan önce bir lexer aşamasına sahiptir. Lexer (ayrıca “tarayıcı” veya “belirteç”), girdiyi bir türle ek açıklamalı küçük belirteçlere böler. Bu, ana ayrıştırıcının, her bir karakteri bir terminal olarak ele almak yerine jetonları terminal öğeleri olarak kullanmasına izin verir, bu da fark edilir verimlilik kazanımlarına yol açar. Özellikle, lexer tüm yorumları ve beyaz alanı da kaldırabilir. Bununla birlikte, ayrı bir belirteç aşaması, anahtar kelimelerin tanımlayıcı olarak da kullanılamayacağı anlamına gelir (dil , bir miktar lehinden düşmeyi desteklemiyorsa veya tüm tanımlayıcılara sigil benzeri ön ekler eklemezse$foo ).
Neden? Aşağıdaki belirteçleri anlayan basit bir belirteçimiz olduğunu varsayalım:
FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'
Belirteç her zaman en uzun belirteçle eşleşir ve tanımlayıcılara göre anahtar kelimeleri tercih eder. Bu interestingşekilde olduğu gibi IDENT:interesting, ama asla olduğu gibi inlexedilecektir . Gibi bir kod snippet'iINIDENT:interesting
for(var in expression)
jeton akışına çevrilecek
FOR LPAREN IDENT:var IN IDENT:expression RPAREN
Şimdiye kadar, bu işe yarıyor. Ancak herhangi bir değişken, kodu kıracak bir değişken yerine inanahtar kelime olarak adlandırılır IN. Lexer, jetonlar arasında herhangi bir durum tutmaz ve inbir for döngüsünde olduğumuz durumlar dışında bunun genellikle bir değişken olması gerektiğini bilemez . Ayrıca, aşağıdaki kod yasal olmalıdır:
for(in in expression)
Birincisi inbir tanımlayıcı, ikincisi bir anahtar kelime olacaktır.
Bu soruna iki tepki var:
İçeriğe dayalı anahtar kelimeler kafa karıştırıcıdır, bunun yerine anahtar kelimeleri tekrar kullanalım.
Java'nın, C ++ 'dan Java'ya geçiş yapan programcılara daha yararlı hata mesajları sağlamak dışında bir faydası olmayan birçok ayrılmış kelime vardır. Yeni anahtar kelimeler eklemek kodu kırıyor. Bağlamsal anahtar kelimeler eklemek, iyi bir sözdizimi vurgulaması olmadıkça kod okuyucusunu kafa karıştırıcıdır ve daha gelişmiş ayrıştırma teknikleri kullanmaları gerekeceğinden araçların uygulanmasını zorlaştırır (aşağıya bakın).
Dili genişletmek istediğimizde, aklı başında olan tek yaklaşım daha önce dilde yasal olmayan semboller kullanmaktır. Özellikle, bunlar tanımlayıcı olamaz. Foreach döngü sözdizimiyle Java, mevcut :anahtar kelimeyi yeni bir anlamla yeniden kullandı . Lambdas ile Java, ->daha önce herhangi bir yasal programda bulunamayan bir anahtar kelime ekledi ( -->yine de yasal olduğu gibi lexedilmiş '--' '>'ve ->daha önce olduğu gibi lexedilmiş olabilir '-', '>', ancak bu sıra ayrıştırıcı tarafından reddedilir).
İçeriğe dayalı anahtar kelimeler dilleri basitleştirir, bunları uygulayalım
Lexers tartışmasız faydalıdır. Ancak ayrıştırıcıdan önce bir lexer çalıştırmak yerine bunları ayrıştırıcı ile birlikte çalıştırabiliriz. Aşağıdan yukarıya ayrıştırıcılar her zaman, herhangi bir yerde kabul edilebilir olan token türleri kümesini bilir. Ayrıştırıcı daha sonra lexer'ın bu türden herhangi biriyle geçerli konumda eşleşmesini isteyebilir. Her biri için bir döngüde, ayrıştırıcı ·, değişken bulunduktan sonra (basitleştirilmiş) dilbilgisinde belirtilen konumda olacaktır :
for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'
Bu pozisyonda, yasal jetonlar SEMICOLONya da INdeğil IDENT. Bir anahtar kelime intamamen açık olur.
Bu özel örnekte, yukarıdan aşağıya ayrıştırıcıların da sorunu yoktur, çünkü yukarıdaki dilbilgisini yeniden yazabiliriz.
for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest = · ';' expression ';' expression
for_loop_rest = · 'in' expression
ve karar için gerekli tüm jetonlar geri izlenmeden görülebilir.
Kullanılabilirliği düşünün
Java her zaman anlamsal ve sözdizimsel sadeliğe yönelmiştir. Örneğin, dil, operatörün aşırı yüklenmesini desteklemez, çünkü kod çok daha karmaşık hale gelir. Bu nedenle, her döngü için bir sözdizimi arasında inve :için karar verirken hangisinin daha az kafa karıştırıcı ve kullanıcılar için daha belirgin olduğunu düşünmeliyiz. Aşırı durum muhtemelen
for (in in in in())
for (in in : in())
(Not: Java, tür adları, değişkenler ve yöntemler için ayrı ad alanlarına sahiptir. Bunun çoğunlukla bir hata olduğunu düşünüyorum. Bu, daha sonra dil tasarımının daha fazla hata eklemesi gerektiği anlamına gelmez .)
Hangi alternatif, yineleme değişkeni ile yinelenen koleksiyon arasında daha net görsel ayrımlar sağlar? Koda baktığınızda hangi alternatif daha hızlı tanınabilir? Bu kriterler söz konusu olduğunda, sembolleri ayırmanın bir kelime dizisinden daha iyi olduğunu gördüm. Diğer diller farklı değerlere sahiptir. Örneğin, Python, birçok operatörü doğal olarak okunabilmeleri ve anlaşılması kolay olacak şekilde İngilizce olarak söyler, ancak aynı özellikler bir bakışta bir Python parçasını anlamayı zorlaştırabilir.