Scala 解析器组合器,行尾失败

发布于 2024-11-03 15:28:09 字数 3469 浏览 1 评论 0原文

我正在尝试在 Scala 中为 图标编程语言 构建一个解释器。现在我正在为其设置一个解析器。

到目前为止我编写的代码是:

package interpreter
import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.combinator.PackratParsers
import scala.util.parsing.combinator.JavaTokenParsers

object ExprParser extends JavaTokenParsers with PackratParsers{

def exp : Parser[expr] =
    andexp |
    fail |
    ifexp |
    fromTo |
    write |
    string |
    arithm |
    "(" ~> exp <~ ")" |
    exp

def integer : Parser[CstInt] = wholeNumber ^^ { s => { //println("matching int");
new CstInt(s.toInt)}}
def string : Parser[CstStr] = stringLiteral ^^ { s => { //println("matching string");
new CstStr(s)}}


def fail : Parser[Fail] = "&fail" ^^ { e => Fail()}

def  write : Parser[Write] =  "write" ~> "(" ~> exp <~ ")" ^^ {  e => Write(e)}

def ifexp : Parser[If] = ("if" ~> exp) ~ ("then" ~> exp) ~ ("else" ~> exp) ^^ { case cond ~ suc ~ fail => If(cond, suc, fail)}


// Arithmetic
def arithm : Parser[expr] =
    term ~ ("+" ~> arithm) ^^ { case l ~ r => Prim("+", l, r)} |
    term ~ ("-" ~> arithm) ^^{ case l ~ r => Prim("-", l, r)} |
    term

def term : Parser[expr] =
    factor ~ ("*" ~> term) ^^ { case l ~ r => Prim("*", l, r)} |
    factor ~ ("/" ~> term) ^^ { case l ~ r => Prim("/", l, r)} |
    factor

def factor : Parser[expr] =
    integer |
    "-" ~> arithm |
    "(" ~> arithm <~ ")"


//PackratParser to allow left recursive grammars
lazy val fromTo : PackratParser[FromTo] = exp ~ ("to" ~> exp) ^^ { case from ~ to => FromTo(from, to)}

lazy val andexp : PackratParser[And] = exp ~ ("&" ~> exp) ^^ { case e1 ~ e2 =>{ println("matching and" + e1); println(" arg2: " + e2); And(e1, e2)}}

def parseInput(input: String) : expr =
    parseAll (exp, input) match {
        case Success(tree, _) => tree
        case e: NoSuccess => throw new IllegalArgumentException(e.toString())
    }
}

现在,我的问题是,当我在此输入上运行此代码时:

write(1 to 5) & write(3 to 5)

我得到以下输出:

matching andWrite(FromTo(CstInt(1),CstInt(5)))
arg2: Write(FromTo(CstInt(3),CstInt(5)))
matching andWrite(FromTo(CstInt(1),CstInt(5)))
arg2: Write(FromTo(CstInt(3),CstInt(5)))
java.lang.IllegalArgumentException: [1.30] failure: `&' expected but `' found

write(1 to 5) & write(3 to 5)
                             ^
