| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- const fs = require('fs')
- var tokenizer = function (input) {
- let pos = 0
- let tokens = []
- tokens.push(input)
- while (pos < input.length) {
- let char = input[pos]
- let parens = /[()]/
- if (parens.test(char)) {
- tokens.push({
- type: 'paren',
- value: char
- })
- pos++
- continue
- }
- let whitespace = /\s/
- if (whitespace.test(char)) {
- pos++
- continue
- }
- let numbers = /[0-9]/
- if (numbers.test(char)) {
- let numberString = ''
- while (numbers.test(char)) {
- numberString += char
- char = input[++pos]
- }
- tokens.push({
- type: 'number',
- value: numberString
- })
- continue
- }
- let characters = /[a-zA-Z_]/
- if (characters.test(char)) {
- let name = ''
- while (characters.test(char)) {
- name += char
- char = input[++pos]
- }
- tokens.push({
- type: 'name',
- value: name
- })
- continue
- }
- throw new TypeError("I'm not sure what you are telling me :( Ask my creator to teach me what a: " + char + " is.")
- }
- return tokens
- }
- var parser = function (input) {
- let pos = 1
- function walk() {
- let token = input[pos]
- if (token.type === 'number') {
- pos++
- return {
- type: 'NumberLiteral',
- value: token.value
- }
- }
- if (token.type === 'name') {
- pos++
- return {
- type: 'VariableReference',
- value: token.value
- }
- }
- if (token.type === 'paren' && token.value == '(') {
- token = input[++pos]
- if (token.type !== 'name') {
- throw {
- name: 'Compiler Error',
- message: 'FunctionCall may only be type "name" not "' + token.type + '".'
- }
- }
- let node = {
- type: 'FunctionCall',
- value: token.value,
- params: []
- }
- token = input[++pos]
- while ((token.type !== 'paren') || (token.type === 'paren' && token.value !== ')')) {
- node.params.push(walk())
- token = input[pos]
- }
- pos++
- return node
- }
- throw new TypeError(token.type)
- }
- let ast = {
- type: 'Prog',
- body: []
- }
- while (pos < input.length) {
- ast.body.push(walk())
- }
- return ast
- }
- var traverser = function (ast, visitor) {
- function traverseArray(array, parent) {
- array.forEach(function (child) {
- traverseNode(child, parent)
- })
- }
- function traverseNode(node, parent) {
- const method = visitor[node.type]
-
- if (method) {
- method(node, parent)
- }
- switch (node.type) {
- case 'Prog':
- traverseArray(node.body, node)
- break
- case 'FunctionCall':
- traverseArray(node.params, node)
- break
- case 'VariableReference':
- break
- case 'NumberLiteral':
- break
- default:
- throw {
- name: 'Compiler Error',
- message: 'Unknown leaf in AST: ' + node.type
- }
- }
- }
- traverseNode(ast, null)
- }
- var transformer = function (ast) {
- let newAst = {
- type: 'Prog',
- body: []
- }
- ast._context = newAst.body
- traverser(ast, {
- NumberLiteral: function (node, parent) {
- parent._context.push({
- type: 'NumberLiteral',
- value: node.value
- })
- },
- VariableReference: function (node, parent) {
- parent._context.push({
- type: 'VariableReference',
- value: node.value
- })
- },
- FunctionCall: function (node, parent) {
- let expression = {
- type: 'FunctionCall',
- callee: {
- type: 'FunctionName',
- name: node.value
- },
- args: []
- }
- node._context = expression.args
- if (parent.type !== 'FunctionCall') {
- expression = {
- type: 'Statement',
- expr: expression
- }
- }
- parent._context.push(expression)
- }
- })
- return newAst
- }
- var generator = function (node) {
- switch (node.type) {
- case 'Prog':
- let program = node.body.map(generator)
- program.unshift('var _ = require("./stdlib.js")')
- return program.join('\n')
- break
- case 'Statement':
- return (generator(node.expr) + ';')
- break
- case 'FunctionCall':
- return (generator(node.callee) + '(' + node.args.map(generator).join(', ') + ')')
- break
- case 'FunctionName':
- return '_.' + node.name
- break
- case 'VariableReference':
- return '_.ref("' + node.value + '")'
- break
- case 'NumberLiteral':
- return '{value: ' + node.value + '}'
- break
- default:
- throw {
- name: 'Compiler Error',
- message: 'Unexpected leaf in transformed AST: ' + node.type
- }
- break
- }
- }
- // const myInput = '(assign twelve 12) (assign myvar (add twelve (subtract 6 2))) (log myvar)'
- const myInput = fs.readFileSync(process.argv[2], { encoding: 'utf-8' })
- const myTokens = tokenizer(myInput)
- const parsedTree = parser(myTokens)
- const transformedTree = transformer(parsedTree)
- const output = generator(transformedTree)
- fs.writeFileSync('output.js', output)
|