This is the 9th day of my participation in Gwen Challenge.

preface

From the previous study, we learned about the three modules for template compilation. In this article, we’ll take a closer look at the compiler.

The role of the parser

We can’t do optimizations or generate code strings based on the AST until we parse the template into an AST. How does the parser parse the template into an AST?

Here’s a simple example:

<div>
    <p>{{name}}</p>
</div>
Copy the code

After converting to AST:

{
    tag: "div".type: 1.staticRoot: false.sttatic: false.plain: true.parent: undefined.attrsList: [].atttrsMap: {},
    children: [{tag: "p".type: 1.staticRoot: false.static: false.plain: true.parent: {tag:"div". },atttrsList: [].attrsMap: {},
            children: [{type: 2.text: "{{name}}".static: false.expression: "_s(name)"}]}]}Copy the code

The AST is not a fancy thing, but it uses ordinary objects in Javascript to describe a node. Properties in each object hold various data required by the node. For example, the parent property holds the parent node’s description object, and the children property is an array that holds the child node’s description object. For example, the type attribute indicates the type of a node and so on. When multiple independent nodes are linked together using the parent and children attributes, the tree is called an AST.

Internal operation principle

There are several child parsers inside the parser, the most important of which is the HTML parser.

The function of HTML parser is to parse HTML, it will constantly trigger various hook functions in the process of parsing HTML. These hook functions include the start tag hook function, the end tag hook function, the text hook function, and the comment hook function.

ParseHTML (template, {start(tag, attrs, unary, start, end){parseHTML(template, {start(tag, attrs, unary, start, end)}, end(tag, start, end){ Chars (text, start, end){// Parse plain text, trigger function}, comment(text, start, end){// parse to comment, trigger function}})Copy the code

Take a simple example:

<div><p>hello world</p></div>
Copy the code

When parsing the above template, start(< div>), start(

), chars(Hello World), end(

), end() are triggered in sequence.

If the start hook is triggered, we create an element node and add it to the top of the stack.

Why use a stack to store nodes?

Because of the first-in-last-out nature of the stack, its typical use is the parenthesis matching problem. Here, there are two benefits:

  1. A problem with verifying element matches.
  2. When the stack is empty, template processing is complete.

Start hook – Creates element node

const stack = []; // Temporary storage node
let currentParent; / / the parent node

function createASTElement(tag, attrs, parent){
    return {
        type: 1,
        tag,
        attrsList: attrs,
        parent,
        children
    }
}
parseHTML(template, {
    start(tag, attrs, unary, start, end) {
        let element = createASTElement(tag, attrs, currentParent);
        currentParent = element;
        stack.push(element)
    },
})
Copy the code

End hook – Pops element node

parseHTML(template, {
    end(tag, start, end) {
        const element = stack[stack.length-1];
        stack.length -= 1;
        currentParent = stack[statkc.length-1]; }})Copy the code

Chars — Creates a text node

parseHTML(template, {
    chars(text, start, end){
        let element = { type: 3, text };
        letchildren = currentParent.chilren; children.push(element); }})Copy the code

Comment — Creates a comment node

parseHTML(template, {
    comment(text){
        let element = { type: 3, text, isComment: true}
        letchildren = currentParent.chilren; children.push(element); }})Copy the code

HTML parser

Operation principle

Parsing HTML is simply an HTML template string loop. Each loop takes a small piece of the HTML template string and repeats the loop until the HTML template is truncated into an empty string.

Take the previous example as an example:

`<div>
    <p>{{name}}</p>
</div>`
Copy the code
  1. First cycle, take it out<div>To triggerstartHook function, the result of the interception
`
    <p>{{name}}</p>
</div>`
Copy the code
  1. The second loop takes out a string and firescharsHook function
`
   `
Copy the code

The interception results are as follows:

`<p>{{name}}</p>
</div>`
Copy the code
  1. Third cycle, take it out<p>To triggerstartHook function, the result of the interception
`{{name}}</p>
</div>`
Copy the code
  1. The fourth cycle is taken out{{name}}String, triggercharsThe result of intercepting the hook function:
`</p>
</div>`
Copy the code
  1. Fifth cycle, take it out</p>To triggerendThe result of intercepting the hook function:
 `
</div>`
Copy the code
  1. The sixth loop fetches a string:
`
`
Copy the code

Results after interception:

`</div>`
Copy the code
  1. Seventh cycle, take it out</div>To triggerendThe result of intercepting the hook function:
` `Copy the code

conclusion

In this article, you learned the basic workflow of the parser, as well as the basic principles of the HTML parser. We will continue to learn more about the implementation of the HTML parser, so stay tuned…