Kami menyampaikan kepada perhatian anda terjemahan panduan ringkas kepada ungkapan biasa dalam Java, yang ditulis oleh Jeff Friesen untuk laman web javaworld. Untuk memudahkan pembacaan, kami telah membahagikan artikel tersebut kepada beberapa bahagian. Bahagian ini adalah yang terakhir. Ungkapan Biasa dalam Jawa, Ungkapan Biasa Bahagian 1 dalam Jawa, Ungkapan Biasa Bahagian 2 dalam Jawa, Ungkapan Biasa Bahagian 3 dalam Jawa, Bahagian 4
Ungkapan biasa jauh lebih cekap daripada penganalisis leksikal berasaskan negeri, yang mesti ditulis dengan tangan dan secara amnya tidak boleh digunakan semula. Contoh penganalisis leksikal berasaskan ungkapan biasa ialah JLex , penjana leksikal untuk bahasa Java yang menggunakan ungkapan biasa untuk mentakrifkan peraturan untuk memecahkan aliran data input kepada token. Contoh lain ialah Lexan.
Menggunakan ungkapan biasa untuk analisis leksikal
Aplikasi ungkapan biasa yang lebih berguna ialah perpustakaan kod boleh guna semula untuk melaksanakan analisis leksikal, komponen utama mana-mana pengkompil atau penghimpun. Dalam kes ini, aliran input aksara dikumpulkan ke dalam aliran keluaran token - nama untuk jujukan aksara yang mempunyai maksud yang sama. Sebagai contoh, setelah menemui urutan aksarac
, o
, u
, n
, t
, e
, r
, dalam aliran input, penganalisis leksikal boleh mengeluarkan token ID
(pengecam). Urutan aksara yang sepadan dengan token dipanggil leksem.
Lebih lanjut mengenai penanda dan leksem |
---|
Token seperti ID boleh memadankan banyak jujukan aksara. Dalam kes token tersebut, token sebenar yang sepadan dengan token juga diperlukan oleh pengkompil, penghimpun atau utiliti lain yang memerlukan analisis leksikal. Untuk token yang mewakili satu jujukan aksara tertentu, seperti token PLUS yang sepadan hanya dengan watak + , token sebenar tidak diperlukan kerana ia boleh [unik] ditentukan oleh token. |
Mengenali Lexan
Lexan ialah perpustakaan Java yang boleh digunakan semula untuk analisis leksikal. Ia berdasarkan kod daripada siri catatan blog Menulis Parser dalam Java di tapak web Pembelajaran Cogito . Pustaka terdiri daripada kelas berikut, yang terdapat dalam pakejca.javajeff.lexan
yang disertakan dalam kod yang boleh dimuat turun untuk artikel ini:
Lexan
: penganalisis leksikal;LexException
: pengecualian dilemparkan jika sintaks yang salah dikesan semasa analisis leksikal;Token
: nama dengan atribut ungkapan biasa;TokLex
: pasangan token/token.
LexanException
: pengecualian yang dilemparkan dalam pembina kelasLexan;
Lexan(java.lang.Class tokensClass)
mencipta penganalisis leksikal baharu. Ia memerlukan satu hujah dalam bentuk objek kelas java.lang.Class
yang sepadan dengan kelas pemalar jenis static Token
. Menggunakan API Refleksi, pembina membaca semua pemalar Token
ke dalam tatasusunan nilai Token[]
. Jika Token
tiada pemalar, pengecualian dilemparkan LexanException
. Kelas ini Lexan
juga menyediakan dua kaedah berikut:
- Kaedah ini mengembalikan senarai lexer ini;
List
getTokLexes() Token
Метод void lex(String str)
melakukan analisis leksikal rentetan input [dengan keputusan diletakkan] ke dalam senarai nilai jenisTokLex
. Jika aksara ditemui yang tidak sepadan dengan mana - mana corak tatasusunanToken[]
, pengecualian akan dilemparkanLexException
.
LexanException
tidak mempunyai kaedah; ia menggunakan kaedah yang diwarisi untuk mengembalikan mesej pengecualian getMessage()
. Sebaliknya, kelas LexException
menyediakan kaedah berikut:
- Kaedah ini
int getBadCharIndex()
mengembalikan kedudukan watak yang tidak sepadan dengan mana-mana corak penanda. - Kaedah ini
String getText()
mengembalikan teks yang telah dianalisis apabila pengecualian dijana.
Token
mengatasi kaedah toString()
untuk mengembalikan nama penanda. Ia juga menyediakan kaedah String getPattern()
yang mengembalikan atribut ungkapan biasa token. Kelas TokLex
menyediakan kaedah Token getToken()
yang mengembalikan tokennya. Ia juga menyediakan kaedah String getLexeme()
yang mengembalikan tokennya.
Demonstrasi perpustakaan Lexan
Untuk menunjukkan cara perpustakaan berfungsi,Lexan
saya menulis aplikasi LexanDemo
. Ia terdiri daripada kelas LexanDemo
, BinTokens
, MathTokens
dan NoTokens
. Kod sumber untuk aplikasi LexanDemo
ditunjukkan dalam Penyenaraian 2. Penyenaraian 2. Demonstrasi perpustakaan Lexan dalam tindakan
import ca.javajeff.lexan.Lexan;
import ca.javajeff.lexan.LexanException;
import ca.javajeff.lexan.LexException;
import ca.javajeff.lexan.TokLex;
public final class LexanDemo
{
public static void main(String[] args)
{
lex(MathTokens.class, " sin(x) * (1 + var_12) ");
lex(BinTokens.class, " 1 0 1 0 1");
lex(BinTokens.class, "110");
lex(BinTokens.class, "1 20");
lex(NoTokens.class, "");
}
private static void lex(Class tokensClass, String text)
{
try
{
Lexan lexan = new Lexan(tokensClass);
lexan.lex(text);
for (TokLex tokLex: lexan.getTokLexes())
System.out.printf("%s: %s%n", tokLex.getToken(),
tokLex.getLexeme());
}
catch (LexanException le)
{
System.err.println(le.getMessage());
}
catch (LexException le)
{
System.err.println(le.getText());
for (int i = 0; i < le.getBadCharIndex(); i++)
System.err.print("-");
System.err.println("^");
System.err.println(le.getMessage());
}
System.out.println();
}
}
Kaedah main()
dalam Penyenaraian 2 memanggil utiliti lex()
untuk menunjukkan analisis leksikal menggunakan Lexan. Setiap panggilan kepada kaedah ini diluluskan kelas token dalam objek Class
dan rentetan untuk dihuraikan. Kaedah lex()
pertama mencipta objek kelas Lexan
dengan menghantar objek Class
kepada pembina kelas Lexan
. Dan kemudian ia memanggil kaedah lex()
kelas Lexan
pada rentetan itu. Jika analisis leksikal berjaya, kaedah kelas TokLex
dipanggil untuk mengembalikan senarai objek . Untuk setiap objek ini, kaedah kelasnya dipanggil untuk mengembalikan token dan kaedah kelasnya untuk mengembalikan token. Kedua-dua nilai dicetak ke output standard. Jika analisis leksikal gagal, salah satu pengecualian atau dibuang dan dikendalikan dengan sewajarnya . Untuk ringkasnya, mari kita pertimbangkan hanya kelas yang membentuk aplikasi ini . Penyenaraian 3 menunjukkan kod sumbernya. Penyenaraian 3. Penerangan tentang set token untuk bahasa matematik yang kecilgetTokLexes()
Lexan
getToken()
TokLex
getLexeme()
LexanException
LexException
MathTokens
import ca.javajeff.lexan.Token;
public final class MathTokens
{
public final static Token FUNC = new Token("FUNC", "sin|cos|exp|ln|sqrt");
public final static Token LPAREN = new Token("LPAREN", "\\(");
public final static Token RPAREN = new Token("RPAREN", "\\)");
public final static Token PLUSMIN = new Token("PLUSMIN", "[+-]");
public final static Token TIMESDIV = new Token("TIMESDIV", "[*/]");
public final static Token CARET = new Token("CARET", "\\^");
public final static Token INTEGER = new Token("INTEGER", "[0-9]+");
public final static Token ID = new Token("ID", "[a-zA-Z][a-zA-Z0-9_]*");
}
Penyenaraian 3 menunjukkan bahawa kelas MathTokens
menerangkan jujukan pemalar jenis Token
. Setiap daripada mereka diberikan nilai objek Token
. Pembina untuk objek ini menerima rentetan yang merupakan nama penanda, bersama-sama dengan ungkapan biasa yang menerangkan semua rentetan aksara yang dikaitkan dengan penanda itu. Untuk kejelasan, adalah wajar bahawa nama rentetan penanda adalah sama dengan nama pemalar, tetapi ini tidak diperlukan. Kedudukan pemalar Token
dalam senarai penanda adalah penting. Pemalar yang terletak lebih tinggi dalam senarai Token
diutamakan daripada yang terletak di bawah. Sebagai contoh, apabila menghadapi sin
, Lexan memilih token FUNC
dan bukannya ID
. Jika penanda ID
telah mendahului penanda FUNC
, ia akan dipilih.
Menyusun dan menjalankan aplikasi LexanDemo
Kod yang boleh dimuat turun untuk artikel ini termasuk arkiblexan.zip
yang mengandungi semua fail pengedaran Lexan. Buka pembungkusan arkib ini dan pergi ke subdirektori demos
direktori akar lexan
. Jika anda menggunakan Windows, jalankan arahan berikut untuk menyusun fail kod sumber aplikasi demo:
javac -cp ..\library\lexan.jar *.java
Jika kompilasi berjaya, jalankan arahan berikut untuk menjalankan aplikasi demo:
java -cp ..\library\lexan.jar;. LexanDemo
Anda sepatutnya melihat keputusan berikut:
FUNC: sin
LPAREN: (
ID: x
RPAREN: )
TIMESDIV: *
LPAREN: (
INTEGER: 1
PLUSMIN: +
ID: var_12
RPAREN: )
ONE: 1
ZERO: 0
ONE: 1
ZERO: 0
ONE: 1
ONE: 1
ONE: 1
ZERO: 0
1 20
--^
Неожиданный символ во входном тексте: 20
Mesej Неожиданный символ во входном тексте: 20
berlaku akibat pengecualian yang dilemparkan LexanException
disebabkan oleh fakta bahawa kelas BinTokens
tidak mengisytiharkan pemalar Token
dengan nilai 2
sebagai ungkapan biasa. Ambil perhatian bahawa pengendali pengecualian mengeluarkan kedudukan watak yang tidak sesuai yang diperoleh daripada analisis leksikal teks. Mesej yang tiada token adalah hasil daripada pengecualian yang dilemparkan LexException
kerana NoTokens
tiada pemalar diisytiharkan dalam kelas Token
.
Disebalik tabir
Lexan
menggunakan kelas Lexan sebagai enjinnya. Lihatlah pelaksanaan kelas ini dalam Penyenaraian 4 dan perhatikan sumbangan ungkapan biasa untuk menjadikan enjin boleh digunakan semula. Penyenaraian 4. Mencipta seni bina penganalisis leksikal berdasarkan ungkapan biasa
package ca.javajeff.lexan;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
/**
* Лексический анализатор. Этот класс можно использовать для
* преобразования входного потока символов в выходной поток маркеров.
*
* @Author Джефф Фризен
*/
public final class Lexan
{
private List tokLexes;
private Token[] values;
/**
* Инициализируем лексический анализатор набором an objectов Token.
*
* @параметры tokensClass – an object Class класса, содержащего
* набор an objectов Token
*
* @генерирует исключение LexanException в случае невозможности
* формирования an object Lexan, возможно, из-за отсутствия an objectов
* Token в классе
*/
public Lexan(Class tokensClass) throws LexanException
{
try
{
tokLexes = new ArrayList<>();
List _values = new ArrayList<>();
Field[] fields = tokensClass.getDeclaredFields();
for (Field field: fields)
if (field.getType().getName().equals("ca.javajeff.lexan.Token"))
_values.add((Token) field.get(null));
values = _values.toArray(new Token[0]);
if (values.length == 0)
throw new LexanException("маркеры отсутствуют");
}
catch (IllegalAccessException iae)
{
throw new LexanException(iae.getMessage());
}
/**
* Получаем список TokLex'ов этого лексического анализатора.
*
* @возвращает список TokLex'ов
*/
public List getTokLexes()
{
return tokLexes;
}
/** * Выполняет лексический анализ входной строки [с помещением * результата] в список TokLex'ов. * * @параметры str – строка, подвергаемая лексическому анализу * * @генерирует исключение LexException: во входных данных обнаружен * неожиданный символ */
public void lex(String str) throws LexException
{
String s = new String(str).trim(); // удалить ведущие пробелы
int index = (str.length() - s.length());
tokLexes.clear();
while (!s.equals(""))
{
boolean match = false;
for (int i = 0; i < values.length; i++)
{
Token token = values[i];
Matcher m = token.getPattern().matcher(s);
if (m.find())
{
match = true;
tokLexes.add(new TokLex(token, m.group().trim()));
String t = s;
s = m.replaceFirst("").trim(); // удалить ведущие пробелы
index += (t.length() - s.length());
break;
}
}
if (!match)
throw new LexException("Неожиданный символ во входном тексте: "
+ s, str, index);
}
}
}
Kod kaedah lex()
adalah berdasarkan kod yang disediakan dalam catatan blog "Menulis Parser dalam Java: Penjana Token" di laman web Cogito Learning. Baca siaran ini untuk mengetahui lebih lanjut tentang cara Lexan menggunakan API Regex untuk menyusun kod.
Kesimpulan
Ungkapan biasa ialah alat berguna yang boleh berguna kepada mana-mana pembangun. Regex API bahasa pengaturcaraan Java menjadikannya mudah digunakan dalam aplikasi dan perpustakaan. Memandangkan anda sudah mempunyai pemahaman asas tentang ungkapan biasa dan API ini, lihat dokumentasi SDKjava.util.regex
untuk mengetahui lebih lanjut tentang ungkapan biasa dan kaedah API Regex tambahan.
GO TO FULL VERSION