Created
March 15, 2023 04:06
-
-
Save yhara/a37e38a968c13aa6397f05c7fbd29e7b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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