function wordRegExp(e, g) {
  return /* @__PURE__ */ RegExp((g ? "" : "^") + "(?:" + e.join("|") + ")" + (g ? "$" : "\\b"));
}
function chain(e, g, _) {
  return _.tokenize.push(e), e(g, _);
}
var operators = /^(?:[-+/%|&^]|\*\*?|[<>]{2})/, conditionalOperators = /^(?:[=!]~|===|<=>|[<>=!]=?|[|&]{2}|~)/, indexingOperators = /^(?:\[\][?=]?)/, anotherOperators = /^(?:\.(?:\.{2})?|->|[?:])/, idents = /^[a-z_\u009F-\uFFFF][a-zA-Z0-9_\u009F-\uFFFF]*/, types = /^[A-Z_\u009F-\uFFFF][a-zA-Z0-9_\u009F-\uFFFF]*/, keywords = wordRegExp(/* @__PURE__ */ "abstract.alias.as.asm.begin.break.case.class.def.do.else.elsif.end.ensure.enum.extend.for.fun.if.include.instance_sizeof.lib.macro.module.next.of.out.pointerof.private.protected.rescue.return.require.select.sizeof.struct.super.then.type.typeof.uninitialized.union.unless.until.when.while.with.yield.__DIR__.__END_LINE__.__FILE__.__LINE__".split(".")), atomWords = wordRegExp([
  "true",
  "false",
  "nil",
  "self"
]), indentKeywords = wordRegExp([
  "def",
  "fun",
  "macro",
  "class",
  "module",
  "struct",
  "lib",
  "enum",
  "union",
  "do",
  "for"
]), indentExpressionKeywords = wordRegExp([
  "if",
  "unless",
  "case",
  "while",
  "until",
  "begin",
  "then"
]), dedentKeywordsArray = [
  "end",
  "else",
  "elsif",
  "rescue",
  "ensure"
], dedentKeywords = wordRegExp(dedentKeywordsArray), dedentPunctualsArray = [
  "\\)",
  "\\}",
  "\\]"
], dedentPunctuals = /* @__PURE__ */ RegExp("^(?:" + dedentPunctualsArray.join("|") + ")$"), nextTokenizer = {
  def: tokenFollowIdent,
  fun: tokenFollowIdent,
  macro: tokenMacroDef,
  class: tokenFollowType,
  module: tokenFollowType,
  struct: tokenFollowType,
  lib: tokenFollowType,
  enum: tokenFollowType,
  union: tokenFollowType
}, matching = {
  "[": "]",
  "{": "}",
  "(": ")",
  "<": ">"
};
function tokenBase(e, S) {
  if (e.eatSpace()) return null;
  if (S.lastToken != "\\" && e.match("{%", false)) return chain(tokenMacro("%", "%"), e, S);
  if (S.lastToken != "\\" && e.match("{{", false)) return chain(tokenMacro("{", "}"), e, S);
  if (e.peek() == "#") return e.skipToEnd(), "comment";
  var C;
  if (e.match(idents)) return e.eat(/[?!]/), C = e.current(), e.eat(":") ? "atom" : S.lastToken == "." ? "property" : keywords.test(C) ? (indentKeywords.test(C) ? !(C == "fun" && S.blocks.indexOf("lib") >= 0) && !(C == "def" && S.lastToken == "abstract") && (S.blocks.push(C), S.currentIndent += 1) : (S.lastStyle == "operator" || !S.lastStyle) && indentExpressionKeywords.test(C) ? (S.blocks.push(C), S.currentIndent += 1) : C == "end" && (S.blocks.pop(), --S.currentIndent), nextTokenizer.hasOwnProperty(C) && S.tokenize.push(nextTokenizer[C]), "keyword") : atomWords.test(C) ? "atom" : "variable";
  if (e.eat("@")) return e.peek() == "[" ? chain(tokenNest("[", "]", "meta"), e, S) : (e.eat("@"), e.match(idents) || e.match(types), "propertyName");
  if (e.match(types)) return "tag";
  if (e.eat(":")) return e.eat('"') ? chain(tokenQuote('"', "atom", false), e, S) : e.match(idents) || e.match(types) || e.match(operators) || e.match(conditionalOperators) || e.match(indexingOperators) ? "atom" : (e.eat(":"), "operator");
  if (e.eat('"')) return chain(tokenQuote('"', "string", true), e, S);
  if (e.peek() == "%") {
    var w = "string", T = true, E;
    if (e.match("%r")) w = "string.special", E = e.next();
    else if (e.match("%w")) T = false, E = e.next();
    else if (e.match("%q")) T = false, E = e.next();
    else if (E = e.match(/^%([^\w\s=])/)) E = E[1];
    else if (e.match(/^%[a-zA-Z_\u009F-\uFFFF][\w\u009F-\uFFFF]*/)) return "meta";
    else if (e.eat("%")) return "operator";
    return matching.hasOwnProperty(E) && (E = matching[E]), chain(tokenQuote(E, w, T), e, S);
  }
  return (C = e.match(/^<<-('?)([A-Z]\w*)\1/)) ? chain(tokenHereDoc(C[2], !C[1]), e, S) : e.eat("'") ? (e.match(/^(?:[^']|\\(?:[befnrtv0'"]|[0-7]{3}|u(?:[0-9a-fA-F]{4}|\{[0-9a-fA-F]{1,6}\})))/), e.eat("'"), "atom") : e.eat("0") ? (e.eat("x") ? e.match(/^[0-9a-fA-F_]+/) : e.eat("o") ? e.match(/^[0-7_]+/) : e.eat("b") && e.match(/^[01_]+/), "number") : e.eat(/^\d/) ? (e.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?\d+)?/), "number") : e.match(operators) ? (e.eat("="), "operator") : e.match(conditionalOperators) || e.match(anotherOperators) ? "operator" : (C = e.match(/[({[]/, false)) ? (C = C[0], chain(tokenNest(C, matching[C], null), e, S)) : e.eat("\\") ? (e.next(), "meta") : (e.next(), null);
}
function tokenNest(e, g, _, v) {
  return function(y, b) {
    if (!v && y.match(e)) return b.tokenize[b.tokenize.length - 1] = tokenNest(e, g, _, true), b.currentIndent += 1, _;
    var x = tokenBase(y, b);
    return y.current() === g && (b.tokenize.pop(), --b.currentIndent, x = _), x;
  };
}
function tokenMacro(e, g, _) {
  return function(v, y) {
    return !_ && v.match("{" + e) ? (y.currentIndent += 1, y.tokenize[y.tokenize.length - 1] = tokenMacro(e, g, true), "meta") : v.match(g + "}") ? (--y.currentIndent, y.tokenize.pop(), "meta") : tokenBase(v, y);
  };
}
function tokenMacroDef(e, g) {
  if (e.eatSpace()) return null;
  var _;
  if (_ = e.match(idents)) {
    if (_ == "def") return "keyword";
    e.eat(/[?!]/);
  }
  return g.tokenize.pop(), "def";
}
function tokenFollowIdent(e, g) {
  return e.eatSpace() ? null : (e.match(idents) ? e.eat(/[!?]/) : e.match(operators) || e.match(conditionalOperators) || e.match(indexingOperators), g.tokenize.pop(), "def");
}
function tokenFollowType(e, g) {
  return e.eatSpace() ? null : (e.match(types), g.tokenize.pop(), "def");
}
function tokenQuote(e, g, _) {
  return function(v, y) {
    for (var b = false; v.peek(); ) if (b) v.next(), b = false;
    else {
      if (v.match("{%", false)) return y.tokenize.push(tokenMacro("%", "%")), g;
      if (v.match("{{", false)) return y.tokenize.push(tokenMacro("{", "}")), g;
      if (_ && v.match("#{", false)) return y.tokenize.push(tokenNest("#{", "}", "meta")), g;
      var x = v.next();
      if (x == e) return y.tokenize.pop(), g;
      b = _ && x == "\\";
    }
    return g;
  };
}
function tokenHereDoc(e, g) {
  return function(_, v) {
    if (_.sol() && (_.eatSpace(), _.match(e))) return v.tokenize.pop(), "string";
    for (var y = false; _.peek(); ) if (y) _.next(), y = false;
    else {
      if (_.match("{%", false)) return v.tokenize.push(tokenMacro("%", "%")), "string";
      if (_.match("{{", false)) return v.tokenize.push(tokenMacro("{", "}")), "string";
      if (g && _.match("#{", false)) return v.tokenize.push(tokenNest("#{", "}", "meta")), "string";
      y = _.next() == "\\" && g;
    }
    return "string";
  };
}
const crystal = {
  name: "crystal",
  startState: function() {
    return {
      tokenize: [tokenBase],
      currentIndent: 0,
      lastToken: null,
      lastStyle: null,
      blocks: []
    };
  },
  token: function(e, g) {
    var _ = g.tokenize[g.tokenize.length - 1](e, g), v = e.current();
    return _ && _ != "comment" && (g.lastToken = v, g.lastStyle = _), _;
  },
  indent: function(e, g, _) {
    return g = g.replace(/^\s*(?:\{%)?\s*|\s*(?:%\})?\s*$/g, ""), dedentKeywords.test(g) || dedentPunctuals.test(g) ? _.unit * (e.currentIndent - 1) : _.unit * e.currentIndent;
  },
  languageData: {
    indentOnInput: wordRegExp(dedentPunctualsArray.concat(dedentKeywordsArray), true),
    commentTokens: { line: "#" }
  }
};
export {
  crystal as t
};
