Given this sweet.js macro
macro m {
case { _ ( $a, $b ) } => {
return #{$a + $b};
}
case { _ ( $a ) } => {
return #{$a};
}
case { _ } => {
return #{no};
}
}
export m;
And this source code:
m(1, [m(2)]);
m(1, m(2));
How do you create a case that yields this output:
1 + [2];
1 + 2;
Instead of this?
1 + [2];
no(1, 2);
P.S. the actual use case requires case macros, not rule macros.
The basic thing you're running into here is that a pattern variable
only matches a single token so in m(1, m(2)) the pattern $b gets
bound to m not m(2). This is because the inner m macros don't
get expanded before the outer m macro runs.
The easiest way to fix this is to use ...:
macro m {
case { _ ( $a ..., $b ...) } => {
return #{$a ... + $b ...};
}
case { _ ( $a ... ) } => {
return #{$a ...};
}
case { _ } => {
return #{no};
}
}
Related
Is there a way to construct tests in Rust to throw a warning when not exhaustive? Certainly, I don't expect a solution for this in general, but I'm looking for a solution that would work when the arguments to a function are enumerated types. I'd like to check that all combinations are used in a way a match statement checks that all combinations are covered. For example, consider the code:
// Terrible numerical type
#[derive(Debug,PartialEq)]
pub enum Num {
Int(i32),
Float(f32),
}
// Mathematical operation on this terrible type
pub fn myadd(x : crate::Num, y :Num) -> Num {
match (x,y) {
(Num::Int(x),Num::Int(y)) => Num::Int(x+y),
(Num::Int(x),Num::Float(y)) => Num::Float((x as f32) + y),
(Num::Float(x),Num::Int(y)) => Num::Float(x+(y as f32)),
(Num::Float(x),Num::Float(y)) => Num::Float(x+y),
}
}
// Add testing
#[cfg(test)]
mod test{
use super::*;
#[test]
fn int_int() {
assert_eq!(myadd(Num::Int(1),Num::Int(2)),Num::Int(3));
}
#[test]
fn float_int() {
assert_eq!(myadd(Num::Float(1.),Num::Int(2)),Num::Float(3.));
}
#[test]
fn int_float() {
assert_eq!(myadd(Num::Int(1),Num::Float(2.)),Num::Float(3.));
}
}
Here, we're missing the test float_float. I'd like a way to throw a warning to denote this test is missing. If we forgot the Float,Float case in pattern matching, we'd get the error:
error[E0004]: non-exhaustive patterns: `(Float(_), Float(_))` not covered
--> src/lib.rs:10:11
|
10 | match (x,y) {
| ^^^^^ pattern `(Float(_), Float(_))` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
I'm trying to get something similar for the testing combinations. In case it matters, I don't care if all of the tests are combined to a single function rather than split into four different tests. I didn't know if there was a trick using pattern matching to achieve this or some other mechanism.
This is by no means an ideal solution, but you can do this by using a pretty short macro:
macro_rules! exhaustive_tests {
(
$group_name:ident = match $type:ty {
$( $test_name:ident = $pattern:pat => $body:block )*
}
) => {
paste::item!{
#[allow(warnings)]
fn [< exhaustive_check_ $group_name >]() {
let expr: $type = unreachable!();
match expr {
$( $pattern => unreachable!(), )*
}
}
}
$(
#[test] fn $test_name() { $body }
)*
};
}
The macro will let you declare a group of tests, with a pattern for each test that must be exhaustive to compile.
exhaustive_tests!{
my_tests = match (Num, Num) {
int_int = (Num::Int(_), Num::Int(_)) => {
assert_eq!(myadd(Num::Int(1),Num::Int(2)),Num::Int(3));
}
float_int = (Num::Float(_), Num::Int(_)) => {
assert_eq!(myadd(Num::Int(1),Num::Int(2)),Num::Int(3));
}
int_float = (Num::Int(_), Num::Float(_)) => {
assert_eq!(myadd(Num::Int(1),Num::Float(2.)),Num::Float(3.));
}
}
}
In addition to generating each test (#[test] fn $test_name() { $body }), it will create a single function exhaustive_check_... with a match statement with the pattern for each test. The match statement must cover all patterns (as usual), otherwise compilation will fail:
// generated by macro
fn exhaustive_check_my_tests() {
let expr: (Num, Num) = unreachable!();
match expr {
(Num::Int(_), Num::Int(_)) => unreachable!(),
(Num::Float(_), Num::Int(_)) => unreachable!(),
(Num::Int(_), Num::Float(_)) => unreachable!(),
// error[E0004]: non-exhaustive patterns: `(Float(_), Float(_))` not covered
}
}
Try it in Playground
So I had an assignment in which I was supposed to make a WML parser that obeys the following grammar rules:
Here are the gramma rules
The prof posted the solution and I'm trying to test inside intellij but it keeps giving me an error. here's the code:
import WML.args
import scala.util.parsing.combinator._
import scala.io.Source
// -------------------------------------------------------------------------
// These classes form our AST
abstract class ASTNode(val kind : String)
// Outermost program structure
case class ASTProgram(val outer : List[ASTNode]) extends ASTNode("PROGRAM") {
override def toString : String = kind+" ( " + outer.mkString(" ") + " )"
}
// The other non-terminals
case class ASTInvoke(val name : ASTItext, val targs : ASTTargs) extends ASTNode("INVOKE") {
override def toString : String = kind + " ( " + name + " " + targs + " )"
}
case class ASTTargs(val targs : List[ASTItext]) extends ASTNode("TARGS") {
override def toString : String = kind + " ( " + targs.mkString(" ") + " )"
}
case class ASTItext(val itext : List[ASTNode]) extends ASTNode("ITEXT") {
override def toString : String = kind + " ( " + itext.mkString(" ") + " )"
}
case class ASTTvar(val name : String, val opt: ASTItext) extends ASTNode("TVAR") {
override def toString : String = kind + " ( " + "VNAME" + { if (opt==null) "" else " " +opt.toString } + " )"
}
case class ASTTdef(val name : ASTDtext, val dparams : ASTDparams, val body : ASTDtext ) extends ASTNode("DEFINE") {
override def toString : String = kind + " ( " + name + " " + dparams + " " + body + " )"
}
case class ASTDparams(val dparams : List[ASTDtext]) extends ASTNode("DPARAMS") {
override def toString : String = kind + " ( " + dparams.mkString(" ") + " )"
}
case class ASTDtext(val k:String, val dtext : List[ASTNode]) extends ASTNode(k) {
override def toString : String = kind + " ( " + dtext.mkString(" ") + " )"
}
// Our various baseline forms of plain text, Outertext, Inneritext, Innerdtext, Bodytext
// These are really just tokens, but we use nodes to represent them in order to keep track of the contents.
case class ASTText(val k : String,val s : String) extends ASTNode(k) {
override def toString : String = kind
}
// -------------------------------------------------------------------------
// Now the actual parser
class WMLParser extends RegexParsers {
// Tokens. We start with the fixed character sequences, just as strings.
val TSTART = "{{"
val TEND = "}}"
val DSTART = "{'"
val DEND = "'}"
val VSTART = "{{{"
val VEND = "}}}"
val PIPE = "|"
val PIPES = "||"
// We also have tokens for more complex sequences, forming the outermost text,
// as well as the inner text of invocations, definitions, and arguments.
// anything but TSTART or DSTART
val OUTERTEXT = "^([^{]|\\{(?!([{'])))+".r
// anything but TSTART, DSTART, VSTART, PIPE(s), TEND
val INNERITEXT = "^([^{|}]|\\{(?!([{|']))|\\}(?!\\}))+".r
// anything but TSTART, DSTART, VSTART, PIPE(s), DEND
val INNERDTEXT = "^([^{|']|\\{(?!([{|']))|'(?!\\}))+".r
// anything but TSTART, DSTART, VSTART, DEND
val BODYTEXT = "^([^{']|\\{(?!([{']))|'(?!\\}))+".r
// anything but PIPE or VEND
val VNAME = "^([^\\}|]|\\}(?!\\})|\\}\\}(?!\\}))+".r
// Some helper functions
// This deals with our optional component in Targs.
// Here we assume a missing optional part is the empty string---that's actually
// an over-assumption, but it is ok for now.
def convertStringItextToTargs(x:List[String ~ Option[ASTItext]]) : ASTTargs = {
new ASTTargs(x.map( (z) => z match {case s ~ Some(i) => i
case s ~ None => new ASTItext(new ASTText("INNERITEXT","")::Nil) } ))
}
// A helper to convert dtext string, node pairs into just nodes.
def convertStringItextToDparams(x:List[String ~ ASTDtext]) : ASTDparams = {
new ASTDparams(x.map( {case s ~ i => i}))
}
// Now the grammar rules.
// First a few trivial things. We don't really need these as rules per se, as each of
// these just converts a regular expression match into an ASTNode, but making separate
// rules for these makes the type conversion easier.
def textOuter: Parser[ASTNode] = OUTERTEXT ^^ { (x:String) => new ASTText("OUTERTEXT",x) }
def textInner: Parser[ASTNode] = INNERITEXT ^^ { (x:String) => new ASTText("INNERITEXT",x) }
def textInnerd: Parser[ASTNode] = INNERDTEXT ^^ { (x:String) => new ASTText("INNERDTEXT",x) }
def textBody: Parser[ASTNode] = BODYTEXT ^^ { (x:String) => new ASTText("BODYTEXT",x) }
// Our main entry point.
// <program> ::= (OUTERTEXT |<invoke>|<define>)*
def program: Parser[ASTNode] = rep( textOuter | invoke | define ) ^^ {
(x:List[ASTNode]) => { new ASTProgram(x) }
}
// <invoke> ::= TSTART <itext> <targs> TEND
def invoke: Parser[ASTInvoke] = TSTART ~ itext ~ targs ~ TEND ^^ {
case _ ~ i ~ a ~ _ => { new ASTInvoke(i,a) }
}
// <targs> ::= (PIPE <itext>?)*
def targs: Parser[ASTTargs] = rep( PIPE ~ opt(itext) ) ^^ { convertStringItextToTargs _ }
// Here, note that we list tvar before invoke, to give it preference in parsing
// <itext> ::= (INNERTEXT|<tvar>|<invoke>|<define>)*
def itext: Parser[ASTItext] = rep( textInner | tvar | invoke | define ) ^^ { (x:List[ASTNode]) => { new ASTItext(x) } }
// <tvar> ::= VSTART VNAME (PIPE itext)? VEND
def tvar: Parser[ASTTvar] = VSTART ~ VNAME ~ opt(PIPE ~ itext) ~ VEND ^^ {
case _ ~ n ~ None ~ _ => { new ASTTvar (n,null) }
case _ ~ n ~ Some(_ ~ i) ~ _ => { new ASTTvar (n,i) }
}
// <define> ::= DSTART <dtextn> <dparams> PIPES <dtext> DEND
def define: Parser[ASTTdef] = DSTART ~ dtextn ~ dparams ~ PIPES ~ dtextb ~ DEND ^^ {
case _ ~ d ~ p ~ _ ~ b ~ _ => { new ASTTdef(d,p,b) }
}
// <dparams> ::= (SPIPE <dtextp>)*
def dparams: Parser[ASTDparams] = rep( PIPE ~ dtextp ) ^^ { convertStringItextToDparams _ }
// We have 3 forms of dtext. Each of them allows inner invokes, definitions, and args.
// The template name can be empty, so we use * for repetition
// <dtextn> ::= (INNERDTEXT|<tvar>|<invoke>|<define>)*
def dtextn: Parser[ASTDtext] = rep( textInnerd | tvar | invoke | define ) ^^ { (x:List[ASTNode]) => { new ASTDtext("DTEXTN",x) } }
// The parameters cannot be empty, so we use + (ie rep1) for repetition
// <dtextp> ::= (INNERDTEXT|<tvar>|<invoke>|<define>)+
def dtextp: Parser[ASTDtext] = rep1( textInnerd | tvar | invoke | define ) ^^ { (x:List[ASTNode]) => { new ASTDtext("DTEXTP",x) } }
// Finally, the body itself can be empty, so we use * for repetition. Different from a name, however,
// the text itself can include pipe symbols.
// <dtextb> ::= (BODYTEXT|<tvar>|<invoke>|<define>)*
def dtextb: Parser[ASTDtext] = rep( textBody | tvar | invoke | define ) ^^ { (x:List[ASTNode]) => { new ASTDtext("DTEXTB",x) } }
// We do not want whitespace discarded, we will do that ourselves
override val whiteSpace = "".r
}
// And finally a program to invoke the parser on a file or input string
object WML extends App {
def main(args: Array[String]): Unit = {
def help(): Unit = {
println("Specify (-s string|filename)")
System.exit(1)
}
if (args.length==0) {
help()
}
// taking a string input was not required, but it is convenient for testing, so allow it with a -s specifier.
val source = args match {
case Array(_,"-s",s) => s
case Array("-s",s) => s
case Array(_,fn) => Source.fromFile(fn, "UTF-8").mkString
case Array(fn) => Source.fromFile(fn, "UTF-8").mkString
}
val p = new WMLParser
val result = p.parseAll(p.program,source);
if (result.successful) {
println(result.get)
} else {
println("Parse failure: " + result);
}
}
}
It asks me to override the main method and when I do, I get this error:
objc[12857]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java (0x104e314c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x105e9c4e0). One of the two will be used. Which one is undefined.
Exception in thread "main" scala.MatchError: [Ljava.lang.String;#3551a94 (of class [Ljava.lang.String;)
at WML$.main(solution2.scala:176)
at WML.main(solution2.scala)
Here is what I have on line 176:
case Array(fn) => Source.fromFile(fn, "UTF-8").mkString
Thanks.
Can you please try upgrading your JDK version to update 161?
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
Looks like this is a problem with IntelliJ!
I want to parse this string: "er1r2r3" with: """(e|w|n|s)(r[1-3])*""".r
val SideR = """(e|w|n|s)""".r
val PieceR = """(r)([1-3])""".r
def parseSidedPieces(str: String): (Char, List[Char]) = {
val side = str(0) match {
case SideR(s) => s
}
val pieces = parsePieces(str.tail)
(side, pieces)
}
def parsePieces(str: String): List[Char] = {
PieceR.findAllIn(str).toList map {
case PieceR(c, n) => n
}
}
But this throws on empty string "" because str(0).
Fix this, regex only.
I don't think this can be fixed 'regexes only' (whatever that is supposed to mean), because the code fails before the first regex is used.
It fails because you call apply(index: Int) on an empty String. So, either you do an isEmpty check before calling str(0) or even parseSidedPieces, or you change the code and match the whole String:
val PieceR = """(r)([1-3])""".r
val CombinedR = "(e|w|n|s)((?:r[1-3])*)".r
def parseSidedPieces(str: String): (Char, List[Char]) = {
str match {
case CombinedR(side, pieces) =>
(side(0), parsePieces(pieces))
case "" =>
// hmm, what kind of tuple would be a good return value here? maybe:
throw new IllegalArgumentException(s"Unexpected input: $str")
case _ =>
// handle unmatched strings however you like, I'd do:
throw new IllegalArgumentException(s"Unexpected input: $str")
}
}
def parsePieces(str: String): List[Char] = {
PieceR.findAllIn(str).toList map {
case PieceR(c, n) => n(0)
}
}
parseSidedPieces("er1r2r3") |-> res0: (Char, List[Char]) = (e,List(1, 2, 3))
macro m {
rule {
$a: $b
} => {
$a($b)
}
}
m 1: 2
I think sweetjs is interpreting the colon as a marker for match class. If so, how do I escape that make make sweetjs match it as it is?
This seems to work just fine though
macro m {
rule {
:$b
} => {
$b
}
}
m :1
You can use $[:] to match a literal.
A text file should be parsed line by line, using Scala pattern matching and regular expressions. If a line starts with "names:\t" the subsequent tab-separated names should be provided as a Seq[String] (or something similar).
Here a non-working code example:
val Names = "^names:(?:\t([a-zA-Z0-9_]+))+$".r
"names:\taaa\tbbb\tccc" match {
case Names(names # _*) => println(names)
// […] other cases
case _ => println("no match")
}
Output: List(ccc)
Wanted output: List(aaa, bbb, ccc)
The following code works as desired…
object NamesObject {
private val NamesLine = "^names:\t([a-zA-Z0-9_]+(?:\t[a-zA-Z0-9_]+)*)$".r
def unapplySeq(s: String): Option[Seq[String]] = s match {
case NamesLine(nameString) => Some(nameString.split("\t"))
case _ => None
}
}
"names:\taaa\tbbb\tccc" match {
case NamesObject(names # _*) => println(names)
// […] other cases
case _ => println("no match")
}
Output (as wanted): WrappedArray(aaa, bbb, ccc)
I would like to know: Is this is possible in a simpler manner without creating an object, just like in the first but non-working code example?
Use your working regex.(\w is a predefined character class for[a-zA-Z0-9_])
val Names = """names:\t(\w+(?:\t\w+)*)""".r
"names:\taaa\tbbb\tccc" match {
case Names(names) => println(names.split("\t") toSeq)
case _ => println("no match")
}
With first, second & tail bindings,
val Names = """names:\t(\w+)?\t?(\w+)?\t?((?:\w+?\t?)*)""".r
"names:\taaa\tbbb\tccc\tddd" match {
case Names(first, second, tail) =>
println(first + ", " + second + ", " + (tail.split("\t") toSeq));
case _ => println("no match")
}
As Randall Schulz said, it seems not to be possible just using regular expressions. Therefore the short answer to my question would be no.
My current solution is the following: I use the this class…
class SeparatedLinePattern(Pattern: Regex, separator: String = "\t") {
def unapplySeq(s: String): Option[Seq[String]] = s match {
case Pattern(nameString) => Some(nameString.split(separator).toSeq)
case _ => None
}
}
…to create the patterns:
val Names = new SeparatedLinePattern("""names:\t([A-Za-z]+(?:\t[A-Za-z]+)*)""".r)
val Ints = new SeparatedLinePattern("""ints:\t(\d+(?:\t\d+)*)""".r)
val ValuesWithID = new SeparatedLinePattern("""id-value:\t(\d+\t\w+(?:\t\d+\t\w+)*)""".r)
Here an example how they can be used in a quite flexible manner:
val testStrings = List("names:\taaa", "names:\tbbb\tccc", "names:\tddd\teee\tfff\tggg\thhh",
"ints:\t123", "ints:\t456\t789", "ints:\t100\t200\t300",
"id-value:\t42\tbaz", "id-value:\t2\tfoo\t5\tbar\t23\tbla")
for (s <- testStrings) s match {
case Names(name) => println(s"The name is '$name'")
case Names(a, b) => println(s"The two names are '$a' and '$b'")
case Names(names # _*) => println("Many names: " + names.mkString(", "))
case Ints(a) => println(s"Just $a")
case Ints(a, b) => println(s"$a + $b == ${a.toInt + b.toInt}")
case Ints(nums # _*) => println("Sum is " + (nums map (_.toInt)).sum)
case ValuesWithID(id, value) => println(s"ID of '$value' is $id")
case ValuesWithID(values # _*) => println("As map: " + (values.grouped(2) map (x => x(0).toInt -> x(1))).toMap)
case _ => println("No match")
}
Output:
The name is 'aaa'
The two names are 'bbb' and 'ccc'
Many names: ddd, eee, fff, ggg, hhh
Just 123
456 + 789 == 1245
Sum is 600
ID of 'baz' is 42
As map: Map(2 -> foo, 5 -> bar, 23 -> bla)