at interpreter.ExprParser$.parseInput(parser.scala:62)
at interpreter.Main$.main(main.scala:9)
at interpreter.Main.main(main.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at sbt.Run.run0(Run.scala:60)
at sbt.Run.execute$1(Run.scala:47)
at sbt.Run$$anonfun$run$2.apply(Run.scala:50)
at sbt.Run$$anonfun$run$2.apply(Run.scala:50)
at sbt.TrapExit$.executeMain$1(TrapExit.scala:33)
at sbt.TrapExit$$anon$1.run(TrapExit.scala:42)

我添加了打印输出,以查看它是否与 & 匹配。操作员。

该代码在其他奇怪的输入上运行良好,例如:

write((if &fail then 3 else 5) to (3 to 5))

所以这似乎是一个与 andexp 解析器特别相关的问题。

任何帮助将不胜感激,因为我对 Scala 和解析器组合器完全陌生,而且我认为找到好的文档有点困难。

I am trying to build an interpreter for the Icon programming language, in Scala. Right now I am working on setting up a parser for it.

The code I have written so far is:

package interpreter
import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.combinator.PackratParsers
import scala.util.parsing.combinator.JavaTokenParsers

object ExprParser extends JavaTokenParsers with PackratParsers{

def exp : Parser[expr] =
    andexp |
    fail |
    ifexp |
    fromTo |
    write |
    string |
    arithm |
    "(" ~> exp <~ ")" |
    exp

def integer : Parser[CstInt] = wholeNumber ^^ { s => { //println("matching int");
new CstInt(s.toInt)}}
def string : Parser[CstStr] = stringLiteral ^^ { s => { //println("matching string");
new CstStr(s)}}


def fail : Parser[Fail] = "&fail" ^^ { e => Fail()}

def  write : Parser[Write] =  "write" ~> "(" ~> exp <~ ")" ^^ {  e => Write(e)}

def ifexp : Parser[If] = ("if" ~> exp) ~ ("then" ~> exp) ~ ("else" ~> exp) ^^ { case cond ~ suc ~ fail => If(cond, suc, fail)}


// Arithmetic
def arithm : Parser[expr] =
    term ~ ("+" ~> arithm) ^^ { case l ~ r => Prim("+", l, r)} |
    term ~ ("-" ~> arithm) ^^{ case l ~ r => Prim("-", l, r)} |
    term

def term : Parser[expr] =
    factor ~ ("*" ~> term) ^^ { case l ~ r => Prim("*", l, r)} |
    factor ~ ("/" ~> term) ^^ { case l ~ r => Prim("/", l, r)} |
    factor

def factor : Parser[expr] =
    integer |
    "-" ~> arithm |
    "(" ~> arithm <~ ")"


//PackratParser to allow left recursive grammars
lazy val fromTo : PackratParser[FromTo] = exp ~ ("to" ~> exp) ^^ { case from ~ to => FromTo(from, to)}

lazy val andexp : PackratParser[And] = exp ~ ("&" ~> exp) ^^ { case e1 ~ e2 =>{ println("matching and" + e1); println(" arg2: " + e2); And(e1, e2)}}

def parseInput(input: String) : expr =
    parseAll (exp, input) match {
        case Success(tree, _) => tree
        case e: NoSuccess => throw new IllegalArgumentException(e.toString())
    }
}

Now, my problem is that when I run this code on this input:

write(1 to 5) & write(3 to 5)

I get the following output:

matching andWrite(FromTo(CstInt(1),CstInt(5)))
arg2: Write(FromTo(CstInt(3),CstInt(5)))
matching andWrite(FromTo(CstInt(1),CstInt(5)))
arg2: Write(FromTo(CstInt(3),CstInt(5)))
java.lang.IllegalArgumentException: [1.30] failure: `&' expected but `' found

write(1 to 5) & write(3 to 5)
                             ^
at interpreter.ExprParser$.parseInput(parser.scala:62)
at interpreter.Main$.main(main.scala:9)
at interpreter.Main.main(main.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at sbt.Run.run0(Run.scala:60)
at sbt.Run.execute$1(Run.scala:47)
at sbt.Run$anonfun$run$2.apply(Run.scala:50)
at sbt.Run$anonfun$run$2.apply(Run.scala:50)
at sbt.TrapExit$.executeMain$1(TrapExit.scala:33)
at sbt.TrapExit$anon$1.run(TrapExit.scala:42)

I added the printouts as a way of seeing whether it even matched the & operator.

The code runs fine on other weird inputs like:

write((if &fail then 3 else 5) to (3 to 5))

So it seems to be a problem specifically related to the andexp parser.

Any help would be much appreciated, as I am completely new to Scala and the parser combinators, and it is somewhat hard to find good documentation I think.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

宁愿没拥抱 2024-11-10 15:28:09

这可能是由于 def exp = ... | 中的循环造成的指数?

Could this be due to the cycle in def exp = ... | exp?

单身狗的梦 2024-11-10 15:28:09

请注意,Packrat 解析器具有 关于左递归的限制。在相互递归规则上将 Packrat 与非 Packrat 混合起来是很奇怪的。我不知道这会如何运作(或不运作),我只是避免找出答案。

Note that packrat parsers have limitations with regard to left recursion. And mixing packrat with non-packrat on a mutal recursive rule is way strange. I have no idea how that would work (or not), and I'd just avoid finding out.

莫相离 2024-11-10 15:28:09

因此,正如我对 Ben Jacksons 的回答的评论中提到的,问题是我需要将 exp 设为 PackratParser,工作代码在这里:

package interpreter
import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.combinator.PackratParsers
import scala.util.parsing.combinator.JavaTokenParsers

object ExprParser extends JavaTokenParsers with PackratParsers{

lazy val exp : PackratParser[expr] =
    andexp |
    fail |
    ifexp |
    fromTo |
    write |
    string |
    arithm |
    "(" ~> exp <~ ")" 

def integer : Parser[CstInt] = wholeNumber ^^ { s => { //println("matching int");
new CstInt(s.toInt)}}
def string : Parser[CstStr] = stringLiteral ^^ { s => { //println("matching string");
new CstStr(s)}}


def fail : Parser[Fail] = "&fail" ^^ { e => Fail()}

def  write : Parser[Write] =  "write" ~> "(" ~> exp <~ ")" ^^ {  e => Write(e)}

def ifexp : Parser[If] = ("if" ~> exp) ~ ("then" ~> exp) ~ ("else" ~> exp) ^^ { case cond ~ suc ~ fail => If(cond, suc, fail)}


// Arithmetic
def arithm : Parser[expr] =
    term ~ ("+" ~> arithm) ^^ { case l ~ r => Prim("+", l, r)} |
    term ~ ("-" ~> arithm) ^^{ case l ~ r => Prim("-", l, r)} |
    term

def term : Parser[expr] =
    factor ~ ("*" ~> term) ^^ { case l ~ r => Prim("*", l, r)} |
    factor ~ ("/" ~> term) ^^ { case l ~ r => Prim("/", l, r)} |
    factor

def factor : Parser[expr] =
    integer |
    "-" ~> arithm |
    "(" ~> arithm <~ ")"


//PackratParser to allow left recursive grammars
lazy val fromTo : PackratParser[FromTo] = exp ~ ("to" ~> exp) ^^ { case from ~ to => FromTo(from, to)}

lazy val andexp : PackratParser[And] = exp ~ ("&" ~> exp) ^^ { case e1 ~ e2 =>{ println("matching and" + e1); println(" arg2: " + e2); And(e1, e2)}}

def parseInput(input: String) : expr =
    parseAll (exp, input) match {
        case Success(tree, _) => tree
        case e: NoSuccess => throw new IllegalArgumentException(e.toString())
    }
}

使用我之前的代码,我似乎允许左递归,这是普通解析器无法处理的。
如果有人能澄清为什么 fromTo 在没有 expPackratParser 的情况下工作,而 andexp 却不是,我仍然会有兴趣听听为什么会这样。

So, as mentioned in my comment on Ben Jacksons answer, the problem was that I needed to make exp a PackratParser, the working code is here:

package interpreter
import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.combinator.PackratParsers
import scala.util.parsing.combinator.JavaTokenParsers

object ExprParser extends JavaTokenParsers with PackratParsers{

lazy val exp : PackratParser[expr] =
    andexp |
    fail |
    ifexp |
    fromTo |
    write |
    string |
    arithm |
    "(" ~> exp <~ ")" 

def integer : Parser[CstInt] = wholeNumber ^^ { s => { //println("matching int");
new CstInt(s.toInt)}}
def string : Parser[CstStr] = stringLiteral ^^ { s => { //println("matching string");
new CstStr(s)}}


def fail : Parser[Fail] = "&fail" ^^ { e => Fail()}

def  write : Parser[Write] =  "write" ~> "(" ~> exp <~ ")" ^^ {  e => Write(e)}

def ifexp : Parser[If] = ("if" ~> exp) ~ ("then" ~> exp) ~ ("else" ~> exp) ^^ { case cond ~ suc ~ fail => If(cond, suc, fail)}


// Arithmetic
def arithm : Parser[expr] =
    term ~ ("+" ~> arithm) ^^ { case l ~ r => Prim("+", l, r)} |
    term ~ ("-" ~> arithm) ^^{ case l ~ r => Prim("-", l, r)} |
    term

def term : Parser[expr] =
    factor ~ ("*" ~> term) ^^ { case l ~ r => Prim("*", l, r)} |
    factor ~ ("/" ~> term) ^^ { case l ~ r => Prim("/", l, r)} |
    factor

def factor : Parser[expr] =
    integer |
    "-" ~> arithm |
    "(" ~> arithm <~ ")"


//PackratParser to allow left recursive grammars
lazy val fromTo : PackratParser[FromTo] = exp ~ ("to" ~> exp) ^^ { case from ~ to => FromTo(from, to)}

lazy val andexp : PackratParser[And] = exp ~ ("&" ~> exp) ^^ { case e1 ~ e2 =>{ println("matching and" + e1); println(" arg2: " + e2); And(e1, e2)}}

def parseInput(input: String) : expr =
    parseAll (exp, input) match {
        case Success(tree, _) => tree
        case e: NoSuccess => throw new IllegalArgumentException(e.toString())
    }
}

With my previous code I seem to have been allowing for left recursion, which cannot be handled by the normal parser.
If anyone can clarify why fromTo was working without exp being a PackratParser, while andexp was not, I would still be interested to hear why that is so.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文