I have a grammar rule like this:
a returns [string expr] : (b | c) {$expr = "Build expression from b OR c here";}
b returns [string expr] : 'B' {$expr = "From b";}
c returns [string expr] : 'C' {$expr = "From c";}
I would like to replace
$expr = "Build expression from b OR c here";
with an instruction that puts in the $expr variable whatever was returned from b OR c. I know there is a solution to this by performing this assignment like this:
a returns [string expr] : b {$expr = $b.expr;} | c {$expr = $c.expr;}
but was wondering whether there is a much simpler way like naming the whole group with a variable and using that instead:
a returns [string expr] : group = (b | c) {$expr = $group.expr;}
I've tried this and it doesn't work in ANTLR, even though the group variable is used to get the value returned by "b".
You cannot use the same label for multiple non-terminals unless they reference the same rule in the grammar. This means syntax like group=(A | B) only works for token references (A and B are terminals). The following syntax can be used for this.
a returns [string expr]
: b {$expr = $b.expr;}
| c {$expr = $c.expr;}
;
Related
I'm de-serializing some mappings from JSON and later on I need to pattern match based on a string field of the de-serialized types like this:
let mappings = getWorkItemMappings
let result =
workItemMappings
|> Seq.find (fun (m: WorkItemMapping) -> m.Uuid = workTime.workItemUuid)
match mapping.Name with
Even if I complete the pattern match for all cases I still get Incomplete pattern matches on this expression.. Which is obvious to me due to the string type of the Name field.
Is there a way tell the compiler which values for the Name field are available?.
I think I could create a union type for the possible mapping types and try to de-serialize the JSON to this union type but I would like to if there's another option.
If you are pattern matching on a string value, the compiler has no static guarantee that it will only have certain values, because it is always possible to construct a string of a different value. The fact that it comes from JSON does not help - you may always have an invalid JSON.
The best option is to add a default case which throws a custom descriptive exception. Either one that you handle somewhere else (to indicate that the JSON file was invalid) or (if you check the validity elsewhere) something like this:
let parseFood f =
match f with
| "burger" -> 1
| "pizza" -> 2
| _ -> raise(invalidArg "f" $"Expected burger or pizza but got {f}")
Note that the F# compiler is very cautious. It does not even let you handle enum values using pattern matching, because under the cover, there are ways of creating invalid enum values! For example:
type Foo =
| A = 1
let f (a:Foo) =
match a with
| Foo.A -> 0
warning FS0104: Enums may take values outside known cases. For example, the value 'enum (0)' may indicate a case not covered by the pattern(s).
Very hard to understand what you're asking. Maybe this snippet can be of help. It demos how literal string constants can be used in pattern matching, and reused in functions. This gives some added safety and readability when adding and removing cases. If you prefer not to serialize a DU directly, then perhaps this is useful as part of the solution.
type MyDu =
| A
| B
| C
let [<Literal>] A' = "A"
let [<Literal>] B' = "B"
let [<Literal>] C' = "C"
let strToMyDuOption (s: string) =
match s with
| A' -> Some A
| B' -> Some B
| C'-> Some C
| _ -> None
let strToMyDu (s: string) =
match s with
| A' -> A
| B' -> B
| C'-> C
| s -> failwith $"MyDu case {s} is unknown."
let myDuToStr (x: MyDu) =
match x with
| A -> A'
| B -> B'
| C -> C'
// LINQPad
let dump x = x.Dump()
strToMyDuOption A' |> dump
strToMyDuOption "x" |> dump
myDuToStr A |> dump
If the following nix expression
let
f = "f";
o = "o";
b = "b";
func = {a ? f, b ? "a" , c ? ""}: a+b+c; #only modify this line!
in
rec {
foo = func {b="o"; c=o;}; #must evaluate to "foo"
bar = func {a=b; c="r";}; #must evaluate to "bar"
foobar = func {a=foo;b=bar;}; #must evaluate to "foobar"
}
Here {a ? f, b ? "a" , c ? ""}: a+b+c; we are declaring that a
function takes attribute set {a ? f, b ? "a" , c ? ""} as
argument.
Here func {b="o"; c=o;} we are passing an attribute set({b="o"; c=o;})to the
function.
In the first expression we have "," separating the keys of attribute set and in the second "," is separating the keys. To represent attribute set we use two different methods. Is this understanding correct.?
The syntax { param ? default }: x defines an anonymous function that takes an attribute set with optional attribute param.
in the second "," is separating the keys.
In an attribute set, you use ; as a separator. I think this is a typo because your example is correct.
You can read more about "set patterns" in function definitions in the Nix manual.
Your understanding seems to be correct.
I have a DU and I'm overriding the Equals method. Based on the current DU value, I would like to call the base equality method or my custom one. However, it's not letting me access "base". Any idea on how to work around this?
type Test =
| A of string
| B of int64
override this.Equals(other) =
let other' = other :?> Test
match other' with
| A str -> str = "a"
| B i -> base.Equals this other //how do I do this?
First, any F# discriminated union will have obj as base class, so just use obj.Equals.
Second, Equals is a .NET method, not an F# function, so its arguments must be given in a tupled form - i.e. Equals(x,y) instead of Equals x y.
Finally, if you implement a custom Equals, you also need to add [<CustomEquality; NoComparison>]
So:
[<CustomEquality; NoComparison>]
type Test =
| A of string
| B of int64
override this.Equals(other) =
let other' = other :?> Test
match other' with
| A str -> str = "a"
| B i -> obj.Equals(this, other)
I'm doing a fun project in F#, which is a DSL for Camel.Net.
At some point, I want to check conditions. But the conditions entered by the programmer should evaluate to an object tree. So I want the experssion "a = b" evaluate to "SomeType.Equals(a,b)"
Is that even possible in F#?
I have this:
type Macro =
| Header of string
| XPath of string
| Const of string
| Func of (Message -> string)
with
static member (=) (l:Macro, r:Macro) = Equals(l,r)
static member (=) (l:Macro, r:string) = Equals(l,Const(r))
and Comparison =
| Equals of Macro * Macro
Now everything in "Macro" will work as "Macro.Func" - with "Func"; a function is executed with "Message" as input param and will output the string. So the Equals(a,b) will evaluate to a string comparison during runtime.
But this code has a problem. Operator (=) does compile (it has a warning), but it can't be used as I would like.
This does not compile in the fsi:
let c1 = Header("property") = "somevalue"
I did read another question about this topic, and a bit more.
It does not answer my question.
[<NoEquality; NoComparison>] - completely shuts off the (=) operator.
[<CustomEquality; CustomComparison>] - wants you to implement an (=) operator which returns bool.
Is it even possible in F# what I want? And assuming that I can find a way, does match x with still work?
Sure, I did this reimplement to the operator in terms of System.IEquatable<T> for performance reasons:
#nowarn "86" // F# doesn't like it when you do this
[<AutoOpen>]
module FastEquals =
let inline eq<'a when 'a :> System.IEquatable<'a>> (x:'a) (y:'a) = x.Equals y
let inline (=) x y = eq x y
let inline (<>) x y = not (eq x y)
Just an example, you'll need to adapt for your own purposes.
Thanks to Asik's answer above, in combination with a reread of this post:
This works in the fsi:
type Message = class end
type Macro =
| Header of string
| XPath of string
| Const of string
| Func of (Message -> string)
type Comparison =
| Equals of Macro * Macro
type Operators = Operation with
static member CompareEquals (Operation, l:Macro, r:Macro) = Equals(l,r)
static member CompareEquals (Operation, l:Macro, r:string) = Equals(l,Const(r))
#nowarn "0086" "0064"
let inline (=) (l:'N) (r:'M) = ((^T or ^N or ^M) : (static member CompareEquals : ^T * ^N * ^M -> _) (Operation, l, r))
let c1 = Header("property1") = Header("property2")
let c2 = Header("property") = "somevalue"
Note that it does not work when the static "CompareEquals" methods are located in the "Macro" type.
If you look at the signature of op_Equals:
val inline ( = ) :
l: ^N -> r: ^M -> 'a
when (Operators or ^N or ^M) : (static member CompareEquals : Operators * ^N * ^M -> 'a)
That is a really weird syntax. I don't understand the part after "when". It works, that counts.
I want to implement a F# function which may accept 1 or 2 arguments. I would like to use the function like this:
let foo = ...
foo "a"
foo "a" "b"
Both the arguments can be the same type. I read the pages about match pattern, active pattern, but cannot find one works for me.
I believe this is due to some of the underlying .Net features, but I think you have to use a class with overloaded methods - something like
type t() =
static member foo a = "one arg"
static member foo (a,b) = "two args"
On a type member, you can use optional params:
type Helper private () =
static member foo (input1, ?input2) =
let input2 = defaultArg input2 "b"
input1, input2
To call this method:
Helper.foo("a")
Helper.foo("a", "b")
Is this what you're after?
You can't use optional params on a function though, unfortunately.
In addition to the other answers, here are a few more "almost solutions". They are not strictly what you wanted, but are worth knowing anyway.
Using a list (or an array) and pattern matching:
let f = function
| [a, b] -> ...
| [a] -> ...
| [] -> failwith "too few arguments"
| _ -> failwith "too many arguments"
f ["a"]
f ["a" ; "b"]
Problems: parameters are not named, not clear from function signature how many parameters it takes.
Using a record to pass all optional parameters:
type FParams = { a : string; b : string }
let fdefault = { a = "a" ; b = "b" }
let f (pars: FParams) = ...
f { fdefault with b = "c" }
Problem: a is also optional, which is not what you wanted. Can be useful though.
In addition to the other answers, you might also be able to do what you want via partial application and currying. Like this:
let foo a b =
a + b
let foo2 a =
foo 1 a;;
Obviously you'd want to fix the first parameter in the call to foo within foo2 to whatever default you want.