• 解析HTML字符串成AST树


    1. 如何将一个字符传转换成一个AST树结构。

    直接上代码:

     
    
    const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
    const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`
    const qnameCapture = `((?:${ncname}\\:)?${ncname})`
    // 匹配 <div
    const startTagOpen = new RegExp(`^<${qnameCapture}`)
    // 匹配 > />
    const startTagClose = /^\s*(\/?)>/
    // 匹配 </div>
    const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
    const doctype = /^<!DOCTYPE [^>]+>/i
    
    //注意树形的html 只能有一个根节点
    function parseHtmlToAst(html){
    
    	let text,root,currentParent,stack=[];
    	
    	while(html){
    		let textEnd=html.indexOf("<");
    		if(textEnd===0){
    			//查找开始tag
    			const startTagMatch=parseStartTag();
    			if(startTagMatch){
    				//生成AST树
    				start(startTagMatch.tagName,startTagMatch.attrs);
    				continue;
    			}
    			//查找结束标签
    			const endTagMatch=html.match(endTag);
    			if(endTagMatch){
    				advance(endTagMatch[0].length);
    				//构造ast树
    				end(endTagMatch[1]);
    				continue;
    			}
    		
    		}
    		//文本节点
    		if(textEnd>0){
    			text=html.substring(0,textEnd);
    		}
    		if(text){
    			//截取字符串
    			advance(text.length);
    			chars(text);
    		}
    		
    		 
    	}
    	//截些开始标记
    	function parseStartTag(){
    		const start=html.match(startTagOpen);
    		
    		let end,attr;
    		//找到开始标记
    		if(start){
    			const match={
    				tagName:start[1],
    				attrs:[]
    			}
    
    			advance(start[0].length)
    			//配置属性
    			while(!(end=html.match(startTagClose)) && (attr=html.match(attribute))){
    			
    				match.attrs.push({
    					name:attr[1],
    					value: attr[3] || attr[4] || attr[5]
    				})
    				advance(attr[0].length);
    				 
    			}
    			//匹配结束字符 > 或 />
    			if(end){
    				advance(end[0].length);
            		return match;
    			}
    		}
    		
    	}
    	
    	//截取字符串
    	function advance (n) {
        		html = html.substring(n)
      }
    	//构造AST树形
    	function start(tagName,attrs){
    		const element=createAstElement(tagName,attrs);
    		
    		if(!root){
    			root=element;
    		}
    		currentParent=element;
    		stack.push(element);
    	}
    	
    	//结束钩爪树形
    	function end(tagName){
    		const element=stack.pop();
    		currentParent=stack[stack.length-1];
    		if(currentParent){
    			element.parent=currentParent;
    			currentParent.children.push(element);
    		}
    
    	}
    	//处理文本节点
    	function chars(text){
    		text=text.trim();
    		if(text.length>0){
    			currentParent.children.push({
    				type:3,
    				text
    			})
    		}
    		 
    	}
    	
    	
    	function createAstElement(tagName,attrs){
    		return {
    			tag: tagName,
    			type: 1,
    			children:[],
    			attrs,
    			parent
    		
    		}
    	}
    	return root;
    
    }
    
    let html=`<div id="app" style="color:red;200px">
    	你好:{{name}}
    	<span class="text" style="color:green">{{age}}
    	</span>
    	</div>
    `;
    
    
    let root=parseHtmlToAst(html);
    console.info(root)
    
    
    
    

    代码的逻辑是:
    对字符串的处理,从头开始处理,先找 < 开头的字符,如果找到则用 正则表达式查找 <div 的标签,找到后,截取 后面的字符串,然后循环查找 属性,直到 找到 > 或 />字符为止,找到让后截取后面的字符串。

    中间用到了堆栈作为树节点作为串联。

    2.构造结果

    image

  • 相关阅读:
    Redis常用数据类型及应用场景之Set
    Redis常用数据类型及应用场景之List
    Redis常用数据类型及应用场景之Hash
    exists & not exists
    oracle 中 dblink 的简单使用
    DockerCompose之数据卷Volume
    DockerCompose之常见编排脚本
    160308-学习State Pattern Actor
    12.3-框架维护
    12.2-机器人协作系统
  • 原文地址:https://www.cnblogs.com/yg_zhang/p/16487466.html
Copyright © 2020-2023  润新知