StringTemplate.ceylon

"""A string template, that is,
   a *string start*, followed by any number of value expression and *string mid* pairs,
   followed by a value expression and a *string end*.
   
   (The distinction between string starts, mids, and ends is a purely lexical one,
   and not covered in `ceylon.ast`.)
   
   There must be at least one expression in a string template,
   and the number of string literals must be exactly the number
   of expressions plus one.
   
   Examples:
   
       "Elapsed: `` tEnd - tStart ``ms"
       "Hello, ``person.firstName`` ``person.lastName``. How nice to see you back in ``person.homeTown``.""""
shared class StringTemplate(literals, expressions)
        extends Atom() {
    
    "The string literals of the string template, in order of appearance."
    shared [StringLiteral+] literals;
    "The expressions in the string template, in order of appearance."
    shared [Expression+] expressions;
    
    "There must be exactly one more string literal than there are expressions"
    assert (literals.size == expressions.size + 1);
    
    assert (nonempty children_ = concatenate([literals.first], *zipPairs(expressions, literals.rest)));
    shared actual [<StringLiteral|Expression>+] children = children_;
    
    shared actual Result transform<out Result>(Transformer<Result> transformer)
            => transformer.transformStringTemplate(this);
    
    shared actual void visit(Visitor visitor)
            => visitor.visitStringTemplate(this);

    shared actual Boolean equals(Object that) {
        if (is StringTemplate that) {
            return literals == that.literals && expressions == that.expressions;
        } else {
            return false;
        }
    }
    
    shared actual Integer hash
            => 31 * (literals.hash + 31 * expressions.hash);
    
    shared StringTemplate copy([StringLiteral+] literals = this.literals, [Expression+] expressions = this.expressions) {
        value ret = StringTemplate(literals, expressions);
        copyExtraInfoTo(ret);
        return ret;
    }
}