'use strict'

module.exports = lisp
lisp.displayName = 'lisp'
lisp.aliases = []
function lisp(Prism) {
  ;(function (Prism) {
    /**
     * Functions to construct regular expressions
     * e.g. (interactive ... or (interactive)
     *
     * @param {string} name
     * @returns {RegExp}
     */
    function simple_form(name) {
      return RegExp(/(\()/.source + '(?:' + name + ')' + /(?=[\s\)])/.source)
    }
    /**
     * booleans and numbers
     *
     * @param {string} pattern
     * @returns {RegExp}
     */
    function primitive(pattern) {
      return RegExp(
        /([\s([])/.source + '(?:' + pattern + ')' + /(?=[\s)])/.source
      )
    } // Patterns in regular expressions
    // Symbol name. See https://www.gnu.org/software/emacs/manual/html_node/elisp/Symbol-Type.html
    // & and : are excluded as they are usually used for special purposes
    var symbol = /(?!\d)[-+*/~!@$%^=<>{}\w]+/.source // symbol starting with & used in function arguments
    var marker = '&' + symbol // Open parenthesis for look-behind
    var par = '(\\()'
    var endpar = '(?=\\))' // End the pattern with look-ahead space
    var space = '(?=\\s)'
    var nestedPar =
      /(?:[^()]|\((?:[^()]|\((?:[^()]|\((?:[^()]|\((?:[^()]|\([^()]*\))*\))*\))*\))*\))*/
        .source
    var language = {
      // Three or four semicolons are considered a heading.
      // See https://www.gnu.org/software/emacs/manual/html_node/elisp/Comment-Tips.html
      heading: {
        pattern: /;;;.*/,
        alias: ['comment', 'title']
      },
      comment: /;.*/,
      string: {
        pattern: /"(?:[^"\\]|\\.)*"/,
        greedy: true,
        inside: {
          argument: /[-A-Z]+(?=[.,\s])/,
          symbol: RegExp('`' + symbol + "'")
        }
      },
      'quoted-symbol': {
        pattern: RegExp("#?'" + symbol),
        alias: ['variable', 'symbol']
      },
      'lisp-property': {
        pattern: RegExp(':' + symbol),
        alias: 'property'
      },
      splice: {
        pattern: RegExp(',@?' + symbol),
        alias: ['symbol', 'variable']
      },
      keyword: [
        {
          pattern: RegExp(
            par +
              '(?:and|(?:cl-)?letf|cl-loop|cond|cons|error|if|(?:lexical-)?let\\*?|message|not|null|or|provide|require|setq|unless|use-package|when|while)' +
              space
          ),
          lookbehind: true
        },
        {
          pattern: RegExp(
            par +
              '(?:append|by|collect|concat|do|finally|for|in|return)' +
              space
          ),
          lookbehind: true
        }
      ],
      declare: {
        pattern: simple_form(/declare/.source),
        lookbehind: true,
        alias: 'keyword'
      },
      interactive: {
        pattern: simple_form(/interactive/.source),
        lookbehind: true,
        alias: 'keyword'
      },
      boolean: {
        pattern: primitive(/nil|t/.source),
        lookbehind: true
      },
      number: {
        pattern: primitive(/[-+]?\d+(?:\.\d*)?/.source),
        lookbehind: true
      },
      defvar: {
        pattern: RegExp(par + 'def(?:const|custom|group|var)\\s+' + symbol),
        lookbehind: true,
        inside: {
          keyword: /^def[a-z]+/,
          variable: RegExp(symbol)
        }
      },
      defun: {
        pattern: RegExp(
          par +
            /(?:cl-)?(?:defmacro|defun\*?)\s+/.source +
            symbol +
            /\s+\(/.source +
            nestedPar +
            /\)/.source
        ),
        lookbehind: true,
        greedy: true,
        inside: {
          keyword: /^(?:cl-)?def\S+/,
          // See below, this property needs to be defined later so that it can
          // reference the language object.
          arguments: null,
          function: {
            pattern: RegExp('(^\\s)' + symbol),
            lookbehind: true
          },
          punctuation: /[()]/
        }
      },
      lambda: {
        pattern: RegExp(
          par +
            'lambda\\s+\\(\\s*(?:&?' +
            symbol +
            '(?:\\s+&?' +
            symbol +
            ')*\\s*)?\\)'
        ),
        lookbehind: true,
        greedy: true,
        inside: {
          keyword: /^lambda/,
          // See below, this property needs to be defined later so that it can
          // reference the language object.
          arguments: null,
          punctuation: /[()]/
        }
      },
      car: {
        pattern: RegExp(par + symbol),
        lookbehind: true
      },
      punctuation: [
        // open paren, brackets, and close paren
        /(?:['`,]?\(|[)\[\]])/, // cons
        {
          pattern: /(\s)\.(?=\s)/,
          lookbehind: true
        }
      ]
    }
    var arg = {
      'lisp-marker': RegExp(marker),
      varform: {
        pattern: RegExp(
          /\(/.source + symbol + /\s+(?=\S)/.source + nestedPar + /\)/.source
        ),
        inside: language
      },
      argument: {
        pattern: RegExp(/(^|[\s(])/.source + symbol),
        lookbehind: true,
        alias: 'variable'
      },
      rest: language
    }
    var forms = '\\S+(?:\\s+\\S+)*'
    var arglist = {
      pattern: RegExp(par + nestedPar + endpar),
      lookbehind: true,
      inside: {
        'rest-vars': {
          pattern: RegExp('&(?:body|rest)\\s+' + forms),
          inside: arg
        },
        'other-marker-vars': {
          pattern: RegExp('&(?:aux|optional)\\s+' + forms),
          inside: arg
        },
        keys: {
          pattern: RegExp('&key\\s+' + forms + '(?:\\s+&allow-other-keys)?'),
          inside: arg
        },
        argument: {
          pattern: RegExp(symbol),
          alias: 'variable'
        },
        punctuation: /[()]/
      }
    }
    language['lambda'].inside.arguments = arglist
    language['defun'].inside.arguments = Prism.util.clone(arglist)
    language['defun'].inside.arguments.inside.sublist = arglist
    Prism.languages.lisp = language
    Prism.languages.elisp = language
    Prism.languages.emacs = language
    Prism.languages['emacs-lisp'] = language
  })(Prism)
}
