parser.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. package parser
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "net/http"
  8. "path/filepath"
  9. "strings"
  10. )
  11. type Parser struct {
  12. scanner *scanner
  13. filename string
  14. fs http.FileSystem
  15. currenttoken *token
  16. namedBlocks map[string]*NamedBlock
  17. parent *Parser
  18. result *Block
  19. }
  20. func newParser(rdr io.Reader) *Parser {
  21. p := new(Parser)
  22. p.scanner = newScanner(rdr)
  23. p.namedBlocks = make(map[string]*NamedBlock)
  24. return p
  25. }
  26. func StringParser(input string) (*Parser, error) {
  27. return newParser(bytes.NewReader([]byte(input))), nil
  28. }
  29. func ByteParser(input []byte) (*Parser, error) {
  30. return newParser(bytes.NewReader(input)), nil
  31. }
  32. func (p *Parser) SetFilename(filename string) {
  33. p.filename = filename
  34. }
  35. func (p *Parser) SetVirtualFilesystem(fs http.FileSystem) {
  36. p.fs = fs
  37. }
  38. func FileParser(filename string) (*Parser, error) {
  39. data, err := ioutil.ReadFile(filename)
  40. if err != nil {
  41. return nil, err
  42. }
  43. parser := newParser(bytes.NewReader(data))
  44. parser.filename = filename
  45. return parser, nil
  46. }
  47. func VirtualFileParser(filename string, fs http.FileSystem) (*Parser, error) {
  48. file, err := fs.Open(filename)
  49. if err != nil {
  50. return nil, err
  51. }
  52. data, err := ioutil.ReadAll(file)
  53. if err != nil {
  54. return nil, err
  55. }
  56. parser := newParser(bytes.NewReader(data))
  57. parser.filename = filename
  58. parser.fs = fs
  59. return parser, nil
  60. }
  61. func (p *Parser) Parse() *Block {
  62. if p.result != nil {
  63. return p.result
  64. }
  65. defer func() {
  66. if r := recover(); r != nil {
  67. if rs, ok := r.(string); ok && rs[:len("Amber Error")] == "Amber Error" {
  68. panic(r)
  69. }
  70. pos := p.pos()
  71. if len(pos.Filename) > 0 {
  72. panic(fmt.Sprintf("Amber Error in <%s>: %v - Line: %d, Column: %d, Length: %d", pos.Filename, r, pos.LineNum, pos.ColNum, pos.TokenLength))
  73. } else {
  74. panic(fmt.Sprintf("Amber Error: %v - Line: %d, Column: %d, Length: %d", r, pos.LineNum, pos.ColNum, pos.TokenLength))
  75. }
  76. }
  77. }()
  78. block := newBlock()
  79. p.advance()
  80. for {
  81. if p.currenttoken == nil || p.currenttoken.Kind == tokEOF {
  82. break
  83. }
  84. if p.currenttoken.Kind == tokBlank {
  85. p.advance()
  86. continue
  87. }
  88. block.push(p.parse())
  89. }
  90. if p.parent != nil {
  91. p.parent.Parse()
  92. for _, prev := range p.parent.namedBlocks {
  93. ours := p.namedBlocks[prev.Name]
  94. if ours == nil {
  95. // Put a copy of the named block into current context, so that sub-templates can use the block
  96. p.namedBlocks[prev.Name] = prev
  97. continue
  98. }
  99. top := findTopmostParentWithNamedBlock(p, prev.Name)
  100. nb := top.namedBlocks[prev.Name]
  101. switch ours.Modifier {
  102. case NamedBlockAppend:
  103. for i := 0; i < len(ours.Children); i++ {
  104. nb.push(ours.Children[i])
  105. }
  106. case NamedBlockPrepend:
  107. for i := len(ours.Children) - 1; i >= 0; i-- {
  108. nb.pushFront(ours.Children[i])
  109. }
  110. default:
  111. nb.Children = ours.Children
  112. }
  113. }
  114. block = p.parent.result
  115. }
  116. p.result = block
  117. return block
  118. }
  119. func (p *Parser) pos() SourcePosition {
  120. pos := p.scanner.Pos()
  121. pos.Filename = p.filename
  122. return pos
  123. }
  124. func (p *Parser) parseRelativeFile(filename string) *Parser {
  125. if len(p.filename) == 0 {
  126. panic("Unable to import or extend " + filename + " in a non filesystem based parser.")
  127. }
  128. filename = filepath.Join(filepath.Dir(p.filename), filename)
  129. if strings.IndexRune(filepath.Base(filename), '.') < 0 {
  130. filename = filename + ".amber"
  131. }
  132. parser, err := FileParser(filename)
  133. if err != nil && p.fs != nil {
  134. parser, err = VirtualFileParser(filename, p.fs)
  135. }
  136. if err != nil {
  137. panic("Unable to read " + filename + ", Error: " + string(err.Error()))
  138. }
  139. return parser
  140. }
  141. func (p *Parser) parse() Node {
  142. switch p.currenttoken.Kind {
  143. case tokDoctype:
  144. return p.parseDoctype()
  145. case tokComment:
  146. return p.parseComment()
  147. case tokText:
  148. return p.parseText()
  149. case tokIf:
  150. return p.parseIf()
  151. case tokEach:
  152. return p.parseEach()
  153. case tokImport:
  154. return p.parseImport()
  155. case tokTag:
  156. return p.parseTag()
  157. case tokAssignment:
  158. return p.parseAssignment()
  159. case tokNamedBlock:
  160. return p.parseNamedBlock()
  161. case tokExtends:
  162. return p.parseExtends()
  163. case tokIndent:
  164. return p.parseBlock(nil)
  165. case tokMixin:
  166. return p.parseMixin()
  167. case tokMixinCall:
  168. return p.parseMixinCall()
  169. }
  170. panic(fmt.Sprintf("Unexpected token: %d", p.currenttoken.Kind))
  171. }
  172. func (p *Parser) expect(typ rune) *token {
  173. if p.currenttoken.Kind != typ {
  174. panic("Unexpected token!")
  175. }
  176. curtok := p.currenttoken
  177. p.advance()
  178. return curtok
  179. }
  180. func (p *Parser) advance() {
  181. p.currenttoken = p.scanner.Next()
  182. }
  183. func (p *Parser) parseExtends() *Block {
  184. if p.parent != nil {
  185. panic("Unable to extend multiple parent templates.")
  186. }
  187. tok := p.expect(tokExtends)
  188. parser := p.parseRelativeFile(tok.Value)
  189. parser.Parse()
  190. p.parent = parser
  191. return newBlock()
  192. }
  193. func (p *Parser) parseBlock(parent Node) *Block {
  194. p.expect(tokIndent)
  195. block := newBlock()
  196. block.SourcePosition = p.pos()
  197. for {
  198. if p.currenttoken == nil || p.currenttoken.Kind == tokEOF || p.currenttoken.Kind == tokOutdent {
  199. break
  200. }
  201. if p.currenttoken.Kind == tokBlank {
  202. p.advance()
  203. continue
  204. }
  205. if p.currenttoken.Kind == tokId ||
  206. p.currenttoken.Kind == tokClassName ||
  207. p.currenttoken.Kind == tokAttribute {
  208. if tag, ok := parent.(*Tag); ok {
  209. attr := p.expect(p.currenttoken.Kind)
  210. cond := attr.Data["Condition"]
  211. switch attr.Kind {
  212. case tokId:
  213. tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "id", attr.Value, true, cond})
  214. case tokClassName:
  215. tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "class", attr.Value, true, cond})
  216. case tokAttribute:
  217. tag.Attributes = append(tag.Attributes, Attribute{p.pos(), attr.Value, attr.Data["Content"], attr.Data["Mode"] == "raw", cond})
  218. }
  219. continue
  220. } else {
  221. panic("Conditional attributes must be placed immediately within a parent tag.")
  222. }
  223. }
  224. block.push(p.parse())
  225. }
  226. p.expect(tokOutdent)
  227. return block
  228. }
  229. func (p *Parser) parseIf() *Condition {
  230. tok := p.expect(tokIf)
  231. cnd := newCondition(tok.Value)
  232. cnd.SourcePosition = p.pos()
  233. readmore:
  234. switch p.currenttoken.Kind {
  235. case tokIndent:
  236. cnd.Positive = p.parseBlock(cnd)
  237. goto readmore
  238. case tokElse:
  239. p.expect(tokElse)
  240. if p.currenttoken.Kind == tokIf {
  241. cnd.Negative = newBlock()
  242. cnd.Negative.push(p.parseIf())
  243. } else if p.currenttoken.Kind == tokIndent {
  244. cnd.Negative = p.parseBlock(cnd)
  245. } else {
  246. panic("Unexpected token!")
  247. }
  248. goto readmore
  249. }
  250. return cnd
  251. }
  252. func (p *Parser) parseEach() *Each {
  253. tok := p.expect(tokEach)
  254. ech := newEach(tok.Value)
  255. ech.SourcePosition = p.pos()
  256. ech.X = tok.Data["X"]
  257. ech.Y = tok.Data["Y"]
  258. if p.currenttoken.Kind == tokIndent {
  259. ech.Block = p.parseBlock(ech)
  260. }
  261. return ech
  262. }
  263. func (p *Parser) parseImport() *Block {
  264. tok := p.expect(tokImport)
  265. node := p.parseRelativeFile(tok.Value).Parse()
  266. node.SourcePosition = p.pos()
  267. return node
  268. }
  269. func (p *Parser) parseNamedBlock() *Block {
  270. tok := p.expect(tokNamedBlock)
  271. if p.namedBlocks[tok.Value] != nil {
  272. panic("Multiple definitions of named blocks are not permitted. Block " + tok.Value + " has been re defined.")
  273. }
  274. block := newNamedBlock(tok.Value)
  275. block.SourcePosition = p.pos()
  276. if tok.Data["Modifier"] == "append" {
  277. block.Modifier = NamedBlockAppend
  278. } else if tok.Data["Modifier"] == "prepend" {
  279. block.Modifier = NamedBlockPrepend
  280. }
  281. if p.currenttoken.Kind == tokIndent {
  282. block.Block = *(p.parseBlock(nil))
  283. }
  284. p.namedBlocks[block.Name] = block
  285. if block.Modifier == NamedBlockDefault {
  286. return &block.Block
  287. }
  288. return newBlock()
  289. }
  290. func (p *Parser) parseDoctype() *Doctype {
  291. tok := p.expect(tokDoctype)
  292. node := newDoctype(tok.Value)
  293. node.SourcePosition = p.pos()
  294. return node
  295. }
  296. func (p *Parser) parseComment() *Comment {
  297. tok := p.expect(tokComment)
  298. cmnt := newComment(tok.Value)
  299. cmnt.SourcePosition = p.pos()
  300. cmnt.Silent = tok.Data["Mode"] == "silent"
  301. if p.currenttoken.Kind == tokIndent {
  302. cmnt.Block = p.parseBlock(cmnt)
  303. }
  304. return cmnt
  305. }
  306. func (p *Parser) parseText() *Text {
  307. tok := p.expect(tokText)
  308. node := newText(tok.Value, tok.Data["Mode"] == "raw")
  309. node.SourcePosition = p.pos()
  310. return node
  311. }
  312. func (p *Parser) parseAssignment() *Assignment {
  313. tok := p.expect(tokAssignment)
  314. node := newAssignment(tok.Data["X"], tok.Value)
  315. node.SourcePosition = p.pos()
  316. return node
  317. }
  318. func (p *Parser) parseTag() *Tag {
  319. tok := p.expect(tokTag)
  320. tag := newTag(tok.Value)
  321. tag.SourcePosition = p.pos()
  322. ensureBlock := func() {
  323. if tag.Block == nil {
  324. tag.Block = newBlock()
  325. }
  326. }
  327. readmore:
  328. switch p.currenttoken.Kind {
  329. case tokIndent:
  330. if tag.IsRawText() {
  331. p.scanner.readRaw = true
  332. }
  333. block := p.parseBlock(tag)
  334. if tag.Block == nil {
  335. tag.Block = block
  336. } else {
  337. for _, c := range block.Children {
  338. tag.Block.push(c)
  339. }
  340. }
  341. case tokId:
  342. id := p.expect(tokId)
  343. if len(id.Data["Condition"]) > 0 {
  344. panic("Conditional attributes must be placed in a block within a tag.")
  345. }
  346. tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "id", id.Value, true, ""})
  347. goto readmore
  348. case tokClassName:
  349. cls := p.expect(tokClassName)
  350. if len(cls.Data["Condition"]) > 0 {
  351. panic("Conditional attributes must be placed in a block within a tag.")
  352. }
  353. tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "class", cls.Value, true, ""})
  354. goto readmore
  355. case tokAttribute:
  356. attr := p.expect(tokAttribute)
  357. if len(attr.Data["Condition"]) > 0 {
  358. panic("Conditional attributes must be placed in a block within a tag.")
  359. }
  360. tag.Attributes = append(tag.Attributes, Attribute{p.pos(), attr.Value, attr.Data["Content"], attr.Data["Mode"] == "raw", ""})
  361. goto readmore
  362. case tokText:
  363. if p.currenttoken.Data["Mode"] != "piped" {
  364. ensureBlock()
  365. tag.Block.pushFront(p.parseText())
  366. goto readmore
  367. }
  368. }
  369. return tag
  370. }
  371. func (p *Parser) parseMixin() *Mixin {
  372. tok := p.expect(tokMixin)
  373. mixin := newMixin(tok.Value, tok.Data["Args"])
  374. mixin.SourcePosition = p.pos()
  375. if p.currenttoken.Kind == tokIndent {
  376. mixin.Block = p.parseBlock(mixin)
  377. }
  378. return mixin
  379. }
  380. func (p *Parser) parseMixinCall() *MixinCall {
  381. tok := p.expect(tokMixinCall)
  382. mixinCall := newMixinCall(tok.Value, tok.Data["Args"])
  383. mixinCall.SourcePosition = p.pos()
  384. return mixinCall
  385. }
  386. func findTopmostParentWithNamedBlock(p *Parser, name string) *Parser {
  387. top := p
  388. for {
  389. if top.namedBlocks[name] == nil {
  390. return nil
  391. }
  392. if top.parent == nil {
  393. return top
  394. }
  395. if top.parent.namedBlocks[name] != nil {
  396. top = top.parent
  397. } else {
  398. return top
  399. }
  400. }
  401. }