package com.googlecode.totallylazy.template.ast; import com.googlecode.totallylazy.Maps; import com.googlecode.totallylazy.Pair; import com.googlecode.totallylazy.parser.Parser; import com.googlecode.totallylazy.parser.Parsers; import com.googlecode.totallylazy.predicates.Predicate; import java.util.List; import java.util.concurrent.Callable; import static com.googlecode.totallylazy.Arrays.list; import static com.googlecode.totallylazy.Characters.alphaNumeric; import static com.googlecode.totallylazy.parser.Parsers.between; import static com.googlecode.totallylazy.parser.Parsers.characters; import static com.googlecode.totallylazy.parser.Parsers.isChar; import static com.googlecode.totallylazy.parser.Parsers.or; import static com.googlecode.totallylazy.parser.Parsers.ws; import static com.googlecode.totallylazy.parser.Parsers.wsChar; import static com.googlecode.totallylazy.predicates.Predicates.is; import static com.googlecode.totallylazy.predicates.Predicates.not; public class Grammar { static Grammar Default = new Grammar(); public static Parser<List<Expression>> parser() { return Default.TEMPLATE; } public static Parser<List<Expression>> parser(char delimiter) { return new Grammar() { @Override char DELIMITER() { return delimiter; } }.TEMPLATE; } char DELIMITER() { return '$'; } Parser<Void> SEPARATOR = wsChar(',').ignore(); Parser<Void> SINGLE_QUOTE = isChar('\'').ignore(); Parser<Void> DOUBLE_QUOTE = isChar('"').ignore(); Parser<String> IDENTIFIER = Parsers.characters(alphaNumeric).map(Object::toString); Parser<Name> NAME = IDENTIFIER.map(Name::name); Parser<Text> TEXT = textExcept(is(DELIMITER())); Parser<Text> SINGLE_QUOTED = between(SINGLE_QUOTE, textExcept('\''), SINGLE_QUOTE); Parser<Text> DOUBLE_QUOTED = between(DOUBLE_QUOTE, textExcept('"'), DOUBLE_QUOTE); Parser<Text> LITERAL = SINGLE_QUOTED.or(DOUBLE_QUOTED); static Parser<Text> textExcept(char c) { return textExcept(is(c)); } static Parser<Text> textExcept(Predicate<Character> predicate) { return Parsers.characters(not(predicate)).map(Text::text); } Parser<Expression> VALUE = Parsers.lazy(new Callable<Parser<Expression>>() { @Override public Parser<Expression> call() throws Exception { return ws(or(LITERAL, FUNCTION_CALL, ATTRIBUTE)); } }); Parser<Expression> EXPRESSION = Parsers.lazy(new Callable<Parser<Expression>>() { @Override public Parser<Expression> call() throws Exception { return Parsers.<Expression>or(FUNCTION_CALL, MAPPING, ATTRIBUTE).surroundedBy(isChar(DELIMITER())); } }); Parser<Expression> NAMES = Parsers.lazy(new Callable<Parser<Expression>>() { @Override public Parser<Expression> call() throws Exception { return or(NAME, INDIRECTION); } }); Parser<Attribute> ATTRIBUTE = NAMES.sepBy1(isChar('.')).map(Attribute::attribute); Parser<Pair<String, Expression>> NAMED_ARGUMENT = IDENTIFIER.followedBy(isChar('=')).then(VALUE); Parser<NamedArguments> NAMED_ARGUMENTS = NAMED_ARGUMENT.sepBy1(SEPARATOR).map(Maps::map).map(NamedArguments::namedArguments); Parser<ImplicitArguments> IMPLICIT_ARGUMENTS = VALUE.sepBy(SEPARATOR).map(ImplicitArguments::implicitArguments); Parser<FunctionCall> FUNCTION_CALL = NAMES.then(between(isChar('('), or(NAMED_ARGUMENTS, IMPLICIT_ARGUMENTS), isChar(')'))). map(pair -> FunctionCall.functionCall(pair.first(), pair.second())); Parser<List<Expression>> TEMPLATE = or(EXPRESSION, TEXT).many1(); Parser<List<String>> PARAMETERS_NAMES = IDENTIFIER.sepBy1(SEPARATOR); Parser<Anonymous> ANONYMOUS_TEMPLATE = between(wsChar('{'), PARAMETERS_NAMES.followedBy(wsChar('|')).optional().map(o -> o.getOrElse(list())). then(characters(not('}')).flatMap(c -> TEMPLATE.parse(c))), wsChar('}')). map(pair -> Anonymous.anonymous(pair.first(), pair.second())); Parser<Mapping> MAPPING = ATTRIBUTE.followedBy(isChar(':')).then(ANONYMOUS_TEMPLATE). map(pair -> Mapping.mapping(pair.first(), pair.second())); Parser<Indirection> INDIRECTION = Parsers.<Expression>or(ATTRIBUTE, ANONYMOUS_TEMPLATE, LITERAL).between(isChar('('), isChar(')')). map(Indirection::indirection); }