FParsec operator precedence parser conflicting with next parser in sequence - fparsec

I have an operator presedence parser that parses mathematical expressions, similar to the calculator example in the FParsec repository.
The next parser in the sequence, however, parses -> which is causing conflicts with the subtraction operator.
Some representative code is:
open FParsec
let ws = spaces
let str s = pstring s .>> ws
let number = pfloat .>> ws
let opp = new OperatorPrecedenceParser<float,unit,unit>()
let expr = opp.ExpressionParser
opp.TermParser <- number <|> between (str "(") (str ")") expr
opp.AddOperator(InfixOperator("+", ws, 1, Associativity.Left, (+)))
opp.AddOperator(InfixOperator("-", ws, 1, Associativity.Left, (-)))
opp.AddOperator(InfixOperator("*", ws, 2, Associativity.Left, (*)))
opp.AddOperator(InfixOperator("/", ws, 2, Associativity.Left, (/)))
opp.AddOperator(InfixOperator("^", ws, 3, Associativity.Right, fun x y -> System.Math.Pow(x, y)))
opp.AddOperator(PrefixOperator("-", ws, 4, true, fun x -> -x))
let completeExpression = expr .>> ws
let program =
pipe3 completeExpression (str "->") completeExpression <| fun e1 _ e2 ->
(e1, e2)
Which doesn't work for, for example, 1+1 -> 1+2.
I know that if I change str "->" to str "=>" then something like 1+1 => 1+2 would work.
Is it possible to add look-ahead into the operator precedence parser? Or is there another way round this?
Thanks!
EDIT
I sorted this by adding a notFollowedBy in the after-string parser for the subtraction operator.
let after = (notFollowedBy (str ">")) >>. ws
opp.AddOperator(InfixOperator("-", after, 1, Associativity.Left, (-)))

Related

Add keys conditionally in Scala to a Map[String,String]?

I want to add keys as shown below:
val x = false
val y = Map[String,String](
"name" -> "AB",
if(x) "placement"->"pc" else null
)
println(y)
Note that its an immutable map, this works when x is true but throws runtime error when x is false, I tried to put else Unit but that doesn't work as well.
Can someone point me the most elegant as well as optimized syntax to do so?
One more option is
val y = Map[String, String](
Seq("name" -> "AB") ++
(if (x) Seq("placement" -> "pc") else Seq()): _*
)
or
val y = (Seq("name" -> "AB") ++
(if (x) Seq("placement" -> "pc") else Seq())).toMap
or
val y = (Seq("name" -> "AB") ++
Option.when(x)("placement" -> "pc").toSeq).toMap
This is not so shorter but maybe slightly less confusing than if (...) Map(...) else List(...).
Sorted it like this:
val x = true
val y = Map[String,String](
"name" -> "AB"
) ++ (if(x) Map[String,String]("place"->"pc") else Nil)
still wondering if this is good or something better should be used.

OperatorPrecedenceParser throw exception about negative priority which I don't have

I am creating a parser for a programming language based on the lambda-calculus. I added an infix operator and their precedence but the parser crashed with an error about negative priority. I am able to do the parsing of operator by hand, but it seem that I cannot get the priority right. So I thought that I may as well learn to use the OperatorPrecedenceParser.
I will show the code because I have no idea why it crash, since I don't have any negative priority.
The language AST
module MiniML
type Exp =
| C of Cst
| Id of Id
| Lam of Id * Exp
| App of Exp * Exp
| Let of Id * Exp * Exp
| Pair of Exp * Exp
| If of Exp * Exp * Exp
and Cst = I of int | B of bool | Unit | Nil
and Id = string;;
let op = ["+";
"-";
"*";
"/";
"=";
"<";
">";
"#";
"and";
"or";
",";
"::"
]
Here is the parser itself. It's my first time with parser combinator (and parsing) so if there is something terribly wrong, I'd like to know. Otherwise, just knowing why it crash would be enough.
open MiniML
open FParsec
let ws = spaces
let operator : Parser<MiniML.Id,unit> = op |> List.map pstring |> choice
let keyword : Parser<string,unit> = ["false";"true";"let";"end";"in";"if";"then";"else";"lam"] |> List.map pstring |> choice
let fstId = asciiLetter <|> pchar '_'
let restId = fstId <|> digit <|> pchar '''
let betweenPar p = between (pchar '(' .>> ws) (pchar ')' .>> ws) p
let cstB = (stringReturn "true" (B true)) <|> (stringReturn "false" (B false))
let cstI = puint32 |>> (int >> I)
let cstU = stringReturn "()" Unit
let cstN = stringReturn "[]" Nil
let expC : Parser<Exp,unit> = cstB <|> cstI <|> cstU <|> cstN |>> C
let expIdStr = notFollowedByL keyword "Cannot use keyword as variable" >>.
notFollowedByL operator "Cannot use operator as variable" >>.
many1CharsTill2 fstId restId (notFollowedBy restId)
let expId : Parser<Exp,unit> = expIdStr |>> (MiniML.Exp.Id)
let exp, expRef = createParserForwardedToRef<Exp, unit>()
let expApp, expAppRef = createParserForwardedToRef<Exp, unit>()
let expLam : Parser<Exp,unit> = (pstring "lam" >>. ws >>. expIdStr .>> ws .>> pchar '.') .>> ws .>>. exp |>> Lam
let expLet = tuple3 (pstring "let" >>. ws >>. expIdStr .>> ws .>> pchar '=' .>> ws) (exp .>> ws .>> pstring "in" .>> ws) (exp .>> ws .>> pstring "end") |>> Let
let expIf = tuple3 (pstring "if" >>. ws >>. exp .>> ws) (pstring "then" >>. ws >>. exp .>> ws) (pstring "else" >>. ws >>. exp) |>> If
let closeEXP, closeEXPRef = createParserForwardedToRef<Exp, unit>()
let expBang = (pstring "!" >>% MiniML.Id "!") .>>. closeEXP |>> App
let buildList (el,ef) =
let rec go l = match l with
| (e::es) -> App(MiniML.Id "cons", Pair(e,go es))
| [] -> C Nil
go (el # [ef])
let expList = between (pchar '[' .>> ws) (pchar ']') (many (exp .>>? (ws .>> pchar ';' .>> ws)) .>>. exp .>> ws
|>> buildList )
do closeEXPRef := choice [expC ; expId ; expBang ; betweenPar exp ; expList] .>> ws
do expAppRef := many1 closeEXP |>> (function (x::xs) -> List.fold (fun x y -> App(x,y)) x xs | [] -> failwith "Impossible")
let opOpp : InfixOperator<Exp,unit,unit> list =
[
InfixOperator("*", ws, 6, Associativity.Left, fun x y -> App(MiniML.Id "*",Pair(x,y)));
InfixOperator("/", ws, 6, Associativity.Left, fun x y -> App(MiniML.Id "/",Pair(x,y)));
InfixOperator("+", ws, 5, Associativity.Left, fun x y -> App(MiniML.Id "+",Pair(x,y)));
InfixOperator("-", ws, 5, Associativity.Left, fun x y -> App(MiniML.Id "-",Pair(x,y)));
InfixOperator("::", ws,4, Associativity.Right, fun x y -> App(MiniML.Id "cons",Pair(x,y)));
InfixOperator("=", ws, 3, Associativity.Left, fun x y -> App(MiniML.Id "=",Pair(x,y)));
InfixOperator("<", ws, 3, Associativity.Left, fun x y -> App(MiniML.Id "<",Pair(x,y)));
InfixOperator(">", ws, 3, Associativity.Left, fun x y -> App(MiniML.Id ">",Pair(x,y)));
InfixOperator("and", ws, 2, Associativity.Right, fun x y -> App(MiniML.Id "and",Pair(x,y)));
InfixOperator("or", ws, 1, Associativity.Right, fun x y -> App(MiniML.Id "or",Pair(x,y)));
InfixOperator(",", ws,0, Associativity.None, fun x y -> Pair(x,y) )
]
let opp = new OperatorPrecedenceParser<Exp,unit,unit>()
let expr = opp.ExpressionParser
let term = exp <|> betweenPar expr
opp.TermParser <- term
List.iter (fun x -> opp.AddOperator(x)) opOpp
do expRef := [expLam;expIf;expLet;expApp] |> choice |> (fun p -> p .>>. opt (expOp operator) |>> binOp )
let mainExp = expr .>> eof
Your sample code doesn't seem to be complete, since expOp and binOp are not included. When I run your code without the last two lines, the OPP throws an ArgumentOutOfRangeException with the message "The operator precedence must be greater than 0." when the comma operator is added. The problem is that you specified 0 as the precedence for the comma operator.
Such problems are easier to diagnose when you use an IDE with a fully integrated debugger like Visual Studio.

Scala Map And Pair Manipulation

I am a beginner in Scala. I read this code about Map manipulation but can't understand how it works.
val terms = Map (1 -> 1.0, 2-> 2.0)
val (exp, coeff) = (2, 4.0)
exp -> (coeff + terms(exp)) //> res: (Int, Double) = (2,6.0)
coeff + terms(exp) //> res: Double = 6.0
I think the third line probably applied a map function,
but the right hand side is apparently a number.
Why the output is a pair?
Thanks.
val (exp, coeff) = (2, 4.0)
This is a deconstructor, it assigns the values 2 and 4.0 to the names exp and coeff. exp is now 2 and coeff is now 4.0.
terms(exp)
This is a map-lookup which results in 2.0
exp -> (coeff + terms(exp))
This arrow -> is a short hand term for constructing a Pair. It now has the values 2 and 6.0 because coeff + terms(exp) is 6.0.
terms(exp) accesses the value of the "map object" terms at the key defined by exp.
In contrast to this, the usage of a "map method" would look like this:
val list = List(1, 2, 3, 4)
val double = (i: Int) => i * 2
val list2 = list.map(double)
printf(list2.toString)
list.map(double) executes the function literal double for every member of the list and gives back a new List object.
The printed output is:
List(2, 4, 6, 8)
terms(exp) is a map lookup, resolving to 2.0
(coeff + terms(exp)) is, therefore 4.0 + 2.0
In exp -> (coeff + terms(exp)) the arrow -> constructs a pair of the left and right operands.

How to configure Emacs' align-rules-list to line up =, -> and | in Haskell code?

I often write code that looks like the following:
data Tok =
TokA String |
TokS Char |
TokL String
deriving (Show, Eq)
data Tokenizer = Tokenizer { runTokenizer :: (Tok, Tokenizer) }
tokenize xs = Tokenizer $ go xs
where
psh [] = go xs
psh (r:rs) = (TokS r, Tokenizer (psh rs))
plg rs = (TokL rs, Tokenizer (go xs))
go (x:xs) =
case span (== '-') x of
(_, "") -> (TokA x, Tokenizer (go xs))
("-", rs) -> psh rs
("--", rs) -> plg rs
_ -> (TokA x, Tokenizer (go xs))
Note that the | symbols are lined up, the -> symbols are lined up and the = symbols are lined up.
I've tried to use the Emacs align command but it doesn't work automatically, so I have to resort to using align-regexp and a lot of typing. I understand I can modify align-rules-list to somehow do detection and application of a specific rule automatically, but I have no idea how to modify the variable to do this. Can someone help me in the three cases above?

Assign multiple variables at once in scala

I have the following code:
val text = "some text goes here"
val (first, rest) = text.splitAt(4)
println(first + " *" + rest)
That works fine.
However, I want to have two cases, defining "first" and "rest" outside, like this:
val text = "some text goes here"
var (first, rest) = ("", "")
if (text.contains("z")) {
(first, rest) = text.splitAt(4)
} else {
(first, rest) = text.splitAt(7)
}
println(first + " *" + rest)
But that gives me an error:
scala> | <console>:2: error: ';' expected but '=' found.
(first, rest) = text.splitAt(4)
Why is it an error to do (first, rest) = text.splitAt(4) but not to do val (first, rest) = text.splitAt(4)? And what can I do?
Edit: Can't re-assign val, changed to var. Same error
The answer by Serj gives a better way of writing this, but for an answer to your question about why your second version doesn't work, you can go to the Scala specification, which makes a distinction between variable definitions and assignments.
From "4.2 Variable Declarations and Definitions":
Variable definitions can alternatively have a pattern (§8.1) as
left-hand side. A variable definition var p = e where p is a
pattern other than a simple name or a name followed by a colon and a
type is expanded in the same way (§4.1) as a value definition val p
= e, except that the free names in p are introduced as mutable variables, not values.
From "6.15 Assignments":
The interpretation of an assignment to a simple variable x = e depends
on the definition of x. If x denotes a mutable variable, then the
assignment changes the current value of x to be the result of
evaluating the expression e.
(first, rest) here is a pattern, not a simple variable, so it works in the variable definition but not in the assignment.
First of all val is immutable, so you can't reassign it. Second, if, like all control structures in Scala, can return a value. So, you can do it like this:
val text = "some text goes here"
val (first, rest) = if (text.contains("z")) text.splitAt(4) else text.splitAt(7)
println(first + " *" + rest)
SerJ de SuDDeN answer is absolutely correct but some more details why the code you mentioned works the way it works.
val (a, b) = (1, 2)
is called an extractor of a pattern-match-expression. The value on the right side is matched to the extractor of the left side. This can be done everywhere in Scala and can have different faces. For example a pattern match on a List can look something like
scala> val head :: tail = 1 :: 2 :: 3 :: Nil
head: Int = 1
tail: List[Int] = List(2, 3)
On the right side the ::-symbol is a method of class List which prepends elements to it. On the left side the ::-symbol is an extractor of class ::, a subclass of List.
Some other places can be for-comprehensions
scala> for ((a, b) <- (1 to 3) zip (4 to 6)) println(a+b)
5
7
9
or the equivalent notation with higher-order-methods
scala> (1 to 3) zip (4 to 6) foreach { case (a, b) => println(a+b) }
5
7
9