You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

604 lines
12 KiB
JavaScript

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')