diff --git a/lib/customModes/nimrod.js b/lib/customModes/nimrod.js new file mode 100755 index 0000000..3269244 --- /dev/null +++ b/lib/customModes/nimrod.js @@ -0,0 +1,376 @@ +const CodeMirror = require('codemirror') + + +CodeMirror.defineMode("nimrod", function(conf, parserConf) { + var ERRORCLASS = 'error'; + + function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b"); + } + + var operators = new RegExp("\\=\\+\\-\\*\\/\\<\\>\\@\\$\\~\\&\\%\\|\\!\\?\\^\\:\\\\"); + var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); + + var commonkeywords = ['addr', 'asm', 'atomic', + 'bind', 'block', 'break', 'case', 'cast', + 'const', 'continue', 'converter', + 'discard', 'distinct', 'do', + 'elif', 'else', 'end', 'enum', 'except', 'export', + 'finally', 'for', 'from', + 'generic', + 'if', 'import', 'include', 'interface', 'iterator', + 'lambda', 'let', + 'macro', 'method', 'mixin', 'nil', + 'object', 'out', + 'proc', 'ptr', + 'raise', 'ref', 'return', + 'shared', 'static', + 'template', 'try', 'tuple', 'type', + 'using', + 'var', + 'when', 'while', 'with', 'without', + 'yield', + + // keyword operators + 'shl', 'shr', 'and', 'or', 'xor', 'not', + 'div', 'mod', 'is', 'isnot', 'in', 'as', 'of']; + + var commonBuiltins = ["int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", + "uint32", "uint64", "float", "float32", "float64", "bool", "char", + "string", "cstring", "pointer", "range", "array", "openarray", + "seq", "set", "Byte", "Natural", "Positive", "TObject", "PObject", + "Conversion", "TResult", "TAddress", "BiggestInt", "BiggestFloat", + "cchar", "cschar", "cshort", "cint", "csize", "cuchar", "cushort", + "clong", "clonglong", "cfloat", "cdouble", "clongdouble", "cuint", + "culong", "culonglong", "cchar", "cstringArray", "TEndian", "PFloat32", + "PFloat64", "PInt64", "PInt32", "TGC_Strategy", "TFile", "TFileMode", + "TFileHandle", "isMainModule", "CompileDate", "CompileTime", "NimrodVersion", + "NimrodMajor", "NimrodMinor", "NimrodPatch", "cpuEndian", "hostOS", "hostCPU", + "inf", "neginf", "nan", "QuitSuccess", "QuitFailure", "dbgLineHook", "stdin", + "stdout", "stderr", "defined", "new", "high", "low", "sizeof", "succ", "pred", + "inc", "dec", "newSeq", "len", "incl", "excl", "card", "ord", "chr", "ze", + "ze64", "toU8", "toU16", "toU32", "abs", "min", "max", "add", "repr", "contains", + "toFloat", "toBiggestFloat", "toInt", "toBiggestInt", "addQuitProc", "copy", + "setLen", "newString", "zeroMem", "copyMem", "moveMem", "equalMem", "alloc", + "alloc0", "realloc", "dealloc", "setLen", "assert", "swap", "getRefcount", + "getCurrentException", "Msg", "getOccupiedMem", "getFreeMem", "getTotalMem", + "isNil", "seqToPtr", "find", "pop", "GC_disable", "GC_enable", "GC_fullCollect", + "GC_setStrategy", "GC_enableMarkAnd", "Sweep", "GC_disableMarkAnd", "Sweep", + "GC_getStatistics", "GC_ref", "GC_ref", "GC_ref", "GC_unref", "GC_unref", + "GC_unref", "quit", "OpenFile", "OpenFile", "CloseFile", "EndOfFile", + "readChar", "FlushFile", "readFile", "write", "readLine", "writeln", + "writeln", "getFileSize", "ReadBytes", "ReadChars", "readBuffer", + "writeBytes", "writeChars", "writeBuffer", "setFilePos", "getFilePos", + "fileHandle", "countdown", "countup", "items", "lines", + "true", "false", + + // exceptions + "E_Base", "EAsynch", "ESynch", "ESystem", "EIO", "EOS", "ERessourceExhausted", + "EArithmetic", "EDivByZero", "EOverflow", "EAccessViolation", "EAssertionFailed", + "EControlC", "EInvalidValue", "EOutOfMemory", "EInvalidIndex", "EInvalidField", + "EOutOfRange", "EStackOverflow", "ENoExceptionToReraise", "EInvalidObjectAssignment", + "EInvalidObject", "EInvalidLibrary", "EInvalidKey", "EInvalidObjectConversion", + "EFloatingPoint", "EFloatInvalidOp", "EFloatDivByZero", "EFloatOverflow", + "EFloatInexact", "EDeadThrea"]; + + if(parserConf.extra_keywords != undefined) + commonkeywords = commonkeywords.concat(parserConf.extra_keywords); + + if(parserConf.extra_builtins != undefined) + commonBuiltins = commonBuiltins.concat(parserConf.extra_builtins); + + var keywords = wordRegexp(commonkeywords); + var builtins = wordRegexp(commonBuiltins); + + var indentInfo = null; + + var stringPrefixes = new RegExp("^(('{3}|\"{3}|['\"]))", "i"); + + // tokenizers + function tokenBase(stream, state) { + // Handle scope changes + if (stream.sol()) { + var scopeOffset = state.scopes[0].offset; + if (stream.eatSpace()) { + var lineOffset = stream.indentation(); + if (lineOffset > scopeOffset) { + indentInfo = 'indent'; + } else if (lineOffset < scopeOffset) { + indentInfo = 'dedent'; + } + return null; + } else { + if (scopeOffset > 0) { + dedent(stream, state); + } + } + } + + if (stream.eatSpace()) + return null; + + var ch = stream.peek(); + + // Handle Comments + if (ch === '#') { + stream.skipToEnd(); + return 'comment'; + } + + // Handle Number Literals + if (stream.match(/^[0-9\.]/, false)) { + var floatLiteral = false; + // Floats + if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } + if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } + if (stream.match(/^\.\d+/)) { floatLiteral = true; } + if (floatLiteral) { + // Float literals may be "imaginary" + stream.eat(/J/i); + return 'number'; + } + // Integers + var intLiteral = false; + // Hex + if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; } + // Binary + if (stream.match(/^0b[01]+/i)) { intLiteral = true; } + // Octal + if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; } + // Decimal + if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { + // Decimal literals may be "imaginary" + stream.eat(/J/i); + // TODO - Can you have imaginary longs? + intLiteral = true; + } + // Zero by itself with no other piece of number. + if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } + if (intLiteral) { + // Integer literals may be "long" + stream.eat(/L/i); + return 'number'; + } + } + + // Handle Strings + if (stream.match(stringPrefixes)) { + state.tokenize = tokenStringFactory(stream.current()); + return state.tokenize(stream, state); + } + + if (stream.match(operators)) + return 'operator'; + + if (stream.match(keywords)) + return 'keyword'; + + if (stream.match(builtins)) + return 'builtin'; + + if (stream.match(identifiers)) { + if (state.lastToken != null && + state.lastToken.match(/proc|iterator|macro|template|class|converter/)) { + return 'def'; + } + + return 'variable'; + } + + // Handle non-detected items + stream.next(); + return ERRORCLASS; + } + + function tokenStringFactory(delimiter) { + var singleline = delimiter.length == 1; + var OUTCLASS = 'string'; + + function tokenString(stream, state) { + while (!stream.eol()) { + stream.eatWhile(/[^'"\\]/); + if (stream.eat('\\')) { + stream.next(); + if (singleline && stream.eol()) { + return OUTCLASS; + } + } else if (stream.match(delimiter)) { + state.tokenize = tokenBase; + return OUTCLASS; + } else { + stream.eat(/['"]/); + } + } + if (singleline) { + if (parserConf.singleLineStringErrors) { + return ERRORCLASS; + } else { + state.tokenize = tokenBase; + } + } + return OUTCLASS; + } + + tokenString.isString = true; + return tokenString; + } + + function indent(stream, state, type) { + type = type || 'nim'; + var indentUnit = 0; + if (type === 'nim') { + if (state.scopes[0].type !== 'nim') { + state.scopes[0].offset = stream.indentation(); + return; + } + for (var i = 0; i < state.scopes.length; ++i) { + if (state.scopes[i].type === 'nim') { + indentUnit = state.scopes[i].offset + conf.indentUnit; + break; + } + } + } else { + indentUnit = stream.column() + stream.current().length; + } + + state.scopes.unshift({ + offset: indentUnit, + type: type + }); + } + + function dedent(stream, state, type) { + type = type || 'nim'; + if (state.scopes.length == 1) return; + if (state.scopes[0].type === 'nim') { + var _indent = stream.indentation(); + var _indent_index = -1; + for (var i = 0; i < state.scopes.length; ++i) { + if (_indent === state.scopes[i].offset) { + _indent_index = i; + break; + } + } + if (_indent_index === -1) { + return true; + } + while (state.scopes[0].offset !== _indent) { + state.scopes.shift(); + } + return false; + } else { + if (type === 'nim') { + state.scopes[0].offset = stream.indentation(); + return false; + } else { + if (state.scopes[0].type != type) { + return true; + } + state.scopes.shift(); + return false; + } + } + } + + function tokenLexer(stream, state) { + indentInfo = null; + var style = state.tokenize(stream, state); + var current = stream.current(); + + // Handle '.' connected identifiers + if (current === '.') { + style = stream.match(identifiers, false) ? null : ERRORCLASS; + if (style === null && state.lastStyle === 'meta') { + // Apply 'meta' style to '.' connected identifiers when + // appropriate. + style = 'meta'; + } + return style; + } + + if ((style === 'variable' || style === 'builtin') + && state.lastStyle === 'meta') { + style = 'meta'; + } + + // Handle scope changes. + if (current.match(/return|break|continue|raise/) || + (current === 'discard' && stream.eol())) + state.dedent += 1; + + if (current === 'lambda' || current === 'proc') + state.lambda = true; + + var delimiter_index = '[({'.indexOf(current); + + if (delimiter_index !== -1) { + indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1)); + } + else if(stream.eol() && current.match(/\=|\:|import|include|type|const|var|let/)) { + indent(stream, state); + } + + if (indentInfo === 'dedent') { + if (dedent(stream, state)) { + return ERRORCLASS; + } + } + + delimiter_index = '])}'.indexOf(current); + if (delimiter_index !== -1) { + if (dedent(stream, state, current)) { + return ERRORCLASS; + } + } + + if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'nim') { + if (state.scopes.length > 1) state.scopes.shift(); + state.dedent -= 1; + } + + return style; + } + + var external = { + startState: function(basecolumn) { + return { + tokenize: tokenBase, + scopes: [{offset:basecolumn || 0, type:'nim'}], + lastStyle: null, + lastToken: null, + lambda: false, + dedent: 0 + }; + }, + + token: function(stream, state) { + var style = tokenLexer(stream, state); + + state.lastStyle = style; + + var current = stream.current(); + if (current && style) + state.lastToken = current; + + if (stream.eol() && state.lambda) + state.lambda = false; + + return style; + }, + + indent: function(state) { + if (state.tokenize != tokenBase) + return state.tokenize.isString ? CodeMirror.Pass : 0; + + return state.scopes[0].offset; + }, + + lineComment: "#", + fold: "indent" + }; + + return external; +}); + +CodeMirror.defineMIME("text/x-nimrod", "nimrod"); +