This repository has been archived by the owner on Jun 10, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 68
/
scanner.coffee
103 lines (84 loc) · 2.38 KB
/
scanner.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{StringScanner} = require "strscan"
{trim} = require "./util"
module.exports = class Scanner
@modePatterns:
data: /(.*?)(<%%|<%\s*(\#)|<%(([=-])?)|\n|$)/
code: /(.*?)((((:|(->|=>))\s*))?%>|\n|$)/
comment: /(.*?)(%>|\n|$)/
@dedentablePattern: /^(end|when|else|catch|finally)(?:\W|$)/
@scan: (source) ->
tokens = []
scanner = new Scanner source
until scanner.done
scanner.scan (token) -> tokens.push token
tokens
constructor: (source) ->
@source = source.replace /\r\n?/g, "\n"
@scanner = new StringScanner @source
@mode = "data"
@buffer = ""
@lineNo = 1
@done = no
scan: (callback) ->
if @done
callback()
else if @scanner.hasTerminated()
@done = yes
switch @mode
when "data"
callback ["printString", @flush()]
when "code"
callback ["fail", "unexpected end of template"]
else
@advance()
switch @mode
when "data"
@scanData callback
when "code"
@scanCode callback
when "comment"
@scanComment callback
advance: ->
@scanner.scanUntil Scanner.modePatterns[@mode]
@buffer += @scanner.getCapture 0
@tail = @scanner.getCapture 1
@comment = @scanner.getCapture 2
@directive = @scanner.getCapture 4
@arrow = @scanner.getCapture 5
scanData: (callback) ->
if @tail is "<%%"
@buffer += "<%"
@scan callback
else if @tail is "\n"
@buffer += @tail
@lineNo++
@scan callback
else if @tail
callback ["printString", @flush()]
if @comment
@mode = "comment"
else
@mode = "code"
callback ["beginCode", print: @directive?, safe: @directive is "-"]
scanCode: (callback) ->
if @tail is "\n"
callback ["fail", "unexpected newline in code block"]
else if @tail
@mode = "data"
code = trim @flush()
code += " #{@arrow}" if @arrow
callback ["dedent"] if @isDedentable code
callback ["recordCode", code]
callback ["indent", @arrow] if @directive
scanComment: (callback) ->
if @tail is "\n"
callback ["fail", "unexpected newline in code block"]
else if @tail
@mode = "data"
@buffer = ""
flush: ->
buffer = @buffer
@buffer = ""
buffer
isDedentable: (code) ->
code.match Scanner.dedentablePattern