目录
为什么AST有用?
AST是用什么?
安装依赖项
你好世界!
AST长什么样?
等等。
AST转换如何工作
如何修改AST的HTML输出?
将降价AST转换为HTML AST
为统一写一个插件
将访问者添加到插件
将图像包裹在图元素中
将图像旁边的文字用作标题
在图中添加小提琴元素
将转换的内容保存到新文件
首页 web前端 css教程 如何在抽象语法树中修改节点

如何在抽象语法树中修改节点

Apr 13, 2025 am 09:50 AM

如何在抽象语法树中修改节点

我最近偶然发现的最强大的概念之一是抽象语法树或AST的想法。如果您曾经学习过炼金术,您可能会记得,炼金术士的整个动机是通过科学或神秘的方法发现某种方法将非金牌转化为黄金。

AST是这样的。使用ASTS,我们可以将Markdown转换为HTML,JSX变成JavaScript,等等。

为什么AST有用?

在职业生涯的早期,我尝试使用发现和重复方法来更改文件。这最终变得相当复杂,因此我尝试使用正则表达式。我最终放弃了这个想法,因为它是如此脆弱。该应用程序一直都破裂了,因为有人会以我没想到的方式输入文本,并且会打破我的正则表达式,从而导致整个应用程序掉落。

之所以如此困难,是因为HTML灵活。这使得使用正则表达式很难解析。基于字符串的替代品很容易打破,因为它可能错过了一场比赛,匹配过多或做一些怪异的事情,从而导致无效的标记使页面看起来很笨拙。

另一方面,ASTS将HTML变成更加结构化的东西,这使得潜入文本节点并仅在该文本上进行替换变得更加简单,或者与元素混乱而无需完全处理文本。

这使AST转换更安全,比纯粹基于字符串的解决方案更安全,并且容易出错。

AST是用什么?

首先,让我们使用几行降价来查看最小文档。将其保存为称为home.md的文件,我们将保存在我们网站的内容文件夹中。

 # 你好世界!

![cardigan corgi](<https:>)一个可爱的corgi!

还有一些文字在这里。</https:>
登录后复制

假设我们知道Markdown,我们可以推断出,当这种降价解析时,最终将是一个H1标签,上面写着“ Hello World!”然后,文本的两个段落:第一个包含corgi的图像和一些旨在描述它的文本,第二个文本说:“还有一些文字在这里。”

但是,如何从降价转换为HTML呢?

那就是AST进来的地方!

由于它支持多种语言,因此我们将使用Unist语法树规范,更具体地说是项目统一。

安装依赖项

首先,我们需要安装所需的依赖项将降价分析为AST并将其转换为HTML。为此,我们需要确保将文件夹初始化为包装。在您的终端中运行以下命令:

 #确保您在根文件夹中(``content''中的位置)
#将此文件夹初始化为NPM软件包
npm init

#安装依赖项
NPM安装统一的备注备注html
登录后复制

如果我们假设我们的降价存储在home.md中,则可以通过以下代码获得AST:

 const fs = require('fs');
const Unified = require('unified');
const markdown = require('eRext-parse');
const html = require('dempress-html');

const contents = unified()
  。
  .use(html)
  。
  .tostring();

console.log(内容);
登录后复制

该代码利用了Node的内置FS模块,这使我们能够访问和操纵文件系统。有关其工作原理的更多信息,请查看官方文档。

如果我们将其保存为src/index.js并使用节点从命令行执行此脚本,我们将在终端中看到以下内容:

 $ node src/index.js 
<h1 id="你好世界">你好世界!</h1>
<p> <img  src="%E2%80%9C" alt="如何在抽象语法树中修改节点" >” alt =“ cardigan corgi”>
</p><p>还有更多文字。</p>
登录后复制

我们告诉Unified使用Relecor-Parse将Markdown文件转换为AST,然后使用Relever-HTML将Markdown AST变成HTML,或者更具体地说,它将其变成称为VFILE的东西。使用ToString()方法将AST变成可以在浏览器中显示的实际HTML字符串!

多亏了开源社区的辛勤工作,言论为将降级变成HTML所做的所有辛勤工作。 (请参阅差异)

接下来,让我们看一下这是如何工作的。

AST长什么样?

