The heavy open recursion check, src/test/regress/rt/recog-11.fdoc is giving
the correct answer but the output is wrong:
Xlib=list((expr, [(term "+" expr) | term]), (term, [(factor "*" term) | factor]), (factor, [(atom "^" factor) | atom]), (atom, [("(" expr ")") | "9"]))
Closure=list('atom', 'factor', 'term', 'expr')
Got parser
Test1: End pos (should be 11)=@1
Test1: End pos (should be 11)=@3
Test1: End pos (should be 11)=@5
Test1: End pos (should be 11)=@11
should be
Xlib=list(expr ::= [(term "+" expr) | term], term ::= [(factor "*" term) | factor], factor ::= [(atom "^" factor) | atom], atom ::= [("(" expr ")") | "9"])
Closure=list('atom', 'factor', 'term', 'expr')
Got parser
Test1: End pos (should be 11)=@1
Test1: End pos (should be 11)=@3
Test1: End pos (should be 11)=@5
Test1: End pos (should be 11)=@11
The problem is that the Xlib should be a list of prodictions.
And it must be or the answer wouldn’t be right.
But it is printing as a list with a single element, which is itself
a list of pairs of a nontermal and production.
My guess was a maladjusted fixpoint term which hadn’t been adjusted
after beta-reduction. However I just added a check in beta-reduction
on both the function body and the argument, to see if either is incomplete,
and it never happens in any test case.
The type of that thing is this:
typedef gramlib_t = Grammars[recog_t]::gramlib_t;
Now here’s how it’s defined:
class Grammars[Terminal_t] {
typedef generic_gramentry_t[T] = string * T;
typedef generic_gramlib_t[T] = list[generic_gramentry_t[T]];
typedef generic_grammar_t[T] = string * generic_gramlib_t[T];
typedef open_prod_t[T] =
(
| `Terminal of string * Terminal_t
| `Nonterminal of string
| `Epsilon
| `Seq of list[T]
| `Alt of list[T]
)
;
instance[T with Str[T]] Str[open_prod_t[T]]
{
fun str: open_prod_t[T] -> string =
| `Terminal (s,r) => '"' + s + '"'
| `Nonterminal name => name
| `Epsilon => "Eps"
| `Seq ss => "(" + catmap " " (str of T) ss + ")"
| `Alt ss => "[" + catmap " | " (str of T) ss + "]"
;
}
typedef open_gramentry_t[T] = string * open_prod_t[T];
typedef open_gramlib_t[T] = list[open_gramentry_t[T]];
typedef open_grammar_t[T] = string * open_gramlib_t[T];
// FIXATE
typedef prod_t = open_prod_t[prod_t];
// CLOSE RECURSION
typedef gramentry_t = open_gramentry_t[prod_t];
typedef gramlib_t = open_gramlib_t[prod_t];
typedef grammar_t = open_grammar_t[prod_t];
instance Str[gramentry_t] {
fun str (nt: string, p: prod_t) => nt + " ::= " + str p;
}
Now here’s the thing: a gramlib is a list of gramentry, and a gramentry is
a list of pairs.
The instance of Str prints a gramentry with a ::= separating the components.
However, a Str of a pair just prints a pair.
So actually BOTH forms are correct because we have a structural type and one
thing I keep saying about type classes is that they should ONLY be used on nominal
types. Instead, generic operations should handle structural types.
The problem here is, why did the system not detect a conflict, or, why
did the more specialised gramentry operation NOT get chosen?
Note the call in the code:
// library
var xlib = ([
("expr",xprod),
("term",tprod),
("factor",fprod),
("atom",atom)
]);
println$ "Xlib=" + xlib.str;
Now, there is a Str for lists, which is used, and prints a comma separated
list of elements inside “list(“ .. “)”, and it’s seeing here a pair, which is
printed by code shown at the bottom of this post. That code uses an alternate
form of a tuple type
head ** tail
in which tail must be a tuple. The implementation is made a bit complex
by too factors: first, we want parens around the whole tuple, but NOT
around the tail, so there are two classes, one for the outermost tuple,
and one for an inner one.
In addition, since Felix does not have a particular one element tuple,
the polymorphic recursion has to stop at a pair instead of a trailing
unit tuple. In addition, we have to handle the fact that a tuple of all the
same elements is an array, so there is a special case for a pair
of the same type. If there are three elements of the same type,
I don’t know what happens .. arrays also have a Str operation.
So it is all quite compicated. The question is why the specialised
type with a pair
gramentry_t = string * prod_t
was not selected by the type class instantiation routine (which is a form
of overloading) instead of the generic printer for all pairs.
Note that the generic printing operations use polymorphic recursion,
meaning, the type class instance functions are NOT recursive,
because they’re applied to different types in each expansion,
and hopefully those expansions terminate.
SO: it’s not a misplaced fixpoint at all. There’s a bug I think, in that
the specialised str routine accepting
string * prod_t
is not preferred over the routine taking
U * V
I have no idea why … but it used to work, possibly because
a *reference* to a typedef would have excluded the second case
and accepted the first one, but after expansion the same thing
should happen but doesn’t: a pair where the first argument
is a string should be preferred.
Note, it’s trivial to fix this problem by using a nominal type somewhere
instead. But that would ignore the fact the typeclass instantiation
is not proceeding as expected. Possibly, a missing beta-reduction
is the reason so the ::= str function is considered not to match at all.
Remember unification only works with actual type terms.
//------------------------------------------------------------------------------
// Class Str: convert to string
// Tuple class for inner tuple listing
class Tuple[U] {
virtual fun tuple_str (x:U) => str x;
}
instance[U,V with Str[U], Tuple[V]] Tuple[U ** V] {
fun tuple_str (x: U ** V) =>
match x with
| a ,, b => str a +", " + tuple_str b
endmatch
;
}
instance[U,V with Str[U], Str[V]] Tuple[U * V] {
fun tuple_str (x: U * V) =>
match x with
| a , b => str a +", " + str b
endmatch
;
}
// actual Str class impl.
instance [U, V with Tuple[U ** V]] Str[U ** V] {
fun str (x: U ** V) => "(" + tuple_str x +")";
}
instance[T,U] Str[T*U] {
fun str (t:T, u:U) => "("+str t + ", " + str u+")";
}
instance[T] Str[T*T] {
fun str (t1:T, t2:T) => "("+str t1 + ", " + str t2+")";
}
open[U, V with Tuple[U **V]] Str [U**V];
open[U, V with Str[U], Str[V]] Str [U*V];
—
John Skaller
ska...@internode.on.net