Vue Internals①astを中心に  1-2compile(AST)

#はじめに
前回はcompileまでを見てきました。
今回はcompileのast部分を見ていきたいと思います。
##ASTとは？
compilerだったりデータベースだったりにも出てくるんですけど　例えばint　a=5みたいなやつをを処理しやすいデータの形にすることをparseといい、それの役割を担うやつがparserといいます。
そして、そのわかりやすいデータのことをASTといいます。
1-1でも書いた通り

```
"<div id="vue_example">
  {{message}}
</div>"
```
が
parserで

```
Object
type: 1
tag: "div"
attrsList: Array(1)
0: {name: "id", value: "vue_example", start: 5, end: 21}
length: 1
__proto__: Array(0)
attrsMap: {id: "vue_example"}
rawAttrsMap:
id: {name: "id", value: "vue_example", start: 5, end: 21}
__proto__: Object
parent: undefined
children: Array(1)
0: {type: 2, expression: ""\n  "+_s(message)+"\n"", tokens: Array(3), text: "↵  {{message}}↵", start: 22, …}
length: 1
__proto__: Array(0)
start: 0
end: 43
plain: false
attrs: Array(1)
0: {name: "id", value: ""vue_example"", dynamic: undefined, start: 5, end: 21}
length: 1
__proto__: Array(0)
__proto__: Object
```
となります。
ごちゃごちゃしていて分かりづらいですが、これからどうやってこのようなASTになるか見ていきましょう。

#compileToFunctionから
ここのキャッシュは二回目以降のもので、compileToFunctionの最後にcacheに生成物を入れています。

![compileToFunction.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/8485237d-e0c5-6556-c02e-a9fd4d4a9c4e.png)

compile

```js
 return function createCompiler (baseOptions: CompilerOptions) {
    function compile (
      template: string,
      options?: CompilerOptions
    ): CompiledResult {
      const finalOptions = Object.create(baseOptions)
      const errors = []
      const tips = []

        for (const key in options) {
          if (key !== 'modules' && key !== 'directives') {
            finalOptions[key] = options[key]
          }
        }
      

      finalOptions.warn = warn

      const compiled = baseCompile(template.trim(), finalOptions)
      if (process.env.NODE_ENV !== 'production') {
        detectErrors(compiled.ast, warn)
      }
      compiled.errors = errors
      compiled.tips = tips
      return compiled
    }

    return {
      compile,
      compileToFunctions: createCompileToFunctionFn(compile)
    }
  }
```

baseOptionsは

```
expectHTML: true
modules: (3) [{…}, {…}, {…}]
directives: {model: ƒ, text: ƒ, html: ƒ}
isPreTag: ƒ (tag)
isUnaryTag: ƒ (val)
mustUseProp: ƒ (tag, type, attr)
canBeLeftOpenTag: ƒ (val)
isReservedTag: ƒ (tag)
getTagNamespace: ƒ getTagNamespace(tag)
staticKeys: "staticClass,staticStyle"
__proto__: Object
```
となっていてこれをObject.createでfinalOptionとする
そして、引数のoptionをfinalOptionとmerge
してbaseCompileへ

```js
var createCompiler = createCompilerCreator(function baseCompile (
    template,
    options
  ) {
    var ast = parse(template.trim(), options);
    if (options.optimize !== false) {
      optimize(ast, options);
    }
    var code = generate(ast, options);
    return {
      ast: ast,
      render: code.render,
      staticRenderFns: code.staticRenderFns
    }
  });
```

parse

```js
function parse (
    template,
    options
  ) {
    warn$2 = options.warn || baseWarn;

    platformIsPreTag = options.isPreTag || no;
    platformMustUseProp = options.mustUseProp || no;
    platformGetTagNamespace = options.getTagNamespace || no;
    var isReservedTag = options.isReservedTag || no;
    maybeComponent = function (el) { return !!el.component || !isReservedTag(el.tag); };

    transforms = pluckModuleFunction(options.modules, 'transformNode');
    preTransforms = pluckModuleFunction(options.modules, 'preTransformNode');
    postTransforms = pluckModuleFunction(options.modules, 'postTransformNode');

    delimiters = options.delimiters;

    var stack = [];
    var preserveWhitespace = options.preserveWhitespace !== false;
    var whitespaceOption = options.whitespace;
    var root;
    var currentParent;
    var inVPre = false;
    var inPre = false;
    var warned = false;

    function warnOnce (msg, range) {
      if (!warned) {
        warned = true;
        warn$2(msg, range);
      }
    }

    function closeElement (element) {
      trimEndingWhitespace(element);
      if (!inVPre && !element.processed) {
        element = processElement(element, options);
      }
      // tree management
      if (!stack.length && element !== root) {
        // allow root elements with v-if, v-else-if and v-else
        if (root.if && (element.elseif || element.else)) {
          {
            checkRootConstraints(element);
          }
          addIfCondition(root, {
            exp: element.elseif,
            block: element
          });
        } else {
          warnOnce(
            "Component template should contain exactly one root element. " +
            "If you are using v-if on multiple elements, " +
            "use v-else-if to chain them instead.",
            { start: element.start }
          );
        }
      }
      if (currentParent && !element.forbidden) {
        if (element.elseif || element.else) {
          processIfConditions(element, currentParent);
        } else {
          if (element.slotScope) {
            // scoped slot
            // keep it in the children list so that v-else(-if) conditions can
            // find it as the prev node.
            var name = element.slotTarget || '"default"'
            ;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element;
          }
          currentParent.children.push(element);
          element.parent = currentParent;
        }
      }

      // final children cleanup
      // filter out scoped slots
      element.children = element.children.filter(function (c) { return !(c).slotScope; });
      // remove trailing whitespace node again
      trimEndingWhitespace(element);

      // check pre state
      if (element.pre) {
        inVPre = false;
      }
      if (platformIsPreTag(element.tag)) {
        inPre = false;
      }
      // apply post-transforms
      for (var i = 0; i < postTransforms.length; i++) {
        postTransforms[i](element, options);
      }
    }

    function trimEndingWhitespace (el) {
      // remove trailing whitespace node
      if (!inPre) {
        var lastNode;
        while (
          (lastNode = el.children[el.children.length - 1]) &&
          lastNode.type === 3 &&
          lastNode.text === ' '
        ) {
          el.children.pop();
        }
      }
    }

    function checkRootConstraints (el) {
      if (el.tag === 'slot' || el.tag === 'template') {
        warnOnce(
          "Cannot use <" + (el.tag) + "> as component root element because it may " +
          'contain multiple nodes.',
          { start: el.start }
        );
      }
      if (el.attrsMap.hasOwnProperty('v-for')) {
        warnOnce(
          'Cannot use v-for on stateful component root element because ' +
          'it renders multiple elements.',
          el.rawAttrsMap['v-for']
        );
      }
    }
    
    //optionがすごい長い
    parseHTML(template, {
      warn: warn$2,
      expectHTML: options.expectHTML,
      isUnaryTag: options.isUnaryTag,
      canBeLeftOpenTag: options.canBeLeftOpenTag,
      shouldDecodeNewlines: options.shouldDecodeNewlines,
      shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
      shouldKeepComment: options.comments,
      outputSourceRange: options.outputSourceRange,
      start: function start (tag, attrs, unary, start$1, end) {
        // check namespace.
        // inherit parent ns if there is one
        var ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag);

        // handle IE svg bug
        /* istanbul ignore if */
        if (isIE && ns === 'svg') {
          attrs = guardIESVGBug(attrs);
        }

        var element = createASTElement(tag, attrs, currentParent);
        if (ns) {
          element.ns = ns;
        }

        {
          if (options.outputSourceRange) {
            element.start = start$1;
            element.end = end;
            element.rawAttrsMap = element.attrsList.reduce(function (cumulated, attr) {
              cumulated[attr.name] = attr;
              return cumulated
            }, {});
          }
          attrs.forEach(function (attr) {
            if (invalidAttributeRE.test(attr.name)) {
              warn$2(
                "Invalid dynamic argument expression: attribute names cannot contain " +
                "spaces, quotes, <, >, / or =.",
                {
                  start: attr.start + attr.name.indexOf("["),
                  end: attr.start + attr.name.length
                }
              );
            }
          });
        }

        if (isForbiddenTag(element) && !isServerRendering()) {
          element.forbidden = true;
          warn$2(
            'Templates should only be responsible for mapping the state to the ' +
            'UI. Avoid placing tags with side-effects in your templates, such as ' +
            "<" + tag + ">" + ', as they will not be parsed.',
            { start: element.start }
          );
        }

        // apply pre-transforms
        for (var i = 0; i < preTransforms.length; i++) {
          element = preTransforms[i](element, options) || element;
        }

        if (!inVPre) {
          processPre(element);
          if (element.pre) {
            inVPre = true;
          }
        }
        if (platformIsPreTag(element.tag)) {
          inPre = true;
        }
        if (inVPre) {
          processRawAttrs(element);
        } else if (!element.processed) {
          // structural directives
          processFor(element);
          processIf(element);
          processOnce(element);
        }

        if (!root) {
          root = element;
          {
            checkRootConstraints(root);
          }
        }

        if (!unary) {
          currentParent = element;
          stack.push(element);
        } else {
          closeElement(element);
        }
      },

      end: function end (tag, start, end$1) {
        var element = stack[stack.length - 1];
        // pop stack
        stack.length -= 1;
        currentParent = stack[stack.length - 1];
        if (options.outputSourceRange) {
          element.end = end$1;
        }
        closeElement(element);
      },

      chars: function chars (text, start, end) {
        if (!currentParent) {
          {
            if (text === template) {
              warnOnce(
                'Component template requires a root element, rather than just text.',
                { start: start }
              );
            } else if ((text = text.trim())) {
              warnOnce(
                ("text \"" + text + "\" outside root element will be ignored."),
                { start: start }
              );
            }
          }
          return
        }
        // IE textarea placeholder bug
        /* istanbul ignore if */
        if (isIE &&
          currentParent.tag === 'textarea' &&
          currentParent.attrsMap.placeholder === text
        ) {
          return
        }
        var children = currentParent.children;
        if (inPre || text.trim()) {
          text = isTextTag(currentParent) ? text : decodeHTMLCached(text);
        } else if (!children.length) {
          // remove the whitespace-only node right after an opening tag
          text = '';
        } else if (whitespaceOption) {
          if (whitespaceOption === 'condense') {
            // in condense mode, remove the whitespace node if it contains
            // line break, otherwise condense to a single space
            text = lineBreakRE.test(text) ? '' : ' ';
          } else {
            text = ' ';
          }
        } else {
          text = preserveWhitespace ? ' ' : '';
        }
        if (text) {
          if (!inPre && whitespaceOption === 'condense') {
            // condense consecutive whitespaces into single space
            text = text.replace(whitespaceRE$1, ' ');
          }
          var res;
          var child;
          if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
            child = {
              type: 2,
              expression: res.expression,
              tokens: res.tokens,
              text: text
            };
          } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
            child = {
              type: 3,
              text: text
            };
          }
          if (child) {
            if (options.outputSourceRange) {
              child.start = start;
              child.end = end;
            }
            children.push(child);
          }
        }
      },
      comment: function comment (text, start, end) {
        // adding anyting as a sibling to the root node is forbidden
        // comments should still be allowed, but ignored
        if (currentParent) {
          var child = {
            type: 3,
            text: text,
            isComment: true
          };
          if (options.outputSourceRange) {
            child.start = start;
            child.end = end;
          }
          currentParent.children.push(child);
        }
      }
    });

    //parseのreturn
    return root
  }
```

