2016-04-12 7 views
1

Необходимо извлечь жетоны и фиксированный текст. Пример: parboiled2 parser для извлечения токена и фиксированного текста

"Hello {token1} текущая дата {token2} вы хотели бы назвать {token3}"

вернется

  • FixedPart("Hello ")
  • TokenPart(token1)
  • FixedPart(" today's date is ")
  • TokenPart(token2)
  • FixedPart(" would you like to call ")
  • TokenPart(token3)

Вот наивная реализация

import org.parboiled2.ParserInput 
import org.parboiled2.Parser 
import org.parboiled2.CharPredicate 
sealed trait Part 
case class TokenPart(tokenName : String) extends Part 
case class FixedPart(text : String) extends Part 
class MyParser(val input: ParserInput) extends Parser { 
    def Token = rule { '{' ~ capture(TokenName) ~> (TokenPart(_)) ~'}'  } 

    //how this should be implemented?? 
    def NotToken = rule { capture (!Token) ~>(FixedPart(_))} 
    def TokenName = rule { CharPredicate.Alpha ~ oneOrMore (CharPredicate.AlphaNum) } 

    // This would not work 
    def TokenNotToken = rule { (Token|NotToken) } 
    def InputLine = rule { zeroOrMore (TokenNotToken) } 

} 
object MyParser { 
    def main(args: Array[String]) { 
    val res = new MyParser("Hello {token1} today's date is {token2} would you like to call {token3}").InputLine.run() // Success 
    println(res)  
    } 
} 

Любой другой осуществить это ??

+0

Пожалуйста, не звоните 'CharPredicate.NAME' внутри правила. Создайте переменную и присвойте значение предиката. В вашем коде 'CharPredicate.NAME' будет оцениваться каждый раз, когда ваш парсер сталкивается с правилом. Это ухудшает производительность. – ppopoff

ответ

1

Привет Я изменил код и добавил некоторые комментарии (я надеюсь, что они будут полезны), поэтому она работает, и (я думаю) делает то, что вы хотели, чтобы это сделать:

import org.parboiled2.ParserInput 
import org.parboiled2.Parser 
import org.parboiled2.CharPredicate 

sealed trait Token 
case class TokenPart(tokenName : String) extends Token 
case class StringToken(text: String) extends Token 

// I moved pre-evaluated char predicates to the companion 
// you may leave them inside the class if you want. 
// I also moved literals like startToken and endToken here 
object TokenExtractor { 
    val AlphaChar = CharPredicate.Alpha 
    val AlphaNum = CharPredicate.AlphaNum 

    val startToken = "{" 
    val endToken = "}" 
} 


class TokenExtractor(val input: ParserInput) extends Parser { 
    import TokenExtractor._ 

    // may be you wanted zero or more? Anyway in this case 
    // shortcut can play nice here. In fact, if you want to stick 
    // with oneOrMore you can user AlphaNum.+ instead 
    def TokenName = rule { 
    AlphaChar ~ AlphaNum.* 
    } 

    // There's a shortcut for Extraction syntax. If you are extracting 
    // data to the case class and Rule arguments match the number of 
    // items in the case class's apply method 
    // you can simply give a name of this case class: 
    // the extraction operator '~>' should be located at the end of the 
    // from the official documtation: 
    // https://github.com/sirthias/parboiled2 
    // One more very useful feature is special support for 
    // case class instance creation: 
    // 
    // case class Person(name: String, age: Int) 
    // (foo: Rule2[String, Int]) ~> Person 
    // 
    def Token = rule { 
    startToken ~ capture(TokenName) ~ endToken ~> TokenPart 
    } 

    // the text should follow until the parser will meet the 
    // enclosing '{' character. Disclosing is not mandatory :) 
    def Text = rule { 
    oneOrMore(noneOf(startToken)) 
    } 

    // Here we're capturing a data that matches 
    // pre-defined rule (in our case Text) 
    def TextString = rule { 
    capture(Text) ~> StringToken 
    } 


    def TextPart = rule { 
    TextString | Token 
    } 


    // EOI is mandatory. Parser is greedy, so it tells the parser 
    // where parsing procedure must end, so please, add it at the 
    // end of the input 
    def InputLine = rule { 
    zeroOrMore(TextPart) ~ EOI 
    } 
} 


object Main { 
    def main(args: Array[String]) { 
    val example = 
     "Hello {token1} today's date is {token2} would you like to call {token3}" 

    // parser input can be string, so put it inside the constructor 
    val result = new TokenExtractor(example).InputLine.run() 
    println(result) 
    } 
}