compiler.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. package amber
  2. import (
  3. "bytes"
  4. "container/list"
  5. "errors"
  6. "fmt"
  7. "go/ast"
  8. gp "go/parser"
  9. gt "go/token"
  10. "html/template"
  11. "io"
  12. "net/http"
  13. "os"
  14. "path/filepath"
  15. "reflect"
  16. "regexp"
  17. "sort"
  18. "strconv"
  19. "strings"
  20. "github.com/eknkc/amber/parser"
  21. )
  22. var builtinFunctions = [...]string{
  23. "len",
  24. "print",
  25. "printf",
  26. "println",
  27. "urlquery",
  28. "js",
  29. "json",
  30. "index",
  31. "html",
  32. "unescaped",
  33. }
  34. const (
  35. dollar = "__DOLLAR__"
  36. )
  37. // Compiler is the main interface of Amber Template Engine.
  38. // In order to use an Amber template, it is required to create a Compiler and
  39. // compile an Amber source to native Go template.
  40. // compiler := amber.New()
  41. // // Parse the input file
  42. // err := compiler.ParseFile("./input.amber")
  43. // if err == nil {
  44. // // Compile input file to Go template
  45. // tpl, err := compiler.Compile()
  46. // if err == nil {
  47. // // Check built in html/template documentation for further details
  48. // tpl.Execute(os.Stdout, somedata)
  49. // }
  50. // }
  51. type Compiler struct {
  52. // Compiler options
  53. Options
  54. filename string
  55. node parser.Node
  56. indentLevel int
  57. newline bool
  58. buffer *bytes.Buffer
  59. tempvarIndex int
  60. mixins map[string]*parser.Mixin
  61. }
  62. // New creates and initialize a new Compiler.
  63. func New() *Compiler {
  64. compiler := new(Compiler)
  65. compiler.filename = ""
  66. compiler.tempvarIndex = 0
  67. compiler.PrettyPrint = true
  68. compiler.Options = DefaultOptions
  69. compiler.mixins = make(map[string]*parser.Mixin)
  70. return compiler
  71. }
  72. // Options defines template output behavior.
  73. type Options struct {
  74. // Setting if pretty printing is enabled.
  75. // Pretty printing ensures that the output html is properly indented and in human readable form.
  76. // If disabled, produced HTML is compact. This might be more suitable in production environments.
  77. // Default: true
  78. PrettyPrint bool
  79. // Setting if line number emitting is enabled
  80. // In this form, Amber emits line number comments in the output template. It is usable in debugging environments.
  81. // Default: false
  82. LineNumbers bool
  83. // Setting the virtual filesystem to use
  84. // If set, will attempt to use a virtual filesystem provided instead of os.
  85. // Default: nil
  86. VirtualFilesystem http.FileSystem
  87. }
  88. // DirOptions is used to provide options to directory compilation.
  89. type DirOptions struct {
  90. // File extension to match for compilation
  91. Ext string
  92. // Whether or not to walk subdirectories
  93. Recursive bool
  94. }
  95. // DefaultOptions sets pretty-printing to true and line numbering to false.
  96. var DefaultOptions = Options{true, false, nil}
  97. // DefaultDirOptions sets expected file extension to ".amber" and recursive search for templates within a directory to true.
  98. var DefaultDirOptions = DirOptions{".amber", true}
  99. // Compile parses and compiles the supplied amber template string. Returns corresponding Go Template (html/templates) instance.
  100. // Necessary runtime functions will be injected and the template will be ready to be executed.
  101. func Compile(input string, options Options) (*template.Template, error) {
  102. comp := New()
  103. comp.Options = options
  104. err := comp.Parse(input)
  105. if err != nil {
  106. return nil, err
  107. }
  108. return comp.Compile()
  109. }
  110. // Compile parses and compiles the supplied amber template []byte.
  111. // Returns corresponding Go Template (html/templates) instance.
  112. // Necessary runtime functions will be injected and the template will be ready to be executed.
  113. func CompileData(input []byte, filename string, options Options) (*template.Template, error) {
  114. comp := New()
  115. comp.Options = options
  116. err := comp.ParseData(input, filename)
  117. if err != nil {
  118. return nil, err
  119. }
  120. return comp.Compile()
  121. }
  122. // MustCompile is the same as Compile, except the input is assumed error free. If else, panic.
  123. func MustCompile(input string, options Options) *template.Template {
  124. t, err := Compile(input, options)
  125. if err != nil {
  126. panic(err)
  127. }
  128. return t
  129. }
  130. // CompileFile parses and compiles the contents of supplied filename. Returns corresponding Go Template (html/templates) instance.
  131. // Necessary runtime functions will be injected and the template will be ready to be executed.
  132. func CompileFile(filename string, options Options) (*template.Template, error) {
  133. comp := New()
  134. comp.Options = options
  135. err := comp.ParseFile(filename)
  136. if err != nil {
  137. return nil, err
  138. }
  139. return comp.Compile()
  140. }
  141. // MustCompileFile is the same as CompileFile, except the input is assumed error free. If else, panic.
  142. func MustCompileFile(filename string, options Options) *template.Template {
  143. t, err := CompileFile(filename, options)
  144. if err != nil {
  145. panic(err)
  146. }
  147. return t
  148. }
  149. // CompileDir parses and compiles the contents of a supplied directory path, with options.
  150. // Returns a map of a template identifier (key) to a Go Template instance.
  151. // Ex: if the dirname="templates/" had a file "index.amber" the key would be "index"
  152. // If option for recursive is True, this parses every file of relevant extension
  153. // in all subdirectories. The key then is the path e.g: "layouts/layout"
  154. func CompileDir(dirname string, dopt DirOptions, opt Options) (map[string]*template.Template, error) {
  155. dir, err := os.Open(dirname)
  156. if err != nil && opt.VirtualFilesystem != nil {
  157. vdir, err := opt.VirtualFilesystem.Open(dirname)
  158. if err != nil {
  159. return nil, err
  160. }
  161. dir = vdir.(*os.File)
  162. } else if err != nil {
  163. return nil, err
  164. }
  165. defer dir.Close()
  166. files, err := dir.Readdir(0)
  167. if err != nil {
  168. return nil, err
  169. }
  170. compiled := make(map[string]*template.Template)
  171. for _, file := range files {
  172. // filename is for example "index.amber"
  173. filename := file.Name()
  174. fileext := filepath.Ext(filename)
  175. // If recursive is true and there's a subdirectory, recurse
  176. if dopt.Recursive && file.IsDir() {
  177. dirpath := filepath.Join(dirname, filename)
  178. subcompiled, err := CompileDir(dirpath, dopt, opt)
  179. if err != nil {
  180. return nil, err
  181. }
  182. // Copy templates from subdirectory into parent template mapping
  183. for k, v := range subcompiled {
  184. // Concat with parent directory name for unique paths
  185. key := filepath.Join(filename, k)
  186. compiled[key] = v
  187. }
  188. } else if fileext == dopt.Ext {
  189. // Otherwise compile the file and add to mapping
  190. fullpath := filepath.Join(dirname, filename)
  191. tmpl, err := CompileFile(fullpath, opt)
  192. if err != nil {
  193. return nil, err
  194. }
  195. // Strip extension
  196. key := filename[0 : len(filename)-len(fileext)]
  197. compiled[key] = tmpl
  198. }
  199. }
  200. return compiled, nil
  201. }
  202. // MustCompileDir is the same as CompileDir, except input is assumed error free. If else, panic.
  203. func MustCompileDir(dirname string, dopt DirOptions, opt Options) map[string]*template.Template {
  204. m, err := CompileDir(dirname, dopt, opt)
  205. if err != nil {
  206. panic(err)
  207. }
  208. return m
  209. }
  210. // Parse given raw amber template string.
  211. func (c *Compiler) Parse(input string) (err error) {
  212. defer func() {
  213. if r := recover(); r != nil {
  214. err = errors.New(r.(string))
  215. }
  216. }()
  217. parser, err := parser.StringParser(input)
  218. if err != nil {
  219. return
  220. }
  221. c.node = parser.Parse()
  222. return
  223. }
  224. // Parse given raw amber template bytes, and the filename that belongs with it
  225. func (c *Compiler) ParseData(input []byte, filename string) (err error) {
  226. defer func() {
  227. if r := recover(); r != nil {
  228. err = errors.New(r.(string))
  229. }
  230. }()
  231. parser, err := parser.ByteParser(input)
  232. parser.SetFilename(filename)
  233. if c.VirtualFilesystem != nil {
  234. parser.SetVirtualFilesystem(c.VirtualFilesystem)
  235. }
  236. if err != nil {
  237. return
  238. }
  239. c.node = parser.Parse()
  240. return
  241. }
  242. // ParseFile parses the amber template file in given path.
  243. func (c *Compiler) ParseFile(filename string) (err error) {
  244. defer func() {
  245. if r := recover(); r != nil {
  246. err = errors.New(r.(string))
  247. }
  248. }()
  249. p, err := parser.FileParser(filename)
  250. if err != nil && c.VirtualFilesystem != nil {
  251. p, err = parser.VirtualFileParser(filename, c.VirtualFilesystem)
  252. }
  253. if err != nil {
  254. return
  255. }
  256. c.node = p.Parse()
  257. c.filename = filename
  258. return
  259. }
  260. // Compile amber and create a Go Template (html/templates) instance.
  261. // Necessary runtime functions will be injected and the template will be ready to be executed.
  262. func (c *Compiler) Compile() (*template.Template, error) {
  263. return c.CompileWithName(filepath.Base(c.filename))
  264. }
  265. // CompileWithName is the same as Compile, but allows to specify a name for the template.
  266. func (c *Compiler) CompileWithName(name string) (*template.Template, error) {
  267. return c.CompileWithTemplate(template.New(name))
  268. }
  269. // CompileWithTemplate is the same as Compile but allows to specify a template.
  270. func (c *Compiler) CompileWithTemplate(t *template.Template) (*template.Template, error) {
  271. data, err := c.CompileString()
  272. if err != nil {
  273. return nil, err
  274. }
  275. tpl, err := t.Funcs(FuncMap).Parse(data)
  276. if err != nil {
  277. return nil, err
  278. }
  279. return tpl, nil
  280. }
  281. // CompileWriter compiles amber and writes the Go Template source into given io.Writer instance.
  282. // You would not be using this unless debugging / checking the output. Please use Compile
  283. // method to obtain a template instance directly.
  284. func (c *Compiler) CompileWriter(out io.Writer) (err error) {
  285. defer func() {
  286. if r := recover(); r != nil {
  287. err = errors.New(r.(string))
  288. }
  289. }()
  290. c.buffer = new(bytes.Buffer)
  291. c.visit(c.node)
  292. if c.buffer.Len() > 0 {
  293. c.write("\n")
  294. }
  295. _, err = c.buffer.WriteTo(out)
  296. return
  297. }
  298. // CompileString compiles the template and returns the Go Template source.
  299. // You would not be using this unless debugging / checking the output. Please use Compile
  300. // method to obtain a template instance directly.
  301. func (c *Compiler) CompileString() (string, error) {
  302. var buf bytes.Buffer
  303. if err := c.CompileWriter(&buf); err != nil {
  304. return "", err
  305. }
  306. result := buf.String()
  307. return result, nil
  308. }
  309. func (c *Compiler) visit(node parser.Node) {
  310. defer func() {
  311. if r := recover(); r != nil {
  312. if rs, ok := r.(string); ok && rs[:len("Amber Error")] == "Amber Error" {
  313. panic(r)
  314. }
  315. pos := node.Pos()
  316. if len(pos.Filename) > 0 {
  317. panic(fmt.Sprintf("Amber Error in <%s>: %v - Line: %d, Column: %d, Length: %d", pos.Filename, r, pos.LineNum, pos.ColNum, pos.TokenLength))
  318. } else {
  319. panic(fmt.Sprintf("Amber Error: %v - Line: %d, Column: %d, Length: %d", r, pos.LineNum, pos.ColNum, pos.TokenLength))
  320. }
  321. }
  322. }()
  323. switch node.(type) {
  324. case *parser.Block:
  325. c.visitBlock(node.(*parser.Block))
  326. case *parser.Doctype:
  327. c.visitDoctype(node.(*parser.Doctype))
  328. case *parser.Comment:
  329. c.visitComment(node.(*parser.Comment))
  330. case *parser.Tag:
  331. c.visitTag(node.(*parser.Tag))
  332. case *parser.Text:
  333. c.visitText(node.(*parser.Text))
  334. case *parser.Condition:
  335. c.visitCondition(node.(*parser.Condition))
  336. case *parser.Each:
  337. c.visitEach(node.(*parser.Each))
  338. case *parser.Assignment:
  339. c.visitAssignment(node.(*parser.Assignment))
  340. case *parser.Mixin:
  341. c.visitMixin(node.(*parser.Mixin))
  342. case *parser.MixinCall:
  343. c.visitMixinCall(node.(*parser.MixinCall))
  344. }
  345. }
  346. func (c *Compiler) write(value string) {
  347. c.buffer.WriteString(value)
  348. }
  349. func (c *Compiler) indent(offset int, newline bool) {
  350. if !c.PrettyPrint {
  351. return
  352. }
  353. if newline && c.buffer.Len() > 0 {
  354. c.write("\n")
  355. }
  356. for i := 0; i < c.indentLevel+offset; i++ {
  357. c.write("\t")
  358. }
  359. }
  360. func (c *Compiler) tempvar() string {
  361. c.tempvarIndex++
  362. return "$__amber_" + strconv.Itoa(c.tempvarIndex)
  363. }
  364. func (c *Compiler) escape(input string) string {
  365. return strings.Replace(strings.Replace(input, `\`, `\\`, -1), `"`, `\"`, -1)
  366. }
  367. func (c *Compiler) visitBlock(block *parser.Block) {
  368. for _, node := range block.Children {
  369. if _, ok := node.(*parser.Text); !block.CanInline() && ok {
  370. c.indent(0, true)
  371. }
  372. c.visit(node)
  373. }
  374. }
  375. func (c *Compiler) visitDoctype(doctype *parser.Doctype) {
  376. c.write(doctype.String())
  377. }
  378. func (c *Compiler) visitComment(comment *parser.Comment) {
  379. if comment.Silent {
  380. return
  381. }
  382. c.indent(0, false)
  383. if comment.Block == nil {
  384. c.write(`{{unescaped "<!-- ` + c.escape(comment.Value) + ` -->"}}`)
  385. } else {
  386. c.write(`<!-- ` + comment.Value)
  387. c.visitBlock(comment.Block)
  388. c.write(` -->`)
  389. }
  390. }
  391. func (c *Compiler) visitCondition(condition *parser.Condition) {
  392. c.write(`{{if ` + c.visitRawInterpolation(condition.Expression) + `}}`)
  393. c.visitBlock(condition.Positive)
  394. if condition.Negative != nil {
  395. c.write(`{{else}}`)
  396. c.visitBlock(condition.Negative)
  397. }
  398. c.write(`{{end}}`)
  399. }
  400. func (c *Compiler) visitEach(each *parser.Each) {
  401. if each.Block == nil {
  402. return
  403. }
  404. if len(each.Y) == 0 {
  405. c.write(`{{range ` + each.X + ` := ` + c.visitRawInterpolation(each.Expression) + `}}`)
  406. } else {
  407. c.write(`{{range ` + each.X + `, ` + each.Y + ` := ` + c.visitRawInterpolation(each.Expression) + `}}`)
  408. }
  409. c.visitBlock(each.Block)
  410. c.write(`{{end}}`)
  411. }
  412. func (c *Compiler) visitAssignment(assgn *parser.Assignment) {
  413. c.write(`{{` + assgn.X + ` := ` + c.visitRawInterpolation(assgn.Expression) + `}}`)
  414. }
  415. func (c *Compiler) visitTag(tag *parser.Tag) {
  416. type attrib struct {
  417. name string
  418. value func() string
  419. condition string
  420. }
  421. attribs := make(map[string]*attrib)
  422. for _, item := range tag.Attributes {
  423. attritem := item
  424. attr := new(attrib)
  425. attr.name = item.Name
  426. attr.value = func() string {
  427. if !attritem.IsRaw {
  428. return c.visitInterpolation(attritem.Value)
  429. } else if attritem.Value == "" {
  430. return ""
  431. } else {
  432. return attritem.Value
  433. }
  434. }
  435. if len(attritem.Condition) != 0 {
  436. attr.condition = c.visitRawInterpolation(attritem.Condition)
  437. }
  438. if attr.name == "class" && attribs["class"] != nil {
  439. prevclass := attribs["class"]
  440. prevvalue := prevclass.value
  441. prevclass.value = func() string {
  442. aval := attr.value()
  443. if len(attr.condition) > 0 {
  444. aval = `{{if ` + attr.condition + `}}` + aval + `{{end}}`
  445. }
  446. if len(prevclass.condition) > 0 {
  447. return `{{if ` + prevclass.condition + `}}` + prevvalue() + `{{end}} ` + aval
  448. }
  449. return prevvalue() + " " + aval
  450. }
  451. } else {
  452. attribs[attritem.Name] = attr
  453. }
  454. }
  455. keys := make([]string, 0, len(attribs))
  456. for key := range attribs {
  457. keys = append(keys, key)
  458. }
  459. sort.Strings(keys)
  460. c.indent(0, true)
  461. c.write("<" + tag.Name)
  462. for _, name := range keys {
  463. value := attribs[name]
  464. if len(value.condition) > 0 {
  465. c.write(`{{if ` + value.condition + `}}`)
  466. }
  467. val := value.value()
  468. if val == "" {
  469. c.write(` ` + name)
  470. } else {
  471. c.write(` ` + name + `="` + val + `"`)
  472. }
  473. if len(value.condition) > 0 {
  474. c.write(`{{end}}`)
  475. }
  476. }
  477. if tag.IsSelfClosing() {
  478. c.write(` />`)
  479. } else {
  480. c.write(`>`)
  481. if tag.Block != nil {
  482. if !tag.Block.CanInline() {
  483. c.indentLevel++
  484. }
  485. c.visitBlock(tag.Block)
  486. if !tag.Block.CanInline() {
  487. c.indentLevel--
  488. c.indent(0, true)
  489. }
  490. }
  491. c.write(`</` + tag.Name + `>`)
  492. }
  493. }
  494. var textInterpolateRegexp = regexp.MustCompile(`#\{(.*?)\}`)
  495. var textEscapeRegexp = regexp.MustCompile(`\{\{(.*?)\}\}`)
  496. func (c *Compiler) visitText(txt *parser.Text) {
  497. value := textEscapeRegexp.ReplaceAllStringFunc(txt.Value, func(value string) string {
  498. return `{{"{{"}}` + value[2:len(value)-2] + `{{"}}"}}`
  499. })
  500. value = textInterpolateRegexp.ReplaceAllStringFunc(value, func(value string) string {
  501. return c.visitInterpolation(value[2 : len(value)-1])
  502. })
  503. lines := strings.Split(value, "\n")
  504. for i := 0; i < len(lines); i++ {
  505. c.write(lines[i])
  506. if i < len(lines)-1 {
  507. c.write("\n")
  508. c.indent(0, false)
  509. }
  510. }
  511. }
  512. func (c *Compiler) visitInterpolation(value string) string {
  513. return `{{` + c.visitRawInterpolation(value) + `}}`
  514. }
  515. func (c *Compiler) visitRawInterpolation(value string) string {
  516. if value == "" {
  517. value = "\"\""
  518. }
  519. value = strings.Replace(value, "$", dollar, -1)
  520. expr, err := gp.ParseExpr(value)
  521. if err != nil {
  522. panic("Unable to parse expression.")
  523. }
  524. value = strings.Replace(c.visitExpression(expr), dollar, "$", -1)
  525. return value
  526. }
  527. func (c *Compiler) visitExpression(outerexpr ast.Expr) string {
  528. stack := list.New()
  529. pop := func() string {
  530. if stack.Front() == nil {
  531. return ""
  532. }
  533. val := stack.Front().Value.(string)
  534. stack.Remove(stack.Front())
  535. return val
  536. }
  537. var exec func(ast.Expr)
  538. exec = func(expr ast.Expr) {
  539. switch expr := expr.(type) {
  540. case *ast.BinaryExpr:
  541. {
  542. be := expr
  543. exec(be.Y)
  544. exec(be.X)
  545. negate := false
  546. name := c.tempvar()
  547. c.write(`{{` + name + ` := `)
  548. switch be.Op {
  549. case gt.ADD:
  550. c.write("__amber_add ")
  551. case gt.SUB:
  552. c.write("__amber_sub ")
  553. case gt.MUL:
  554. c.write("__amber_mul ")
  555. case gt.QUO:
  556. c.write("__amber_quo ")
  557. case gt.REM:
  558. c.write("__amber_rem ")
  559. case gt.LAND:
  560. c.write("and ")
  561. case gt.LOR:
  562. c.write("or ")
  563. case gt.EQL:
  564. c.write("__amber_eql ")
  565. case gt.NEQ:
  566. c.write("__amber_eql ")
  567. negate = true
  568. case gt.LSS:
  569. c.write("__amber_lss ")
  570. case gt.GTR:
  571. c.write("__amber_gtr ")
  572. case gt.LEQ:
  573. c.write("__amber_gtr ")
  574. negate = true
  575. case gt.GEQ:
  576. c.write("__amber_lss ")
  577. negate = true
  578. default:
  579. panic("Unexpected operator!")
  580. }
  581. c.write(pop() + ` ` + pop() + `}}`)
  582. if !negate {
  583. stack.PushFront(name)
  584. } else {
  585. negname := c.tempvar()
  586. c.write(`{{` + negname + ` := not ` + name + `}}`)
  587. stack.PushFront(negname)
  588. }
  589. }
  590. case *ast.UnaryExpr:
  591. {
  592. ue := expr
  593. exec(ue.X)
  594. name := c.tempvar()
  595. c.write(`{{` + name + ` := `)
  596. switch ue.Op {
  597. case gt.SUB:
  598. c.write("__amber_minus ")
  599. case gt.ADD:
  600. c.write("__amber_plus ")
  601. case gt.NOT:
  602. c.write("not ")
  603. default:
  604. panic("Unexpected operator!")
  605. }
  606. c.write(pop() + `}}`)
  607. stack.PushFront(name)
  608. }
  609. case *ast.ParenExpr:
  610. exec(expr.X)
  611. case *ast.BasicLit:
  612. stack.PushFront(strings.Replace(expr.Value, dollar, "$", -1))
  613. case *ast.Ident:
  614. name := expr.Name
  615. if len(name) >= len(dollar) && name[:len(dollar)] == dollar {
  616. if name == dollar {
  617. stack.PushFront(`.`)
  618. } else {
  619. stack.PushFront(`$` + expr.Name[len(dollar):])
  620. }
  621. } else {
  622. stack.PushFront(`.` + expr.Name)
  623. }
  624. case *ast.SelectorExpr:
  625. se := expr
  626. exec(se.X)
  627. x := pop()
  628. if x == "." {
  629. x = ""
  630. }
  631. name := c.tempvar()
  632. c.write(`{{` + name + ` := ` + x + `.` + se.Sel.Name + `}}`)
  633. stack.PushFront(name)
  634. case *ast.CallExpr:
  635. ce := expr
  636. for i := len(ce.Args) - 1; i >= 0; i-- {
  637. exec(ce.Args[i])
  638. }
  639. name := c.tempvar()
  640. builtin := false
  641. if ident, ok := ce.Fun.(*ast.Ident); ok {
  642. for _, fname := range builtinFunctions {
  643. if fname == ident.Name {
  644. builtin = true
  645. break
  646. }
  647. }
  648. for fname, _ := range FuncMap {
  649. if fname == ident.Name {
  650. builtin = true
  651. break
  652. }
  653. }
  654. }
  655. if builtin {
  656. stack.PushFront(ce.Fun.(*ast.Ident).Name)
  657. c.write(`{{` + name + ` := ` + pop())
  658. } else if se, ok := ce.Fun.(*ast.SelectorExpr); ok {
  659. exec(se.X)
  660. x := pop()
  661. if x == "." {
  662. x = ""
  663. }
  664. stack.PushFront(se.Sel.Name)
  665. c.write(`{{` + name + ` := ` + x + `.` + pop())
  666. } else {
  667. exec(ce.Fun)
  668. c.write(`{{` + name + ` := call ` + pop())
  669. }
  670. for i := 0; i < len(ce.Args); i++ {
  671. c.write(` `)
  672. c.write(pop())
  673. }
  674. c.write(`}}`)
  675. stack.PushFront(name)
  676. default:
  677. panic("Unable to parse expression. Unsupported: " + reflect.TypeOf(expr).String())
  678. }
  679. }
  680. exec(outerexpr)
  681. return pop()
  682. }
  683. func (c *Compiler) visitMixin(mixin *parser.Mixin) {
  684. c.mixins[mixin.Name] = mixin
  685. }
  686. func (c *Compiler) visitMixinCall(mixinCall *parser.MixinCall) {
  687. mixin := c.mixins[mixinCall.Name]
  688. switch {
  689. case mixin == nil:
  690. panic(fmt.Sprintf("unknown mixin %q", mixinCall.Name))
  691. case len(mixinCall.Args) < len(mixin.Args):
  692. panic(fmt.Sprintf(
  693. "not enough arguments in call to mixin %q (have: %d, want: %d)",
  694. mixinCall.Name,
  695. len(mixinCall.Args),
  696. len(mixin.Args),
  697. ))
  698. case len(mixinCall.Args) > len(mixin.Args):
  699. panic(fmt.Sprintf(
  700. "too many arguments in call to mixin %q (have: %d, want: %d)",
  701. mixinCall.Name,
  702. len(mixinCall.Args),
  703. len(mixin.Args),
  704. ))
  705. }
  706. for i, arg := range mixin.Args {
  707. c.write(fmt.Sprintf(`{{%s := %s}}`, arg, c.visitRawInterpolation(mixinCall.Args[i])))
  708. }
  709. c.visitBlock(mixin.Block)
  710. }