2016-04-08 11 views
3

が見つかりました:私は((1+2) > (3+4))に式を変更した場合スカラコンビネータJavaTokennParsers '||'私はこのテストを実行すると予想されるが、「>」

Left("`||' expected but `>' found") did not equal Right(BinaryOp(>,BinaryOp(+,Number(1.0),Number(2.0),Num),BinaryOp(+,Number(3.0),Number(4.0),Num),Bool)) 

が、それは動作しますが、私は、オプションをサポートできるようにする必要があります。

import com.dvMENTALmadness.parsers.{BinaryOp, ExprType, Number, SimpleEquationParser} 
import org.scalatest.FlatSpec 

class SimpleEquationParserTest extends FlatSpec { 
    "(1+2) > (3+4)" should " == false" in { 
    val result = SimpleEquationParser("(1+2) > (3+4)") 
    println(result) 
    assert(result == Right(BinaryOp(">",BinaryOp("+",Number(1.0),Number(2.0),ExprType.Num),BinaryOp("+",Number(3.0),Number(4.0),ExprType.Num),ExprType.Bool))) 
    } 
} 

を私が取得括弧。助言がありますか?以下は、クラス定義と同様に、ログのトレースです:

package com.dvMENTALmadness.parsers 

import com.dvMENTALmadness.parsers.ExprType.ExprType 
import scala.util.parsing.combinator.{JavaTokenParsers, PackratParsers} 

sealed trait Expr 
sealed trait Var extends Expr { 
    def key: String 
} 
object ExprType extends Enumeration { 
    type ExprType = Value 
    val Var, Num, Text, Bool = Value 
} 

case class Text(value: String) extends Expr 
case class Number(value: Double) extends Expr 
case class Bool(value: Boolean) extends Expr 
case class NumericVar(key: String) extends Var 
case class TextVar(key: String) extends Var 
case class BoolVar(key: String) extends Var 
case class AnyVar(key: String) extends Var 
case class UnaryOp(operator: String, arg: Expr, expType: ExprType) extends Expr 
case class BinaryOp(operator: String, left : Expr, right: Expr, expType: ExprType) extends Expr 

trait ExprParser extends JavaTokenParsers with PackratParsers { 

    def foldExpr(etype: ExprType)(pat: Expr ~ List[String ~ Expr]) : Expr = pat match { 
    case left ~ xs => xs.foldLeft(left)((left, acc) => acc match { 
     case op ~ right => BinaryOp(op, left, right, etype) 
    }) 
    } 

    // see: http://jim-mcbeath.blogspot.com/2011/07/debugging-scala-parser-combinators.html 
    implicit def toLogged(name: String) = new { 
    def !!![T](p:Parser[T]) = log(p)(name) // for debugging 
    //def !!![T](p:Parser[T]) = p   // for production 
    } 
} 

trait BoolParser extends ExprParser { 

    // Operator precedence: http://www.tutorialspoint.com/scala/scala_operators.htm 
    def expr = "expr" !!! bool_expr | num_expr | text_expr 
    def bool_expr = "bool_expr" !!! or | bool_term 
    def num_expr = "num_expr" !!! num_equality | num_term 
    def text_expr = "text_expr" !!! text_equality 

    // operations 
    def or = "or" !!! and ~ rep("||" ~ and) ^^ foldExpr(ExprType.Bool) 
    def and = "and" !!! equality ~ rep("&&" ~ equality) ^^ foldExpr(ExprType.Bool) 

    def equality = "equality" !!! bool_equality | num_equality | text_equality 
    def bool_equality = "bool_equality" !!! bool_term ~ rep("==" ~ bool_term | "!=" ~ bool_term) ^^ foldExpr(ExprType.Bool) 
    def num_equality = "num_equality" !!! relational ~ rep("==" ~ relational | "!=" ~ relational) ^^ foldExpr(ExprType.Num) 
    def text_equality = "text_equality" !!! concat ~ rep("==" ~ concat | "!=" ~ concat) ^^ foldExpr(ExprType.Text) 

    def relational = "relational" !!! additive ~ rep(">=" ~ additive | "<=" ~ additive | ">" ~ additive | "<" ~ additive) ^^ foldExpr(ExprType.Num) 
    def additive = "additive" !!! multiplicative ~ rep("+" ~ multiplicative | "-" ~ multiplicative) ^^ foldExpr(ExprType.Num) 
    def multiplicative = "multiplicative" !!! num_term ~ rep("*" ~ num_term | "/" ~ num_term | "%" ~ num_term) ^^ foldExpr(ExprType.Num) 
    def concat = "concat" !!! text ~ rep("+" ~ text) ^^ foldExpr(ExprType.Text) 

    def operators = "*" | "/" | "%" | "+" | "-" | "&&" | "||" 

    // terms 
    def term = "term" !!! bool_term | num_term 
    def bool_term = "bool_term" !!! bool | bool_parens | not 
    def num_term = "num_term" !!! num | num_parens | neg 

