com.sillelien.dollar.script.DollarLexer.java Source code

Java tutorial

Introduction

Here is the source code for com.sillelien.dollar.script.DollarLexer.java

Source

/*
 * Copyright (c) 2014-2015 Neil Ellis
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sillelien.dollar.script;

import com.sillelien.dollar.api.Type;
import com.sillelien.dollar.api.types.DollarFactory;
import com.sillelien.dollar.api.var;
import org.apache.commons.lang.StringEscapeUtils;
import org.codehaus.jparsec.*;
import org.codehaus.jparsec.functors.Map;
import org.codehaus.jparsec.pattern.CharPredicates;
import org.codehaus.jparsec.pattern.Pattern;
import org.codehaus.jparsec.pattern.Patterns;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.math.BigDecimal;

import static com.sillelien.dollar.api.DollarStatic.*;
import static org.codehaus.jparsec.Parsers.or;
import static org.codehaus.jparsec.Parsers.token;

class DollarLexer {
    public static final Terminals KEYWORDS = Terminals.operators("out", "err", "debug", "fix", "causes", "when",
            "if", "then", "for", "each", "fail", "assert", "switch", "choose", "not", "dollar", "fork", "join",
            "print", "default", "debug", "error", "filter", "every", "until", "unless", "and", "or", "dispatch",
            "send", "publish", "subscribe", "emit", "drain", "all", "import", "reduce", "truthy", "is", "else",
            "const", "in", "true", "false", "yes", "no", "void", "error", "to", "from", "size", "as", "while",
            "collect", "module", "include", "export", "with", "parallel", "serial", "fork", "null", "volatile",
            "read", "write", "block", "mutate", "pure", "variant", "variance", "pluripotent", "vary", "varies",
            "infinity");
    public static final Terminals OPERATORS = Terminals.operators("|", ">>", "<<", "->", "=>", ".:", "<=", ">=",
            "<-", "(", ")", "--", "++", ".", ":", "<", ">", "?", "?:", "!", "!!", ">&", "{", "}", ",", "$", "=",
            ";", "[", "]", "??", "!!", "*>", "==", "!=", "+", "-", "\n", ":=", "&", "&=", "@", "+>", "<+", "*>",
            "<*", "*|", "|*", "*|*", "|>", "<|", "&>", "<&", "?>", "<?", "?->", "<=>", "<$", "-_-", "::", "/", "%",
            "*", "&&", "||", "<--", "<++", "\u2357", "~", "?$?", "-:", "..", "?..?", "", "@@", "<@", "@>", "#",
            "!?#*!", "?*", "(!)", "(>)", "(=)", "(~)", "(-)", "(+)", "(?)", "?->", "|:|", "|..|", "-<", "?=", "*=");

    public static final Map<String, Tokens.Fragment> BACKTICK_QUOTE_STRING = new Map<String, Tokens.Fragment>() {
        public Tokens.Fragment map(@NotNull String text) {
            return Tokens.fragment(tokenizeBackTick(text), "java");
        }

        @NotNull
        @Override
        public String toString() {
            return "JAVA_STRING";
        }
    };

    public static final Parser<?> TERMINATOR_SYMBOL = or(OP("\n"), OP(";")).many1();
    public static final Parser<?> COMMA_TERMINATOR = Parsers.sequence(OP(","), OP("\n").many());
    public static final Parser<?> COMMA_OR_NEWLINE_TERMINATOR = or(OP(","), OP("\n")).many1();
    public static final Parser<?> SEMICOLON_TERMINATOR = or(OP(";").followedBy(OP("\n").many()), OP("\n").many1());
    public static final Parser<Void> IGNORED = or(Scanners.JAVA_LINE_COMMENT, Scanners.JAVA_BLOCK_COMMENT,
            Scanners.among(" \t\r").many1(), Scanners.lineComment("#!")).skipMany();
    public static final Parser<var> IDENTIFIER = identifier();
    public static final Parser<var> IDENTIFIER_KEYWORD = identifierKeyword();
    public static final Parser<var> STRING_LITERAL = Terminals.StringLiteral.PARSER
            .map((o) -> DollarFactory.fromStringValue(o));
    public static final Parser<var> DECIMAL_LITERAL = Terminals.DecimalLiteral.PARSER
            .map(s -> s.contains(".") ? $(Double.parseDouble(s)) : $(Long.parseLong(s)));
    public static final Parser<var> INTEGER_LITERAL = Terminals.IntegerLiteral.PARSER.map(s -> {
        if (new BigDecimal(s).compareTo(new BigDecimal(Long.MAX_VALUE)) > 0) {
            return $(Double.parseDouble(s));
        } else if (new BigDecimal(s).compareTo(new BigDecimal(-Long.MAX_VALUE)) < 0) {
            return $(Double.parseDouble(s));
        } else {
            return $(Long.parseLong(s));
        }
    });
    public static final Parser<var> BUILTIN = token(new TokenTagMap("builtin")).map(s -> {
        var v = $(s);
        v.setMetaAttribute("__builtin", s);
        return v;
    });
    public static final Parser<var> URL = token(new TokenTagMap("uri")).map(DollarFactory::fromURI);
    public static final Parser<?> TOKENIZER = or(DollarLexer.url(), OPERATORS.tokenizer(), DollarLexer.decimal(),
            java(), Scanners.DOUBLE_QUOTE_STRING.map(new Map<String, String>() {
                public String map(@NotNull String text) {
                    return tokenizeDoubleQuote(text);
                }

                @NotNull
                @Override
                public String toString() {
                    return "DOUBLE_QUOTE_STRING";
                }
            }), Scanners.SINGLE_QUOTE_STRING.map(new Map<String, String>() {
                public String map(@NotNull String text) {
                    return tokenizeSingleQuote(text);
                }

                @NotNull
                @Override
                public String toString() {
                    return "SINGLE_QUOTE_STRING";
                }
            }), Terminals.IntegerLiteral.TOKENIZER,
            Parsers.longest(DollarLexer.builtin(), KEYWORDS.tokenizer(), Terminals.Identifier.TOKENIZER));

    static String tokenizeDoubleQuote(@NotNull String text) {
        return StringEscapeUtils.unescapeJava(text.substring(1, text.length() - 1));

    }

    static String tokenizeSingleQuote(@NotNull String text) {
        return StringEscapeUtils.unescapeJava(text.substring(1, text.length() - 1));
    }

    public static Parser<?> url() {
        return (Scanners
                .pattern(
                        Patterns.isChar(CharPredicates.IS_ALPHA_).many1()
                                .next(Patterns.isChar(':').next(Patterns.among("=\"").not())
                                        .next(Patterns.among("-._~:/?#@!$&'*+,;=%")
                                                .or(Patterns.isChar(CharPredicates.IS_ALPHA_NUMERIC_)).many1())),
                        "uri")
                .source()).map(new Map<String, Tokens.Fragment>() {
                    public Tokens.Fragment map(String text) {
                        return Tokens.fragment(text, "uri");
                    }

                    @NotNull
                    @Override
                    public String toString() {
                        return "URI";
                    }
                });
    }

    public static Parser<?> java() {
        Parser<?> quote = Scanners.isChar('`');
        return Scanners.pattern(Patterns.regex("((``)|[^`])*"), "Java Snippet").between(quote, quote).source()
                .map(BACKTICK_QUOTE_STRING);
    }

    @NotNull
    public static String tokenizeBackTick(@NotNull String text) {
        int end = text.length() - 1;
        StringBuilder buf = new StringBuilder();
        for (int i = 1; i < end; i++) {
            char c = text.charAt(i);
            if (c != '`') {
                buf.append(c);
            } else {
                buf.append('`');
                i++;
            }
        }
        return buf.toString();
    }

    public static Parser<?> decimal() {
        return Scanners
                .pattern(
                        Patterns.INTEGER.next(Patterns.isChar('.').next(Patterns.many1(CharPredicates.IS_DIGIT))
                                .next(Patterns.sequence(Patterns.among("eE"), Patterns.among("+-").optional(),
                                        Patterns.INTEGER).optional())),
                        "decimal")
                .source().map(new Map<String, Tokens.Fragment>() {
                    public Tokens.Fragment map(String text) {
                        return Tokens.fragment(text, Tokens.Tag.DECIMAL);
                    }

                    @Override
                    public String toString() {
                        return String.valueOf(Tokens.Tag.DECIMAL);
                    }
                });
    }

    public static Parser<?> OP(String name, String keyword) {
        return OPERATORS.token(name).or(KEYWORDS.token(keyword));
    }

    // --Commented out by Inspection START (04/12/14 18:27):
    //    static Parser<?> OP_NLS(String... names) {
    //        return OP("\n").many().followedBy(OPERATORS.token(names).followedBy(OP("\n").many()));
    //    }
    // --Commented out by Inspection STOP (04/12/14 18:27)

    public static Parser<?> OP_NL(String... names) {
        return OPERATORS.token(names).followedBy(OP("\n").many());
    }

    public static Parser<?> OP(String name) {
        return OPERATORS.token(name);
    }

    public static Parser<?> KEYWORD_NL(String... names) {
        return KEYWORDS.token(names).followedBy(OP("\n").many());
    }

    public static Parser<?> NL_OP(String... names) {
        return OP("\n").many().followedBy(OPERATORS.token(names));
    }

    public static Parser<String> identifierTokenizer() {
        return Scanners.pattern(Patterns.isChar(CharPredicates.IS_ALPHA_).many1(), "identifier").source();

    }

    public static Parser<?> builtin() {
        return Scanners.pattern(new Pattern() {
            @Override
            public int match(@NotNull CharSequence src, int begin, int end) {
                int i = begin;
                //noinspection StatementWithEmptyBody
                for (; i < end && Character.isAlphabetic(src.charAt(i)); i++) {
                    //
                }
                final String name = src.subSequence(begin, i).toString();
                if (Builtins.exists(name)) {
                    return i - begin;
                } else {
                    return Pattern.MISMATCH;
                }
            }
        }, "builtin").source().map(new Map<String, Tokens.Fragment>() {
            public Tokens.Fragment map(String text) {
                return Tokens.fragment(text, "builtin");
            }

            @NotNull
            @Override
            public String toString() {
                return "builtin";
            }
        });
    }

    public static Parser<var> identifier() {
        return Terminals.Identifier.PARSER.map(i -> {
            switch (i) {
            case "true":
                return DollarFactory.TRUE;
            case "false":
                return DollarFactory.FALSE;
            case "yes":
                return DollarFactory.TRUE;
            case "no":
                return DollarFactory.FALSE;
            case "infinity":
                return DollarFactory.INFINITY;
            case "void":
                return $void();
            case "null":
                return $null(Type.ANY);
            default:
                return $(i);
            }
        });
    }

    public static Parser<var> identifierKeyword() {
        return or(KEYWORD("true"), KEYWORD("false"), KEYWORD("yes"), KEYWORD("no"), KEYWORD("null"),
                KEYWORD("void"), KEYWORD("infinity")).map(new Map<Object, var>() {
                    @NotNull
                    @Override
                    public var map(@NotNull Object i) {
                        switch (i.toString()) {
                        case "true":
                            return DollarFactory.TRUE;
                        case "false":
                            return DollarFactory.FALSE;
                        case "yes":
                            return DollarFactory.TRUE;
                        case "no":
                            return DollarFactory.FALSE;
                        case "null":
                            return DollarFactory.newNull(Type.ANY);
                        case "infinity":
                            return DollarFactory.INFINITY;
                        case "void":
                            return DollarFactory.VOID;
                        default:
                            return $(i);
                        }
                    }
                });
    }

    public static Parser<?> KEYWORD(String... names) {
        return KEYWORDS.token(names);
    }

    public static class TokenTagMap implements TokenMap<String> {
        private final String tag;

        private TokenTagMap(String tag) {
            this.tag = tag;
        }

        @Nullable
        @Override
        public String map(@NotNull Token token) {
            final Object val = token.value();
            if (val instanceof Tokens.Fragment) {
                Tokens.Fragment c = (Tokens.Fragment) val;
                if (!c.tag().equals(tag)) {
                    return null;
                }
                return c.text();
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return tag;
        }
    }
}