要查看实际的AST,让我们编写一个小插件来记录它:

 const fs = require('fs');
const Unified = require('unified');
const markdown = require('eRext-parse');
const html = require('dempress-html');

const contents = unified()
	。
  。
	.use(html)
	。
	.tostring();
登录后复制

运行脚本的输出现在将为:

 {
  “ type”:“ root”,
  “孩子们”: [
    {
      “类型”:“标题”,
      “深度”:1,
      “孩子们”: [
        {
          “ type”:“ text”,
          “价值”:“你好世界!”,
          “位置”: {}
        }
      ],,
      “位置”: {}
    },,
    {
      “ type”:“段落”,
      “孩子们”: [
        {
          “类型”:“图像”,
          “标题”:null,
          “ url”:“ <https:>”,
          “ Alt”:“ Cardigan Corgi”,
          “位置”: {}
        },,
        {
          “ type”:“ text”,
          “价值”:“可爱的corgi!”,
          “位置”: {}
        }
      ],,
      “位置”: {}
    },,
    {
      “ type”:“段落”,
      “孩子们”: [
        {
          “ type”:“ text”,
          “ value”:“还有更多文本。”,
          “位置”: {}
        }
      ],,
      “位置”: {}
    }
  ],,
  “位置”: {}
}</https:>
登录后复制

请注意,位置值已被截断以节省空间。它们包含有关文档中节点位置的信息。出于本教程的目的,我们不会使用此信息。 (请参阅差异)

这是有点不知所措,但是如果我们放大,我们可以看到降价的每个部分都变成了一种节点,其中包含文本节点。

例如,标题变为:

 {
  “类型”:“标题”,
  “深度”:1,
  “孩子们”: [
    {
      “ type”:“ text”,
      “价值”:“你好世界!”,
      “位置”: {}
    }
  ],,
  “位置”: {}
}
登录后复制

这就是这意味着:

  • 这些类型告诉我们我们正在处理哪种节点。
  • 每个节点类型都有描述节点的其他属性。标题上的深度属性告诉我们它的标题是多少 - 深度为1是一个

    标签,2表示

    等等。

  • 孩子们的数组告诉我们这个节点里面有什么。在标题和段落中,只有文字,但是我们也可以在这里看到内联元素,例如

这是ASTS的力量:我们现在将Markdown文档描述为计算机可以理解的对象。如果我们想将其打印回Markdown,Markdown编译器将知道一个深度为1的“标题”节点以#开始,并且带有“ Hello”的子文本节点意味着最终行应该是#Hello。

AST转换如何工作

通常使用访问者模式进行转换AST。知道这是如何生产力的来源的来源和出现并不重要的,但是如果您很好奇,Soham Kamani对人类的JavaScript设计模式有一个很好的例子,可以帮助解释其工作原理。重要的是要知道的是,AST工作中的大多数资源都会谈论“访问节点”,该节点大致转化为“找到AST的一部分,以便我们可以用它来做些事情”。这种工作实践的方式是,我们编写了一个将应用于与我们的标准匹配的AST节点的函数。

关于其工作原理的一些重要说明:

  • AST可能是巨大的,因此出于绩效原因,我们将直接突变节点。这与我通常如何处理事物的方式背道而驰 - 作为一般规则,我不喜欢变异全球状态 - 但在这种情况下这是有意义的。
  • 访客递归工作。这意味着,如果我们处理一个节点并创建相同类型的新节点,则访问者也将在新创建的节点上运行,除非我们明确告诉访客不这样做。
  • 在本教程中,我们不会太深入,但是这两个想法将帮助我们了解我们开始弄乱代码时发生的事情。

如何修改AST的HTML输出?

但是,如果我们想更改降价的输出怎么办?假设我们的目标是用图形元素包装图像标签并提供标题,这样:

 <figud>
  <img  src="%E2%80%9C" alt="如何在抽象语法树中修改节点" >”
    alt =“ Cardigan Corgi”
  />
  <figcaption>可爱的corgi!</figcaption>
</figud>
登录后复制

为此,我们需要转换HTML AST,而不是降价AST,因为Markdown没有一种创建数字或Figcaption元素的方法。幸运的是,由于Unified与多个解析器可互操作,因此我们可以在不编写一堆自定义代码的情况下做到这一点。

