Skip to content

Instantly share code, notes, and snippets.

@yhara
Created March 15, 2023 04:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yhara/a37e38a968c13aa6397f05c7fbd29e7b to your computer and use it in GitHub Desktop.
Save yhara/a37e38a968c13aa6397f05c7fbd29e7b to your computer and use it in GitHub Desktop.
function parseSExp(input) {
const tokens = tokenize(input);
const [exp, remainingTokens] = readFromTokens(tokens);
if (remainingTokens.length !== 0) {
throw new SyntaxError(`unexpected token(s): ${remainingTokens.join(' ')}`);
}
return exp;
}
function tokenize(input) {
return input
.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.trim()
.split(/\s+/);
}
function readFromTokens(tokens) {
if (tokens.length === 0) {
throw new SyntaxError('unexpected EOF while reading');
}
const token = tokens.shift();
if (token === '(') {
return readList(tokens);
} else if (token === ')') {
throw new SyntaxError('unexpected )');
} else {
return readAtom(token);
}
}
function readList(tokens) {
const lst = [];
while (tokens[0] !== ')') {
const [exp, remainingTokens] = readFromTokens(tokens);
lst.push(exp);
tokens = remainingTokens;
}
if (tokens.shift() !== ')') {
throw new SyntaxError('missing closing )');
}
return [lst, tokens];
}
function readAtom(token) {
const num = parseFloat(token);
if (!isNaN(num)) {
return [num, []];
} else if (token.startsWith('"')) {
return readString(token);
} else if (token === '#t') {
return [true, []];
} else if (token === '#f') {
return [false, []];
} else if (token === '#\\newline') {
return ['\n', []];
} else if (token.startsWith('#\\')) {
return [readChar(token), []];
} else {
return [token, []];
}
}
function readString(token) {
let str = '';
let i = 0;
while (i < token.length) {
if (token[i] === '\\') {
if (token[i+1] === 'n') {
str += '\n';
i += 2;
} else if (token[i+1] === '"') {
str += '"';
i += 2;
} else if (token[i+1] === '\\') {
str += '\\';
i += 2;
} else {
throw new SyntaxError(`invalid escape sequence: \\${token[i+1]}`);
}
} else if (token[i] === '"') {
return [str, []];
} else {
str += token[i];
i++;
}
}
throw new SyntaxError('missing closing "');
}
function readChar(token) {
if (token === '#\\space') {
return ' ';
} else if (token === '#\\tab') {
return '\t';
} else if (token === '#\\return') {
return '\r';
} else if (token.length === 3) {
return token[2];
} else {
throw new SyntaxError(`invalid character literal: ${token}`);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment