记录一下如何实现一门编程语言~(5)

283 阅读3分钟

只是替换一下解释器其实并不是很想水一篇

首先, 修正一下之前简易解释器的 traverse = =

image.png

估计也只有这些类型了, 缺了再补 orz

一个小工具函数~

image.png

然后稍微调整一下 visitor 的用到的参数

image.png

哦耶, 从结果上看大功告成!

image.png

由于没有输出所以声明的参数都没有被使用到~

对, 这就是不想单独为解释水一篇对原因

所以增加一个结果输出很困难吗

其实, 这并不应该是一件很困难的事情

但是, 解释 AST 时的幸福程度完全取决于之前设计 AST 时的健康状况, 至少我这一颗 AST 的健康状况不太乐观 = =

经过激烈而且不懈努力的强效, 这颗小树苗终于有了一丝丝微微要活过来的迹象, 不过看样子以及撑不了太久了

image.png

嘛...至少现在还能用 (手动斜眼

然后修改一下 visitor 的 CallExpression 的部分, 其实也只有这里有变动, 需要分别处理字面量和变量两种情况

image.png

调用函数也稍微有那么一丢丢的修改, 英文中间多了一个 simpleAST 的步骤 orz

image.png

好的~ 接下来就是输出四个变量~

image.png

image.png

完美撒花~

诶, 好像看起来也没有增加多少东西, 就要这么结束了吗

(话说为什么 print 不能放在同一行输出 4 个变量呢 (喂难道你忘记了之前就没有处理过 "," 这个 token 吗, 没处理过的东西要怎么解释呀啊喂 orz

嗯, 没错, 这次就是这么短, 再次撒花~

PS: 稍微总结一下, 至少目前来看, 这种程度的 “编程语言” 还属于 “业务” 的范畴, 就是等于说我接到了一个要处理文本和字符串的需求这样, 短期看基本也是这样一个节奏, 以处理字符串的思路继续构造这样一个 “编程语言”, 目前来看并没有太多需要参考编译相关书籍的必要

PS2: 毕竟以学习为目的, 在遇到问题之后再去找资料或者一边写一边找会好一些, 将书籍当作一个解决问题的工具, 而不是学习的工具, 至少...没有遇到难以解决的问题的时候, 可以先不考虑翻书 (纯个人看法, 不过早晚都会翻书的 = =

附上源码.jpg

traverser.js

const fs = require('fs')
const path = require('path')

function executeAST(ast, execPath) {
    const vars = {}
    const types = {}
    traverser(ast, {
        ImportDeclaration(node, parent) {
            const p = path.resolve(execPath, node.source.value)
            const js = fs.readFileSync(p, { encoding: 'utf-8' })
            // 使用 js 的语法特性, 在内存中申请变量空间
            vars[node.property.value] = eval(js)
        },
        CallExpression(node, parent) {
            const key = parent.object.value.value
            const obj = vars[key]
            function printValue(val, type) {
                // 浮点数特殊转换一下再输出 = =
                return type !== 'float' ? val : val.toFixed(1)
            }
            const params = node.arguments.map(arg => {
                const type = types[arg.value].value
                if (arg.type === 'Identifier') {
                    return printValue(vars[arg.value], type)
                }
                if (arg.type.endsWith('Literal')) {
                    return printValue(arg.value, type)
                }
            })
            // 链式调用的问题早晚要解决 = =
            obj[node.property.value](...params)
        },
        VariableDeclaration(node, parent) {
            vars[node.property.value] = node.value.value
            types[node.property.value] = node.datatype
        },
        IntegerLiteral(node, parent) {
            node.value = parseInt(node.value)
        },
        FloatLiteral(node, parent) {
            node.value = parseFloat(node.value)
        },
        BooleanLiteral(node, parent) {
            node.value = node.value === 'true' ? true : false
        },
    })
}

function traverser(ast, visitor) {
    function traverseArray(array, parent) {
        array.forEach(child => {
          traverseNode(child, parent)
        })
    }
    function traverseNodePropertys(node, props) {
        props.forEach(p => {
            traverseNode(node[p], node)
        })
    }
    function traverseNode(node, parent) {
        if (!node) {
            return
        }
        const method = visitor[node.type]
        // 有预感这个 switch 会很庞大
        switch (node.type) {
        case 'Program':
            traverseArray(node.body, node)
            break
        case 'ImportDeclaration':
            traverseNodePropertys(node, ['source', 'property'])
            break
        
        case 'VariableDeclaration':
            traverseNodePropertys(node, ['property', 'value', 'datatype'])
            break
        case 'ExpressionStatement':
            traverseNodePropertys(node, ['object', 'callee'])
            break
        case 'MemberExpression':
            traverseNodePropertys(node, ['value'])
            break
        case 'CallExpression':
            traverseArray(node.arguments, node)
            traverseNodePropertys(node, ['property'])
            break
        case 'Identifier':
        case 'StringLiteral':
        case 'IntegerLiteral':
        case 'FloatLiteral':
        case 'BooleanLiteral':
            traverseNode(node.value, node)
            break
        }
        // 嗯, 还是目前看后序遍历足够了 orz
        if (method) {
            method(node, parent)
        }
    }

    traverseNode(ast, null)
}

module.exports = { traverser, executeAST }

ast.js // 仅改动的函数

ExpressionStatement(node, parent) {
    const parenStart = node.params.findIndex(p =>
        p.type === 'ParenIdentifier' && p.value === '('
    )
    const parenEnd = node.params.findIndex(p =>
        p.type === 'ParenIdentifier' && p.value === ')'
    )

    if (parenStart > -1 && parenEnd > -1) {
        node.callee = { type: 'CallExpression' } 
        node.callee.arguments = node.params.slice(parenStart + 1, parenEnd)
        node.callee.property = node.params[0]
        delete node.params
        return
    }

    // 函数调用也是很麻烦的事情, 先不管, 这次主要是数据类型 orz
    const objIdx = node.params.findIndex(p =>
        p.type === 'OperatorIdentifier' && p.value === '.'
    )
    const calleeIdx = node.params.findIndex(p =>
        p.type === 'ExpressionStatement' && p.callee
    )
    // 临时处理括号深度问题 T T
    if (parenEnd > -1) {
        node.callee = { type: 'CallExpression' }
        node.callee.arguments = [node.params[0]]
        delete node.params
        return
    }   
    if (parenStart > -1 && calleeIdx > -1) {
        node.callee = node.params[calleeIdx].callee
        node.callee.property = node.params[0]
        delete node.params
        return
    }
    if (objIdx > -1 && calleeIdx > -1) {
        node.object = { type: 'MemberExpression', value: node.params[objIdx -1 ]}
        node.callee = node.params[calleeIdx].callee
        delete node.params
        return
    }
},