function parseWords(e) {
  for (var i = {}, a = e.split(" "), o = 0; o < a.length; ++o) i[a[o]] = true;
  return i;
}
var keywords = parseWords("#end #else #break #stop #[[ #]] #{end} #{else} #{break} #{stop}"), functions = parseWords("#if #elseif #foreach #set #include #parse #macro #define #evaluate #{if} #{elseif} #{foreach} #{set} #{include} #{parse} #{macro} #{define} #{evaluate}"), specials = parseWords("$foreach.count $foreach.hasNext $foreach.first $foreach.last $foreach.topmost $foreach.parent.count $foreach.parent.hasNext $foreach.parent.first $foreach.parent.last $foreach.parent $velocityCount $!bodyContent $bodyContent"), isOperatorChar = /[+\-*&%=<>!?:\/|]/;
function chain(e, i, a) {
  return i.tokenize = a, a(e, i);
}
function tokenBase(e, l) {
  var u = l.beforeParams;
  l.beforeParams = false;
  var d = e.next();
  if (d == "'" && !l.inString && l.inParams) return l.lastTokenWasBuiltin = false, chain(e, l, tokenString(d));
  if (d == '"') {
    if (l.lastTokenWasBuiltin = false, l.inString) return l.inString = false, "string";
    if (l.inParams) return chain(e, l, tokenString(d));
  } else if (/[\[\]{}\(\),;\.]/.test(d)) return d == "(" && u ? l.inParams = true : d == ")" && (l.inParams = false, l.lastTokenWasBuiltin = true), null;
  else if (/\d/.test(d)) return l.lastTokenWasBuiltin = false, e.eatWhile(/[\w\.]/), "number";
  else if (d == "#" && e.eat("*")) return l.lastTokenWasBuiltin = false, chain(e, l, tokenComment);
  else if (d == "#" && e.match(/ *\[ *\[/)) return l.lastTokenWasBuiltin = false, chain(e, l, tokenUnparsed);
  else if (d == "#" && e.eat("#")) return l.lastTokenWasBuiltin = false, e.skipToEnd(), "comment";
  else if (d == "$") return e.eat("!"), e.eatWhile(/[\w\d\$_\.{}-]/), specials && specials.propertyIsEnumerable(e.current()) ? "keyword" : (l.lastTokenWasBuiltin = true, l.beforeParams = true, "builtin");
  else if (isOperatorChar.test(d)) return l.lastTokenWasBuiltin = false, e.eatWhile(isOperatorChar), "operator";
  else {
    e.eatWhile(/[\w\$_{}@]/);
    var f = e.current();
    return keywords && keywords.propertyIsEnumerable(f) ? "keyword" : functions && functions.propertyIsEnumerable(f) || e.current().match(/^#@?[a-z0-9_]+ *$/i) && e.peek() == "(" && !(functions && functions.propertyIsEnumerable(f.toLowerCase())) ? (l.beforeParams = true, l.lastTokenWasBuiltin = false, "keyword") : l.inString ? (l.lastTokenWasBuiltin = false, "string") : e.pos > f.length && e.string.charAt(e.pos - f.length - 1) == "." && l.lastTokenWasBuiltin ? "builtin" : (l.lastTokenWasBuiltin = false, null);
  }
}
function tokenString(e) {
  return function(i, a) {
    for (var o = false, s, c = false; (s = i.next()) != null; ) {
      if (s == e && !o) {
        c = true;
        break;
      }
      if (e == '"' && i.peek() == "$" && !o) {
        a.inString = true, c = true;
        break;
      }
      o = !o && s == "\\";
    }
    return c && (a.tokenize = tokenBase), "string";
  };
}
function tokenComment(e, i) {
  for (var a = false, o; o = e.next(); ) {
    if (o == "#" && a) {
      i.tokenize = tokenBase;
      break;
    }
    a = o == "*";
  }
  return "comment";
}
function tokenUnparsed(e, i) {
  for (var a = 0, o; o = e.next(); ) {
    if (o == "#" && a == 2) {
      i.tokenize = tokenBase;
      break;
    }
    o == "]" ? a++ : o != " " && (a = 0);
  }
  return "meta";
}
const velocity = {
  name: "velocity",
  startState: function() {
    return {
      tokenize: tokenBase,
      beforeParams: false,
      inParams: false,
      inString: false,
      lastTokenWasBuiltin: false
    };
  },
  token: function(e, i) {
    return e.eatSpace() ? null : i.tokenize(e, i);
  },
  languageData: { commentTokens: {
    line: "##",
    block: {
      open: "#*",
      close: "*#"
    }
  } }
};
export {
  velocity as t
};