長いけど実際重要なのはparseHTML

parseHTML
ここでastを作っていきます、まずどのような処理が行われるかを説明します。
![parseHTML説明.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/5df5bc9f-19da-363c-ffd5-ca1d5073023d.png)

このように与えられたhtmlを切り取っていって徐々にastを生成していく感じです。
今回は三巡目までなので順番に見ていきましょう。

parseHTML一巡目

```js
 function parseHTML (html, options) {
    var stack = [];
    var expectHTML = options.expectHTML;
    var isUnaryTag$$1 = options.isUnaryTag || no;
    var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no;
    var index = 0;
    var last, lastTag;
    while (html) {
      last = html;
      // Make sure we're not in a plaintext content element like script/style
      if (!lastTag || !isPlainTextElement(lastTag)) {
        var textEnd = html.indexOf('<');
        if (textEnd === 0) {
          // Comment:
          if (comment.test(html)) {
            var commentEnd = html.indexOf('-->');

            if (commentEnd >= 0) {
              if (options.shouldKeepComment) {
                options.comment(html.substring(4, commentEnd), index, index + commentEnd + 3);
              }
              advance(commentEnd + 3);
              continue
            }
          }

          // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
          if (conditionalComment.test(html)) {
            var conditionalEnd = html.indexOf(']>');

            if (conditionalEnd >= 0) {
              advance(conditionalEnd + 2);
              continue
            }
          }

          // Doctype:
          var doctypeMatch = html.match(doctype);
          if (doctypeMatch) {
            advance(doctypeMatch[0].length);
            continue
          }

          // End tag:
          var endTagMatch = html.match(endTag);
          if (endTagMatch) {
            var curIndex = index;
            advance(endTagMatch[0].length);
            parseEndTag(endTagMatch[1], curIndex, index);
            continue
          }

          // Start tag:
          var startTagMatch = parseStartTag();
          if (startTagMatch) {
            handleStartTag(startTagMatch);
            if (shouldIgnoreFirstNewline(startTagMatch.tagName, html)) {
              advance(1);
            }
            continue//ここで二巡目へ
          }
        }
```
一巡目はtextEnd=0となるのでif (textEnd === 0)へ、html.matchは正規表現であてはまるものを探しています。当てはまらなかったのでStartTagの処理がされます。

