// CodeMirror, copyright © by Marijn Haverbeke and others // Distributed under an MIT license: codemirror.net/LICENSE

(function(mod) {

if (typeof exports == "object" && typeof module == "object") // CommonJS
  mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
  define(["../../lib/codemirror"], mod);
else // Plain browser env
  mod(CodeMirror);

})(function(CodeMirror) { “use strict”;

CodeMirror.defineMode(“dylan”, function(_config) {

// Words
var words = {
  // Words that introduce unnamed definitions like "define interface"
  unnamedDefinition: ["interface"],

  // Words that introduce simple named definitions like "define library"
  namedDefinition: ["module", "library", "macro",
                    "C-struct", "C-union",
                    "C-function", "C-callable-wrapper"
                   ],

  // Words that introduce type definitions like "define class".
  // These are also parameterized like "define method" and are
  // appended to otherParameterizedDefinitionWords
  typeParameterizedDefinition: ["class", "C-subtype", "C-mapped-subtype"],

  // Words that introduce trickier definitions like "define method".
  // These require special definitions to be added to startExpressions
  otherParameterizedDefinition: ["method", "function",
                                 "C-variable", "C-address"
                                ],

  // Words that introduce module constant definitions.
  // These must also be simple definitions and are
  // appended to otherSimpleDefinitionWords
  constantSimpleDefinition: ["constant"],

  // Words that introduce module variable definitions.
  // These must also be simple definitions and are
  // appended to otherSimpleDefinitionWords
  variableSimpleDefinition: ["variable"],

  // Other words that introduce simple definitions
  // (without implicit bodies).
  otherSimpleDefinition: ["generic", "domain",
                          "C-pointer-type",
                          "table"
                         ],

  // Words that begin statements with implicit bodies.
  statement: ["if", "block", "begin", "method", "case",
              "for", "select", "when", "unless", "until",
              "while", "iterate", "profiling", "dynamic-bind"
             ],

  // Patterns that act as separators in compound statements.
  // This may include any general pattern that must be indented
  // specially.
  separator: ["finally", "exception", "cleanup", "else",
              "elseif", "afterwards"
             ],

  // Keywords that do not require special indentation handling,
  // but which should be highlighted
  other: ["above", "below", "by", "from", "handler", "in",
          "instance", "let", "local", "otherwise", "slot",
          "subclass", "then", "to", "keyed-by", "virtual"
         ],

  // Condition signaling function calls
  signalingCalls: ["signal", "error", "cerror",
                   "break", "check-type", "abort"
                  ]
};

words["otherDefinition"] =
  words["unnamedDefinition"]
  .concat(words["namedDefinition"])
  .concat(words["otherParameterizedDefinition"]);

words["definition"] =
  words["typeParameterizedDefinition"]
  .concat(words["otherDefinition"]);

words["parameterizedDefinition"] =
  words["typeParameterizedDefinition"]
  .concat(words["otherParameterizedDefinition"]);

words["simpleDefinition"] =
  words["constantSimpleDefinition"]
  .concat(words["variableSimpleDefinition"])
  .concat(words["otherSimpleDefinition"]);

words["keyword"] =
  words["statement"]
  .concat(words["separator"])
  .concat(words["other"]);

// Patterns
var symbolPattern = "[-_a-zA-Z?!*@<>$%]+";
var symbol = new RegExp("^" + symbolPattern);
var patterns = {
  // Symbols with special syntax
  symbolKeyword: symbolPattern + ":",
  symbolClass: "<" + symbolPattern + ">",
  symbolGlobal: "\\*" + symbolPattern + "\\*",
  symbolConstant: "\\$" + symbolPattern
};
var patternStyles = {
  symbolKeyword: "atom",
  symbolClass: "tag",
  symbolGlobal: "variable-2",
  symbolConstant: "variable-3"
};

// Compile all patterns to regular expressions
for (var patternName in patterns)
  if (patterns.hasOwnProperty(patternName))
    patterns[patternName] = new RegExp("^" + patterns[patternName]);

// Names beginning "with-" and "without-" are commonly
// used as statement macro
patterns["keyword"] = [/^with(?:out)?-[-_a-zA-Z?!*@<>$%]+/];

var styles = {};
styles["keyword"] = "keyword";
styles["definition"] = "def";
styles["simpleDefinition"] = "def";
styles["signalingCalls"] = "builtin";

// protected words lookup table
var wordLookup = {};
var styleLookup = {};

[
  "keyword",
  "definition",
  "simpleDefinition",
  "signalingCalls"
].forEach(function(type) {
  words[type].forEach(function(word) {
    wordLookup[word] = type;
    styleLookup[word] = styles[type];
  });
});

function chain(stream, state, f) {
  state.tokenize = f;
  return f(stream, state);
}

var type, content;

function ret(_type, style, _content) {
  type = _type;
  content = _content;
  return style;
}

function tokenBase(stream, state) {
  // String
  var ch = stream.peek();
  if (ch == "'" || ch == '"') {
    stream.next();
    return chain(stream, state, tokenString(ch, "string", "string"));
  }
  // Comment
  else if (ch == "/") {
    stream.next();
    if (stream.eat("*")) {
      return chain(stream, state, tokenComment);
    } else if (stream.eat("/")) {
      stream.skipToEnd();
      return ret("comment", "comment");
    } else {
      stream.skipTo(" ");
      return ret("operator", "operator");
    }
  }
  // Decimal
  else if (/\d/.test(ch)) {
    stream.match(/^\d*(?:\.\d*)?(?:e[+\-]?\d+)?/);
    return ret("number", "number");
  }
  // Hash
  else if (ch == "#") {
    stream.next();
    // Symbol with string syntax
    ch = stream.peek();
    if (ch == '"') {
      stream.next();
      return chain(stream, state, tokenString('"', "symbol", "string-2"));
    }
    // Binary number
    else if (ch == "b") {
      stream.next();
      stream.eatWhile(/[01]/);
      return ret("number", "number");
    }
    // Hex number
    else if (ch == "x") {
      stream.next();
      stream.eatWhile(/[\da-f]/i);
      return ret("number", "number");
    }
    // Octal number
    else if (ch == "o") {
      stream.next();
      stream.eatWhile(/[0-7]/);
      return ret("number", "number");
    }
    // Hash symbol
    else {
      stream.eatWhile(/[-a-zA-Z]/);
      return ret("hash", "keyword");
    }
  } else if (stream.match("end")) {
    return ret("end", "keyword");
  }
  for (var name in patterns) {
    if (patterns.hasOwnProperty(name)) {
      var pattern = patterns[name];
      if ((pattern instanceof Array && pattern.some(function(p) {
        return stream.match(p);
      })) || stream.match(pattern))
        return ret(name, patternStyles[name], stream.current());
    }
  }
  if (stream.match("define")) {
    return ret("definition", "def");
  } else {
    stream.eatWhile(/[\w\-]/);
    // Keyword
    if (wordLookup[stream.current()]) {
      return ret(wordLookup[stream.current()], styleLookup[stream.current()], stream.current());
    } else if (stream.current().match(symbol)) {
      return ret("variable", "variable");
    } else {
      stream.next();
      return ret("other", "variable-2");
    }
  }
}

function tokenComment(stream, state) {
  var maybeEnd = false,
  ch;
  while ((ch = stream.next())) {
    if (ch == "/" && maybeEnd) {
      state.tokenize = tokenBase;
      break;
    }
    maybeEnd = (ch == "*");
  }
  return ret("comment", "comment");
}

function tokenString(quote, type, style) {
  return function(stream, state) {
    var next, end = false;
    while ((next = stream.next()) != null) {
      if (next == quote) {
        end = true;
        break;
      }
    }
    if (end)
      state.tokenize = tokenBase;
    return ret(type, style);
  };
}

// Interface
return {
  startState: function() {
    return {
      tokenize: tokenBase,
      currentIndent: 0
    };
  },
  token: function(stream, state) {
    if (stream.eatSpace())
      return null;
    var style = state.tokenize(stream, state);
    return style;
  },
  blockCommentStart: "/*",
  blockCommentEnd: "*/"
};

});

CodeMirror.defineMIME(“text/x-dylan”, “dylan”);

});