// CodeMirror, copyright (c) by Marijn Haverbeke and others

// Distributed under an MIT license: http://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";



function forEach(arr, f) {

  for (var i = 0; i < arr.length; i++) f(arr[i], i)

}

function some(arr, f) {

  for (var i = 0; i < arr.length; i++) if (f(arr[i], i)) return true

  return false

}



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 = {};



  forEach([

    "keyword",

    "definition",

    "simpleDefinition",

    "signalingCalls"

  ], function(type) {

    forEach(words[type], function(word) {

      wordLookup[word] = type;

      styleLookup[word] = styles[type];

    });

  });





  function chain(stream, state, f) {

    state.tokenize = f;

    return f(stream, state);

  }



  function tokenBase(stream, state) {

    // String

    var ch = stream.peek();

    if (ch == "'" || ch == '"') {

      stream.next();

      return chain(stream, state, tokenString(ch, "string"));

    }

    // Comment

    else if (ch == "/") {

      stream.next();

      if (stream.eat("*")) {

        return chain(stream, state, tokenComment);

      } else if (stream.eat("/")) {

        stream.skipToEnd();

        return "comment";

      }

      stream.backUp(1);

    }

    // Decimal

    else if (/[+\-\d\.]/.test(ch)) {

      if (stream.match(/^[+-]?[0-9]*\.[0-9]*([esdx][+-]?[0-9]+)?/i) ||

          stream.match(/^[+-]?[0-9]+([esdx][+-]?[0-9]+)/i) ||

          stream.match(/^[+-]?\d+/)) {

        return "number";

      }

    }

    // Hash

    else if (ch == "#") {

      stream.next();

      // Symbol with string syntax

      ch = stream.peek();

      if (ch == '"') {

        stream.next();

        return chain(stream, state, tokenString('"', "string"));

      }

      // Binary number

      else if (ch == "b") {

        stream.next();

        stream.eatWhile(/[01]/);

        return "number";

      }

      // Hex number

      else if (ch == "x") {

        stream.next();

        stream.eatWhile(/[\da-f]/i);

        return "number";

      }

      // Octal number

      else if (ch == "o") {

        stream.next();

        stream.eatWhile(/[0-7]/);

        return "number";

      }

      // Token concatenation in macros

      else if (ch == '#') {

        stream.next();

        return "punctuation";

      }

      // Sequence literals

      else if ((ch == '[') || (ch == '(')) {

        stream.next();

        return "bracket";

      // Hash symbol

      } else if (stream.match(/f|t|all-keys|include|key|next|rest/i)) {

        return "atom";

      } else {

        stream.eatWhile(/[-a-zA-Z]/);

        return "error";

      }

    } else if (ch == "~") {

      stream.next();

      ch = stream.peek();

      if (ch == "=") {

        stream.next();

        ch = stream.peek();

        if (ch == "=") {

          stream.next();

          return "operator";

        }

        return "operator";

      }

      return "operator";

    } else if (ch == ":") {

      stream.next();

      ch = stream.peek();

      if (ch == "=") {

        stream.next();

        return "operator";

      } else if (ch == ":") {

        stream.next();

        return "punctuation";

      }

    } else if ("[](){}".indexOf(ch) != -1) {

      stream.next();

      return "bracket";

    } else if (".,".indexOf(ch) != -1) {

      stream.next();

      return "punctuation";

    } else if (stream.match("end")) {

      return "keyword";

    }

    for (var name in patterns) {

      if (patterns.hasOwnProperty(name)) {

        var pattern = patterns[name];

        if ((pattern instanceof Array && some(pattern, function(p) {

          return stream.match(p);

        })) || stream.match(pattern))

          return patternStyles[name];

      }

    }

    if (/[+\-*\/^=<>&|]/.test(ch)) {

      stream.next();

      return "operator";

    }

    if (stream.match("define")) {

      return "def";

    } else {

      stream.eatWhile(/[\w\-]/);

      // Keyword

      if (wordLookup.hasOwnProperty(stream.current())) {

        return styleLookup[stream.current()];

      } else if (stream.current().match(symbol)) {

        return "variable";

      } else {

        stream.next();

        return "variable-2";

      }

    }

  }



  function tokenComment(stream, state) {

    var maybeEnd = false, maybeNested = false, nestedCount = 0, ch;

    while ((ch = stream.next())) {

      if (ch == "/" && maybeEnd) {

        if (nestedCount > 0) {

          nestedCount--;

        } else {

          state.tokenize = tokenBase;

          break;

        }

      } else if (ch == "*" && maybeNested) {

        nestedCount++;

      }

      maybeEnd = (ch == "*");

      maybeNested = (ch == "/");

    }

    return "comment";

  }



  function tokenString(quote, style) {

    return function(stream, state) {

      var escaped = false, next, end = false;

      while ((next = stream.next()) != null) {

        if (next == quote && !escaped) {

          end = true;

          break;

        }

        escaped = !escaped && next == "\\";

      }

      if (end || !escaped) {

        state.tokenize = tokenBase;

      }

      return 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");



});

