silver
silver copied to clipboard
Improved support for pattern matching on primitives
I was going through some old starred emails, cleaning out the junk. Here's one we never got around to:
-
We should be able to pattern match on terminals. Possibly something like
case '+' of terminal("+") -> ... end
. -
Right now, we have primitives (sting, bool, list) polluting the modification primitive match. These should probably be 100% taken care of by the extension implementing nested pattern matching. That way, primitive match is only about productions.
-
We lack to ability to pattern match on annotation values. This should be fixed. In the extension module as well, despite it appearing to be about productions. That is
case t of foo(x, y, location=l) -> ...
should probably translate to a primitive match on foo withlet l = t.location in ...
added to the RHS expression. This enhancement is related to issue #32, about enhancements to annotations.
N.B. if we choose to eventually allow named parameters besides annotations on productions, then we have to figure out how to implement that w.r.t. patterns because the above approach won't work. (Because things are now per-production instead of nonterminal-wide.) Perhaps if we have that feature, we should push annotation matching down to the primitive match modification. One possibility: don't allow named parameters on productions! Instead, just allow positional parameters but we can supply them via their names. Then we would just be pattern matching on them via their names as well. e.g. case t of foo(left=x, right=y) -> ... x ... y ... end
. (However, this has the downside of introducing the names as part of the public interface. But maybe that's okay. It's just weird in conjunction with aspects allowing renaming. But perhaps we should warn if aspect try to rename. :))
Terminal patterns would be nice to have for #321 - see Ted's comment there. The terminal pattern syntax should probably also include the location, but I don't think the type is actually needed.
@krame505 Just want to check my understanding of this issue. If I understand what it's saying correct, we want to not use the primitive "patterns" in PrimitiveMatch.sv which directly generate Java translation and instead replace them with appropriate Silver expressions, so for instance if x is an Integer
then
case x of
| 4 -> E0
| 5 -> E1
| _ -> E2
end;
would become
let genName :: Integer = x in -- some generated name
if genName == 4 then E0
else if genName == 5 then E1
else E2
end
(obviously this is roughly speaking, what I mean is more that the caseExpr
abstract production that is equivalent to the case statement above should produce the code I gave, roughly speaking the production invoked would be caseExpr([x], [4 -> E0, 5 -> E1], E2, Type)
)
If that's right, I can understand how to handle the non-list primitives. How should lists be handled then? If lst
is of type [Maybe<Integer>]
would we do something like
case lst of
| nothing() :: just(4) :: [] -> E3
| _ -> E4
end
becoming
let getName :: [Maybe<Integer>] = lst in
let genFailName :: Type = E4 in
if null(genName)
then genFailName
else
case head(genName), tail(genName) of
| nothing(), just(4) :: [] -> E3
| _, _ -> genFailName
end end end
(Again where the original case expression would be something like caseExpr([lst], [nothing() :: just(4) :: [] -> E3], E4, Type)
and the case expression in the translation would be something like caseExpr([head(genName), tail(genName)], [nothing(), just(4) :: [] -> E3], genFailName, Type)
)
I believe that is one of the things it is talking about (number two in Ted's list). However, I had to rearrange the compilation of case expressions to get completeness checking to work correctly, so this is a bad thing to work on until that is merged.
Makes sense. Thanks
Yeah, I think that's correct for primitives. Lists aren't really a primitive, though, as they are more like a nonterminal type (they are sort of pretending to be one...) so I don't think their handling needs to change.
You could work on primitive match support for terminals - I don't think that would be affected by @RandomActsOfGrammar's work?
Correct. I'm in the pattern matching extension, so anything only changing primitive matching is safe to change.
Alright. Can someone give me a little clarification on what terminal matching is and how it should work? Neither of the tickets that mention it give a good example, so I'm not clear on how its used and what the syntax should be. Also, I'm a little confused about how this works since isn't each terminal a different type?
Basically, we want to be able to pattern match on terminals. Currently this is a bug in the silver:compiler:metatranslation
library where we are emitting wildcards instead of terminal patterns, and thus might match somethings that shouldn't match.
Ted proposed the syntax terminal(lexeme)
for pattern matching on terminals, where lexeme
is a pattern that expects to match a string. Yes all terminals are different types, but we can infer the type of the terminal from the expression against which we are matching, so I don't think there is a need to include the type in the pattern syntax for terminals.