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
|
-- cskin.lua
local M = require 'macro'
M.define_tokens{'&&','||','!=','{|'}
M.define '&& and'
M.define '|| or'
M.define '!= ~='
M.define '! not'
local push,pop = table.insert,table.remove
local bstack = {}
local btop = {}
local function push_brace_stack (newv)
newv = newv or {}
newv.lev = 0
push(bstack,btop)
btop = newv
end
local disabled = false
-- skinning can be temporarily disabled; this macro re-enables skinning
M.define('_SKIN_',function() disabled = false end)
local function block_statement (word,end_handler)
return function(get,put)
if disabled then return end
get:expecting '('
local stuff = get:upto ')'
get:expecting '{'
push_brace_stack {handler = end_handler}
return put:space():tokens(stuff):space():keyword(word)
end
end
local function endif_handler (get,put)
local t,v = get:next()
if t == 'keyword' and v == 'else' then
local nt,nv = get:next()
if nt == 'keyword' and nv == 'if' then
return put:keyword 'elseif'
else
M.assert(nt == '{')
end
push_brace_stack() -- opening else block
return put:keyword 'else'
else
return put:keyword 'end' (t,v)
end
end
M.keyword_handler ('for',block_statement 'do')
M.keyword_handler ('while',block_statement 'do')
M.keyword_handler ('if',block_statement ('then',endif_handler))
M.keyword_handler ('elseif',block_statement ('then',endif_handler))
M.define('{',function()
if btop.lev then
btop.lev = btop.lev + 1
end
return nil,true --> pass-through macro
end)
M.define('}',function(get,put)
if btop.lev == 0 then
local res
if btop.handler then res = btop.handler(get,put) end
if not res then res = put:space():keyword'end' end
btop = pop(bstack)
return res
else
btop.lev = btop.lev - 1
return nil,true --> pass-through macro
end
end)
-- def for function is a little easier; it's a plain macro,
-- and we deliberately don't want to get rid of the parens.
M.define('def',function(get,put)
local stuff = get:list('{','')
put:keyword('function')
if btop.classname then put:iden(btop.classname) '.' end
push_brace_stack()
return put:list(stuff)
end)
----- OOP support ------
M.define('class',function(get,put)
local name,t,v = get:iden(),get:next()
local base = ''
if t == ':' then base = get:iden(); t = get:next() end
M.assert(t == '{','expecting {')
push_brace_stack {classname = name}
return 'do '..name..' = class_('..base..')'
end)
class_ = require 'macro.lib.class'
---- making an existing macro play nicely with the C skin ---
require 'macro.forall'
local fun = M.get_macro_value 'forall'
M.define('forall',function (get,put)
get:expecting '('
local stuff = get:upto ')'
get:expecting'{'
stuff:space():keyword 'do'
stuff = fun(get.from_tl(stuff),put)
-- let the unskinned Lua pass through with _SKIN_ to re-enable skinning
disabled = true
stuff:iden '_SKIN_':space()
push_brace_stack()
return stuff
end)
-- often easier to express the macro in a skinned form
M.define('{|',function(get,put)
local expr = tostring(get:upto '|')
local select = tostring(get:upto '}')
return ('(def(){local r={}; forall(%s) {r[#r+1]=%s} return r})()'):format(select,expr)
end)
|