jsbasic/cm2/mode/basic/basic.js

223 lines
5.5 KiB
JavaScript

// Really just a lexer
CodeMirror.defineMode('basic', function(config, parserConfig) {
var STATEMENT = 'basic-statement',
OPERATOR = 'basic-operator',
FUNCTION = 'basic-function',
UNSUPPORTED = 'basic-unsupported';
var reserved = {
"ABS": FUNCTION,
"AND": OPERATOR,
"ASC": FUNCTION,
"ATN": FUNCTION,
"AT": STATEMENT,
"CALL": STATEMENT,
"CHR$": FUNCTION,
"CLEAR": STATEMENT,
"COLOR=": STATEMENT,
"CONT": UNSUPPORTED,
"COS": FUNCTION,
"DATA": STATEMENT,
"DEF": STATEMENT,
"DEL": UNSUPPORTED,
"DIM": STATEMENT,
"DRAW": UNSUPPORTED,
"END": STATEMENT,
"EXP": FUNCTION,
"FLASH": STATEMENT,
"FN": STATEMENT,
"FOR": STATEMENT,
"FRE": FUNCTION,
"GET": STATEMENT,
"GOSUB": STATEMENT,
"GOTO": STATEMENT,
"GR": STATEMENT,
"HCOLOR=": STATEMENT,
"HGR2": STATEMENT,
"HGR": STATEMENT,
"HIMEM:": UNSUPPORTED,
"HLIN": STATEMENT,
"HOME": STATEMENT,
"HPLOT": STATEMENT,
"HTAB": STATEMENT,
"IF": STATEMENT,
"IN#": UNSUPPORTED,
"INPUT": STATEMENT,
"INT": FUNCTION,
"INVERSE": STATEMENT,
"LEFT$": FUNCTION,
"LEN": FUNCTION,
"LET": STATEMENT,
"LIST": UNSUPPORTED,
"LOAD": UNSUPPORTED,
"LOG": FUNCTION,
"LOMEM:": UNSUPPORTED,
"MID$": FUNCTION,
"NEW": UNSUPPORTED,
"NEXT": STATEMENT,
"NORMAL": STATEMENT,
"NOTRACE": STATEMENT,
"NOT": OPERATOR,
"ONERR": STATEMENT,
"ON": STATEMENT,
"OR": OPERATOR,
"PDL": FUNCTION,
"PEEK": FUNCTION,
"PLOT": STATEMENT,
"POKE": STATEMENT,
"POP": STATEMENT,
"POS": FUNCTION,
"PRINT": STATEMENT,
"PR#": STATEMENT,
"READ": STATEMENT,
"RECALL": UNSUPPORTED,
"REM": STATEMENT,
"RESTORE": STATEMENT,
"RESUME": STATEMENT,
"RETURN": STATEMENT,
"RIGHT$": FUNCTION,
"RND": FUNCTION,
"ROT=": UNSUPPORTED,
"RUN": UNSUPPORTED,
"SAVE": UNSUPPORTED,
"SCALE=": UNSUPPORTED,
"SCRN": FUNCTION,
"SGN": FUNCTION,
"SHLOAD": UNSUPPORTED,
"SIN": FUNCTION,
"SPC": FUNCTION,
"SPEED=": STATEMENT,
"SQR": FUNCTION,
"STEP": STATEMENT,
"STOP": STATEMENT,
"STORE": UNSUPPORTED,
"STR$": FUNCTION,
"TAB": FUNCTION,
"TAN": FUNCTION,
"TEXT": STATEMENT,
"THEN": STATEMENT,
"TO": STATEMENT,
"TRACE": STATEMENT,
"USR": FUNCTION,
"VAL": FUNCTION,
"VLIN": STATEMENT,
"VTAB": STATEMENT,
"WAIT": UNSUPPORTED,
"XDRAW": UNSUPPORTED,
"&": UNSUPPORTED,
"?": STATEMENT
};
var reservedKeys = (function() {
// Need longer stems first: ATN/AT, ONERR/ON, NOTRACE/NOT
var keys = [], name;
for (name in reserved) {
if (Object.prototype.hasOwnProperty.call(reserved, name)) {
keys.push(name);
}
}
keys.sort();
keys.reverse();
return keys;
} ());
// states are 'normal' and 'comment'
return {
startState: function() {
return {
state: 'normal'
};
},
token: function(stream, state) {
var name, i;
if (state.state === 'normal') {
if (stream.eatSpace()) {
return null;
}
else if (/[0-9.]/.test(stream.peek())) {
stream.eatWhile(/[0-9]/);
if (stream.peek() === '.') {
stream.next();
stream.eatWhile(/[0-9]/);
}
if (/[eE]/.test(stream.peek())) {
stream.next();
stream.eatWhile(/[ \u00a0]/);
if (stream.peek() === '-' || stream.peek() === '+') {
stream.next();
}
stream.eatWhile(/[ \u00a0]/);
stream.eatWhile(/[0-9]/);
}
return 'basic-number';
}
else if (stream.peek() === '"') {
stream.next();
while (!stream.eol()) {
if (stream.next() === '"') {
break;
}
}
return 'basic-string';
}
else if (/[;=<>+\-*\/\^(),]/.test(stream.peek())) {
stream.next();
return 'basic-operator';
}
else if (stream.peek() === ':') {
stream.next();
return 'basic-separator';
}
else if (stream.match('REM', true, true)) {
stream.eatWhile(/[ \u00a0]/);
if (!stream.eol()) {
state.state = 'comment';
}
return 'basic-statement';
}
// TODO: Applesoft-style space ignoring within reserved words
for (i = 0; i < reservedKeys.length; i += 1) {
name = reservedKeys[i];
if (stream.match(name, true, true)) {
return reserved[name];
}
}
if (/[A-Za-z]/.test(stream.peek())) {
stream.next();
stream.eatWhile(/[A-Za-z0-9]/);
if (stream.peek() === '$' || stream.peek() === '%') {
stream.next();
}
return 'basic-identifier';
}
stream.next();
return 'basic-error';
}
else if (state.state === 'comment') {
while (!stream.eol()) {
stream.next();
}
state.state = 'normal';
return 'basic-comment';
}
else {
throw 'WTF!?';
}
}
};
});
CodeMirror.defineMIME("text/x-basic", "basic");
CodeMirror.defineMIME("text/x-applesoft", "basic");