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
103
104
105
106
107
108
109
110
|
function string:starts_with(s)
return self:sub(1, #s) == s
end
function string:flag_name()
if self:sub(1, 2) ~= "--" then
return nil
end
return self:sub(3)
end
function string:split(sep)
splits = {}
for split in self:gmatch("[^" .. sep .. "]+") do
splits[#splits + 1] = split
end
return splits
end
-- NOTE: This function is for certain not complete and not very efficient
-- but for now it works good enough to proceed with development.
-- We might need to rewrite this function in the future if it is a bottleneck.
function string:split_into_template_blocks()
local splits = { { kind = 'text', content = '' } }
local stack = {}
local current = self
local matching = {
["{"] = "}",
["["] = "]",
["("] = ")",
}
while #current > 0 do
local add = true
if #stack == 0 and current:starts_with("@{") then
current = current:sub(2, #current)
stack[#stack + 1] = "}"
splits[#splits + 1] = { kind = 'code', content = '' }
add = false
elseif #stack > 0 and (
current:starts_with('"') or
current:starts_with("'") or
current:starts_with('[[')
) then
local string_end = current:starts_with('"') and '"' or (current:starts_with("'") and "'" or ']]')
splits[#splits].content = splits[#splits].content .. current:sub(1, 1)
current = current:sub(2)
while #current > 0 do
if current:starts_with('\\') then
splits[#splits].content = splits[#splits].content .. current:sub(1, 1)
current = current:sub(2)
elseif current:starts_with(string_end) then
break
end
splits[#splits].content = splits[#splits].content .. current:sub(1, 1)
current = current:sub(2)
end
elseif #stack > 0 and current:starts_with("--") then
local comment_end = current:starts_with('--[[') and ']]' or '\n'
splits[#splits].content = splits[#splits].content .. current:sub(1, (current:find(comment_end) + #comment_end) or #current + 1)
current = current:sub((current:find(comment_end) + #comment_end) or #current + 1)
add = false
elseif #stack > 0 then
local start = current:sub(1, 1)
local closing = matching[start]
if start == stack[#stack] then
stack[#stack] = nil
if #stack == 0 then
current = current:sub(2, #current)
splits[#splits + 1] = { kind = 'text', content = '' }
end
elseif closing then
stack[#stack + 1] = closing
end
end
if add then
splits[#splits].content = splits[#splits].content .. current:sub(1, 1)
end
current = current:sub(2)
end
return splits
end
return {
tests = {
function()
local path = 'some/path/here'
assert.equals({ 'some', 'path', 'here' }, path:split("/"))
end,
function()
local f = Path:new("example/test.py"):open("r")
local raw = f:read("*all")
f:close()
for _, value in ipairs(raw:split_into_template_blocks()) do
print(require('inspect')(value))
end
end
}
}
|