0

У меня есть набор входящих записей, которые должны оцениваться в соответствии с набором логических предложений, определенных и сохраненных. Пример логического раздел быть как:анализатор парциальных логических параметров java

Acct1 != 'Y' AND Acct2 > 1004 AND Acct3 >= 96 AND Acct4 < 1004 AND Acct5 = 99 AND ((Acct6 <= 9090 OR Acct7 IN (A1,A2,A6) AND Acct1 NOT IN (A3,A4)) AND Formatted LIKE 'LINUX' AND Acct9 NOT LIKE 'WINDOWS' AND (Acct10 = 'N' AND NOT Acct11 = 'N') AND EditableField BETWEEN (10 AND 20)) 

Мой ввод данных в пункте быть как:

map.put(Acct1,"Y") 
map.put(Acct2,1010) 
map.put(Acct3,99) 
map.put(Acct4,1015) 
map.put(Acct5,99) 
map.put(Acct6,9090) 
map.put(Acct7,"A3") 
map.put(Formatted,"LINUX_INST") 
map.put(Updated,"LINUX_TMP") 
map.put(Acct10,"Y") 
map.put(Acct11,"N") 
map.put(EditableFIeld,25) 

Я должен оценить входящие записи заселенных в карту на пункте определено выше, и печати истинным или false на основе результата оценки.

Условия предложения и значения карты будут изменены и выполнены.

У меня есть следующие условные пункты должны быть оценены:

