J'ai créé un analyseur de phrases simple à utiliser dans mon propre programme de console Java. J'ai créé une classe de jetons dans Article précédent, donc cette fois ce sera le corps principal de l'analyseur de phrases. Pour l'analyse des phrases, il est plus facile d'utiliser diverses bibliothèques, mais comme il est utilisé sur le lieu de travail où le téléchargement à partir de sites externes est interdit, il est créé à partir de zéro en utilisant uniquement les fonctions Java8.
2018/10/22 La source a été modifiée. 2018/10/24 Correction de l'enregistrement d'une copie de la chaîne de caractères cible d'analyse dans le constructeur.
En le créant, je me réfère à Implémentation d'une analyse de phrase simple en Java.
Cette fois, la source et son explication seront longues, veuillez donc consulter Article précédent.
LexicalAnalyzer.java
package console;
public class LexicalAnalyzer {
public static LexicalAnalyzer create(String target) {
if ( target == null || target.trim().isEmpty() ) { target = ""; }
return new LexicalAnalyzer(target);
}
public java.util.List<Token> analyze() {
char c;
while ( (c = next()) != '\0' ) {
if ( isSymbol_1(c) ) {
tokens_.add( Token.create(c) );
continue;
}
if ( isQuote(c) ) {
quotedText(c);
continue;
}
text(c);
}
return new java.util.ArrayList<>(tokens_);
}
// query methods ================================================================================
public boolean isEmpty() { return tokens_.size() == 0;}
public boolean isValid() {
return !isEmpty() && tokens_.stream().noneMatch( e -> e.kind() == Token.Kinds.Unknown );
}
// internal methods ======================================================================
/**Découpez le caractère de citation suivant sous forme de bloc de jetons. */
private void quotedText(char quote) {
tokens_.add( Token.create(quote)); // create token of begin quote
java.lang.StringBuilder builder = new java.lang.StringBuilder();
char c;
while ( (c = nextAll()) != '\0' && c != quote) { builder.append(c); }
if ( builder.length() != 0 ) {
tokens_.add( Token.create(builder.toString()) ); // append string
}
tokens_.add( Token.create(c) ); // append token of end quote
}
/**Découpez les séparateurs et les caractères vides sous forme de bloc de jetons. */
private void text(char first) {
java.lang.StringBuilder builder = new java.lang.StringBuilder();
builder.append(first);
char c;
while ( (c = nextAll()) != '\0' && !isSeparator(c) && !isWhitespace(c) ) {
builder.append(c);
}
tokens_.add( Token.create(builder.toString()) );
// append separator token, if not end of text
if ( isEnd() ) { return; }
tokens_.add( isWhitespace(c) ? Token.create(' ') : Token.create(c) );
}
private char next() {
skipSpace();
return nextAll();
}
private char nextAll() {
char c = aChar();
++pos_;
return c;
}
private char aChar() { return isEnd() ? '\0' : target_.charAt(pos_); }
private void skipSpace() {
while ( !isEnd() && Character.isWhitespace(aChar()) ) { pos_++; }
}
private boolean isEnd() { return length_ <= pos_; }
private boolean isSeparator(char c) { return exists(separators_, c); }
private boolean isQuote(char c) { return exists(quotes_, c); }
private boolean isSymbol_1(char c) { return exists(symbol1_, c); }
private boolean isWhitespace(char c) { return Character.isWhitespace(c); }
private boolean exists(char[] arr, char c) {
return java.util.Arrays.binarySearch(arr, c) >= 0;
}
private LexicalAnalyzer(String target) {
target_ = target;
length_ = target.length();
}
// internal fields ======================================================================
private static final char[] separators_ = { ':', ',', '=', '(', ')', '{', '}' };
static { Arrays.sort(separators_); }
private static final char[] quotes_ = { '"', '\'' };
static { Arrays.sort(quotes_); }
private static final char[] symbol1_ = {'(', ')', '{', '}', ':', ',', '=', '&' };
static { Arrays.sort(symbol1_); }
final String target_; // analyze target string
final int length_; // length of target string
int pos_ = 0; // "next" analyzing position
java.util.List<Token> tokens_ = new java.util.ArrayList<>(); // result
}
À l'origine, je veux rendre la chaîne de caractères à analyser comme argument obligatoire, mais il n'y a aucun moyen de la forcer à l'appelant, alors définissez une chaîne de caractères vide sur la chaîne de caractères cible. Cela libère l'appelant des tracas de la vérification des valeurs nulles et de la gestion des exceptions. En passant, il n'est pas nécessaire de vérifier les arguments dans le constructeur.
**
public static LexicalAnalyzer create(String target) {
if ( target == null || target.trim().isEmpty() ) { target = ""; }
return new LexicalAnalyzer(target);
}
private LexicalAnalyzer(String target) {
target_ = new String(target);
length_ = target.length();
}
final String target_; // analyze target string
final int length_; // length of target string
Puisqu'il s'agit d'un analyseur de phrases, il extrait la chaîne de caractères cible caractère par caractère et la traite. Les fonctions de base pour cela sont le groupe de méthodes suivant.
static {...}
immédiatement après l'initialisation du tableau) Le tri se fait avec. private char next() {
skipSpace();
return nextAll();
}
private char nextAll() {
char c = aChar();
++pos_;
return c;
}
private char aChar() { return isEnd() ? '\0' : target_.charAt(pos_); }
private void skipSpace() {
while ( !isEnd() && Character.isWhitespace(aChar()) ) { pos_++; }
}
private boolean isEnd() { return length_ <= pos_; }
private boolean isSeparator(char c) { return exists(separators_, c); }
private boolean isQuote(char c) { return exists(quotes_, c); }
private boolean isSymbol_1(char c) { return exists(symbol1_, c); }
private boolean isWhitespace(char c) { return Character.isWhitespace(c); }
private boolean exists(char[] arr, char c) {
return java.util.Arrays.binarySearch(arr, c) >= 0;
}
private static final char[] separators_ = { ',', '=', '(', ')', '{', '}', ':' };
static { Arrays.sort(separators_); }
private static final char[] quotes_ = { '"', '\'' };
static { Arrays.sort(quotes_); }
private static final char[] symbol1_ = { '(', ')', '{', '}', ':', ',', '=', '&' };
static { Arrays.sort(symbol1_); }
int pos_ = 0; // "next" analyzing position
Effectue une analyse lexicale et renvoie le résultat sous la forme d'une liste d'objets Token créés précédemment. La valeur de retour est une copie de la liste de jetons tokens_ conservée en interne. Comme il s'agit d'une liste standard, l'appelant peut ajouter / supprimer des éléments, ce qui affecte les jetons_ détenus par l'objet (faisant exactement référence au même objet). Si vous appelez analy () une deuxième fois ou plus tard, il renverra une copie de la liste de jetons qui a déjà été créée. Aucune réanalyse n'est effectuée.
En modifiant l'implémentation de next () (en sautant les caractères vides) et en ajoutant une méthode de jugement, il est plus propre que le contrôle de l'instruction switch précédente. En passant, l'intention du jugement est reflétée dans le nom de la méthode, donc aucun commentaire n'est nécessaire. (N'est-ce pas?)
public java.util.List<Token> analyze() {
char c;
while ( (c = next()) != '\0' ) {
if ( isSymbol_1(c) ) {
tokens_.add( Token.create(c) );
continue;
}
if ( isQuote(c) ) {
quotedText(c);
continue;
}
text(c);
}
return new java.util.ArrayList<>(tokens_);
}
java.util.List<Token> tokens_ = new java.util.ArrayList<>(); // result
Transformez la chaîne entre guillemets en un seul jeton, y compris des caractères vides. L'espace blanc entre le guillemet et le premier caractère et entre le dernier caractère et le guillemet est supprimé, mais l'espace blanc entre les autres chaînes, y compris les sauts de ligne et les tabulations, est conservé. ..
/**Découpez le caractère de citation suivant sous forme de bloc de jetons. */
private void quotedText(char quote) {
tokens_.add( Token.create(quote)); // create token of begin quote
java.lang.StringBuilder builder = new java.lang.StringBuilder();
char c;
while ( (c = nextAll()) != '\0' && c != quote) { builder.append(c); }
if ( builder.length() != 0 ) {
tokens_.add( Token.create(builder.toString()) ); // append string
}
tokens_.add( Token.create(c) ); // append token of end quote
}
Découpez un jeton de chaîne qui n'est pas entre guillemets.
/**Découpez les séparateurs et les caractères vides sous forme de bloc de jetons. */
private void text(char first) {
java.lang.StringBuilder builder = new java.lang.StringBuilder();
builder.append(first);
char c;
while ( (c = nextAll()) != '\0' && !isSeparator(c) && !Character.isWhitespace(c) ) {
builder.append(c);
}
tokens_.add( Token.create(builder.toString()) );
if ( isEnd() ) { return; }
tokens_.add( isWhitespace(c) ? Token.create(' ') : Token.create(c) );
}
Méthode d'acquisition d'état LexicalAnalyzer.
Token.kind () == Kinds.Unknown```. public boolean isEmpty() { return tokens_.size() == 0;}
public boolean isValid() {
return !isEmpty() && tokens_.stream().noneMatch( e -> e.kind() == Token.Kinds.Unknown );
}
Dernier point mais non le moindre, voici la méthode main () pour tester et traiter les résultats. ~~ Je ne connais pas JUnit ~~ Le jeton délimiteur est masqué pour réduire le nombre de lignes dans le résultat du traitement.
public static void main(String[] args) {
String s;
java.util.List<Token> tokens;
LexicalAnalyzer lex;
System.out.println("test 1 -----------------------------------------------------------------------------------");
LexicalAnalyzer.create(null).analyze().stream().filter( e -> e.kind() != Token.Kinds.Separator )
.forEach( e -> System.out.println(e) );
System.out.printf("isEmpty() after analyze : %s\r\n", lex.isEmpty());
System.out.println("\r\ntest 2 -------------------------------------------------------------------------------");
s = "s";
lex = LexicalAnalyzer.create(s);
System.out.printf("isEmpty() before analyze() : %s\r\n", lex.isEmpty());
lex.analyze().stream().filter( e -> e.kind() != Token.Kinds.Separator )
.forEach( e -> System.out.println(e) );
System.out.printf("isEmpty() after analyze() : %s\r\n", lex.isEmpty());
System.out.println("test 3 -----------------------------------------------------------------------------------");
s = " [some] -c \" text document \", \r\n (sequence1, \"seq 2\r\n quoted\",seq3 seq4 'seq 5 ')\"ss";
lex = LexicalAnalyzer.create(s);
System.out.printf("isValid() before analyze() : %s\r\n", lex.isValid());
lex.analyze().stream().filter( e -> e.kind() != Token.Kinds.Separator )
.forEach( e -> System.out.println(e) );
System.out.printf("isValid() after analyze() : %s\r\n", lex.isValid());
}
Dans la méthode factory, une chaîne de caractères vide est définie pour qu'elle fonctionne sans erreur, donc il n'y a pas de problème même si analy () et le stream () suivant sont exécutés. Rien ne s'affiche. "isEmpty () ..." est le résultat du test de la méthode isEmpty ().
Plutôt que de vérifier les résultats de l'analyse, vérifiez les spécifications de isEmpty () avant et après l'exécution d'analyser ().
Puisque "seq 2 ..." contient un saut de ligne dans le devis, le résultat du traitement contient également un saut de ligne. La raison pour laquelle isValid () à la fin est false est que l'analyse a été lancée sous la forme d'une chaîne de caractères entre guillemets, mais il n'y avait pas de paire de guillemets et la chaîne de caractères s'est terminée. S'il est fermé correctement, ce sera vrai.
test 1 -------------------------------------------------------------------------------
isEmpty() after analyze : true
test 2 -------------------------------------------------------------------------------
isEmpty() before analyze() : true
[String : "s"]
isEmpty() after analyze() : false
test 3 -------------------------------------------------------------------------------
isValid() before analyze() : false
[String : "[some]"]
[String : "-c"]
[DoubleQuote : """]
[String : "text document"]
[DoubleQuote : """]
[LeftParenthesis : "("]
[String : "sequence1"]
[DoubleQuote : """]
[String : "seq 2
quoted"]
[DoubleQuote : """]
[String : "seq3"]
[String : "seq4"]
[SingleQuote : "'"]
[String : "seq 5"]
[SingleQuote : "'"]
[RightParenthesis: ")"]
[DoubleQuote : """]
[String : "ss"]
[Unknown : "**Unknown**"]
isValid() after analyze() : false
Cela fait longtemps, mais c'est la fin de la création d'un analyseur de phrases.
Qu'en est-il de l'analyse syntaxique jusqu'à présent? Cependant, je ne peux pas l'écrire car la syntaxe de l'utilisateur n'a pas été décidée. La grammaire attendue a été décidée dans une certaine mesure, mais on ne sait pas quand l'écrire car la préparation ne rattrape pas. (Vous devez d'abord terminer la classe de console ...)
Recommended Posts