В основном, это медленно и потребляет слишком много памяти, потому что ваша грамматика невероятно неэффективна.
Давайте рассмотрим вторую строку: B = A:(1+2)
.Он попытается проанализировать эту строку следующим образом:
- term4
OR
term4 и затем term4. - term3
AND
term3, а затем term3. - term2
<>
term2, затем =
, затем NE
, затем EQ
, а затем term2. - term1 8 различных операторов term1, затем term1.
- term
+
term, term-
термин, термин :
термин, а затем термин. - коэффициент * коэффициент 1026 * коэффициент, коэффициент
/
коэффициент, коэффициент MOD
коэффициент и затем коэффициент. - выражение в скобках,унарный множитель, функция, числовой литерал, строковый литерал, идентификатор.
Первая попытка выглядит следующим образом:
ident * factor + term < term1 <> term2 AND term3 OR term4
Я пропускаю скобки, унарные, функции, числовые истроковые литералы, потому что они не соответствуют A
- хотя function
, вероятно, соответствует, но его определение недоступно.Теперь, поскольку :
не соответствует *
, он попытается выполнить следующее:
ident / factor + term < term1 <> term2 AND term3 OR term4
ident MOD factor + term < term1 <> term2 AND term3 OR term4
ident + term < term1 <> term2 AND term3 OR term4
Теперь он перейдет к следующему term1
:
ident * factor - term < term1 <> term2 AND term3 OR term4
ident / factor - term < term1 <> term2 AND term3 OR term4
ident MOD factor - term < term1 <> term2 AND term3 OR term4
ident - term < term1 <> term2 AND term3 OR term4
И затем:
ident * factor : term < term1 <> term2 AND term3 OR term4
ident / factor : term < term1 <> term2 AND term3 OR term4
ident MOD factor : term < term1 <> term2 AND term3 OR term4
ident : term < term1 <> term2 AND term3 OR term4
Ага!Мы наконец получили матч на term1!Но (
не соответствует <
, поэтому он должен попробовать следующий термин2:
ident * factor + term > term1 <> term2 AND term3 OR term4
и т. Д.
Все, потому что первый член в каждой строке для каждогосрок всегда будет соответствовать!Чтобы сопоставить простое число, нужно проанализировать factor
2 * 2 * 5 * 9 * 4 * 4 = 2880 раз!
Но это не половина истории!Видите ли, поскольку termX повторяется дважды, он будет повторять все это на обеих сторонах.Например, первое совпадение для A:(1+2)
выглядит следующим образом:
ident : term < term1 <> term2 AND term3 OR term4
where ident = A
and term = (1+2)
Что неверно, поэтому он попытается >
вместо <
, а затем <=
и т. Д. И т. Д.
Я помещаю регистрационную версию этого парсера ниже.Попробуйте запустить его и посмотреть все, что он пытается проанализировать.
Между тем, есть хорошие примеры того, как написать эти парсеры.Используя sbaz
, попробуйте:
sbaz install scala-devel-docs
Затем загляните в каталог doc/scala-devel-docs/examples/parsing
дистрибутива Scala, и вы найдете несколько примеров.
Вот версия вашего анализатора (безfunction
), который регистрирует все, что пытается:
sealed trait Expression
case class Variable(id: String) extends Expression
case class Literal(s: String) extends Expression
case class Number(x: String) extends Expression
case class UnaryOp(op: String, rhs: Expression) extends Expression
case class BinaryOp(op: String, lhs: Expression, rhs: Expression) extends Expression
object TestParser extends scala.util.parsing.combinator.syntactical.StdTokenParsers {
import scala.util.parsing.combinator.lexical.StdLexical
type Tokens = StdLexical
val lexical = new StdLexical
lexical.delimiters ++= List("(", ")", "+", "-", "*", "/", "=", "OR", "AND", "NE", "EQ", "LT", "GT", "LE", "GE", ":", "MOD")
def stmts: Parser[Any] = log(expr.*)("stmts")
def stmt: Parser[Expression] = log(expr <~ "\n")("stmt")
def expr: Parser[Expression] = log(term5)("expr")
def term5: Parser[Expression] = (
log((term4 ~ "OR" ~ term4) ^^ { case lhs~o~rhs => BinaryOp("OR", lhs, rhs) })("term5 OR")
| log(term4)("term5 term4")
)
def term4: Parser[Expression] = (
log((term3 ~ "AND" ~ term3) ^^ { case lhs~a~rhs => BinaryOp("AND", lhs, rhs) })("term4 AND")
| log(term3)("term4 term3")
)
def term3: Parser[Expression] = (
log((term2 ~ "<>" ~ term2) ^^ { case lhs~ne~rhs => BinaryOp("NE", lhs, rhs) })("term3 <>")
| log((term2 ~ "=" ~ term2) ^^ { case lhs~eq~rhs => BinaryOp("EQ", lhs, rhs) })("term3 =")
| log((term2 ~ "NE" ~ term2) ^^ { case lhs~ne~rhs => BinaryOp("NE", lhs, rhs) })("term3 NE")
| log((term2 ~ "EQ" ~ term2) ^^ { case lhs~eq~rhs => BinaryOp("EQ", lhs, rhs) })("term3 EQ")
| log(term2)("term3 term2")
)
def term2: Parser[Expression] = (
log((term1 ~ "<" ~ term1) ^^ { case lhs~lt~rhs => BinaryOp("LT", lhs, rhs) })("term2 <")
| log((term1 ~ ">" ~ term1) ^^ { case lhs~gt~rhs => BinaryOp("GT", lhs, rhs) })("term2 >")
| log((term1 ~ "<=" ~ term1) ^^ { case lhs~le~rhs => BinaryOp("LE", lhs, rhs) })("term2 <=")
| log((term1 ~ ">=" ~ term1) ^^ { case lhs~ge~rhs => BinaryOp("GE", lhs, rhs) })("term2 >=")
| log((term1 ~ "LT" ~ term1) ^^ { case lhs~lt~rhs => BinaryOp("LT", lhs, rhs) })("term2 LT")
| log((term1 ~ "GT" ~ term1) ^^ { case lhs~gt~rhs => BinaryOp("GT", lhs, rhs) })("term2 GT")
| log((term1 ~ "LE" ~ term1) ^^ { case lhs~le~rhs => BinaryOp("LE", lhs, rhs) })("term2 LE")
| log((term1 ~ "GE" ~ term1) ^^ { case lhs~ge~rhs => BinaryOp("GE", lhs, rhs) })("term2 GE")
| log(term1)("term2 term1")
)
def term1: Parser[Expression] = (
log((term ~ "+" ~ term) ^^ { case lhs~plus~rhs => BinaryOp("+", lhs, rhs) })("term1 +")
| log((term ~ "-" ~ term) ^^ { case lhs~minus~rhs => BinaryOp("-", lhs, rhs) })("term1 -")
| log((term ~ ":" ~ term) ^^ { case lhs~concat~rhs => BinaryOp(":", lhs, rhs) })("term1 :")
| log(term)("term1 term")
)
def term: Parser[Expression] = (
log((factor ~ "*" ~ factor) ^^ { case lhs~times~rhs => BinaryOp("*", lhs, rhs) })("term *")
| log((factor ~ "/" ~ factor) ^^ { case lhs~div~rhs => BinaryOp("/", lhs, rhs) })("term /")
| log((factor ~ "MOD" ~ factor) ^^ { case lhs~div~rhs => BinaryOp("MOD", lhs, rhs) })("term MOD")
| log(factor)("term factor")
)
def factor: Parser[Expression] = (
log("(" ~> expr <~ ")")("factor (expr)")
| log(("+" | "-") ~ factor ^^ { case op~rhs => UnaryOp(op, rhs) })("factor +-")
//| function |
| log(numericLit ^^ { case x => Number(x/*.toFloat*/) })("factor numericLit")
| log(stringLit ^^ { case s => Literal(s) })("factor stringLit")
| log(ident ^^ { case id => Variable(id) })("factor ident")
)
def parse(s: String) = stmts(new lexical.Scanner(s))
}