最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 记录一下如何实现一门编程语言~(5)

    正文概述 掘金(lynnkoo)   2021-03-12   564

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

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

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

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

    一个小工具函数~

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    完美撒花~

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

    (话说为什么 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
        }
    },
    

    起源地下载网 » 记录一下如何实现一门编程语言~(5)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元