parseStartTag
![parseStartTag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/85faeca7-101f-d600-3cda-708652f0ece6.png)


html.match(startTagOpen)の結果

```
0: "<div"
1: "div"
index: 0
input: "<div id="vue_example">↵  {{message}}↵</div>"
```
でこれをつかってadvanceでhtml.substringでmatch↓分を切り取るとhtmlは

```
" id="vue_example">
  {{message}}
</div>"
```
となる

whileでhtml.match(attribute)がmatchしてattrが

```
0: " id="vue_example""
1: "id"
2: "="
3: "vue_example"
4: undefined
5: undefined
index: 0
input: " id="vue_example">↵  {{message}}↵</div>"
groups: undefined
start: 4
length: 6
```
となる、attr.startはindexで<divの分advanceしたのでindexは４

" id="vue_example""分advanceで進めて、
htmlは

```
">
  {{message}}
</div>"
```
indexは21となる

whileループの最後でmatchにattrを入れる。
whileループでendにマッチしたらwhileを抜けて、endでもadvanceをする。
最終的にmatchは

```
tagName: "div"
attrs: Array(1)
0: (6) [" id="vue_example"", "id", "=", "vue_example", undefined, undefined, index: 0, input: " id="vue_example">↵  {{message}}↵</div>", groups: undefined, start: 4, end: 21]
length: 1
__proto__: Array(0)
start: 0
unarySlash: ""
end: 22
__proto__: Object
```
となり、これがstartTagMatchに入る

今度はhandleStartTagでhandleStartTagは返ってきたmatchを見やすい形に成形してあげることと、あとはstackにpushしてあげること、このpushしたやつはparseEndtagで使う

![handleStartTag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/e6e64322-c559-483c-582f-d603baea4c91.png)

matchからattrに成形していて、option.startへ
option.startはparseHTMLのoptionで渡した奴

createASTElement

```js
function createASTElement (
    tag,
    attrs,
    parent
  ) {
    return {
      type: 1,
      tag: tag,
      attrsList: attrs,
      attrsMap: makeAttrsMap(attrs),
      rawAttrsMap: {},
      parent: parent,
      children: []
    }
  }
```

このmakeAttrsMapではattrsのname:id、value:"vue_example"をmapにして
id:vue_exampleとしている
![makeAttrsMap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/07947795-1243-83cb-c1d7-de70d25c9d62.png)

startに戻ってelementにはcreateASTElementの返り値が入り、さらにstart,end,rawAttrsMapが追加される。
process～系はまたv-系列のところでやります。
最後にrootとcurrentElementが空なのでelementをその二つに入れて,startは終わり

一巡目終了時
index=22
html

```
"
  {{message}}
</div>"
```
これでhandleStartTagも終わり、ここまでで一巡目が終了

parseHTML二巡目

