vue源码解析---AST抽象语法树

2023-11-06

目录

首先AST是什么?

 指针思想

递归深入(斐波那契数列)

递归深入(形式转换)

 栈结构

 AST语法树


首先AST是什么?

在计算机科学中,抽象
 语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。

我们可以理解为:把 template(模板)解析成一个对象,该对象是包含这个模板所以信息的一种数据,而这种数据浏览器是不支持的,为Vue后面的处理template提供基础数据。

 

 指针思想

JavaScript中的指针其实就是下标位置,下面以一个例子来使用指针思想。

寻找字符串中连续最多的字符

(如果i和j指向的字是一样的j不动,i后移,不一样说明之间 的字母是连续的让j追上i  后移)

let str = 'aaaaabbcccdd'

// 指针
let i = 0, j = 0, maxlen = 0, maxStr = '';
while (i <= str.length) {
    if (str[i] != str[j]) {
        console.log(`${j}和${i}之间的文字相同都是字符${str[j]}他们重复了${i - j}次`);
        if (maxlen < i - j) {
            maxlen = i - j
            maxStr = str[j];
        }
        j = i
    }
    i++

}
console.log(maxStr);

递归深入(斐波那契数列)

规则复现就要想到递归

试输出斐波那契数列第十项思考是否有重复计算(采用缓存的思路减少重复计算)

function fib(n) {
    console.count('计数')
    let v = n == 0 || n == 1 ? 1 : fib(n - 1) + fib(n - 2)
    return v
}
let cache = {}
function fib2(n) {
    console.count('计数')
    // hasOwnProperty表示是否有自己的属性。这个方法会查找一个对象是否有某个属性,
    if (cache.hasOwnProperty(n)) {
        return cache[n]
    }
    let v = n == 0 || n == 1 ? 1 : fib2(n - 1) + fib2(n - 2)
    cache[n] = v
    return v
}
for (let i = 0; i <= 9; i++) {
    fib2(i)

}
console.log(cache);

递归深入(形式转换)

 栈结构

我们都知道数组是一种线性结构,并且可以在任意位置插入和删除数据。但是有时为了实现某些功能,我们必须对这种任意性加以限制。而栈和队列就是常见的受限的线性结构。

栈是一种受限的线性表,后进先出。其限制是仅允许在表的一端进行插入和删除操作,这一端被称为栈顶,另一端称为栈底。LIFO(last in first out)表示就是后进入的元素,第一个弹出栈空间。向一个栈插入新元素又称为进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素。从一个栈删除元素又称作出栈或退栈,它把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素

需求:实现一个“智能重复”函数,实现下面字符串的转换

  将 2[abc] 转成 abcabc
  将 2[1[a]2[b]] 转成 abbabb
  将 2[1[a]2[3[b]4[c]]] 转成 abbbccccbbbccccabbbccccbbbcccc

思路:

1、定义两个栈(数组),栈1(stack1)、栈2(stack2),stack1用来存储数字,stack2用来存储字符串

2、定义一个指针(索引),默认从0开始

3、循环这个字符串,指针一直向后移动,直到指针等于字符串长度减1

4、获取当前指针指向的这个字符,然后进行判断

        a)、如果是数字stack1中推入这个数字,stack2中推入一个空字符,指针继续向后移动

        b)、如果是“[”什么都不做,指针继续向后移动

        c)、如果是字符串将stack2栈顶项修改为该字符串,指针继续向后移动

          d)、如果是“]”

  •         stack1栈顶数字弹出,得到需要重复的次数,
  •         stack2栈顶字符串弹
  •         将stack2栈顶项修改为:tack2栈顶项 + 重复后的字符串
  •         指针继续向后移动

 

function handleStr(str) {
    let stack1 = [] // 存放数字
    let stack2 = [''] // 存放字母
    // 指针
    let index = 0
    while (index < str.length) {
        // 剩余部分
        let nowStr = str.substring(index)
        // 以数字开头
        if (/^\d+\[/.test(nowStr)) {
            // 得到这个数字
            let num = nowStr.match(/^(\d+)\[/)[1]
            // 数字stack1中推入这个数字,stack2中推入一个空字符,指针继续向后移动
            stack1.push(Number(num))
            stack2.push('')
            index += num.length + 1
        } else if (/^\w+\]/.test(nowStr)) {
            // 如果是字符串将stack2栈顶项修改为该字符串,指针继续向后移动
            let word = nowStr.match(/^(\w+)\]/)[1]
            stack2[stack2.length - 1] = word
            index += word.length
        } else if (nowStr[0] == ']') {
            //   stack1栈顶数字弹出,得到需要重复的次数
            let num = stack1.pop()
            //   stack2栈顶字符串弹出
            let subStr = stack2.pop()
            //   将stack2栈顶项修改为:tack栈顶项 + 重复后的字符串
            stack2[stack2.length - 1] += subStr.repeat(num)
            index++
        }

    }

    return stack2[0]
}
console.log(handleStr('2[1[a]2[b]]'));

 AST语法树

 有识别开始标签的正则表达式(识别a-z的小写字母和0-6数字组成的标签)
识别结束符号的正则表达式(识别闭合的标签)
识别文字的正则表达式(不考虑文字与标签同级的情况)
在while循环中遍历传入的字符串
定义两个栈,栈1和栈2。
 

当识别到一个开始的标签符号,那么就将这个标签入栈1,把空字符串入栈2。

当识别到的字符是标签内的文字,那么就将栈2栈顶这项改为文字。

当识别到结束标签,那么就将这个标签符号弹栈,就把栈2栈顶的元素push到栈二新的栈顶上的。

var templateString = `<div>
<h1>你好</h1>
<ul>
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>
</div>
`
function parse(templateString) {
    //准备指针
    var index = 0;
    //剩余部分
    var rest = '';
    //开始标记
    var startRegExp = /^\<([a-z]+[1-6]?)(\s[^\<]+)?\>/;
    //结束标签
    var endRegExp = /^\<\/([a-z]+[1-6]?)\>/;
    //识别文字
    var wordRegExp = /^([^\<]+)\<\/([a-z]+[1-6]?)\>/;
    //准备两个栈
    var stack1 = [];
    var stack2 = [{ 'children': [] }];
    //遍历
    while (index < templateString.length - 1) {
        rest = templateString.substring(index);
        //识别遍历到的这个字符,是不是一个开始标签
        if (startRegExp.test(rest)) {

            let tag = rest.match(startRegExp)[1];
            let attrsString = rest.match(startRegExp)[2];

            const attrsStringLength = attrsString != null ? attrsString.length : 0;

            console.log("检测到开始标记", tag);

            //将开始标记推入栈1中
            stack1.push(tag);
            //将空数组推入栈2 中
            stack2.push({ 'tag': tag, 'children': [], 'attrs': parseAttrsString(attrsString) });

            //指针移动标签的长度+2?因为<>也占两位
            index += tag.length + 2 + attrsStringLength;
        } else if (endRegExp.test(rest)) {
            //识别这个字符是不是一个结束标签
            //指针移动标签的长度加3,</>占三位
            let tag = rest.match(endRegExp)[1];
            console.log("检测到结束标记", tag);
            //此时tag一定是和栈1顶部的是相同的
            let pop_tag = stack1.pop();
            if (tag == pop_tag) {
                //    弹栈
                let pop_arr = stack2.pop();
                console.log(pop_arr);
                if (stack2.length > 0) {
                    stack2[stack2.length - 1].children.push(pop_arr);
                }
            } else {
                throw new Error(stack1[stack1.length - 1] + "标签没有封闭");
            }
            index += tag.length + 3;
            console.log(stack1);
            console.log(stack2);
        } else if (wordRegExp.test(rest)) {
            //    遍历到这个字符是不是文字
            let word = rest.match(wordRegExp)[1];
            if (!/^\s+$/.test(word)) {
                //    不是全是空
                console.log("检测到文字", word);
                //    改变此时Stack2栈顶元素中
                stack2[stack2.length - 1].children.push({ 'text': word, 'type': 3 });
            }

            index += word.length;
        } else {
            index++;
            //    标签中的文字
        }

    }
    //    此时stack2就是我们之前默认放置的一项了,此时要返回这一项的children即可
    return stack2[0].children[0];

}
const ast = parse(templateString)

 识别attrs

