2016-04-08 13 views
3

bulundu: Ben ((1+2) > (3+4)) ifadeyi değiştirirsenizScala Kombinatörleri JavaTokennParsers '||' Ben bu testi çalıştırdığınızda beklendiği ancak '>'

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)) 

çalıştığını ancak isteğe bağlı destek edebilmek gerekir:

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))) 
    } 
} 

alıyorum parantez. Baska öneri?

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) 
    } 
    } 
} 

günlük izleme:

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 scala.util.parsing.combinator.Packra[email protected] 
trying bool_term at [email protected]004 
trying bool at [email protected]004 
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)' nin bool_expr ile ayrıştırıldığını unutmayın: bool_parens -> [1.6] ayrıştırılmış: BinaryOp (+, Sayı (1.0), Sayı (2.0), Num) '. Ancak dikkate alınması gereken 'ilişkisel' ayrıştırıcı için bir 'num_expr' ile ayrıştırılmalıdır. – Kolmar

+0

Ayrıca, 'foldExpr''deki bazı türlerin yanlış olduğuna inanıyorum. Örneğin. "num_equality" ifadesinde sonuç, ExprType.Bool olmalıdır, ancak '^^ foldExpr (ExprType.Num)' olmalıdır. – Kolmar

+0

@Kolmar Eşitlik ayrıştırıcılarının sırasını değiştirerek çalışmayı başarabildim, böylece "num_equality", "bool_equality" den önce gelir. Ancak, bunu yaptığımda "true" ve "false", ayrılmış boolean anahtar kelimeler yerine "ident" değerleri olarak ayrıştırıldı. Bu yüzden bunu düzeltmek için bir 'ayrılmış' çözümleyici ekledim. Bu tavsiyeye göre düştüğüne inanıyorum. –

cevap

0

@Kolmar giriş num_expr ile ayrıştırıldı edilmesi gerektiğini doğru Aşağıdaki tablo sınıf tanımın yanı sıra günlük izleme bulunmaktadır. Aşağıdaki çözüm, neyle geldiğim, ama istediğimden daha az güçlü hissettiriyor. Sorun önceliğe dayanıyor ama ben ayrıştırıcıyı boolean şubesi başarısız olduktan sonra kontrol etmeye devam etmenin bir yolu olduğunu umuyordum. Bunun yerine kontrol etmek equality ayrıştırıcı sırasını takas num_equalitybool_equality önce:

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

Ama true ve false anahtar sayısal değerler sonra değerlendirilir boolean şube, tanımlı oldukları için, bunlar tip AnyVar yerine olarak ayrıştırıldı Bool. Ben reserved ayrıştırıcı eklendi ve şu şekilde var_factor değiştiğini düzeltmek için:

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 

Şu anda tüm şimdi geçiyoruz yaklaşık 70 testler var. Daha sağlam çözümler için önerilere hâlâ açığım (daha az kırılgan) ama şu an için yeterince iyi çalışıyor gibi görünüyor.

İlgili konular