    def not:PackratParser[Expr] = "not" !!! "!" ~> bool_term ^^ (x => UnaryOp("!", x, ExprType.Bool)) 
    def neg:PackratParser[Expr] = "neg" !!! "-" ~> num_term ^^ (x => UnaryOp("-", x, ExprType.Num)) 

    def parens:PackratParser[Expr] = "parens" !!! "(" ~> expr <~ ")" 
    def bool_parens:PackratParser[Expr] = "bool_parens" !!! "(" ~> bool_expr <~ ")" 
    def num_parens:PackratParser[Expr] = "num_parens" !!! "(" ~> num_expr <~ ")" 
    def text_parens:PackratParser[Expr] = "text_parens" !!! "(" ~> text_expr <~ ")" 

    //values 
    def bool: PackratParser[Expr] = "bool" !!! 
    "true" ^^^ (Bool(true)) | 
    "false" ^^^ (Bool(false)) | 
    var_factor 

    def num: PackratParser[Expr] = "num" !!! 
    floatingPointNumber ^^ (x => Number(x.toDouble)) | 
    wholeNumber ^^ (x => Number(x.toDouble)) | 
    var_factor 

    def text: PackratParser[Expr] = "text" !!! 
    stringLiteral ^^ (x => Text(stripQuote(x))) | 
    var_factor 

    def var_factor: Parser[Expr] = "var_factor" !!! 
    id <~ ".asNumber" ^^ (x => NumericVar(x)) | 
    id <~ ".asText" ^^ (x => TextVar(x)) | 
    id <~ ".asBool" ^^ (x => BoolVar(x)) | 
    id ^^ (x => AnyVar(x)) 

    def id: PackratParser[String] = "id" !!! opt("{") ~> ident <~ opt("}") 

    private def stripQuote(s: String) = { 
    s.substring(1, s.length - 1) 
    } 
} 

object SimpleEquationParser extends BoolParser { 
    def apply(input: String) : Either[String,Expr] = { 

    parseAll("root" !!! expr, input) match { 
     case Success(r, _) => Right(r) 
     case Failure(msg, _) => Left(msg) 
     case Error(msg, _) => Left(msg) 
    } 
    } 
} 

ログトレース:

trying root at [email protected]004 
trying expr at [email protected]004 
trying bool_expr at [email protected]004 
trying or at [email protected]004 
trying and at [email protected]004 
trying equality at [email protected]004 
trying bool_equality at [email protected]004 
trying bool_term at [email protected]004 
trying bool at scala.util.parsing[email protected] 
bool --> [1.1] failure: `true' expected but `(' found 

(1+2) > (3+4) 
^ 
trying var_factor at [email protected]004 
trying id at [email protected]004 
id --> [1.1] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `(' found 

(1+2) > (3+4) 
^ 
var_factor --> [1.1] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `(' found 

(1+2) > (3+4) 
^ 
trying id at [email protected]004 
id --> [1.1] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `(' found 

(1+2) > (3+4) 
^ 
trying id at [email protected]004 
id --> [1.1] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `(' found 

(1+2) > (3+4) 
^ 
trying id at [email protected]004 
id --> [1.1] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `(' found 

(1+2) > (3+4) 
^ 
bool_term --> [1.1] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `(' found 

(1+2) > (3+4) 
^ 
trying bool_parens at [email protected]004 
trying bool_expr at sca[email protected]59d016c9 
trying or at sca[email protected]59d016c9 
trying and at sca[email protected]59d016c9 
trying equality at sca[email protected]59d016c9 
trying bool_equality at sca[email protected]59d016c9 
trying bool_term at sca[email protected]59d016c9 
trying bool at sca[email protected]59d016c9 
bool --> [1.2] failure: `true' expected but `1' found 

(1+2) > (3+4) 
^ 
trying var_factor at sca[email protected]59d016c9 
trying id at sca[email protected]59d016c9 
id --> [1.2] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `1' found 

(1+2) > (3+4) 
^ 
var_factor --> [1.2] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `1' found 

(1+2) > (3+4) 
^ 
trying id at sca[email protected]59d016c9 
id --> [1.2] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `1' found 

(1+2) > (3+4) 
^ 
trying id at sca[email protected]59d016c9 
id --> [1.2] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `1' found 

(1+2) > (3+4) 
^ 
trying id at sca[email protected]59d016c9 
id --> [1.2] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `1' found 

(1+2) > (3+4) 
^ 
bool_term --> [1.2] failure: string matching regex `\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*' expected but `1' found 

(1+2) > (3+4) 
^ 
trying bool_parens at sca[email protected]59d016c9 
bool_parens --> [1.2] failure: `(' expected but `1' found 

(1+2) > (3+4) 
^ 
trying not at sca[email protected]59d016c9 
not --> [1.2] failure: `!' expected but `1' found 

(1+2) > (3+4) 
^ 
bool_equality --> [1.2] failure: `!' expected but `1' found 

(1+2) > (3+4) 
^ 
equality --> [1.2] failure: `!' expected but `1' found 

(1+2) > (3+4) 
^ 
trying num_equality at sca[email protected]59d016c9 
trying relational at sca[email protected]59d016c9 
trying additive at sca[email protected]59d016c9 
trying multiplicative at sca[email protected]59d016c9 
trying num_term at sca[email protected]59d016c9 
trying num at sca[email protected]59d016c9 
num --> [1.3] parsed: 1 
num_term --> [1.3] parsed: Number(1.0) 
multiplicative --> [1.3] parsed: (Number(1.0)~List()) 
trying multiplicative at sca[email protected]36c88a32 
trying num_term at sca[email protected]36c88a32 
trying num at sca[email protected]36c88a32 
num --> [1.5] parsed: 2 
num_term --> [1.5] parsed: Number(2.0) 
multiplicative --> [1.5] parsed: (Number(2.0)~List()) 
additive --> [1.5] parsed: (Number(1.0)~List((+~Number(2.0)))) 
relational --> [1.5] parsed: (BinaryOp(+,Number(1.0),Number(2.0),Num)~List()) 
num_equality --> [1.5] parsed: (BinaryOp(+,Number(1.0),Number(2.0),Num)~List()) 
and --> [1.5] parsed: (BinaryOp(+,Number(1.0),Number(2.0),Num)~List()) 
or --> [1.5] parsed: (BinaryOp(+,Number(1.0),Number(2.0),Num)~List()) 
bool_expr --> [1.5] parsed: BinaryOp(+,Number(1.0),Number(2.0),Num) 
bool_parens --> [1.6] parsed: BinaryOp(+,Number(1.0),Number(2.0),Num) 
bool_equality --> [1.6] parsed: (BinaryOp(+,Number(1.0),Number(2.0),Num)~List()) 
equality --> [1.6] parsed: BinaryOp(+,Number(1.0),Number(2.0),Num) 
and --> [1.6] parsed: (BinaryOp(+,Number(1.0),Number(2.0),Num)~List()) 
or --> [1.6] parsed: (BinaryOp(+,Number(1.0),Number(2.0),Num)~List()) 
bool_expr --> [1.6] parsed: BinaryOp(+,Number(1.0),Number(2.0),Num) 
expr --> [1.6] parsed: BinaryOp(+,Number(1.0),Number(2.0),Num) 
root --> [1.6] parsed: BinaryOp(+,Number(1.0),Number(2.0),Num) 
Left(`||' expected but `>' found) 
+0

'(1 + 2)'は 'bool_expr'で解析されることに注意してください:' bool_parens - > [1.6]構文解析:BinaryOp(+、Number(1.0)、Number(2.0)、Num) '。しかし、必要な ''リレーショナル 'パーサが考慮されるためには 'num_expr'で解析されるべきです。 – Kolmar

+0

また、 'foldExpr'のいくつかの型が間違っていると思います。例えば。 'num_equality'の結果は' ExprType.Bool'でなければなりませんが、 '^^ foldExpr(ExprType.Num)'を持ちます。 – Kolmar

+0

@Kolmar 'num_equality'が' bool_equality'の前に来るように、等価パーサーの順番を入れ替えることで動作させることができました。しかし、私がしたとき、 'true'と' false'は予約されたブールキーワードの代わりに 'ident'値として解析されました。だから私はそれを修正するために '予約された'パーサーを追加しました。私はそれがあなたの推薦に沿っていると信じています。 –

答えて

0

@Kolmarは、入力がnum_exprで解析する必要があることが正しいです。以下の解決策は、私が思いついたものですが、私が望むよりも強固ではないと感じています。問題は優先順位に至りましたが、ブール分岐が失敗した後もパーサにチェックを継続させる方法があることを期待していました。代わりに私がチェックするequalityパーサの順番を入れ替えnum_equalitybool_equality前:

def equality = "equality" !!! num_equality | bool_equality | text_equality 

しかしtruefalseキーワードは、数値の後に評価されるブールのブランチで定義されているので、彼らはタイプAnyVarの代わりとして解析しました。 Bool。私はreservedパーサを追加し、次へvar_factorを変更することを修正するには:

def var_factor: Parser[Expr] = "var_factor" !!! 
    id <~ ".asNumber" ^^ (x => NumericVar(x)) | 
    id <~ ".asText" ^^ (x => TextVar(x)) | 
    id <~ ".asBool" ^^ (x => BoolVar(x)) | 
    not(reserved) ~> id ^^ (x => AnyVar(x)) 

def id: PackratParser[String] = "id" !!! opt("{") ~> ident <~ opt("}") 
def reserved: Parser[String] = """\b(true|false)\b""".r 

私は現在、すべての今渡している約70のテストを持っています。私はまだ堅牢なソリューション(脆弱ではない)の提案はまだ開いていますが、今のところ十分に機能しているようです。

関連する問題