函数功能:将获取到的字符串attrs转换成数组对象的形式。

思路:利用引号和空格将其拆分。

首先遍历字符串,遇到引号后将其 isStr 属性设置成true,此时在引号内遇到空格不用管,当遇到下一个引号时设置 isStr 为false。此时在 isStr为false的情况下再遇见引号就将前面的字符串截取出来放入到数组中。
最后将数组里面的内容利用map进行拆分。
 

function parseAttrsString(attrsString) {

    if (attrsString == undefined) return [];
    //    当前是否在引号内
    var isStr = false;
    //    断点
    var point = 0;
    //    结果数组
    var result = [];
    //    遍历attrsString
    for (let i = 0; i < attrsString; i++) {
        let char = attrsString[i];
        if (char == '"') {
            isStr = !isStr;
        } else if (char == ' ' && !isStr) {
            //    遇见了空格,并且不在引号内
            console.log(i);
            if (!/^\s*$/.test(attrsString.substring(point, i))) {
                result.push(attrsString.substring(point, i).trim());
                point = i;
            }

        }
    }
    //    循环结束之后,最后还剩一个属性
    result.push(attrsString.substring(point).trim());

    result = result.map(item => {
        //    根据等号拆分
        const o = item.match(/^(.+)="(.+)$/);
        return {
            name: o[1],
            name: o[2]
        }
    });
    return result;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue源码解析---AST抽象语法树 的相关文章

随机推荐

  • android 浅探打包安装APK

    打包安装过程 Run as Android Application 1 生成apk文件 1 生成 dex文件 DVM java gt javac gt class gt dx bat gt dex 架构 寄存器 cpu上一块高速的缓存 2
  • 谷歌地图旋转图片marker(图片旋转转base64)

    custom rotate icon method for Google map var RotateIcon function options this options options this rImg options img new
  • QT中使用winsock创建Tcp连接传文件

    第一步链接库 qmake LIBS lws2 32 cmake target link libraries send send是项目名称替换自己的 PUBLIC lt
  • 小程序动画 animation 的常规使用

    公司小程序项目比较多 最近正好有时间看一下小程序的动画 同时记录一下我的学习过程 看到这个文章的 我建议你直接去小程序后台 https developers weixin qq com miniprogram dev api ui anim
  • 从零开始教你如何完成一个基于Vite+Vue3+TS的后台管理系统

    项目大致效果 心动了吗 没错 没错 你没看错 在学习了前端也有一年多的时间了 先后学习了 html css html5 css3 js 微信小程序 nodejs vue react ts等 现在也是时候来对之前学的知识进行一个综合的练习了
  • c语言文件操作

    目录 一 什么是文件 1 1 程序文件 1 2 数据文件 1 3 文件名 二 文件的打开和关闭 2 1 文件指针 2 2 文件的打开和关闭 2 3 文件的顺序读写 编辑 三 文件的随机读写 3 1 fseek ftell rewind 四
  • Android WebView加载本地统一HTML界面样式文件并填充内容

    前言 之前加载HTMl图文都是使用TextView 但是现在需要统一三个端的样式 给出了一个HTML文件 我想反正都是HTML格式的 TextView应该也没问题 我就将文本直接填充进去 一运行 发现Html fromHtml 无法解析 l
  • ARM芯片开发(S5PV210芯片)——SD卡启动

    1 SD卡启动 顾名思义就是启动代码存放在SD卡中 设备从SD卡中启动 用SD卡启动有一些好处 譬如可以在不借用专用烧录工具 类似Jlink 的情况下对SD卡进行刷机 然后刷机后的SD卡插入卡槽 SoC既可启动 譬如可以用SD卡启动进行量产
  • 边缘云计算简介

    去年底 中国电子技术标准化研究院 阿里云等单位共同编制并发布了一份 边缘云计算技术与标准化白皮书 定义了边缘云计算的概念和标准等 白皮书篇幅略长 边缘计算社区将通过几篇文章拆解白皮书 边缘计算概念 和云计算出现的时候一样 目前业界对边缘计算
  • UniswapV2核心合约学习(1)— UniswapV2Factory.sol

    记得朋友圈看到过一句话 如果Defi是以太坊的皇冠 那么Uniswap就是这顶皇冠中的明珠 Uniswap目前已经是V2版本 相对V1 它的功能更加全面优化 然而其合约源码却并不复杂 本文为个人学习UniswapV2源码的系列记录 一 Un
  • 笔记/mysql数据库备份与恢复

    MySQL mysqldump命令详解 1 数据库信息 数据库地址 127 0 0 1 数据库用户名 root 数据库密码 1234 数据库名称 test1 数据库名称 test2 数据库名称 test3 mysqldump目录 usr b
  • 高级I/O函数

    应用层程序能往一个TCP连接中写入多少字节的数据 取决于对方的接收窗口的大小和本端的拥塞窗口的大小 管道本身有一个容量限制 规定如果应用程序不将数据从管道读走的话 该管道最多能写入多少字节的数据 自Linux 2 6 11内核起 管道容量的
  • AI Studio 飞桨 零基础入门深度学习笔记2-基于Python编写完成房价预测任务的神经网络模型

    AI Studio 飞桨 零基础入门深度学习笔记2 基于Python编写完成房价预测任务的神经网络模型 波士顿房价预测任务 线性回归模型 线性回归模型的神经网络结构 构建波士顿房价预测任务的神经网络模型 1 数据处理 1 1 读入数据 1
  • 企业订单管理软件

    企业订单管理软件 网上订货宝APP系统功能介绍 一 系统概述和用途 系统基于网络 实现厂家和代理商批发商通过网络下单订货功能 二 解决问题 1 解决订单管理混乱问题 2 解决订单遗漏问题 3 解决订单欠款遗漏问题 4 解决不同客户不同价格管
  • 在Windows下使用Python嵌入式环境包

    在 Python Releases for Windows 页面下载你需要的那个版本的 Windows embeddable package 64 bit 文件 这样就得到一个 python x x x embed amd64 zip 文件
  • Hash算法的使用

    Hash算法的使用 标签 默认分类 发表时间 2011 08 06 06 35 作者 GliderX khsing 分享到 出处 http hi baidu com gliderx 在对语料文本进行2 3元切分时 需要借助hash表来获得切
  • 信息学奥赛一本通 1170:计算2的N次方

    题目链接 http ybt ssoier cn 8088 problem show php pid 1170 思路 计算 2 2 2 的 n n n 次方相当于大整数 1
  • 目标检测-yolov50-火灾识别可视化

    使用yolov5模型对利用开源火灾数据集进行训练模型 实现对照片 视频以及摄像头的火焰烟雾的检测 详细的环境配置见 目标检测 YOLOV5 口罩检测 学习目标 数据集的准备 预训练权重 训练模型 训练模型路径的修改 模型测试 pyqt可视化
  • 完整代码CNN-LSTM多变量时间序列预测

    import torch from torch import nn import pandas as pd import numpy as np import matplotlib pyplot as plt from sklearn pr
  • vue源码解析---AST抽象语法树

    目录 首先AST是什么 指针思想 递归深入 斐波那契数列 递归深入 形式转换 栈结构 AST语法树 首先AST是什么 在计算机科学中 抽象 语法树 abstract syntax tree或者缩写为AST 或者语法树 syntax tree