HELP: How to work with (or around) Go's AST package when compiling into Go?

polaris · · 808 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Go&#39;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&#39;m compiling into Go and I&#39;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&#39;t have a good idea about how to do this with Go&#39;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&#39;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&#39;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&#39;s at least a chance you&#39;re only off by a bit, because I do think this should generally work.</p></pre>weberc2: <pre><p>I don&#39;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&#39;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&#39;t love about Go&#39;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&#39;t. As a consequence there&#39;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&#39;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&#39;ll take another look, but I&#39;m pretty sure they&#39;re necessary to represent any sort of information. Iirc, an Ident isn&#39;t a string and a token pos, it&#39;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&#39;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&#39;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&#39;s AST is fine for cases where my language&#39;s concepts map neatly with Go&#39;s concepts, but for more complex cases, it seems helpful to target Go&#39;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&#39;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&#39;t look trivial. How is that much less work than generating AST nodes? Perhaps you mean <code>go/ast</code>&#39;s implementation of AST nodes specifically, and not AST nodes generally? Or maybe you mean it&#39;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&#39;s what I mean by saying &#39;rather trivial&#39;.</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&#39;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&#39;ve had good luck just using <code>text/template</code>, then passing the result through <code>go/format</code>. It&#39;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 := &amp;ast.BinaryExpr{ X: &amp;ast.BasicLit{ Kind: token.INT, Value: &#34;23&#34;, }, Op: token.ADD, Y: &amp;ast.BasicLit{ Kind: token.INT, Value: &#34;42&#34;, }, } </code></pre> <p>vs text approach</p> <pre><code> g.w(&#34;%v + %v&#34;, 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

808 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传