readNode reads node starting at current token pointed by the lexer's cursor (it should point to node name). After readNode returns, the lexer's cursor will point to the last token of the parsed Node. This ensures predictable cursor location independently of the EOF state. Thus code reading multiple
()
| 104 | // |
| 105 | // readNode calls readNodes if currently parsed node is a block. |
| 106 | func (ctx *parseContext) readNode() (Node, error) { |
| 107 | node := Node{} |
| 108 | node.File = ctx.File() |
| 109 | node.Line = ctx.Line() |
| 110 | |
| 111 | if ctx.Val() == "{" { |
| 112 | return node, ctx.SyntaxErr("block header") |
| 113 | } |
| 114 | |
| 115 | node.Name = ctx.Val() |
| 116 | if ok, name := ctx.isSnippet(node.Name); ok { |
| 117 | node.Name = name |
| 118 | node.Snippet = true |
| 119 | } |
| 120 | |
| 121 | var continueOnLF bool |
| 122 | for { |
| 123 | for ctx.NextArg() || (continueOnLF && ctx.NextLine()) { |
| 124 | continueOnLF = false |
| 125 | // name arg0 arg1 { |
| 126 | // # ^ called when we hit this token |
| 127 | // c0 |
| 128 | // c1 |
| 129 | // } |
| 130 | if ctx.Val() == "{" { |
| 131 | var err error |
| 132 | node.Children, err = ctx.readNodes() |
| 133 | if err != nil { |
| 134 | return node, err |
| 135 | } |
| 136 | break |
| 137 | } |
| 138 | |
| 139 | node.Args = append(node.Args, ctx.Val()) |
| 140 | } |
| 141 | |
| 142 | // Continue reading the same Node if the \ was used to escape the newline. |
| 143 | // E.g. |
| 144 | // name arg0 arg1 \ |
| 145 | // arg2 arg3 |
| 146 | if len(node.Args) != 0 && node.Args[len(node.Args)-1] == `\` { |
| 147 | last := len(node.Args) - 1 |
| 148 | node.Args[last] = node.Args[last][:len(node.Args[last])-1] |
| 149 | if len(node.Args[last]) == 0 { |
| 150 | node.Args = node.Args[:last] |
| 151 | } |
| 152 | continueOnLF = true |
| 153 | continue |
| 154 | } |
| 155 | break |
| 156 | } |
| 157 | |
| 158 | macroName, macroArgs, err := ctx.parseAsMacro(&node) |
| 159 | if err != nil { |
| 160 | return node, err |
| 161 | } |
| 162 | if macroName != "" { |
| 163 | node.Name = macroName |