whalesong/js-assembler/runtime-src/read.js

162 lines
3.9 KiB
JavaScript

var readSchemeExpressions;
function tokenize(s) {
function replaceEscapes(s) {
return s.replace(/\\./g, function(match, submatch, index) {
// FIXME: add more escape sequences.
if (match == '\\n') {
return "\n";
}
else {
return match.substring(1);
}
});
}
var tokens = [];
var PATTERNS = [['whitespace' , /^(\s+)/],
['comment' , /(^;[^\n]*)/],
['(' , /^(\(|\[)/],
[')' , /^(\)|\])/],
['\'' , /^(\')/],
['`' , /^(`)/],
[',' , /^(,)/],
['char', /^\#\\(newline)/],
['char', /^\#\\(.)/],
['number' , /^([+\-]?(?:\d+\.\d+|\d+\.|\.\d+|\d+))/],
['string' , /^"((?:([^\\"]|(\\.)))*)"/], // comment (emacs getting confused with quote): "
['symbol' ,/^([a-zA-Z\:\+\=\~\_\?\!\@\#\$\%\^\&\*\-\/\.\>\<][\w\:\+\=\~\_\?\!\@\#\$\%\^\&\*\-\/\.\>\<]*)/]
];
while (true) {
var shouldContinue = false;
for (var i = 0; i < PATTERNS.length; i++) {
var patternName = PATTERNS[i][0];
var pattern = PATTERNS[i][1]
var result = s.match(pattern);
if (result != null) {
if (patternName == 'string') {
result[1] = replaceEscapes(result[1]);
}
if (patternName != 'whitespace' && patternName != 'comment') {
tokens.push([patternName, result[1]]);
}
s = s.substring(result[0].length);
shouldContinue = true;
}
}
if (! shouldContinue) {
break;
}
}
return [tokens, s];
}
(function(){
readSchemeExpressions = function(s) {
var tokensAndError = tokenize(s);
var tokens = tokensAndError[0];
if (tokensAndError[1].length > 0) {
throw new Error("Error while tokenizing: the rest of the stream is: " + tokensAndError[1]);
}
var quoteSymbol = plt.types.Symbol.makeInstance("quote");
var quasiquoteSymbol = plt.types.Symbol.makeInstance("quasiquote");
var unquoteSymbol = plt.types.Symbol.makeInstance("unquote");
var empty = plt.types.Empty.EMPTY;
function isType(type) {
return (tokens.length > 0 && tokens[0][0] == type);
}
function eat(expectedType) {
if (tokens.length == 0)
throw new Error("token stream exhausted while trying to eat " +
expectedType);
var t = tokens.shift();
if (t[0] == expectedType) {
return t;
} else {
throw new Error("Unexpected token " + t);
}
}
function readExpr() {
var t;
if (isType('(')) {
eat('(');
var result = readExprs();
eat(')');
return result;
} else if (isType("'")) {
eat("'");
var quoted = readExpr();
return plt.Kernel.cons(quoteSymbol,
plt.Kernel.cons(quoted, empty));
} else if (isType('`')) {
eat("`");
return plt.Kernel.cons(quasiquoteSymbol,
plt.kernel.cons(quoted, empty));
} else if (isType(',')) {
eat(",");
return plt.Kernel.cons(unquoteSymbol,
plt.kernel.cons(quoted, empty));
} else if (isType('number')) {
t = eat('number');
if (t[1].match(/\./)) {
return plt.types.FloatPoint.makeInstance(parseFloat(t[1]));
} else {
return plt.types.Rational.makeInstance(parseInt(t[1]), 1);
}
} else if (isType('string')) {
t = eat('string');
return plt.types.String.makeInstance(t[1]);
} else if (isType('char')) {
t = eat('char');
if (t[1] == 'newline') {
return plt.types.Char.makeInstance('\n');
}
else {
return plt.types.Char.makeInstance(t[1]);
}
} else if (isType('symbol')) {
t = eat('symbol');
return plt.types.Symbol.makeInstance(t[1]);
} else {
throw new Error("Parse broke with token stream " + tokens);
}
}
function readExprs() {
var result = plt.types.Empty.EMPTY;
while (true) {
if (tokens.length == 0 || isType(')')) {
break;
} else {
var nextElt = readExpr();
result = plt.types.Cons.makeInstance(nextElt, result);
}
}
return plt.Kernel.reverse(result);
}
return readExprs();
}
}());