diff --git a/components/prism-lisp.js b/components/prism-lisp.js index 8de9386221..e0e2392fde 100644 --- a/components/prism-lisp.js +++ b/components/prism-lisp.js @@ -1,20 +1,29 @@ (function (Prism) { - // Functions to construct regular expressions - // simple form - // e.g. (interactive ... or (interactive) + /** + * Functions to construct regular expressions + * e.g. (interactive ... or (interactive) + * + * @param {string} name + * @returns {RegExp} + */ function simple_form(name) { - return RegExp('(\\()' + name + '(?=[\\s\\)])'); + return RegExp(/(\()/.source + '(?:' + name + ')' + /(?=[\s\)])/.source); } - // booleans and numbers + /** + * booleans and numbers + * + * @param {string} pattern + * @returns {RegExp} + */ function primitive(pattern) { - return RegExp('([\\s([])' + pattern + '(?=[\\s)])'); + 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 = '[-+*/~!@$%^=<>{}\\w]+'; + var symbol = /(?!\d)[-+*/~!@$%^=<>{}\w]+/.source; // symbol starting with & used in function arguments var marker = '&' + symbol; // Open parenthesis for look-behind @@ -22,6 +31,7 @@ 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. @@ -68,21 +78,21 @@ }, ], declare: { - pattern: simple_form('declare'), + pattern: simple_form(/declare/.source), lookbehind: true, alias: 'keyword' }, interactive: { - pattern: simple_form('interactive'), + pattern: simple_form(/interactive/.source), lookbehind: true, alias: 'keyword' }, boolean: { - pattern: primitive('(?:t|nil)'), + pattern: primitive(/nil|t/.source), lookbehind: true }, number: { - pattern: primitive('[-+]?\\d+(?:\\.\\d*)?'), + pattern: primitive(/[-+]?\d+(?:\.\d*)?/.source), lookbehind: true }, defvar: { @@ -94,13 +104,9 @@ } }, defun: { - pattern: RegExp( - par + - '(?:cl-)?(?:defmacro|defun\\*?)\\s+' + - symbol + - '\\s+\\([\\s\\S]*?\\)' - ), + 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 @@ -116,6 +122,7 @@ 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 @@ -141,29 +148,22 @@ var arg = { 'lisp-marker': RegExp(marker), - rest: { - argument: { - pattern: RegExp(symbol), - alias: 'variable' - }, - varform: { - pattern: RegExp(par + symbol + '\\s+\\S[\\s\\S]*' + endpar), - lookbehind: true, - inside: { - string: language.string, - boolean: language.boolean, - number: language.number, - symbol: language.symbol, - punctuation: /[()]/ - } - } - } + '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 + '[\\s\\S]*' + endpar), + pattern: RegExp(par + nestedPar + endpar), lookbehind: true, inside: { 'rest-vars': { diff --git a/components/prism-lisp.min.js b/components/prism-lisp.min.js index 1c638fa70e..4ee5290b13 100644 --- a/components/prism-lisp.min.js +++ b/components/prism-lisp.min.js @@ -1 +1 @@ -!function(e){function n(e){return RegExp("(\\()"+e+"(?=[\\s\\)])")}function a(e){return RegExp("([\\s([])"+e+"(?=[\\s)])")}var t="[-+*/~!@$%^=<>{}\\w]+",r="(\\()",s="(?=\\))",i="(?=\\s)",o={heading:{pattern:/;;;.*/,alias:["comment","title"]},comment:/;.*/,string:{pattern:/"(?:[^"\\]|\\.)*"/,greedy:!0,inside:{argument:/[-A-Z]+(?=[.,\s])/,symbol:RegExp("`"+t+"'")}},"quoted-symbol":{pattern:RegExp("#?'"+t),alias:["variable","symbol"]},"lisp-property":{pattern:RegExp(":"+t),alias:"property"},splice:{pattern:RegExp(",@?"+t),alias:["symbol","variable"]},keyword:[{pattern:RegExp(r+"(?:and|(?:cl-)?letf|cl-loop|cond|cons|error|if|(?:lexical-)?let\\*?|message|not|null|or|provide|require|setq|unless|use-package|when|while)"+i),lookbehind:!0},{pattern:RegExp(r+"(?:append|by|collect|concat|do|finally|for|in|return)"+i),lookbehind:!0}],declare:{pattern:n("declare"),lookbehind:!0,alias:"keyword"},interactive:{pattern:n("interactive"),lookbehind:!0,alias:"keyword"},boolean:{pattern:a("(?:t|nil)"),lookbehind:!0},number:{pattern:a("[-+]?\\d+(?:\\.\\d*)?"),lookbehind:!0},defvar:{pattern:RegExp(r+"def(?:const|custom|group|var)\\s+"+t),lookbehind:!0,inside:{keyword:/^def[a-z]+/,variable:RegExp(t)}},defun:{pattern:RegExp(r+"(?:cl-)?(?:defmacro|defun\\*?)\\s+"+t+"\\s+\\([\\s\\S]*?\\)"),lookbehind:!0,inside:{keyword:/^(?:cl-)?def\S+/,arguments:null,function:{pattern:RegExp("(^\\s)"+t),lookbehind:!0},punctuation:/[()]/}},lambda:{pattern:RegExp(r+"lambda\\s+\\(\\s*(?:&?"+t+"(?:\\s+&?"+t+")*\\s*)?\\)"),lookbehind:!0,inside:{keyword:/^lambda/,arguments:null,punctuation:/[()]/}},car:{pattern:RegExp(r+t),lookbehind:!0},punctuation:[/(?:['`,]?\(|[)\[\]])/,{pattern:/(\s)\.(?=\s)/,lookbehind:!0}]},l={"lisp-marker":RegExp("&[-+*/~!@$%^=<>{}\\w]+"),rest:{argument:{pattern:RegExp(t),alias:"variable"},varform:{pattern:RegExp(r+t+"\\s+\\S[\\s\\S]*"+s),lookbehind:!0,inside:{string:o.string,boolean:o.boolean,number:o.number,symbol:o.symbol,punctuation:/[()]/}}}},p="\\S+(?:\\s+\\S+)*",d={pattern:RegExp(r+"[\\s\\S]*"+s),lookbehind:!0,inside:{"rest-vars":{pattern:RegExp("&(?:body|rest)\\s+"+p),inside:l},"other-marker-vars":{pattern:RegExp("&(?:aux|optional)\\s+"+p),inside:l},keys:{pattern:RegExp("&key\\s+"+p+"(?:\\s+&allow-other-keys)?"),inside:l},argument:{pattern:RegExp(t),alias:"variable"},punctuation:/[()]/}};o.lambda.inside.arguments=d,o.defun.inside.arguments=e.util.clone(d),o.defun.inside.arguments.inside.sublist=d,e.languages.lisp=o,e.languages.elisp=o,e.languages.emacs=o,e.languages["emacs-lisp"]=o}(Prism); \ No newline at end of file +!function(e){function n(e){return RegExp("(\\()(?:"+e+")(?=[\\s\\)])")}function a(e){return RegExp("([\\s([])(?:"+e+")(?=[\\s)])")}var t="(?!\\d)[-+*/~!@$%^=<>{}\\w]+",r="(\\()",i="(?=\\s)",s="(?:[^()]|\\((?:[^()]|\\((?:[^()]|\\((?:[^()]|\\((?:[^()]|\\([^()]*\\))*\\))*\\))*\\))*\\))*",l={heading:{pattern:/;;;.*/,alias:["comment","title"]},comment:/;.*/,string:{pattern:/"(?:[^"\\]|\\.)*"/,greedy:!0,inside:{argument:/[-A-Z]+(?=[.,\s])/,symbol:RegExp("`"+t+"'")}},"quoted-symbol":{pattern:RegExp("#?'"+t),alias:["variable","symbol"]},"lisp-property":{pattern:RegExp(":"+t),alias:"property"},splice:{pattern:RegExp(",@?"+t),alias:["symbol","variable"]},keyword:[{pattern:RegExp(r+"(?:and|(?:cl-)?letf|cl-loop|cond|cons|error|if|(?:lexical-)?let\\*?|message|not|null|or|provide|require|setq|unless|use-package|when|while)"+i),lookbehind:!0},{pattern:RegExp(r+"(?:append|by|collect|concat|do|finally|for|in|return)"+i),lookbehind:!0}],declare:{pattern:n("declare"),lookbehind:!0,alias:"keyword"},interactive:{pattern:n("interactive"),lookbehind:!0,alias:"keyword"},boolean:{pattern:a("nil|t"),lookbehind:!0},number:{pattern:a("[-+]?\\d+(?:\\.\\d*)?"),lookbehind:!0},defvar:{pattern:RegExp(r+"def(?:const|custom|group|var)\\s+"+t),lookbehind:!0,inside:{keyword:/^def[a-z]+/,variable:RegExp(t)}},defun:{pattern:RegExp(r+"(?:cl-)?(?:defmacro|defun\\*?)\\s+"+t+"\\s+\\("+s+"\\)"),lookbehind:!0,greedy:!0,inside:{keyword:/^(?:cl-)?def\S+/,arguments:null,function:{pattern:RegExp("(^\\s)"+t),lookbehind:!0},punctuation:/[()]/}},lambda:{pattern:RegExp(r+"lambda\\s+\\(\\s*(?:&?"+t+"(?:\\s+&?"+t+")*\\s*)?\\)"),lookbehind:!0,greedy:!0,inside:{keyword:/^lambda/,arguments:null,punctuation:/[()]/}},car:{pattern:RegExp(r+t),lookbehind:!0},punctuation:[/(?:['`,]?\(|[)\[\]])/,{pattern:/(\s)\.(?=\s)/,lookbehind:!0}]},o={"lisp-marker":RegExp("&(?!\\d)[-+*/~!@$%^=<>{}\\w]+"),varform:{pattern:RegExp("\\("+t+"\\s+(?=\\S)"+s+"\\)"),inside:l},argument:{pattern:RegExp("(^|[\\s(])"+t),lookbehind:!0,alias:"variable"},rest:l},p="\\S+(?:\\s+\\S+)*",d={pattern:RegExp(r+s+"(?=\\))"),lookbehind:!0,inside:{"rest-vars":{pattern:RegExp("&(?:body|rest)\\s+"+p),inside:o},"other-marker-vars":{pattern:RegExp("&(?:aux|optional)\\s+"+p),inside:o},keys:{pattern:RegExp("&key\\s+"+p+"(?:\\s+&allow-other-keys)?"),inside:o},argument:{pattern:RegExp(t),alias:"variable"},punctuation:/[()]/}};l.lambda.inside.arguments=d,l.defun.inside.arguments=e.util.clone(d),l.defun.inside.arguments.inside.sublist=d,e.languages.lisp=l,e.languages.elisp=l,e.languages.emacs=l,e.languages["emacs-lisp"]=l}(Prism); \ No newline at end of file diff --git a/tests/languages/lisp/defun_feature.test b/tests/languages/lisp/defun_feature.test index b9c1f7fdc5..85fa71f2ab 100644 --- a/tests/languages/lisp/defun_feature.test +++ b/tests/languages/lisp/defun_feature.test @@ -1,27 +1,297 @@ (defun foo ()) (defun foo (bar)) + (defun foo (bar &body arg1) ) + (defun foo (bar &rest arg1) ) + (defun foo (bar &body arg1 arg2) ) + (defun foo (bar &key arg1) ) (defun foo (bar &key arg1 &allow-other-keys) ) + (defun foo (&optional arg1) ) + (defun defabc ()) +(defun show-members (a b &rest values) (write (list a b values))) +(cl-defun find-thing (thing &rest rest &key need &allow-other-keys) + (or (apply 'cl-member thing thing-list :allow-other-keys t rest) + (if need (error "Thing not found")))) + +(defun foo (a b &optional (c 3 c-supplied-p)) + (list a b c c-supplied-p)) + +(defun foo (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p)) + (list a b c c-supplied-p)) + ---------------------------------------------------- [ - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "foo"], ["punctuation", "("], ["arguments", []], ["punctuation", ")"]]], ["punctuation", ")"], - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "foo"], ["punctuation", "("], ["arguments", [[ "argument", "bar" ]]], ["punctuation", ")"]]], ["punctuation", ")"], - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "foo"], ["punctuation", "("], ["arguments", [[ "argument", "bar" ], ["rest-vars", [["lisp-marker", "&body" ], ["argument", "arg1"]]]]], ["punctuation", ")"]] ], ["punctuation", ")"], - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "foo"], ["punctuation", "("], ["arguments", [[ "argument", "bar" ], ["rest-vars", [["lisp-marker", "&rest" ], ["argument", "arg1"]]]]], ["punctuation", ")"]] ], ["punctuation", ")"], - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "foo"], ["punctuation", "("], ["arguments", [[ "argument", "bar" ], ["rest-vars", [["lisp-marker", "&body" ], ["argument", "arg1"], ["argument", "arg2"]]]]], ["punctuation", ")"]] ], ["punctuation", ")"], - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "foo"], ["punctuation", "("], ["arguments", [[ "argument", "bar" ], ["keys", [["lisp-marker", "&key" ], ["argument", "arg1"]]]]], ["punctuation", ")"]] ], ["punctuation", ")"], - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "foo"], ["punctuation", "("], ["arguments", [[ "argument", "bar" ], ["keys", [["lisp-marker", "&key" ], ["argument", "arg1"], ["lisp-marker", "&allow-other-keys"]]]]], ["punctuation", ")"]] ], ["punctuation", ")"], - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "foo"], ["punctuation", "("], ["arguments", [["other-marker-vars", [["lisp-marker", "&optional" ], ["argument", "arg1"]]]]], ["punctuation", ")"]] ], ["punctuation", ")"], - ["punctuation", "("], ["defun", [ ["keyword", "defun" ], ["function", "defabc"], ["punctuation", "("], ["arguments", []], ["punctuation", ")"]]], ["punctuation", ")"] + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["argument", "bar"] + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["argument", "bar"], + ["rest-vars", [ + ["lisp-marker", "&body"], + ["argument", "arg1"] + ]] + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["argument", "bar"], + ["rest-vars", [ + ["lisp-marker", "&rest"], + ["argument", "arg1"] + ]] + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["argument", "bar"], + ["rest-vars", [ + ["lisp-marker", "&body"], + ["argument", "arg1"], + ["argument", "arg2"] + ]] + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["argument", "bar"], + ["keys", [ + ["lisp-marker", "&key"], + ["argument", "arg1"] + ]] + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["argument", "bar"], + ["keys", [ + ["lisp-marker", "&key"], + ["argument", "arg1"], + ["lisp-marker", "&allow-other-keys"] + ]] + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["other-marker-vars", [ + ["lisp-marker", "&optional"], + ["argument", "arg1"] + ]] + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "defabc"], + ["punctuation", "("], + ["arguments", [ + ]], + ["punctuation", ")"] + ]], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "show-members"], + ["punctuation", "("], + ["arguments", [ + ["argument", "a"], + ["argument", "b"], + ["rest-vars", [ + ["lisp-marker", "&rest"], + ["argument", "values"] + ]] + ]], + ["punctuation", ")"] + ]], + ["punctuation", "("], + ["car", "write"], + ["punctuation", "("], + ["car", "list"], + " a b values", + ["punctuation", ")"], + ["punctuation", ")"], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "cl-defun"], + ["function", "find-thing"], + ["punctuation", "("], + ["arguments", [ + ["argument", "thing"], + ["rest-vars", [ + ["lisp-marker", "&rest"], + ["argument", "rest"], + ["lisp-marker", "&key"], + ["argument", "need"], + ["lisp-marker", "&allow-other-keys"] + ]] + ]], + ["punctuation", ")"] + ]], + + ["punctuation", "("], + ["keyword", "or"], + ["punctuation", "("], + ["car", "apply"], + ["quoted-symbol", "'cl-member"], + " thing thing-list ", + ["lisp-property", ":allow-other-keys"], + ["boolean", "t"], + " rest", + ["punctuation", ")"], + + ["punctuation", "("], + ["keyword", "if"], + " need ", + ["punctuation", "("], + ["keyword", "error"], + ["string", ["\"Thing not found\""]], + ["punctuation", ")"], + ["punctuation", ")"], + ["punctuation", ")"], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["argument", "a"], + ["argument", "b"], + ["other-marker-vars", [ + ["lisp-marker", "&optional"], + ["varform", [ + ["punctuation", "("], + ["car", "c"], + ["number", "3"], + " c-supplied-p", + ["punctuation", ")"] + ]] + ]] + ]], + ["punctuation", ")"] + ]], + + ["punctuation", "("], + ["car", "list"], + " a b c c-supplied-p", + ["punctuation", ")"], + ["punctuation", ")"], + + ["punctuation", "("], + ["defun", [ + ["keyword", "defun"], + ["function", "foo"], + ["punctuation", "("], + ["arguments", [ + ["keys", [ + ["lisp-marker", "&key"], + ["punctuation", "("], + ["punctuation", "("], + ["lisp-property", ":apple"], + ["argument", "a"], + ["punctuation", ")"], + ["punctuation", ")"], + ["punctuation", "("], + ["punctuation", "("], + ["lisp-property", ":box"], + ["argument", "b"], + ["punctuation", ")"], + ["number", "0"], + ["punctuation", ")"], + ["punctuation", "("], + ["punctuation", "("], + ["lisp-property", ":charlie"], + ["argument", "c"], + ["punctuation", ")"], + ["number", "0"], + ["argument", "c-supplied-p"], + ["punctuation", ")"] + ]] + ]], + ["punctuation", ")"] + ]], + + ["punctuation", "("], + ["car", "list"], + " a b c c-supplied-p", + ["punctuation", ")"], + ["punctuation", ")"] ] ---------------------------------------------------- -Checks for defun. \ No newline at end of file +Checks for defun. diff --git a/tests/languages/lisp/punctuation_feature.test b/tests/languages/lisp/punctuation_feature.test index 904ef79f48..d0dba5f017 100644 --- a/tests/languages/lisp/punctuation_feature.test +++ b/tests/languages/lisp/punctuation_feature.test @@ -1,16 +1,19 @@ () ( ) [] +. ( ---------------------------------------------------- [ - ["punctuation", "("], ["punctuation", ")"], - ["punctuation", "("], ["punctuation", ")"], - ["punctuation", "["], ["punctuation", "]"], + ["punctuation", "("], ["punctuation", ")"], + ["punctuation", "("], ["punctuation", ")"], + ["punctuation", "["], ["punctuation", "]"], + ["punctuation", "."], ["punctuation", "("] ] + ---------------------------------------------------- -Checks for punctuation. \ No newline at end of file +Checks for punctuation.