/** * @param {string} value * @returns {RegExp} * */ /** * @param {RegExp | string } re * @returns {string} */ function source(re) { if (!re) return null; if (typeof re === "string") return re; return re.source; } /** * @param {RegExp | string } re * @returns {string} */ function lookahead(re) { return concat('(?=', re, ')'); } /** * @param {...(RegExp | string) } args * @returns {string} */ function concat(...args) { const joined = args.map((x) => source(x)).join(""); return joined; } function stripOptionsFromArgs(args) { const opts = args[args.length - 1]; if (typeof opts === 'object' && opts.constructor === Object) { args.splice(args.length - 1, 1); return opts; } else { return {}; } } /** * Any of the passed expresssions may match * * Creates a huge this | this | that | that match * @param {(RegExp | string)[] } args * @returns {string} */ function either(...args) { const opts = stripOptionsFromArgs(args); const joined = '(' + (opts.capture ? "" : "?:") + args.map((x) => source(x)).join("|") + ")"; return joined; } /* Language: Wren Description: Think Smalltalk in a Lua-sized package with a dash of Erlang and wrapped up in a familiar, modern syntax. Category: scripting Author: @joshgoebel Maintainer: @joshgoebel Website: https://wren.io/ */ /** @type LanguageFn */ function wren(hljs) { const IDENT_RE = /[a-zA-Z]\w*/; const KEYWORDS = [ "as", "break", "class", "construct", "continue", "else", "for", "foreign", "if", "import", "in", "is", "return", "static", "var", "while" ]; const LITERALS = [ "true", "false", "null" ]; const LANGUAGE_VARS = [ "this", "super" ]; const CORE_CLASSES = [ "Bool", "Class", "Fiber", "Fn", "List", "Map", "Null", "Num", "Object", "Range", "Sequence", "String", "System" ]; const OPERATORS = [ "-", "~", /\*/, "%", /\.\.\./, /\.\./, /\+/, "<<", ">>", ">=", "<=", "<", ">", /\^/, /!=/, /!/, /\bis\b/, "==", "&&", "&", /\|\|/, /\|/, /\?:/, "=" ]; const FUNCTION = { relevance: 0, match: concat(/\b(?!(if|while|for|else|super)\b)/, IDENT_RE, /(?=\s*[({])/), className: "title.function" }; const FUNCTION_DEFINITION = { match: concat( either( concat(/\b(?!(if|while|for|else|super)\b)/, IDENT_RE), either(...OPERATORS) ), /(?=\s*\([^)]+\)\s*\{)/), className: "title.function", starts: { contains: [ { begin: /\(/, end: /\)/, contains: [ { relevance: 0, scope: "params", match: IDENT_RE } ] } ] } }; const CLASS_DEFINITION = { variants: [ { match: [ /class\s+/, IDENT_RE, /\s+is\s+/, IDENT_RE ] }, { match: [ /class\s+/, IDENT_RE ] } ], scope: { 2: "title.class", 4: "title.class.inherited" }, keywords: KEYWORDS }; const OPERATOR = { relevance: 0, match: either(...OPERATORS), className: "operator" }; const TRIPLE_STRING = { className: "string", begin: /"""/, end: /"""/ }; const PROPERTY = { className: "property", begin: concat(/\./, lookahead(IDENT_RE)), end: IDENT_RE, excludeBegin: true, relevance: 0 }; const FIELD = { relevance: 0, match: concat(/\b_/, IDENT_RE), scope: "variable" }; // CamelCase const CLASS_REFERENCE = { relevance: 0, match: /[A-Z]+[a-z]+([A-Z]+[a-z]+)*/, scope: "title.class", keywords: { _: CORE_CLASSES } }; // TODO: add custom number modes const NUMBER = hljs.C_NUMBER_MODE; const SETTER = { match: [ IDENT_RE, /\s*/, /=/, /\s*/, /\(/, IDENT_RE, /\)\s*\{/ ], scope: { 1: "title.function", 3: "operator", 6: "params" } }; const COMMENT_DOCS = hljs.COMMENT( /\/\*\*/, /\*\//, { contains: [ { match: /@[a-z]+/, scope: "doctag" }, "self" ] } ); const SUBST = { scope: "subst", begin: /%\(/, end: /\)/, contains: [ NUMBER, CLASS_REFERENCE, FUNCTION, FIELD, OPERATOR ] }; const STRING = { scope: "string", begin: /"/, end: /"/, contains: [ SUBST, { scope: "char.escape", variants: [ { match: /\\\\|\\["0%abefnrtv]/ }, { match: /\\x[0-9A-F]{2}/ }, { match: /\\u[0-9A-F]{4}/ }, { match: /\\U[0-9A-F]{8}/ } ] } ] }; SUBST.contains.push(STRING); const ALL_KWS = [...KEYWORDS, ...LANGUAGE_VARS, ...LITERALS]; const VARIABLE = { relevance: 0, match: concat( "\\b(?!", ALL_KWS.join("|"), "\\b)", /[a-zA-Z_]\w*(?:[?!]|\b)/ ), className: "variable" }; // TODO: reconsider this in the future const ATTRIBUTE = { // scope: "meta", scope: "comment", variants: [ { begin: [/#!?/, /[A-Za-z_]+(?=\()/], beginScope: { // 2: "attr" }, keywords: { literal: LITERALS }, contains: [ // NUMBER, // VARIABLE ], end: /\)/ }, { begin: [/#!?/, /[A-Za-z_]+/], beginScope: { // 2: "attr" }, end: /$/ } ] }; return { name: "Wren", keywords: { keyword: KEYWORDS, "variable.language": LANGUAGE_VARS, literal: LITERALS }, contains: [ ATTRIBUTE, NUMBER, STRING, TRIPLE_STRING, COMMENT_DOCS, hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE, CLASS_REFERENCE, CLASS_DEFINITION, SETTER, FUNCTION_DEFINITION, FUNCTION, OPERATOR, FIELD, PROPERTY, VARIABLE ] }; } module.exports = wren;