Closed PaulKlint closed 8 years ago
The advantages are clear, but there are clear disadvantages which makes this not always the best solution:
eval([Stat head, *Stat tail]) = eval(head) o eval(tail)
, should it match with any Stat*
non-terminal or with list[Stat]
or with both? This breaks our type system/pattern matching consistency (pattern matching is a conservative extension of the sub-type relation now, but not if we add that kind of coercion where two different kinds of containers suddenly become comparable.I think we should have this, but it does not replace the need for implode or data type usage for ASTs.
We can also write a static implode checker which lines up a concrete grammar with an abstract grammar and checks if at least at every level there would be an alternative abstract constructor for every concrete constructor.
Another note: pattern matching should ignore/step-over brackets rules, so the bracket rule should not have to be written in principle. There are some dark corners there which still have to be considered, namely regarding the identity function and not letting brackets magically disappear.
Many points to comment on:
eval({Stat ";"}+ stats) = eval(head(stats)) o eval(tail(stats))
. But I agree: further exploration needed.implode
does not offer a solution either :-(implode
will succeed.add(num(1),num(2))
match/produce? and on top of that we need a concrete->concrete formatter too.My feeling is that the world is just not simple. We have different usage scenarios for dealing with syntax trees and we need to satisfy them with different features:
(3) I am talking about a model where there is no abstract representation. Everything uses a concrete tree and there is therefore no standard way to go from abstract to concrete. The only purpose of the "abstract" pattern mul(Exp e1, Exp2)
is to match concrete trees. As I mentioned we still do need a pretty printer for a concrete -> concrete mapping.
(4) Of course a manual implode can do everything and we do need nothing extra for that. If I see how the current heuristics of implode can bite, I doubt whether more heuristics will work.
I prefer to design a world view that is as simple as possible but is still usable ;-)
Well, if we take this one thing at a time, let's finish the abstract matching on concrete trees and that's one down.
I would like to propose that top-level list patterns only match abstract lists, and that nested lists under an abstract constructor pattern will have the concrete list matching behavior (namely skipping layout). For example, eval(if(Exp _, [*Stat _, Stat last]))
, would actually work on the concrete parse tree and get the last statement out of the body of an if statement, and eval([*Stat _, Stat last])
would not match with just tree of type Stat*
because that is an Tree.appl
and not a list[Stat]
.
Let's talk offline about number 2.
Just for the records: the latest discussion I had with @PaulKlint was triggered by having to deal with overloaded constructors for normalization. This seems to be the only real problem. It's probably even implemented wrong in the interpreter now. In the compiler it's a can of worms.
Normalizing constructor overloads are not lexically scoped: implode
needs to know about functions in the caller's environment, not the lexical environment where implode
(or any intermediate function) is defined. Even if we keep implode
as function (be it in Java, or Rascal), it will need special treatment.
Another solution: build it in completely and make implode
a keyword.
Another few cents:
X*
values and lists. Keeping them completely distinct resolves the pattern matching ambiguity.visit
(i.e. visit that is not type preserving). with your few cents @tvdstorm:
After repeated discussions (latest today with @tvdstorm) we seem to agree that we have to reconsider how we handle the relation between a concrete and an abstract representation of syntax trees. At the moment we have two options:
implode
(error prone! since it may be impossible to map the concrete syntax to your AST data type).A third alternative is to always work on the concrete syntax tree but give the illusion of working on an abstract version. We have discussed this before and called this feature "overlay" or "view". The main point of this issue is that we are nearly there. This approach requires support for abstract patterns that match concrete subjects. For example, given a syntax rule with constructor name
mul
(see example below) the following will match:Here is a complete example (runnable using the compiler, not yet fully supported by the interpreter):
Apart from the evaluation rule for bracket, all other rules have a clear abstract flavor. Points for discussion: