Looking for advice on project. Parsing logical expression

倖福魔咒の 提交于 2019-11-29 02:38:16

If you're allowed to use a parser generator tool like ANTLR, here's how you could get started. The grammar for a simple logic-language could look like this:

grammar Logic;

parse
  :  expression EOF
  ;

expression
  :  implication
  ;

implication
  :  or ('->' or)*
  ;

or
  :  and ('||' and)*
  ;

and
  :  not ('&&' not)*
  ;

not
  :  '~' atom
  |  atom
  ;

atom
  :  ID
  |  '(' expression ')'
  ;

ID    : ('a'..'z' | 'A'..'Z')+;
Space : (' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;};

However, if you'd parse input like (P || Q || R) && ((P -> R) -> Q) with a parser generated from the grammar above, the parse tree would contain the parenthesis (something you're not interested in after parsing the expression) and the operators would not be the root of each sub-trees, which doesn't make your life any easier if you're interested in evaluating the expression.

You'll need to tell ANTLR to omit certain tokens from the AST (this can be done by placing a ! after the token/rule) and make certain tokens/rules the root of their (sub) tree (this can be done by placing a ^ after it). Finally, you need to indicate in the options section of your grammar that you want a proper AST to be created instead of a simple parse tree.

So, the grammar above would look like this:

// save it in a file called Logic.g
grammar Logic;

options {
  output=AST;
}

// parser/production rules start with a lower case letter
parse
  :  expression EOF!    // omit the EOF token
  ;

expression
  :  implication
  ;

implication
  :  or ('->'^ or)*    // make `->` the root
  ;

or
  :  and ('||'^ and)*    // make `||` the root
  ;

and
  :  not ('&&'^ not)*      // make `&&` the root
  ;

not
  :  '~'^ atom    // make `~` the root
  |  atom
  ;

atom
  :  ID
  |  '('! expression ')'!    // omit both `(` and `)`
  ;

// lexer/terminal rules start with an upper case letter
ID    : ('a'..'z' | 'A'..'Z')+;
Space : (' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;};

You can test the parser with the following class:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;

public class Main {
  public static void main(String[] args) throws Exception {

    // the expression
    String src = "(P || Q || R) && ((P -> R) -> Q)";

    // create a lexer & parser
    LogicLexer lexer = new LogicLexer(new ANTLRStringStream(src));
    LogicParser parser = new LogicParser(new CommonTokenStream(lexer));

    // invoke the entry point of the parser (the parse() method) and get the AST
    CommonTree tree = (CommonTree)parser.parse().getTree();

    // print the DOT representation of the AST 
    DOTTreeGenerator gen = new DOTTreeGenerator();
    StringTemplate st = gen.toDOT(tree);
    System.out.println(st);
  }
}

Now to run the Main class, do:

*nix/MacOS

java -cp antlr-3.3.jar org.antlr.Tool Logic.g 
javac -cp antlr-3.3.jar *.java
java -cp .:antlr-3.3.jar Main

Windows

java -cp antlr-3.3.jar org.antlr.Tool Logic.g 
javac -cp antlr-3.3.jar *.java
java -cp .;antlr-3.3.jar Main

which will print a DOT source of the following AST:

(image produced with graphviz-dev.appspot.com)

Now all you need to do is evaluate this AST! :)

In Perl you can use Regexp::Grammars to do the parsing. It may be a little on the "grenade to kill an ant" side, but it should work.

Edit: Here is a (very quick) example which might get you going.

#!/usr/bin/env perl

use strict;
use warnings;

use Regexp::Grammars;
use Data::Dumper;

my $parser = qr/
  <nocontext:>

  <Logic>

  <rule: Logic>     <[Element]>*

  <rule: Element>   <Group> | <Operator> | <Item>

  <rule: Group>     \( <[Element]>* \)

  <rule: Operator>  (?:&&) | (?:\|\|) | (?:\-\>)

  <rule: Item>      \w+
/xms;                    #/ #Fix Syntax Highlight

my $text = '(P && Q) -> R';

print Dumper \%/ if $text =~ $parser; #/ #Fix Syntax Highlight

Look into JavaCC or ANTLR. Regexps won't work.

You can probably also run your own parser using StreamTokenizer.

Ira Baxter

Building an expression parser is easy. Attaching actions to compute a value as you parse it is easy, too.

I assume you can write a BNF for your expression language.

This answer shows you how to build a parser easily, if you have a BNF.

Is there an alternative for flex/bison that is usable on 8-bit embedded systems?

If you want to write your own parser, use the Shunting-yard algorithm to get rid of parentheses by converting the expression from infix into postfix notation or directly into a tree.

Another parser generator for Java is CUP.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!