```js
// Start tag:
          var startTagMatch = parseStartTag();
          if (startTagMatch) {
            handleStartTag(startTagMatch);
            if (shouldIgnoreFirstNewline(startTagMatch.tagName, html)) {
              advance(1);
            }
            continue
          }
        }
        
       //ここから二巡目で使うところ
        var text = (void 0), rest = (void 0), next = (void 0);
        if (textEnd >= 0) {
          rest = html.slice(textEnd);
          while (
            !endTag.test(rest) &&
            !startTagOpen.test(rest) &&
            !comment.test(rest) &&
            !conditionalComment.test(rest)
          ) {
            // < in plain text, be forgiving and treat it as text
            next = rest.indexOf('<', 1);
            if (next < 0) { break }
            textEnd += next;
            rest = html.slice(textEnd);
          }
          text = html.substring(0, textEnd);
        }

        if (textEnd < 0) {
          text = html;
        }

        if (text) {
          advance(text.length);
        }

        if (options.chars && text) {
          options.chars(text, index - text.length, index);
        }
      }
```
handleStartTagでlastTagはdivになっている。
textEndは{{message}}</div>の<にヒットして15となり0ではないので
var text = (void 0), rest = (void 0), next = (void 0);のところから始める。
(void　0)というのはundefinedと同じだと思っていい、undefinedが上書きできるから(void　0)を使っていたらしいが、今はもうundefinedで大丈夫

まずrestにhtml.substringで</div>が入り、textには"
  {{message}}
"が入る

textの分だけadvanceで進めて
index=37
html=```"</div>"```

option.chars
charsは前巡startのcreateASTElementで作ったcurrentParent=elementにchildrenを作ることと、
parseTextでmountComponentようにtextを_s()みたいな形に成形してあげること
謎の_sとか_cは関数で、mountComponentで使われるので後でやりましょう。

![char11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/b707731e-0bb2-05cd-a962-92f60101ebb4.png)
![chars12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/0d8a9f7b-7cea-3159-1ca2-5a32904d8383.png)


parseTextは"↵  {{message}}↵"を成形していく、まずmatch=tagRE.exec(text)で("↵  ")をtokenに入れる
matchは

```
0: "{{message}}"
1: "message"
index: 3
input: "↵  {{message}}↵"
groups: undefined
length: 2
```
![parseText1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/fd60ac84-1ba2-2655-3a5f-4ca345e76546.png)
parseTextから帰ってくるresは

```
expression: ""\n  "+_s(message)+"\n""
tokens: Array(3)
0: "↵  "
1: {@binding: "message"}
2: "↵"
length: 3
__proto__: Array(0)
__proto__: Object
```

となる
parseFilterでは括弧とかブラケットとか処理しているんだけど今回は関係ないので飛ばす
charsに戻って、作ったchildをchildrenにpushしてcharsは終わり
ここまでで二巡目終了
二巡目終了時
index=37
html=```"</div>"```

parseHTML三巡目
再びtextendが0なので一巡目と同じところへ
ただ、endTagにマッチする

```js
//End tag:
          var endTagMatch = html.match(endTag);
          if (endTagMatch) {
            var curIndex = index;
            advance(endTagMatch[0].length);
            parseEndTag(endTagMatch[1], curIndex, index);
            continue
          }
```
advanceでindex=43、html=""となる

parseEndTag
parseEndTagではhandleStartTagでstackにpushしたやつを使って対応するtagをoption.endで処理していく、終わったらstackから取り除く
今回はdiv一組しかないので一回やって終わり

end→closeElement→processElementとやっていく、processElementでattrをelementに追加することだけ今回は押さえておく

```js
function parseEndTag (tagName, start, end) {
      var pos, lowerCasedTagName;
      if (start == null) { start = index; }
      if (end == null) { end = index; }

      // Find the closest opened tag of the same type
      if (tagName) {
        lowerCasedTagName = tagName.toLowerCase();
        for (pos = stack.length - 1; pos >= 0; pos--) {
          if (stack[pos].lowerCasedTag === lowerCasedTagName) {
            break
          }
        }
      } else {
        // If no tag name is provided, clean shop
        pos = 0;
      }

      if (pos >= 0) {
        // Close all the open elements, up the stack
        for (var i = stack.length - 1; i >= pos; i--) {
          if (i > pos || !tagName &&
            options.warn
          ) {
            options.warn(
              ("tag <" + (stack[i].tag) + "> has no matching end tag."),
              { start: stack[i].start, end: stack[i].end }
            );
          }
          if (options.end) {
            options.end(stack[i].tag, start, end);
          }
        }

        // Remove the open elements from the stack
        stack.length = pos;
        lastTag = pos && stack[pos - 1].tag;
      } else if (lowerCasedTagName === 'br') {
        if (options.start) {
          options.start(tagName, [], true, start, end);
        }
      } else if (lowerCasedTagName === 'p') {
        if (options.start) {
          options.start(tagName, [], false, start, end);
        }
        if (options.end) {
          options.end(tagName, start, end);
        }
      }
    }
  }
```

