Question

I would like to know how I can handle multiple optionals without concrete pattern matching for each possible permutation.

Below is a simplified example of the problem I am facing:

lexical Int = [0-9]+;
syntax Bool = "True" | "False";
syntax Period = "Day" | "Month" | "Quarter" | "Year";
layout Standard = [\ \t\n\f\r]*; 
syntax Optionals = Int? i Bool? b Period? p;

str printOptionals(Optionals opt){
    str res = "";
    if(!isEmpty("<opt.i>")) { // opt has i is always true (same for opt.i?)
        res += printInt(opt.i);
    }
    if(!isEmpty("<opt.b>")){
        res += printBool(opt.b);
    }
    if(!isEmpty("<opt.p>")) {
        res += printPeriod(opt.period);
    }
    return res;
}

str printInt(Int i) = "<i>";
str printBool(Bool b) = "<b>";
str printPeriod(Period p) = "<p>";

However this gives the error message:

The called signature: printInt(opt(lex("Int"))), does not match the declared signature: str printInt(sort("Int"));

How do I get rid of the opt part when I know it is there?

Was it helpful?

Solution

I'm not sure how ideal this is, but you could do this for now:

if (/Int i := opt.i) {
    res += printInt(i);
}

This will extract the Int from within opt.i if it is there, but the match will fail if Int was not provided as one of the options.

OTHER TIPS

The current master on github has the following feature to deal with optionals: they can be iterated over.

For example:

if (Int i <- opt.i) {
    res += printInt(i);
}

The <- will produce false immediately if the optional value is absent, and otherwise loop once through and bind the value which is present to the pattern.

An untyped solution is to project out the element from the parse tree:

rascal>opt.i.args[0];
Tree: `1`
Tree: appl(prod(lex("Int"),[iter(\char-class([range(48,57)]))],{}),[appl(regular(iter(\char-class([range(48,57)]))),[char(49)])[@loc=|file://-|(0,1,<1,0>,<1,1>)]])[@loc=|file://-|(0,1,<1,0>,<1,1>)]

However, then to transfer this back to an Int you'd have to pattern match, like so:

rascal>if (Int i := opt.i.args[0]) { printInt(i); }
str: "1"

One could write a generic cast function to help out here:

rascal>&T cast(type[&T] t, value v) { if (&T a := v) return a; throw "cast exception"; }
ok
rascal>printInt(cast(#Int, opt.i.args[0]))
str: "1"

Still, I believe Rascal is missing a feature here. Something like this would be a good feature request:

rascal>Int j = opt.i.value;
rascal>opt.i has value
bool: true
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top