精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

AST解析基礎: 如何寫一個簡單的html語法分析庫

開發 前端
這篇文章主要介紹如何去寫一個AST解析器, 但是并不是通過分析JavaScript, 而是通過分析 html5 的語法樹來介紹。

前言

虛擬語法樹(Abstract Syntax Tree, AST)是解釋器/編譯器進行語法分析的基礎, 也是眾多前端編譯工具的基礎工具, 比如webpack, postcss, less等. 對于ECMAScript, 由于前端輪子眾多, 人力過于充足, 早已經被人們玩膩了. 光是語法分析器就有 uglify , acorn , bablyon , typescript , esprima 等等若干種. 并且也有了AST的社區標準: ESTree。

這篇文章主要介紹如何去寫一個AST解析器, 但是并不是通過分析JavaScript, 而是通過分析 html5 的語法樹來介紹, 使用 html5 的原因有兩點: 一個是其語法簡單, 歸納起來只有兩種: Text 和 Tag , 其次是因為JavaScript的語法分析器已經有太多太多, 再造一個輪子毫無意義, 而對于 html5 , 雖然也有不少的AST分析器, 比如 htmlparser2 , parser5 等等, 但是沒有像 ESTree 那么標準, 同時, 這些分析器都有一個問題: 那就是定義的語法樹中無法對標簽屬性進行操作. 所以為了解決這個問題, 才寫了一個html的語法分析器, 同時定義了一個完善的AST結構, 然后再有的這篇文章。

AST解析基礎: 如何寫一個簡單的html語法分析庫

AST定義