将降价AST转换为HTML AST

要将Markdown AST转换为HTML AST,请添加Reform-Hearize,然后切换到将AST转回HTML的重新构造。

 NPM安装插图读物重新构造
登录后复制

在SRC/index.js中进行以下更改以切换到rehype:

 const fs = require('fs');
const Unified = require('unified');
const markdown = require('eRext-parse');
const备注2 heaute = require('eREAND-HERAIMET');
const html = require('rehype-stringify');

const contents = unified()
	。
  。
	。
	.use(html)
	。
	.tostring();

console.log(内容);
登录后复制

请注意,HTML变量从RelectH-HTML更改为重新构造 - 两者都将AST转换为可以将其串起为HTML的格式

如果我们运行脚本,我们可以在AST中看到图像元素如下:

 {
  “类型”:“元素”,
  “ tagname”:“ img”,
  “特性”: {
    “ src”:“ https://images.dog.ceo/breeds/corgi-cardigan/n02113186_1030.jpg”,
    “ Alt”:“ Cardigan Corgi”
  },,
  “孩子们”: [],
  “位置”: {}
}
登录后复制

这是图像的HTML表示形式的AST,因此我们可以开始更改它以使用图形元素。 (请参阅差异)

为统一写一个插件

为了用图形元素包装我们的IMG元素,我们需要编写一个插件。在Unified中,使用use()方法添加插件,该方法接受插件作为第一个参数,并将任何选项作为第二个参数:

 .use(插件,选项)
登录后复制

插件代码是接收选项的函数(在统一行话中称为“附件”)。这些选项用于创建一个新功能(称为“变压器”),该功能接收AST并确实可以对其进行转换。有关插件的更多详细信息,请查看统一文档中的插件概述。

它返回的功能将接收整个AST作为参数,并且不会返回任何内容。 (请记住,AST在全球范围内被突变。)创建一个与index.js同一文件夹中的称为img-to-gigure.js的新文件,然后将以下内容放置在内部:

 Module.exports = options => tree => {
  console.log(tree);
};
登录后复制

要使用此功能,我们需要将其添加到src/index.js:

 const fs = require('fs');
const Unified = require('unified');
const markdown = require('eRext-parse');
const备注2 heaute = require('eREAND-HERAIMET');
const html = require('rehype-stringify');
const imgtofigure = require('./ img-to-to-figure');

const contents = unified()
  。
  。
  .use(imgtofigure)
  。
  .tostring();

console.log(内容);
登录后复制

如果我们运行脚本,我们将看到整棵树在控制台中登录:

 {
  类型:“根”,
  孩子们: [
    {
      类型:“元素”,
      tagname:'p',
      特性: {},
      孩子:[阵列],
      位置:[对象]
    },,
    {type:'text',value:'\\ n'},
    {
      类型:“元素”,
      tagname:'p',
      特性: {},
      孩子:[阵列],
      位置:[对象]
    }
  ],,
  位置: {
    开始:{行:1,列:1,偏移:0},
    结束:{行:4,列:1,偏移:129}
  }
}
登录后复制

(请参阅差异)

将访问者添加到插件

接下来,我们需要添加一个访客。这将使我们真正了解该代码。统一利用了许多带有Unist-util-*前缀的实用程序软件包,使我们可以在不编写自定义代码的情况下使用AST进行常见的事情。

我们可以使用Unist-Util-访问来修改节点。这给了我们一个参观三个论点的访问助手:

  • 我们正在与之合作的整个AST
  • 一个谓词功能,以识别我们要访问的节点
  • 对我们想要进行的任何更改的函数

要安装,请在命令行中运行以下内容:

 NPM安装Unist-Util-访问
登录后复制

让我们通过添加以下代码在插件中实现访问者:

 cont访问= require('Unist-util-visit');

  Module.exports = options => tree => {
    访问(
      树,
      //仅访问包含IMG元素的P标签
      节点=>
        node.tagname ==='p'&& node.children.some(n => n.tagname ===='img'),
      节点=> {
        console.log(node);
      }
    );
};
登录后复制

