I am trying to get my thick head around the following problem:
class LineParser extends JavaTokenParsers {
def lines: Parser[Any] = rep(line)
def line: Parser[String] = """^.+$""".r
}
object LineParserTest extends LineParser {
def main(args: Array[String]) {
val reader = new FileReader(args(0))
println("input : "+ args(0))
println(parseAll(lines, reader))
}
}
Input file:
one
two
When I run the program, it gives me this error:
[1.1] failure: string matching regex `^.+$' expected but `o' found
one
^
Apparently, I did something stupid, but couldn't figure out why. Please advise.
The above is a simplified version of the real goal: to parse a cisco-like configuration file which include commands and sub-commands and build an AST. There are several commands that I don't care and would like to ignore them using the pattern """^.+$""" above.
You don't need ^ and $ in the regex. By default . doesn't match end of line, so the following will make it work:
def line: Parser[String] = """.+""".r
In Java, if you want ^ and $ to match the line terminator, you have to set the Pattern.MULTILINE flag. So the following definition will work as well:
def line: Parser[String] = """(?m)^.+$""".r
Related
I wish to parse a sequence of string into individual tokens. Right now it only parses the first word.
class SimpleRegexParser extends RegexParsers{
def word: Parser[String] = """[a-z]+""".r ^^ { _.toString }
}
object SimpleRegexParserMain extends SimpleRegexParser{
def main(args: Array[String]) = {
println(parse(word, "johnny has a little lamb"))
}
}
Right now I am getting : [1.7] parsed: johnny
How can I parse the whole string into individual tokens, so that this works for variable string length.
Any pointers to make this work are welcome. Please tell me how I can make it work in scala.
Okay. I found the answer to this.
I had to change the definition of word.
Here is the updated definition:
class SimpleRegexParser extends RegexParsers{
def word: Parser[String] = rep("""[a-z]+""".r) ^^ { _.toString }
}
Previous expression was working for single word, I now have a repetition of word rep().
Here is the result:
[1.25] parsed: List(johnny, has, a, little, lamb)
I'm trying to write an implicit def macro that captures the invoking statement (and preferably nothing else) as a string, that can hopefully be used just by having the desired output object as an implicit parameter. But I'm having some trouble getting it to work with multi line statements.
For example:
case class HowWasICalled(statement:String, lineNumber:Int)
object HowWasICalled {
implicit def generate: HowWasICalled = macro macro_impl
}
def callMe(something: Any)(implicit context: HowWasICalled)
// macro: c is the blackbox Context
def macro_impl(c: blackbox.Context): c.Expr[HowWasICalled] = { ... }
And the first attempt at implementing a method inside the macro that returns the invoking statement as a string:
def makeString:String = show(c.macroApplication)
But, this only returned "HowWasICalled.generate". I actually wanted the statement invoking callMe.
I was hoping the following would work, but it doesn't. The -Yrangepos compiler flag does not seem to result in range positions in def macros? :(
def makeString:String = {
if (c.enclosingPosition.isRange) {
new String(c.enclosingPosition.source.content.slice(
c.enclosingPosition.start,
c.enclosingPosition.end
))
}
}
The closest I got to any working implementation doesn't exactly capture just statement nor the entire statement, but at least I get the line of source invoking callMe.
def makeString:String = {
val lineNumber = c.enclosingPosition.line
val lineIndex = lineNumber-1
c.enclosingPosition.source.lineToString(lineIndex)
}
How can I improve the macro to handle the following case? It should ideally generate something like a HowIWasCalled("callMe( 1 + 2 )", 123) given the following input
0; 0; 0; val x = callMe(1 +
2)
I am trying out scala parser combinators with the following object:
object LogParser extends JavaTokenParsers with PackratParsers {
Some of the parsers are working. But the following one is getting tripped up:
def time = """([\d]{2}:[\d]{2}:[\d]{2}\.[\d]+)"""
Following is the input not working:
09:58:24.608891
On reaching that line we get:
[2.22] failure: `([\d]{2}:[\d]{2}:[\d]{2}\.[\d]+)' expected but `:' found
09:58:24.608891
Note: I did verify correct behavior of that regex within the scala repl on the same input pattern.
val r = """([\d]{2}):([\d]{2}):([\d]{2}\.[\d]+)""".r
val s = """09:58:24.608891"""
val r(t,t2,t3) = s
t: String = 09
t2: String = 58
t3: String = 24.608891
So.. AFA parser combinator: is there an issue with the ":" token itself - i.e. need to create my own custom Lexer and add ":" to lexical.delimiters?
Update an answer was provided to add ".r". I had already tried that- but in any case to be explicit: the following has the same behavior (does not work):
def time = """([\d]{2}:[\d]{2}:[\d]{2}.[\d]+)""".r
I think you're just missing an .r at the end here to actually have a Regex as opposed to a string literal.
def time = """([\d]{2}:[\d]{2}:[\d]{2}\.[\d]+)"""
it should be
def time = """([\d]{2}:[\d]{2}:[\d]{2}\.[\d]+)""".r
The first one expects the text to be exactly like the regex string literal (which obviously isn't present), the second one expects text that actually matches the regex. Both create a Parser[String], so it's not immediately obvious that something is missing.
There's an implicit conversion from java.lang.String to Parser[String], so that string literals can be used as parser combinators.
There's an implicit conversion from scala.util.matching.Regex to > Parser[String], so that regex expressions can be used as parser combinators.
http://www.scala-lang.org/files/archive/api/2.11.2/scala-parser-combinators/#scala.util.parsing.combinator.RegexParsers
Is it possible to invert matches with Scala parser combinators? I am trying to match lines with a parser that do not start with a set of keywords. I could do this with an annoying zero width negative lookahead regular expression (e.g. "(?!h1|h2).*"), but I'd rather do it with a Scala parser. The best I've been able to come up with is this:
def keyword = "h1." | "h2."
def alwaysfails = "(?=a)b".r
def linenotstartingwithkeyword = keyword ~! alwaysfails | ".*".r
The idea is here that I use ~! to forbid backtracking to the all-matching regexp, and then continue with a regex "(?=a)b".r that matches nothing. (By the way, is there a predefined parser that always fails?) That way the line would not be matched if a keyword is found but would be matched if keyword does not match.
I am wondering if there is a better way to do this. Is there?
You can use not here:
import scala.util.parsing.combinator._
object MyParser extends RegexParsers {
val keyword = "h1." | "h2."
val lineNotStartingWithKeyword = not(keyword) ~> ".*".r
def apply(s: String) = parseAll(lineNotStartingWithKeyword, s)
}
Now:
scala> MyParser("h1. test")
res0: MyParser.ParseResult[String] =
[1.1] failure: Expected failure
h1. test
^
scala> MyParser("h1 test")
res1: MyParser.ParseResult[String] = [1.8] parsed: h1 test
Note that there is also a failure method on Parsers, so you could just as well have written your version with keyword ~! failure("keyword!"). But not's a lot nicer, anyway.
I am learning Scala with Scala-IDE in Eclipse. While following the Chapter 9: Control Abstraction, part 1: Reducing Code Duplication in the Programming in Scala book, I have written the code from the book (fully represented below), it worked fine! When I started removing the unnecessary blank lines, a strange thing happened. Here is the entire code before the removal of the blank lines in the filterFiles() method:
object Code_c09s01_ControlAbstraction extends App{
object FilesFilter {
private def filterFiles(path: String, pattern: String, matcher: (String, String) => Boolean) = {
val files = (new java.io.File(path)) listFiles
for(file <- files if matcher(file.getName, pattern)) yield file
}
def filterExtension(path: String, pattern: String) = filterFiles(path, pattern, _.endsWith(_))
def filterName(path: String, pattern: String) = filterFiles(path, pattern, _.contains(_))
def filterRegex(path: String, pattern: String) = filterFiles(path, pattern, _.matches(_))
}
def printArray[A](message: String, arr: Array[A]) {
println (message)
println (arr mkString("\n"))
}
def test() {
val path = "C:\\";
printArray("--- filtering by ext: ---", FilesFilter.filterExtension(path, ".txt"))
printArray("--- filtering by containment: ---", FilesFilter.filterName(path, "1"))
printArray("--- filtering by regex: ---", FilesFilter.filterRegex(path, "."))
}
test
}
which works just fine! However, after removing the blank lines from the filterFiles() method, the method now looks like this:
private def filterFiles(path: String, pattern: String, matcher: (String, String) => Boolean) = {
val files = (new java.io.File(path)) listFiles
for(file <- files if matcher(file.getName, pattern)) yield file
}
And the IDE gives me errors on both lines of the body. The error of the first line says:
ambiguous reference to overloaded definition, both method listFiles in class File of type (x$1: java.io.FileFilter)Array[java.io.File] and method listFiles in class File of type
(x$1: java.io.FilenameFilter)Array[java.io.File] match argument types (Null)
the error on the second line says:
illegal start of simple expression
and all the three calls to the printArray() in the test() method now also tell this:
type mismatch; found : Unit required: Array[?]
What does it all mean? Scala is not supposed to behave like Python when code alignment can ruin the code flow... so how come that removing the blank line between the first and the second line of the body of the filterFiles() method puts up such a serious error? Is it a bug somewhere or does it follow directly from the rules of Scala? Note: if I add ; between the line, it sorts everything out. Is it just semicolon inference bug?
When object's method can none or single argument you can call it like
val files = object method arg
or, in your snippet
val files = object method
arg
In your code compiler wants to call listfiles with an for expression as an agument which returns Array[File]. And complains that listFiles hasn't implementation with this type of argument. Empty line prevents treating for expression as a parameter for listFiles function
Looks like semicolon inference problem as you suggest, I guess the compiler expects method arguments if you leave parenthesis out. Instead of semicolon, you can add parenthesis after listFiles:
val files = (new java.io.File(path)) listFiles ()