UNPKG

fez-lisp

Version:

Lisp interpreted & compiled to JavaScript

146 lines (145 loc) 4.33 kB
import { APPLY, ATOM, TYPE, WORD, VALUE } from './keywords.js' export const leaf = (type, value) => [type, value] export const isLeaf = ([car]) => car === APPLY || car === ATOM || car === WORD export const LISP = { parse: (source) => { const tree = [] const stack = [tree] let head = tree let acc = '' for (let i = 0; i < source.length; ++i) { const cursor = source[i] if (cursor === '(') { const temp = [] head.push(temp) stack.push(head) head = temp } else if (cursor === ')' || cursor === ' ') { let token = acc acc = '' if (token) { if (!head.length) head.push(leaf(APPLY, token)) else if (token.match(/^-?[0-9]\d*(\.\d+)?$/)) head.push(leaf(ATOM, Number(token))) else head.push(leaf(WORD, token)) } if (cursor === ')') head = stack.pop() } else acc += cursor } return tree }, stringify: (array) => { if (typeof array === 'function') return '(lambda)' else if (typeof array === 'boolean') return +array else if (typeof array === 'object') if (Array.isArray(array)) return array.length ? `(array ${array.map(LISP.stringify).join(' ')})` : '(array)' else return `(array ${array .map(([key, value]) => `("${key}" ${LISP.stringify(value)})`) .join(' ')})` else return array }, json: (item) => { if (item === null) return 0 else if (typeof item === 'boolean') return item else if (typeof item === 'string') return `"${item}"` // return item.split('').map((x) => x.charCodeAt()) else if (typeof item === 'object') if (Array.isArray(item)) return item.length ? `[${item.map(LISP.json).join(' ')}]` : '[]' else return `({ ${Object.entries(item) .map(([key, value]) => `"${key}" ${LISP.json(value)}`) .join(' ')} })` else return item }, source: (ast) => { const dfs = (exp) => { let out = '' const [head, ...tail] = isLeaf(exp) ? [exp] : exp switch (head[TYPE]) { case WORD: out += head[VALUE] break case ATOM: out += head[VALUE] break case APPLY: out += `(${head[VALUE]} ${tail.map(dfs).join(' ')})` break } return out } return dfs(ast) } } export const AST = { parse: (source) => { const tree = [] const stack = [tree] let head = tree let acc = '' for (let i = 0; i < source.length; ++i) { const cursor = source[i] if (cursor === '"') { acc += '"' ++i while (source[i] !== '"') { acc += source[i] ++i } } if (cursor === '[') { const temp = [] head.push(temp) stack.push(head) head = temp } else if (cursor === ']' || cursor === ',') { let token = acc acc = '' if (token) { if (!head.length) head.push(Number(token)) else if (token[0] === '"' && token[token.length - 1] === '"') head.push(token.substring(1, token.length - 1)) else head.push(Number(token)) } if (cursor === ']') head = stack.pop() } else acc += cursor } return tree }, stringify: (ast) => typeof ast === 'object' ? `[${ast.map(AST.stringify).join(',')}]` : typeof ast === 'string' ? `"${ast}"` : ast, struct: (ast) => { const dfs = (exp) => { let out = '' const [head, ...tail] = isLeaf(exp) ? [exp] : exp switch (head[TYPE]) { case WORD: out += `Expression::Word("${head[VALUE]}".to_string())` break case ATOM: out += `Expression::Atom(${ Number.isInteger(head[VALUE]) ? `${head[VALUE]}.0` : head[VALUE].toString() })` break case APPLY: out += `Expression::Apply(vec![Expression::Word("${ head[VALUE] }".to_string()),${tail.map(dfs).join(',')}])` break } return out } return dfs(ast) } }