```js
  end: function end (tag, start, end$1) {
        var element = stack[stack.length - 1];
        // pop stack
        stack.length -= 1;
        currentParent = stack[stack.length - 1];
        if (options.outputSourceRange) {
          element.end = end$1;
        }
        closeElement(element);
      }
```

```js
function closeElement (element) {
      trimEndingWhitespace(element);
      if (!inVPre && !element.processed) {
        element = processElement(element, options);
      }
      // final children cleanup
      // filter out scoped slots
      element.children = element.children.filter(function (c) { return !(c).slotScope; });
      // remove trailing whitespace node again
      trimEndingWhitespace(element);

    }
```
processAttrはdirectiveがattrに含まれるか含まれないかで処理が変わる、今回は含まれないのでparseText
ただ今回はparseTextは何もしないので、単にaddAttr
elは

```
type: 1
tag: "div"
attrsList: Array(1)
0: {name: "id", value: "vue_example", start: 5, end: 21}
length: 1
__proto__: Array(0)
attrsMap: {id: "vue_example"}
rawAttrsMap: {id: {…}}
parent: undefined
children: [{…}]
start: 0
end: 43
plain: false
__proto__: Object
```
```js
function processAttrs (el) {
    var list = el.attrsList;
    var i, l, name, rawName, value, modifiers, syncGen, isDynamic;
    for (i = 0, l = list.length; i < l; i++) {
      name = rawName = list[i].name;
      value = list[i].value;
      if (dirRE.test(name)) {
       
      } else {
        // literal attribute
        {
          var res = parseText(value, delimiters);
          if (res) {
            warn$2(
              name + "=\"" + value + "\": " +
              'Interpolation inside attributes has been removed. ' +
              'Use v-bind or the colon shorthand instead. For example, ' +
              'instead of <div id="{{ val }}">, use <div :id="val">.',
              list[i]
            );
          }
        }
        addAttr(el, name, JSON.stringify(value), list[i]);
        // #6887 firefox doesn't update muted state if set via attribute
        // even immediately after element creation
        if (!el.component &&
            name === 'muted' &&
            platformMustUseProp(el.tag, el.attrsMap.type, name)) {
          addProp(el, name, 'true', list[i]);
        }
      }
    }
  }
```
これで三巡目も終わって、parseHTMLは終わります。

ここまでやって特に三巡目の操作は部分的なelementをいろいろいじっていただけで、本元のASTにどう影響していたか分かりづらかったんですが、一巡目のoption.startでroot=elementで、しかもそのelementをstackにpush、そのelementを三巡目で使っていたので結果としてrootをいじっていたということみたいです。。
そしてそのroot=elementがparseから返り,
baseCompileにもどってast=parse()でast=rootとなります。

最終的なast=root

```
Object
type: 1
tag: "div"
attrsList: Array(1)
0: {name: "id", value: "vue_example", start: 5, end: 21}
length: 1
__proto__: Array(0)
attrsMap: {id: "vue_example"}
rawAttrsMap:
id: {name: "id", value: "vue_example", start: 5, end: 21}
__proto__: Object
parent: undefined
children: Array(1)
0: {type: 2, expression: ""\n  "+_s(message)+"\n"", tokens: Array(3), text: "↵  {{message}}↵", start: 22, …}
length: 1
__proto__: Array(0)
start: 0
end: 43
plain: false
attrs: Array(1)
0: {name: "id", value: ""vue_example"", dynamic: undefined, start: 5, end: 21}
length: 1
__proto__: Array(0)
__proto__: Object
```

ここまででAST部分は終了です。次回はoptimazeを飛ばして、generateに入っていきたいと思います。