為了跟蹤每個節點的位置屬性, 首先定義一個基礎節點, 所有的結點都繼承于此結點:

 

  1. export interface IBaseNode { 
  2.   start: number;  // 節點起始位置 
  3.   end: number;    // 節點結束位置 

如前所述, html5的語法類型最終可以歸結為兩種: 一種是 Text , 另一種是 Tag , 這里用一個枚舉類型來標志它們.

 

  1. export enum SyntaxKind { 
  2.   Text = 'Text', // 文本類型 
  3.   Tag  = 'Tag',  // 標簽類型 

對于文本, 其屬性只有一個原始的字符串 value , 因此結構如下:

 

  1. export interface IText extends IBaseNode { 
  2.   type: SyntaxKind.Text; // 類型 
  3.   value: string;         // 原始字符串 

而對于 Tag , 則應該包括標簽開始部分 open , 屬性列表 attributes , 標簽名稱 name , 子標簽/文本 body , 以及標簽閉合部分 close :

 

  1. export interface ITag extends IBaseNode { 
  2.   type: SyntaxKind.Tag;  // 類型 
  3.   open: IText;           // 標簽開始部分, 比如 <div id="1"
  4.   name: string;          // 標簽名稱, 全部轉換為小寫 
  5.   attributes: IAttribute[];  // 屬性列表 
  6.   body: Array<ITag | IText> // 子節點列表, 如果是一個非自閉合的標簽, 并且起始標簽已結束, 則為一個數組 
  7.     | void                  // 如果是一個自閉合的標簽, 則為void 0 
  8.     | null;                 // 如果起始標簽未結束, 則為null 
  9.   close: IText              // 關閉標簽部分, 存在則為一個文本節點 
  10.     | void                  // 自閉合的標簽沒有關閉部分 
  11.     | null;                 // 非自閉合標簽, 但是沒有關閉標簽部分 

標簽的屬性是一個鍵值對, 包含名稱 name 及值 value 部分, 定義結構如下:

 

  1. export interface IAttribute extends IBaseNode { 
  2.   name: IText;  // 名稱 
  3.   value: IAttributeValue | void; // 值 

其中名稱是普通的文本節點, 但是值比較特殊, 表現在其可能被單/雙引號包起來, 而引號是無意義的, 因此定義一個標簽值結構:

 

  1. export interface IAttributeValue extends IBaseNode { 
  2.   value: string; // 值, 不包含引號部分 
  3.   quote: '\'' | '"' | void; // 引號類型, 可能是', ", 或者沒有 

Token解析

AST解析首先需要解析原始文本得到符號列表, 然后再通過上下文語境分析得到最終的語法樹.

相對于JSON, html雖然看起來簡單, 但是上下文是必需的, 所以雖然JSON可以直接通過token分析得到最終的結果, 但是html卻不能, token分析是***步, 這是必需的. (JSON解析可以參考我的另一篇文章: 徒手寫一個JSON解析器(Golang) ).

token解析時, 需要根據當前的狀態來分析token的含義, 然后得出一個token列表.

首先定義token的結構:

 

  1. export interface IToken { 
  2.   start: number;    // 起始位置 
  3.   end: number;      // 結束位置 
  4.   value: string;    // token 
  5.   type: TokenKind;  // 類型 

Token類型一共有以下幾種:

 

  1. export enum TokenKind { 
  2.   Literal     = 'Literal',      // 文本 
  3.   OpenTag     = 'OpenTag',      // 標簽名稱 
  4.   OpenTagEnd  = 'OpenTagEnd',   // 開始標簽結束符, 可能是 '/', 或者 '''--' 
  5.   CloseTag    = 'CloseTag',     // 關閉標簽 
  6.   Whitespace  = 'Whitespace',   // 開始標簽類屬性值之間的空白 
  7.   AttrValueEq = 'AttrValueEq',  // 屬性中的= 
  8.   AttrValueNq = 'AttrValueNq',  // 屬性中沒有引號的值 
  9.   AttrValueSq = 'AttrValueSq',  // 被單引號包起來的屬性值 
  10.   AttrValueDq = 'AttrValueDq',  // 被雙引號包起來的屬性值 

Token分析時并沒有考慮屬性的鍵/值關系, 均統一視為屬性中的一個片段, 同時, 視 = 為一個

特殊的獨立段片段, 然后交給上層的 parser 去分析鍵值關系. 這么做的原因是為了在token分析

時避免上下文處理, 并簡化狀態機狀態表. 狀態列表如下:

 

  1. enum State { 
  2.   Literal              = 'Literal'
  3.   BeforeOpenTag        = 'BeforeOpenTag'
  4.   OpeningTag           = 'OpeningTag'
  5.   AfterOpenTag         = 'AfterOpenTag'
  6.   InValueNq            = 'InValueNq'
  7.   InValueSq            = 'InValueSq'
  8.   InValueDq            = 'InValueDq'
  9.   ClosingOpenTag       = 'ClosingOpenTag'
  10.   OpeningSpecial       = 'OpeningSpecial'
  11.   OpeningDoctype       = 'OpeningDoctype'
  12.   OpeningNormalComment = 'OpeningNormalComment'
  13.   InNormalComment      = 'InNormalComment'
  14.   InShortComment       = 'InShortComment'
  15.   ClosingNormalComment = 'ClosingNormalComment'
  16.   ClosingTag           = 'ClosingTag'

整個解析采用函數式編程, 沒有使用OO, 為了簡化在函數間傳遞狀態參數, 由于是一個同步操作,

這里利用了JavaScript的事件模型, 采用全局變量來保存狀態. Token分析時所需要的全局變量列表如下:

 

  1. let state: State          // 當前的狀態 
  2. let buffer: string        // 輸入的字符串 
  3. let bufSize: number       // 輸入字符串長度 
  4. let sectionStart: number  // 正在解析的Token的起始位置 
  5. let index: number         // 當前解析的字符的位置 
  6. let tokens: IToken[]      // 已解析的token列表 
  7. let char: number          // 當前解析的位置的字符的UnicodePoint 

在開始解析前, 需要初始化全局變量:

 

  1. function init(input: string) { 
  2.   state        = State.Literal 
  3.   buffer       = input 
  4.   bufSize      = input.length 
  5.   sectionStart = 0 
  6.   index        = 0 
  7.   tokens       = [] 

然后開始解析, 解析時需要遍歷輸入字符串中的所有字符, 并根據當前狀態進行相應的處理

(改變狀態, 輸出token等), 解析完成后, 清空全局變量, 返回結束.

 

  1. export function tokenize(input: string): IToken[] { 
  2.   init(input) 
  3.   while (index < bufSize) { 
  4.     char = buffer.charCodeAt(index
  5.     switch (state) { 
  6.     // ...根據不同的狀態進行相應的處理 
  7.     // 文章忽略了對各個狀態的處理, 詳細了解可以查看源代碼 
  8.     } 
  9.     index++ 
  10.   } 
  11.   const _nodes = nodes 
  12.   // 清空狀態 
  13.   init(''
  14.   return _nodes 

語法樹解析

在獲取到token列表之后, 需要根據上下文解析得到最終的節點樹, 方式與tokenize相似,均采用全局變量保存傳遞狀態, 遍歷所有的token, 不同之處在于這里沒有一個全局的狀態機。

因為狀態完全可以通過正在解析的節點的類型來判斷。

 

  1. export function parse(input: string): INode[] { 
  2.   init(input) 
  3.   while (index < count) { 
  4.     token = tokens[index
  5.     switch (token.type) { 
  6.       case TokenKind.Literal: 
  7.         if (!node) { 
  8.           node = createLiteral() 
  9.           pushNode(node) 
  10.         } else { 
  11.           appendLiteral(node) 
  12.         } 
  13.         break 
  14.       case TokenKind.OpenTag: 
  15.         node = void 0 
  16.         parseOpenTag() 
  17.         break 
  18.       case TokenKind.CloseTag: 
  19.         node = void 0 
  20.         parseCloseTag() 
  21.         break 
  22.       default
  23.         unexpected() 
  24.         break 
  25.     } 
  26.     index++ 
  27.   } 
  28.   const _nodes = nodes 
  29.   init() 
  30.   return _nodes 

不太多解釋, 可以到GitHub查看源代碼.

結語

項目已開源, 名稱是 html5parser , 可以通過npm/yarn安裝:

 

  1. npm install html5parser -S  
  2. OR  
  3. yarn add html5parser 

或者到GitHub查看源代碼: acrazing/html5parser

目前對正常的HTML解析已完全通過測試, 已知的BUG包括對注釋的解析, 以及未正常結束的

輸入的解析處理(均在語法分析層面, token分析已通過測試).

責任編輯:未麗燕 來源: SegmentFault
相關推薦

2019-08-01 12:59:21

Bug代碼程序

2022-10-08 00:06:00

JS運行V8

2023-12-01 08:31:20

HTML解析庫

2025-06-26 00:40:13

2024-02-27 17:32:30

時間序列分析庫PyTimeTK數據科學

2015-04-29 10:02:45

框架如何寫框架框架步驟

2010-04-19 17:21:36

Oracle寫文件

2019-06-05 13:05:47

PHP語法糖編碼

2017-06-08 15:53:38

PythonWeb框架

2014-02-14 09:37:01

JavascriptDOM

2011-05-26 09:39:53

程序

2018-01-29 21:56:28

Bug程序程序員

2016-09-21 12:54:10

CAAS系統鏡像

2023-01-30 09:01:34

DecoratorsJS語法

2013-12-10 09:50:03

技術技術博客

2012-08-13 10:16:34

IBMdW

2024-05-07 09:02:47

2012-07-11 23:26:10

bug測試

2024-11-18 00:21:01

2011-03-24 09:34:41

SPRING
點贊
收藏

51CTO技術棧公眾號

午夜激情小视频| 在线观看美女av| 99久久精品一区二区成人| 国产精品丝袜黑色高跟| 99re视频在线观看| 国产毛片aaa| 97视频精品| 成人激情综合网站| 国产不卡精品视男人的天堂| www.黄色网| 中文日本在线观看| 国产91精品露脸国语对白| 青青精品视频播放| 亚洲国产精品免费在线观看| 色婷婷av一区二区三区丝袜美腿| 夜夜精品浪潮av一区二区三区| 久久精品第九区免费观看 | 黄色影院一级片| 免费观看在线午夜影视| 久久一二三区| 欧美激情第1页| 又色又爽的视频| 午夜欧洲一区| 精品国产百合女同互慰| 在线观看免费不卡av| 黄色综合网址| 午夜免费久久看| 中文字幕の友人北条麻妃| 丁香婷婷在线观看| 久久蜜桃香蕉精品一区二区三区| 国产精品乱码一区二区三区| 国产剧情精品在线| 美腿丝袜亚洲综合| 国产精品久久一区| 久久人妻免费视频| 亚洲伊人网站| 一区二区成人av| 老司机福利av| 日韩精品免费一区二区三区竹菊| 欧美成人r级一区二区三区| 亚洲第一色av| 亚洲精品大片| 欧美日韩亚洲另类| 午夜宅男在线视频| yiren22亚洲综合| 欧美亚洲国产一区二区三区va| 成人小视频在线看| 国产欧美一区二区三区精品酒店| 欧美日韩免费在线观看| 日本男女交配视频| 国产丝袜精品丝袜| 亚洲夂夂婷婷色拍ww47| 欧妇女乱妇女乱视频| 日韩另类在线| 亚洲成人激情自拍| 成人中文字幕在线播放| 涩涩av在线| 色网综合在线观看| 福利在线一区二区三区| 吞精囗交69激情欧美| 亚洲欧美日韩中文播放| 水蜜桃在线免费观看| 色爱综合区网| 五月天激情综合| 中国丰满人妻videoshd| 欧美日韩亚洲国产| 91麻豆精品国产综合久久久久久| 少妇人妻大乳在线视频| av最新在线| 色呦呦一区二区三区| 亚洲精品怡红院| 午夜精品久久久久久毛片| 日韩一区二区电影网| 亚洲婷婷在线观看| 伊人成综合网yiren22| 永久免费看mv网站入口亚洲| av激情在线观看| 亚洲日本免费| 国产精品美女主播| www.xxx国产| 91女厕偷拍女厕偷拍高清| 亚洲va久久久噜噜噜久久天堂| www.日日夜夜| 久久久久久日产精品| 国产99午夜精品一区二区三区| 中文字幕在线播放av| 在线视频观看日韩| 国产va免费精品高清在线观看| 中文字幕一区二区在线视频 | www.亚洲免费| 一区二区欧美精品| 黄色国产精品视频| 欧美视频精品全部免费观看| 日韩精品在线观| 波多野结衣欲乱| 在线播放精品| 国产免费亚洲高清| 天堂在线视频观看| 日韩理论在线观看| 成人在线免费在线观看| www 久久久| 亚洲美女又黄又爽在线观看| 唐朝av高清盛宴| 久久福利毛片| 国产午夜精品一区| 182tv在线播放| 欧美综合色免费| 日本一区二区在线免费观看| 性xxxx欧美老肥妇牲乱| 国产成人免费av电影| 国产综合无码一区二区色蜜蜜| 国产精品色婷婷| 亚洲欧美日韩精品综合在线观看| 国产丝袜在线播放| 91麻豆精品国产91久久久| 天天躁日日躁aaaa视频| 99国产精品99久久久久久粉嫩| 成人av色在线观看| 国产高清不卡视频| 国产免费久久精品| 国自产拍偷拍精品啪啪一区二区 | 视频一区二区国产| 国产伦精品一区二区三毛| 黄色在线免费看| 欧美日韩精品二区第二页| 日本黄色网址大全| 亚洲三级国产| 国产精品制服诱惑| 久草免费在线色站| 亚洲一区二区偷拍精品| 天天影视色综合| 成人影视亚洲图片在线| 国产成人久久久精品一区| 日本ー区在线视频| 一本一道波多野结衣一区二区| 视频免费在线观看| 精品1区2区3区4区| 97碰碰视频| 日韩av毛片| 日韩欧美国产电影| 久草中文在线视频| 成人综合婷婷国产精品久久免费| 中国一级黄色录像| 日韩精品久久久久久久软件91| 久久国产精品久久久久久久久久| 精品一区二区三区人妻| 国内精品在线播放| 男人日女人的bb| 亚洲**毛片| 久久久在线免费观看| 亚洲精品一区二区三区四区| 夜夜嗨av一区二区三区四季av | 亚洲精品无码久久久久久| 老汉色老汉首页av亚洲| 欧美一级电影久久| 国产精品久久久久久免费免熟| 国产精品麻豆欧美日韩ww| 色免费在线视频| 午夜片欧美伦| 97久久夜色精品国产九色| 国产99久久精品一区二区300| 日韩精品一区二区三区在线| 激情五月少妇a| 风间由美性色一区二区三区| 精品无码国产一区二区三区av| 久久97精品| 情事1991在线| 在线免费观看黄色网址| 91精品麻豆日日躁夜夜躁| 中文字幕av免费在线观看| 成人永久aaa| 成年人免费在线播放| 日韩欧美高清| 97netav| 7777kkk亚洲综合欧美网站| 亚洲精品久久久久久久久久久久 | 久久77777| 日韩欧美一区二区久久婷婷| 亚欧视频在线观看| 国产欧美精品区一区二区三区| 久久精品久久99| 99国产成+人+综合+亚洲欧美| 午夜精品一区二区在线观看| 日韩高清二区| 国产成人综合精品在线| 国产调教视频在线观看| 亚洲精品福利在线| 国产精品久久久午夜夜伦鲁鲁| 亚洲18色成人| 9.1片黄在线观看| 丁香亚洲综合激情啪啪综合| 天堂中文视频在线| 国产精品hd| 婷婷五月色综合| 伦理一区二区三区| 国产精品爽爽爽| 玖玖在线播放| 久久精品最新地址| 欧美美女搞黄| 欧美大片在线观看一区二区| 做爰视频毛片视频| 亚洲成av人片一区二区三区| 肉色超薄丝袜脚交69xx图片| 91香蕉视频mp4| 天堂在线精品视频| 丝袜亚洲精品中文字幕一区| 日本wwwcom| 五月天久久网站| 日本欧美色综合网站免费| 99久久香蕉| 成人免费网站在线观看| 欧美舌奴丨vk视频| 性欧美亚洲xxxx乳在线观看| 超碰免费在线播放| 色爱av美腿丝袜综合粉嫩av| 日本学生初尝黑人巨免费视频| 中文字幕国产精品一区二区| 私密视频在线观看| 国产成人综合自拍| 日本高清久久久| 日韩在线观看一区二区| 18禁免费无码无遮挡不卡网站 | 国产乱子夫妻xx黑人xyx真爽| 欧美日韩亚洲三区| 国产欧美综合一区| 久久国产精品成人免费观看的软件| 久久五月天婷婷| 精品按摩偷拍| 国产不卡一区二区在线观看| 电影中文字幕一区二区| 天天干在线视频论坛| 亚洲人成啪啪网站| 日韩在线视频第一页| 欧美大胆a人体大胆做受| 中文字幕欧美日韩| 中文字幕+乱码+中文乱码www| 图片区小说区区亚洲影院| 久久精品www人人爽人人| 亚洲三级电影全部在线观看高清| 超碰人人人人人人人| 国产欧美一区二区精品性色| 亚洲AV无码国产成人久久| 久久久三级国产网站| 欧美老熟妇乱大交xxxxx| 91视频com| 公侵犯人妻一区二区三区| 久久尤物电影视频在线观看| 大又大又粗又硬又爽少妇毛片 | 喷水一区二区三区| 福利在线一区二区三区| 毛片av中文字幕一区二区| 日本肉体xxxx裸体xxx免费| 久久国产欧美日韩精品| 亚洲欧美日韩一二三区| 国产精品91一区二区| 少妇搡bbbb搡bbb搡打电话| av在线不卡免费看| 成年人网站免费在线观看| 国产欧美视频一区二区| 一区二区三区影视| 亚洲综合一区在线| 自拍偷拍欧美亚洲| 色播五月激情综合网| 亚洲中文字幕在线一区| 日韩一级精品视频在线观看| 乱色精品无码一区二区国产盗| 日韩国产欧美精品一区二区三区| 蜜桃视频在线观看视频| 少妇精69xxtheporn| 91大神xh98hx在线播放| 久久综合伊人77777| 丰乳肥臀在线| 国产精品成人国产乱一区 | 亚洲精品久久久久久无码色欲四季| 精品国产人成亚洲区| 欧美精品久久久久久久久久丰满| 日韩中文av在线| 超碰在线97国产| 日本精品久久久久影院| 91国产一区| 久久精品五月婷婷| 久久激情电影| 福利视频免费在线观看| 日韩高清一级片| wwwxxxx在线观看| 国产三区在线成人av| 国产极品国产极品| 色综合久久久久| 国产理论片在线观看| 亚洲精品在线视频| a篇片在线观看网站| 日韩av123| 黑色丝袜福利片av久久| 永久久久久久| 久久亚洲不卡| 四季av综合网站| 中文字幕日韩av资源站| 在线观看免费av片| 日韩视频一区在线观看| 福利视频在线播放| 91精品国产高清久久久久久91| 欧美综合影院| 免费看污久久久| 激情丁香综合| 九九久久久久久| 欧美国产综合色视频| 亚洲一区欧美在线| 日韩欧美在线不卡| 丝袜美腿美女被狂躁在线观看| 5252色成人免费视频| 中文在线免费一区三区| 亚洲欧洲精品一区二区三区波多野1战4| 亚洲精品三级| 绯色av蜜臀vs少妇| 一区精品在线播放| 乱子伦一区二区三区| 日韩黄色av网站| 9999精品成人免费毛片在线看| 成人女保姆的销魂服务| 欧美精品一区二区三区精品| 北条麻妃69av| caoporn国产一区二区| 久久精品这里只有精品| 欧美精选在线播放| 求av网址在线观看| 国产精品久久久久不卡| 激情综合网五月| 日韩videos| 久久xxxx| 亚洲码无人客一区二区三区| 精品久久久久久久中文字幕 | 国产精品久免费的黄网站| 精品久久人人做人人爰| 97超碰在线公开在线看免费| 成人免费淫片视频软件| 婷婷伊人综合| 善良的小姨在线| 亚洲免费观看视频| jizz中国少妇| 色综合导航网站| 99久久香蕉| 精品少妇人妻av免费久久洗澡| 成人av电影免费在线播放| xxxx 国产| 日韩精品丝袜在线| 97成人资源| 深夜福利成人| 久久91精品久久久久久秒播| 国产极品美女在线| 日韩一级二级三级| xxx在线免费观看| 国内一区二区在线视频观看| 国产精品女主播一区二区三区| 中文字幕国产综合| 在线观看日韩精品| 蜜桃视频网站在线观看| 91系列在线观看| 亚洲激情综合| 人妻少妇一区二区| 欧美日韩一区三区四区| 国产精品实拍| 国产一区精品在线| 视频一区视频二区中文| 日韩av毛片在线观看| 日韩写真欧美这视频| 阿v视频在线| 五月天久久综合网| 国产精品1区2区3区在线观看| 日韩精品视频免费看| 亚洲人成亚洲人成在线观看| 91精品亚洲一区在线观看| 久久人人爽人人爽人人av| 91免费在线看| 国产伦精品一区二区三区免.费 | 99久久久免费精品国产一区二区| 中国一级免费毛片| 色偷偷88888欧美精品久久久| 亚洲精品一二三**| 超碰97人人射妻| 亚洲免费电影在线| 日本大臀精品| 5566中文字幕一区二区| 久久激情一区| 欧美日韩国产精品综合| 亚洲男人av电影| 日韩三级网址| 日韩精品一区二区三区不卡| 亚洲久本草在线中文字幕| 黄色片在线看| 成人av免费在线看| 美女看a上一区| 国产成人免费观看视频| 久久精品91久久香蕉加勒比| 日韩在线麻豆| 久久人人爽人人片| 欧美探花视频资源| caoporn视频在线| 韩国黄色一级大片| 国产精品视频一二三| 天天综合天天综合|