2010-12-11 4 views
4

Я пытаюсь написать синтаксический анализатор в scala с помощью Parser Combinators. Если я сопрягать рекурсивно,, возвращающий содержательные сообщения об ошибках из анализатора, написанного с помощью компиляторов Scala Parser

def body: Parser[Body] = 
("begin" ~> statementList ) ^^ { 
    case s => { new Body(s); } 
} 

def statementList : Parser[List[Statement]] = 
    ("end" ^^ { _ => List() })| 
    (statement ~ statementList ^^ { case statement ~ statementList => statement :: statementList }) 

тогда я получаю хорошие ErrorMessages всякий раз, когда есть ошибка в заявлении. Однако этот уродливый длинный код. Так что я хотел бы написать это:

def body: Parser[Body] = 
("begin" ~> statementList <~ "end" ) ^^ { 
    case s => { new Body(s); } 
} 

def statementList : Parser[List[Statement]] = 
    rep(statement) 

Этот код работает, но печатает только значимые сообщения, если есть ошибка в первом заявлении. Если в последующем заявлении, сообщение становится мучительно непригодным, так как анализатор хочет видеть все ошибочное утверждение заменено «конечным» лексемы:

Exception in thread "main" java.lang.RuntimeException: [4.2] error: "end" expected but "let" found 

let b : string = x(3,b,"WHAT???",!ERRORHERE!,7) 

^ 

Мой вопрос: есть ли способ, чтобы получить респ и repsep работают в сочетании со значимыми сообщениями об ошибках, которые помещают каретку в нужное место вместо начала повторяющегося фрагмента?

ответ

1

Ах, нашел решение! Оказывается, вам нужно использовать функциональную фразу на вашем основном синтаксическом анализаторе, чтобы вернуть новый парсер, который менее склонен отслеживать. (Интересно, что это значит, возможно, если он найдет разрыв строки, он не отследит?) отслеживает последнюю позицию, по которой произошел сбой.

изменилось:

def parseCode(code: String): Program = { 
program(new lexical.Scanner(code)) match { 
     case Success(program, _) => program 
     case x: Failure => throw new RuntimeException(x.toString()) 
     case x: Error => throw new RuntimeException(x.toString()) 
    } 

} 

def program : Parser[Program] ... 

в:

def parseCode(code: String): Program = { 
phrase(program)(new lexical.Scanner(code)) match { 
     case Success(program, _) => program 
     case x: Failure => throw new RuntimeException(x.toString()) 
     case x: Error => throw new RuntimeException(x.toString()) 
    } 

} 


def program : Parser[Program] ... 
+1

'фраза' возвращает' Parser', что будет успешным только в том случае, если в нем нет ввода после его принятия. Я понятия не имею, почему это изменило бы такой путь назад. –

+0

Оказывается, он вообще не меняет обратный отсчет, но фраза имеет немного состояния, чтобы проверить сбой, который имел самый длинный результат совпадения. Он описан в параграфе «Отчеты об ошибках» в главе о парсерах в книге Одерского. – Jan

1

Вы можете сделать это путем объединения "самодельный" rep метод с не отслеживанию источников внутри отчетности. Например:

scala> object X extends RegexParsers { 
    | def myrep[T](p: => Parser[T]): Parser[List[T]] = p ~! myrep(p) ^^ { case x ~ xs => x :: xs } | success(List()) 
    | def t1 = "this" ~ "is" ~ "war" 
    | def t2 = "this" ~! "is" ~ "war" 
    | def t3 = "begin" ~ rep(t1) ~ "end" 
    | def t4 = "begin" ~ myrep(t2) ~ "end" 
    | } 
defined module X 

scala> X.parse(X.t4, "begin this is war this is hell end") 
res13: X.ParseResult[X.~[X.~[String,List[X.~[X.~[String,String],String]]],String]] = 
[1.27] error: `war' expected but ` ' found 

begin this is war this is hell end 
         ^

scala> X.parse(X.t3, "begin this is war this is hell end") 
res14: X.ParseResult[X.~[X.~[String,List[X.~[X.~[String,String],String]]],String]] = 
[1.19] failure: `end' expected but ` ' found 

begin this is war this is hell end 
       ^
+0

Привет, Даниэль. Что касается scala 2.11, твой ответ остается прежним? Или укомплектованы персональные компьютеры, чтобы обеспечить собственные сообщения об ошибках? –

+0

@KevinMeredith Я не знаю, но я в этом сомневаюсь. –