<p>Go's AST nodes are clearly designed to be the result of parsing from a file, as they take token indices into a byte slice instead of string values. This is great if your AST comes from Go source code, but I'm compiling into Go and I'd like to compile into its AST for purposes of pretty-printing before I pass the result off to the compiler.</p>
<p>I don't have a good idea about how to do this with Go's standard AST package, and the alternative is to reimplement the AST package. Before I do that, I was wondering if anyone could see an easier way to work with the standard AST package, or if anyone knew of a friendly non-standard AST package?</p>
<p>Please and thank you!</p>
<hr/>**评论:**<br/><br/>sh41: <pre><p>The API of the <code>go/ast</code> package in standard library is very poorly fit for the task of generating an AST. It's only suitable for parsing Go source code into it, and generating Go source code out of it.</p>
<p>I think the most common approach at this time is to generate Go source code via <code>text/template</code> or so.</p>
<p>There may have been an attempt at making another AST package that would be friendly for generation, see <a href="https://github.com/dave/jennifer">https://github.com/dave/jennifer</a>. I have not used it so can't say how well it works.</p></pre>jerf: <pre><p>Do you have some sample code that almost works or shows what you want to do? There's at least a chance you're only off by a bit, because I do think this should generally work.</p></pre>weberc2: <pre><p>I don't have any sample code yet, but the general concept is that I have an AST for one language and I want to compile it into Go (I think we may have talked about this in the Gallium thread?) ideally by compiling first to a Go AST and then pretty printing the result. But <code>go/ast</code> assumes that you have some existing Go string that it points into (instead of an ident being a string, it's a start/end indices pair into the Go string). Make sense?</p></pre>jerf: <pre><p>I never quite finished my work with it, but my understanding is that you can just leave those parameters off and the output will be correct.</p>
<p>One of the things I don't love about Go's standard library (or its authors) is that they do sometimes overload types in a way that is unnecessary in Go, because Go has all the tools to make the correct types easy. Another example of this is in http.Cookie; you really want separate types for incoming cookies (which are really just key/value pairs sent by the browser) and outgoing cookies (which have a lot more attributes to them). The outgoing cookies could easily embed the key/value part of the incoming cookie, but they don't. As a consequence there's some validation opportunities that get passed up.</p>
<p>Similarly, while the AST nodes are all greased up to be used as if they come from a real file, I <em>think</em> you can just ignore those fields and use the output portions and it'll all work for you.</p>
<p>I was fishing for details/examples because if I could run my claims past a compiler before I said this, I would prevent myself from sticking my foot in my mouth. :)</p></pre>weberc2: <pre><p>I'll take another look, but I'm pretty sure they're necessary to represent any sort of information. Iirc, an Ident isn't a string and a token pos, it's just a token pos. So you have no way of specifying the identifier apart from the pos. It might be possible to just keep a string that is just the concatenation of all of the things that are represented by Poses; I doubt the pretty printer cares?</p></pre>TheMerovius: <pre><p><a href="https://godoc.org/go/ast#NewIdent" rel="nofollow">NewIdent</a> is for the specific purpose of doing what you want.</p>
<p>Really, I don't think you need to <em>at all</em> deal with <code>Pos</code>es or <code>FileSet</code>s, if you just generate code.</p>
<p>[edit] To illustrate: <a href="https://play.golang.org/p/kmAcSg4Kaw" rel="nofollow">https://play.golang.org/p/kmAcSg4Kaw</a></p></pre>weberc2: <pre><p>Awesome! Thanks very much!</p></pre>0xjnml: <pre><p>Generate text, not AST. Only after finished, parse it to get an AST, but only if you need the AST. To format the result, use gofmt or the go/format package - you don't need to produce the AST at all for that,</p></pre>weberc2: <pre><p>The AST is only in service of generating text. Generating text directly from my language's AST is fine for cases where my language's concepts map neatly with Go's concepts, but for more complex cases, it seems helpful to target Go's AST and then render that.</p></pre>0xjnml: <pre><p>Generating AST nodes is way more work compared to directly generating Go source code. The later is rather trivial, here's an <a href="https://github.com/cznic/irgo/blob/ccd5eb988d5b96df3a1b515d81f8d74b69e62175/irgo.go" rel="nofollow">example</a>.</p></pre>dominikh: <pre><p>To reinforce this point: <code>go/ast</code> in its current form is absolutely meant for parsing and reading; not for generating or manipulating. Trying to generate ASTs, or even just modify them in non-trivial ways, is an extreme pain.</p></pre>weberc2: <pre><p>I took a look at your example, and it doesn't look trivial. How is that much less work than generating AST nodes? Perhaps you mean <code>go/ast</code>'s implementation of AST nodes specifically, and not AST nodes generally? Or maybe you mean it's less work than writing an AST/pretty-printer library <em>and</em> compiling to it?</p></pre>0xjnml: <pre><p>Every single g.w(...) statement in the example equals a couple of lines when generating an AST directly That's what I mean by saying 'rather trivial'.</p></pre>weberc2: <pre><p>Not true, you have to <code>g.w()</code> every little syntax thing, including commas, parens, braces, etc and then you get no guarantees that the stuff you're outputting is syntactically valid Go. If you have a sane AST/pretty-print library, you can get stronger correctness guarantees and simpler code (<em>maybe</em> more lines, due to multi-line structs and the like, but far fewer statements/expressions/conditionals/etc).</p></pre>connor4312: <pre><p>I've had good luck just using <code>text/template</code>, then passing the result through <code>go/format</code>. It's very effective and easy to write, particularly for smaller or embedded tools.</p></pre>0xjnml: <pre><blockquote>
<p>Not true, you have to g.w() every little syntax thing, ...</p>
</blockquote>
<p>AST approach</p>
<pre><code> e := &ast.BinaryExpr{
X: &ast.BasicLit{
Kind: token.INT,
Value: "23",
},
Op: token.ADD,
Y: &ast.BasicLit{
Kind: token.INT,
Value: "42",
},
}
</code></pre>
<p>vs text approach</p>
<pre><code> g.w("%v + %v", op1, op2)
</code></pre></pre>TheMerovius: <pre><p><a href="https://play.golang.org/p/zejMG9K7mK" rel="nofollow">Works just fine</a>. Just leave out all the position information. The pretty-printer will deal with it.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传