当我们运行此操作时,我们可以看到只有一个段落节点记录了:

 {
  类型:“元素”,
  tagname:'p',
  特性: {},
  孩子们: [
    {
      类型:“元素”,
      tagname:'img',
      属性:[对象],
      孩子们: [],
      位置:[对象]
    },,
    {type:'text',值:'可爱的corgi!',位置:[object]}
  ],,
  位置: {
    开始:{行:3,列:1,偏移:16},
    结束:{行:3,列:102,偏移:117}
  }
}
登录后复制

完美的!我们只会获得具有要修改的图像的段落节点。现在我们可以开始改变AST!

(请参阅差异)

将图像包裹在图元素中

现在我们已经拥有图像属性,我们可以开始更改AST。请记住,因为AST确实可以很大,所以我们将它们变为适当的地方,以避免创建大量副本并可能减慢脚本的速度。

我们首先将节点的标签名更改为数字而不是段落。其余的细节现在可以保持不变。

在src/img-to-gigure.js中进行以下更改:

 cont访问= require('Unist-util-visit');

Module.exports = options => tree => {
  访问(
    树,
    //仅访问包含IMG元素的P标签
    节点=>
    node.tagname ==='p'&& node.children.some(n => n.tagname ===='img'),
    节点=> {
      node.tagname ='figie';
    }
  );
};
登录后复制

如果我们再次运行脚本并查看输出,我们可以看到我们越来越近了!

 <h1 id="你好世界">你好世界!</h1>
<figug> <img  src="%E2%80%9C" alt="如何在抽象语法树中修改节点" >” alt =“ cardigan corgi”>
<p>还有更多文字。</p></figug>
登录后复制

(请参阅差异)

将图像旁边的文字用作标题

为了避免需要编写自定义语法,我们将使用带有图像的任何传递的文本作为图像标题。

我们可以假设通常在Markdown中图像没有内联文字,但是值得注意的是,这可能100%会导致意外字幕出现给人们写Markdown。我们将在本教程中承担这种风险。如果您打算将其投入生产中,请确保权衡权衡取舍,并选择最适合您的情况。

要使用文本,我们将在父节点内寻找文本节点。如果我们找到一个,我们想抓住它的价值作为我们的标题。如果找不到字幕,我们根本不想转换此节点,因此我们可以尽早返回。

对src/img-to-gigure.js进行以下更改以获取标题:

 cont访问= require('Unist-util-visit');

Module.exports = options => tree => {
  访问(
    树,
    //仅访问包含IMG元素的P标签
    节点=>
    node.tagname ==='p'&& node.children.some(n => n.tagname ===='img'),
    节点=> {
      //找到文本节点
      const textNode = node.children.find(n => n.type ===='text');
 
      //如果没有字幕,我们不需要转换节点
      如果(!textNode)返回;
 
      const标题= textnode.value.trim();
 
      console.log({catchion});
      node.tagname ='figie';
    }
  );
};
登录后复制

运行脚本,我们可以看到记录的标题:

 {标题:'可爱的科吉!' }
登录后复制

(请参阅差异)

在图中添加小提琴元素

现在我们有字幕文本,我们可以添加一个图形以显示它。我们可以通过创建一个新的节点并删除旧文本节点来做到这一点,但是由于我们正在将文本节点更改为元素并不那么复杂。

不过,元素没有文本,因此我们需要在Figcaption元素的孩子中添加一个新的文本节点来显示字幕文本。

对src/img-to-gigure.js进行以下更改,以将标题添加到标记中:

 cont访问= require('Unist-util-visit');

Module.exports = options => tree => {
  访问(
    树,
    //仅访问包含IMG元素的P标签
    节点=>
      node.tagname ==='p'&& node.children.some(n => n.tagname ===='img'),
    节点=> {
      //找到文本节点
      const textNode = node.children.find(n => n.type ===='text');

      //如果没有字幕,我们不需要转换节点
      如果(!textNode)返回;

      const标题= textnode.value.trim();
      //将文本节点更改为包含文本节点的figcaption元素
      textnode.type ='element';
      textnode.tagname ='figcaption';
      textnode.Children = [
        {
          类型:“文本”,
          价值:标题
        }
      ];

      node.tagname ='figie';
    }
  );
};
登录后复制

如果我们使用节点src/index.js再次运行脚本,我们会看到包裹在图形元素中的转换图像,并用figcaption进行了描述!

 <h1 id="你好世界">你好世界!</h1>