!= 
> 
>= 
< 
= 
<= 
IN(
NOT IN(
LIKE(
NOT LIKE(
BETWEEN(
AND 
OR 
AND NOT 
OR NOT 

Я попытался с помощью грамматики генераторов, но я сказал, что это не рекомендуемое решение для нашего приложения поэтому я ищу кода Java и I иметь этот подробный пример для ссылки на AND, OR, =. resolving logical operations - AND, OR, looping conditions dynamically и ищем фрагменты, чтобы построить сверху, если это возможно.

+0

Что вы сделали и что не сработали? – kosa

+1

Я пытался использовать генераторы грамматики, поскольку я не могу кодировать такие сложные операторы в java, но это не принято из-за технических ограничений. Следовательно, поиск фрагментов, доступных для оценки условий в java. – Atom

+1

Знаете ли вы, что ограничения _which_ делают генератор синтаксического анализа неосуществимым для вашего случая? Некоторые генераторы парсеров генерируют чистый код без стандартной библиотеки и обычно настроены на то, чтобы быть достаточно эффективными в скорости и хранении. Если генераторы-синтаксические анализаторы невозможны, знание причин может помочь кому-то избежать написания подробного ответа, который не может быть оправдан по тем же причинам. –

ответ

2

Чтобы получить ответ на вопрос, ряд SO вопросов (например, 1, 2) описывают основы написания парсера вручную, хотя на практике это очень необычно, чтобы написать парсер вручную вне курсов компиляторов университета из-за шаблонный и требовательные детали.

Как уже говорилось в комментариях, основная причина избежать генераторов грамматики заключается в том, чтобы избежать зависимости от внешних библиотек. Однако при использовании генератора грамматики (генератора синтаксического анализатора), такого как JavaCC (Java Compiler-Compiler), нет JAR-файлов или внешних зависимостей: двоичный код JavaCC преобразует спецификацию грамматики в код Java, который можно запускать без привлечения каких-либо дополнительных библиотек ,

В качестве примера можно привести учебник IBM, JoAnn Brereton's "Use JavaCC to build a user friendly boolean query language", который, кстати, включает в себя грамматику для языка поиска, в отличие от вашего.

Пример входа:

actor = "Christopher Reeve" and keyword=action and keyword=adventure 
(actor = "Christopher Reeve" and keyword=action) or keyword=romance 
actor = "Christopher Reeve" and (keyword=action or keyword=romance) 

Грамматика выдержки:

TOKEN : 
{ 
<STRING : (["A"-"Z", "0"-"9"])+ > 
<QUOTED_STRING: "\"" (~["\""])+ "\"" > 
} 

void queryTerm() : 
{ 
} 
{ 
     (<TITLE> | <ACTOR> | 
     <DIRECTOR> | <KEYWORD>) 
     (<EQUALS> | <NOTEQUAL>) 
     (<STRING> | <QUOTED_STRING>) 
     | 
     <LPAREN> expression() <RPAREN> 
} 

Выходные файлы:

  • UQLParser.java
  • UQLParserConstants.java
  • ВКС ParserTokenManager.java
  • TokenMgrError.java
  • ParseException.java
  • Token.java
  • SimpleCharStream.java

Это один из нескольких синтаксических анализаторов генераторов, которые можно рассмотреть; другие, такие как yacc and bison, также создают автономные файлы Java, не требуя внешних библиотек.При необходимости вы можете проверить сгенерированные файлы Java непосредственно в своем репозитории, оставив исходный файл компилятора .jj только в том случае, если вам нужно настроить синтаксис. (Хотя лучше было бы лучше компилировать только из исходного кода как часть вашего процесса сборки и не проверять сгенерированные файлы в исходном элементе управления, это может лучше соответствовать вашим ограничениям только для Java-решения.)

4

Если вы хотите избежать генератор синтаксического анализатора, рассмотрите возможность использования StreamTokenizer для реализации рекурсивного парсера спуска, с одним методом для каждого правила грамматики.

Для подмножества вашей грамматики, это должно выглядеть примерно так (и должно быть расширено до вашей полной грамматики):

public class Parser { 

    public static Node parse(String expr) { 
    StreamTokenizer tokenizer =  
     new StreamTokenizer(new StringReader(expr)); 
    tokenizer.nextToken(); 
    Parser parser = new Parser(tokenizer); 
    Node result = parser.parseExpression(); 
    if (tokenizer.ttype != StreamTokenizer.TT_EOF) { 
     throw new RuntimeException("EOF expected, got "  
      + tokenizer.ttype + "/" + tokenizer.sval); 
    } 

    private StreamTokenizer tokenizer; 

    private Parser(StreamTokenizer tokenizer) { 
    this.tokenizer = tokenizer; 
    } 

    private Node parseExpression() { 
    Node left = parseAnd(); 
    if (tokenizer.ttype == StreamTokenizer.TT_WORD 
     && tokenizer.sval.equals("OR")) { 
     tokenizer.nextToken(); 
     return new OperationNode(OperationNode.Type.OR, 
      left, parseExpression()); 
    } 
    return left; 
    } 

    private Node parseAnd() { 
    Node left = parseRelational(); 
    if (tokenizer.ttype == StreamTokenizer.TT_WORD 
     && tokenizer.sval.equals("AND")) { 
     tokenizer.nextToken(); 
     return new OperationNode(OperationNode.Type.AND, 
      left, parseAnd()); 
    } 
    return left; 
    } 

    private Node parseRelational() { 
    Node left = parsePrimary(); 
    OperationNode.Type type; 
    switch (tokenizer.ttype) { 
     case '<': type = OperationNode.Type.LESS; break; 
     case '=': type = OperationNode.Type.EQUAL; break; 
     case '>': type = OperationNode.Type.GREATER; break; 
     default:  
     return left; 
    } 
    tokenizer.nextToken(); 
    return new OperationNode(type, left, parseRelational()); 
    } 

    private Node parsePrimary() { 
    Node result; 
    if (tokenizer.ttype == '(') { 
     tokenizer.nextToken(); 
     result = parseExpression(); 
     if (tokenizer.ttype != ')') { 
     throw new RuntimeException(") expected, got " 
      + tokenizer.ttype + "/" + tokenizer.sval); 
     } 
    } else if (tokenizer.ttype == '"' || tokenizer.ttype == '\'') { 
     result = new LiteralNode(tokenizer.sval); 
    } else if (tokenizer.ttype == TT_NUMBER) { 
     result = new LiteralNode(tokenizer.nval); 
    } else if (tokenizer.ttype == StreamTokenizer.TT_WORD) { 
     result = new FieldNode(tokenizer.sval); 
    } else { 
     throw new RuntimeException("Unrecognized token: "  
      + tokenizer.ttype + "/" + tokenizer.sval); 
    } 
    tokenizer.nextToken(); 
    return result; 
    } 
} 

Это предполагает иерархию объектов Node так:

interface Node { 
    Object eval(Map<String,Object> data); 
} 

class FieldNode implements Node { 
    private String name;  
    FieldNode(String name) { 
    this.name = name; 
    } 
    public Object eval(Map<String,Object> data) { 
    return data.get(name); 
    } 
} 

class LiteralNode implements Node { 
    private Object value;  
    FieldNode(Object value) { 
    this.value = value; 
    } 
    public Object eval(Map<String,Object> data) { 
    return value; 
    } 
} 

class OperationNode implements Node { 
    enum Type { 
    AND, OR, LESS, GREATER, EQUALS 
    } 
    private Type type; 
    private Node leftChild; 
    private Node rightChild; 

    OperationNode(Type type, Node leftChild, Node rightChild) { 
    this.type = type; 
    this.leftChild = leftChild; 
    this.rightChild = rightChild; 
    } 

    public Object eval(Map<String,Object> data) { 
    Object left = leftChild.eval(data); 
    Object right = rightChild.eval(data); 
    switch (type) { 
     case AND: return ((Boolean) left) && ((Boolean) right); 
     case OR: return ((Boolean) left) || ((Boolean) right); 
     case LESS: return ((Comparable) left).compareTo(right) < 0; 
     case EQUALS: return left.equals(right); 
     case GREATE: return ((Comparable) left).compareTo(right) > 0; 
     default: 
     throw new RuntimeException("Invalid op: " + type); 
    } 
    }