aboutsummaryrefslogtreecommitdiffstats
path: root/test/readme.lua
blob: dd39dac864cba6e5f56c42548afa26e34e99dc7b (plain) (blame)
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
local lpeg = require 'lpeg'
local test = require 'test'
local io = require 'io'

local format = string.format
local pack = table.pack
local concat = table.concat

local readme_parser
do
	local P = lpeg.P
	local S = lpeg.S
	local V = lpeg.V
	local C = lpeg.C
	local Cc = lpeg.Cc
	local Ct = lpeg.Ct

	local sp = P(' ')
	local eq = P('=')
	local nl = P('\n')
	local non_nl = P(1)-nl
	local rest_of_line = non_nl^0

	local extraline = (nl * sp * sp)^-1
	local line_or_space = extraline + sp^1
	local div = line_or_space * P('-- returns') * line_or_space
	local assign_mid = sp^1 * eq * sp^1

	local table = P{"{" * ((1 - S('{}')) + V(1))^0 * "}"}
	local not_str = (sp^0 * nl) + div + assign_mid + P('{')
	local plainstr = (P(1)-not_str)^1
	local str = C(plainstr * (table * plainstr^0)^0)
	local example = Ct(Cc('example') * str * div * str)
	local assign_left = P('local ')^-1 * str
	local assign_right = str
	local assignment = Ct(Cc('assignment') * assign_left * assign_mid * assign_right)
	local comment = P('--') * rest_of_line
	local indented_line = sp^2 * (comment + example + assignment) * sp^0
	local anyline = rest_of_line - (sp * rest_of_line)
	local non_match = Ct(Cc('unable to parse line') * C(rest_of_line))
	local line = indented_line + anyline + non_match

	readme_parser = Ct((line * nl)^0 * line^-1 * -1)
end

local env = {
	tostring = tostring,
	require = require,
}

local function run_error(code, err)
	print()
	print('code:', code)
	print('error:', err)
	print()
	return { true, nil, n=3 }
end

local function run(name, code)
	local f, err = load(code, name, 't', env)
	if not f then
		return run_error(code, err)
	end
	local ret = pack(pcall(f))
	if ret[1] then
		return ret
	else
		return run_error(code, ret[2])
	end
end

local function run_example(name, code)
	return run(name, format('return %s', code))
end

local function get_meta_function(t, fname)
	return rawget(getmetatable(t) or {}, fname)
end

local function table2str(t)
	local nt = {}
	for i=1,#t do
		nt[i] = tostring(t[i])
	end
	return '{ ' .. concat(nt, ', ') .. ' }'
end

local function pack2str(t)
	local new = {}
	local n = t.n
	for i=2,n do
		local v = t[i]
		local vt = type(v)
		if vt == 'nil' then
			new[i] = 'nil'
		elseif vt == 'table' then
			local tostr = get_meta_function(v, '__tostring') or table2str
			new[i] = tostr(v)
		else
			new[i] = format('%s "%s"', vt, v)
		end
	end
	return concat(new, ', ', 2, n)
end

local function compare_tables(a, b)
	local aeq = get_meta_function(a, '__eq')
	local beq = get_meta_function(b, '__eq')
	if aeq or beq then
		if aeq ~= beq then return false end
		if a ~= b then return false end
	end
	local a_key_cnt = 0
	for _,_ in pairs(a) do
		a_key_cnt = a_key_cnt + 1
	end
	for k,vb in pairs(b) do
		a_key_cnt = a_key_cnt - 1
		local va = a[k]
		if va == nil then return false end
		local vat = type(va)
		local vbt = type(vb)
		if vat ~= vbt then return false end
		if vat == 'table' then
			if not compare_tables(va, vb) then return false end
		else
			if va ~= vb then return false end
		end
	end
	if a_key_cnt ~= 0 then
		return false
	end
	return true
end

local function hdl_assignment(line)
	local code = format('%s = %s', line[2], line[3])
	run('assignment', code)
end

local function hdl_example(line)
	local t1 = line[2]
	local t2 = line[3]
	local r1 = run_example('left side', t1)
	local r2 = run_example('right side', t2)
	local errmsg = format('"%s" returns %s, not %s', t1, pack2str(r1), pack2str(r2))
	assert(compare_tables(r1, r2), errmsg)
end

local handlers = {
	assignment = hdl_assignment,
	example = hdl_example,
}

local function readme_test()
	local data = assert(io.open('README.rst', 'r')):read('*a')
	local lines = assert(readme_parser:match(data))
	for i=1,#lines do
		local line = lines[i]
		local kind = line[1]
		local handler = handlers[kind]
		if not handler then
			print('unknown handler', kind, line[2])
		end
		handler(line)
	end
end

return test.new(readme_test)