<figug> <img  src="%E2%80%9C" alt="如何在抽象语法树中修改节点" >”

<p>还有更多文字。</p></figug>
登录后复制

(请参阅差异)

将转换的内容保存到新文件

既然我们已经进行了大量转换,我们想将这些调整保存到实际文件中,以便我们可以共享它们。

由于Markdown不包含完整的HTML文档,因此我们将添加一个名为Rehype-Document的Rehype插件,以添加完整的文档结构和标题标签。

通过运行安装:

 NPM安装rehype-Document
登录后复制

接下来,对src/index.js进行以下更改:

 const fs = require('fs');
const Unified = require('unified');
const markdown = require('eRext-parse');
const备注2 heaute = require('eREAND-HERAIMET');
const doc = require('rehype-document');
const html = require('rehype-stringify');

const imgtofigure = require('./ img-to-to-figure');

const contents = unified()
	。
	。
	.use(imgtofigure)
    .use(doc,{title:'转换文档!'})
	.use(html)
	。
	.tostring();

 const outputdir =`$ {process.cwd()}/public`;

  如果(!fs.existsync(outputdir)){
    fs.mkdirsync(outputdir);
  }
 
  fs.WriteFileSync(`$ {outputDir}/home.html`,contents);
登录后复制

再次运行脚本,我们将能够在Root中看到一个名为public的新文件夹,在内部我们会看到home.html。在内部,我们的转换文档保存了!

  

<meta charset="“" utf-8>
<title>转换文档!</title>
<meta name="“" viewport content="“" width="设备宽度,初始尺度=">


	<h1 id="你好世界">你好世界!</h1>
	<figug> <img  src="%E2%80%9C" alt="如何在抽象语法树中修改节点" >”
	<p>还有更多文字。</p>
</figug>
登录后复制

以上是如何在抽象语法树中修改节点的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1653
14
CakePHP 教程
1413
52
Laravel 教程
1304
25
PHP教程
1251
29
C# 教程
1224
24
带有粘性定位的堆叠卡和一点点的杂物 带有粘性定位的堆叠卡和一点点的杂物 Apr 03, 2025 am 10:30 AM

前几天,我发现了科里·金尼文(Corey Ginnivan)网站上的这一点,当您滚动时,彼此之间的卡片堆放集。

Google字体可变字体 Google字体可变字体 Apr 09, 2025 am 10:42 AM

我看到Google字体推出了新设计(Tweet)。与上一次大型重新设计相比,这感觉更加迭代。我几乎无法分辨出区别

如何使用HTML,CSS和JavaScript创建动画倒计时计时器 如何使用HTML,CSS和JavaScript创建动画倒计时计时器 Apr 11, 2025 am 11:29 AM

您是否曾经在项目上需要一个倒计时计时器?对于这样的东西,可以自然访问插件,但实际上更多

HTML数据属性指南 HTML数据属性指南 Apr 11, 2025 am 11:50 AM

您想了解的有关HTML,CSS和JavaScript中数据属性的所有信息。

为什么Flex布局中的紫色斜线区域会被误认为是'溢出空间”? 为什么Flex布局中的紫色斜线区域会被误认为是'溢出空间”? Apr 05, 2025 pm 05:51 PM

关于Flex布局中紫色斜线区域的疑问在使用Flex布局时,你可能会遇到一些令人困惑的现象,比如在开发者工具(d...

如何通过CSS选择第一个类名为item的子元素? 如何通过CSS选择第一个类名为item的子元素? Apr 05, 2025 pm 11:24 PM

在元素个数不固定的情况下如何通过CSS选择第一个指定类名的子元素在处理HTML结构时,常常会遇到元素个数不�...

使Sass更快的概念证明 使Sass更快的概念证明 Apr 16, 2025 am 10:38 AM

在一个新项目开始时,Sass汇编发生在眼睛的眨眼中。感觉很棒,尤其是当它与browsersync配对时,它重新加载

在前端开发中,如何使用CSS和JavaScript实现类似Windows 10设置界面的探照灯效果? 在前端开发中,如何使用CSS和JavaScript实现类似Windows 10设置界面的探照灯效果? Apr 05, 2025 pm 10:21 PM

在前端开发中如何实现类似Windows...

See all articles