Expression.prototype.tokenize = function(expr) {
    var bnfWords = new BNF("words", {
        "words": [
            [ "word", "words", ],
            [ "word", ],
        ],
        "word": [
            [ "boolean-literal" ],
            [ "comparison-operator" ],
            [ "conditional-operator" ],
            [ "conditional-function" ],
            [ "string-literal" ],
            [ "number-literal" ],
            [ "identifier" ],
            [ "white-space" ],
            [ "delimitor" ],
            [ BNF.lex("bool") ],
            [ BNF.lex("comp") ],
            [ BNF.lex("oper") ],
            [ BNF.lex("deli") ],
            [ BNF.lex("func") ],
            [ BNF.lex("str") ],
            [ BNF.lex("num") ],
            [ BNF.lex("ident") ],
        ],
        "boolean-literal": [
            [ BNF.literal("true") ],
            [ BNF.literal("false") ]
        ],
        "comparison-operator": [
            [ BNF.literal("=") ],
            [ BNF.literal("<"), BNF.literal(">") ],
            [ BNF.literal("<"), BNF.literal("=") ],
            [ BNF.literal("<") ],
            [ BNF.literal(">"), BNF.literal("=") ],
            [ BNF.literal(">") ],
        ],
        "conditional-operator": [
            [ BNF.literal("AND") ],
            [ BNF.literal("OR") ],
            [ BNF.literal("NOT") ],
            [ BNF.literal("BETWEEN") ],
            [ BNF.literal("IN") ],
        ],
        "white-space": [
            [ BNF.lex("WS") ]
        ],
        "delimitor": [
            [ BNF.literal(",") ],
            [ BNF.literal("(") ],
            [ BNF.literal(")") ],
            [ BNF.lex("PUNCT") ],
        ],
        "conditional-function": [
            [ BNF.literal("attribute_exists") ],
            [ BNF.literal("attribute_not_exists") ],
            [ BNF.literal("attribute_type") ],
            [ BNF.literal("begins_with") ],
            [ BNF.literal("contains") ],
            [ BNF.literal("size") ],
        ],
        "string-literal": [
            [ "string-literal-dq" ],
            [ "string-literal-sq" ],
        ],
        "string-literal-dq": [
            [ BNF.literal('"'), "string-literal-dq-end" ],
        ],
        "string-literal-dq-end": [
            [ BNF.literalUntil('"') ],
        ],
        "string-literal-sq": [
            [ BNF.literal("'"), "string-literal-sq-end" ],
        ],
        "string-literal-sq-end": [
            [ BNF.literalUntil("'") ],
        ],
        "number-literal": [
            [ BNF.literal("-"), "number-literal" ],
            [ BNF.lex("NUMLIT"), BNF.literal("."), BNF.lex("NUMLIT") ],
            [ BNF.lex("NUMLIT") ],
        ],
        "identifier": [
            [ BNF.ident, BNF.literal("."), "identifier" ],
            [ BNF.literal(":"), BNF.ident ],
            [ BNF.ident ],
        ],
    }, {
        "boolean-literal": "bool",
        "comparison-operator": "comp",
        "conditional-operator": "oper",
        "delimitor": "deli",
        "conditional-function": "func",
        "string-literal": "str",
        "number-literal": "num",
        "identifier": "ident",
    });
    var bnfTokens = BNF.tokenize(expr, bnfWords);
    if(bnfTokens != null && !Array.isArray(bnfTokens) && !bnfTokens.match) {
        throw new Error("Could not tokenize " + expr);
    }
    bnfTokens.forEach( token => {
        var term = token.getTerm();
        if(token.getType() == "str") {
            switch(term.replace(/^(.).*(.)$/, "$1$2")) {
                case '""':
                    token.setTerm(unescapeDQ(term.replace(/^"(.*)"$/, "$1")));
                    break;
                case "''":
                    token.setTerm(unescapeSQ(term.replace(/^'(.*)'$/, "$1")));
                    break;
                default:
                    throw new Error("Broken string-literal " + term);
            }
        }
    });
    this.tokens = bnfTokens.map( t => {
        return { "type": t.getType(), "lex": t.getTerm() };
    });
};
"use strict";
var BNF = require("lex-bnf");

function DynamoDbSqlishParser() {}

//
// Basic Terms
//
var CREATE = BNF.literal("CREATE");
var DROP = BNF.literal("DROP");
var TABLE = BNF.literal("TABLE");
var ALTER = BNF.literal("ALTER");
var SELECT = BNF.literal("SELECT");
var INSERT = BNF.literal("INSERT");
var UPDATE = BNF.literal("UPDATE");
var DELETE = BNF.literal("DELETE");
var SET = BNF.literal("SET");
var INTO = BNF.literal("INTO");
var VALUES = BNF.literal("VALUES");
var FROM = BNF.literal("FROM");
var WHERE = BNF.literal("WHERE");
var FILTER = BNF.literal("FILTER");
var LIMIT = BNF.literal("LIMIT");
var BETWEEN = BNF.literal("BETWEEN");
var AND = BNF.literal("AND");
var OR = BNF.literal("OR");
var NOT = BNF.literal("NOT");
var IN = BNF.literal("IN");

DynamoDbSqlishParser.BNF = {
    "sqlish": [