12
12
--
13
13
-- SIL TeX-like is similar to LaTeX, but with a parity with SIL XML.
14
14
-- I.e. \foo[attr=val]{...} is equivalent to <foo attr="val">...</foo>.
15
- -- Environments are syntactic sugar for commands
15
+ -- Environments are syntactic sugar for commands.
16
16
-- I.e. \begin[attr=val]{foo}...\end{foo} is the same as \foo[attr=val]{...}.
17
17
-- Rules below try to respect this parity.
18
18
19
19
local lexer = lexer
20
- local P , S , R = lpeg .P , lpeg .S , lpeg .R
20
+ local P , S , R , Cb , Cg , Ct , Cmt = lpeg .P , lpeg .S , lpeg .R , lpeg . Cb , lpeg . Cg , lpeg . Ct , lpeg . Cmt
21
21
22
22
local lex = lexer .new (... )
23
23
local ws = lex :get_rule (' whitespace' )
@@ -30,17 +30,17 @@ local identifier = lexer.alnum^1 * (S(':-') * lexer.alnum^1)^0
30
30
31
31
-- Reserved hard-coded "pass-through" commands/environments.
32
32
local reserved_specials = {
33
- ftl = ' text' , -- Well Fluent has a syntax, but let's not care here
33
+ ftl = ' text' , -- Well Fluent has a syntax, but let's not care here.
34
34
lua = ' lua' ,
35
- math = ' latex ' , -- We'd need a LaTeX math-only lexer to handle this properly.
35
+ math = ' tex ' , -- We'd need a (La)TeX math-only lexer to handle this properly.
36
36
raw = ' text' ,
37
37
script = ' lua' ,
38
- -- sil = ... -- It's the default here, so no need to add a rule for it.
38
+ -- sil = ... -- It's the default here, so no need to add a rule for it.
39
39
xml = ' xml' ,
40
40
use = ' lua' ,
41
41
}
42
- -- Other reserved keywords are "comment" and "begin"/"end"
43
- -- But we'll handle them in the rules below.
42
+ -- Other reserved keywords are "comment" and "begin"/"end",
43
+ -- but we'll handle them in the rules below.
44
44
45
45
-- Parameters (key-value pairs).
46
46
local eq = lex :tag (lexer .OPERATOR , ' =' )
@@ -57,22 +57,93 @@ local cmd_comment = P('\\comment') * optparams * lexer.range('{', '}', false, fa
57
57
lex :add_rule (' comment' , lex :tag (lexer .COMMENT , line_comment + env_comment + cmd_comment ))
58
58
59
59
-- 3. Special reserved pass-through commands/environments.
60
+
61
+ local function check_exit_brace_level (_ , _ , current_level )
62
+ current_level = tonumber (current_level )
63
+ return current_level == 0
64
+ end
65
+
66
+ local function increment_brace_level (increment )
67
+ local function update_brace_level (_ , _ , current_level )
68
+ current_level = tonumber (current_level )
69
+ local next_level = tostring (current_level + increment )
70
+ return true , next_level
71
+ end
72
+ return Cg (Cmt (Cb (' brace_level' ), update_brace_level ), ' brace_level' )
73
+ end
74
+
75
+ local is_exit_brace = Cmt (Cb (' brace_level' ), check_exit_brace_level )
76
+ local init_brace_level = Cg (Ct (' ' ) / ' 0' , ' brace_level' )
77
+
60
78
for name , lang in pairs (reserved_specials ) do
61
- -- Order matters: environments, commands with arguments, commands without arguments
62
- -- We need alt names for multiple embeddings and rules
63
- local base_rule_id = name .. lang
64
- local embedder = lexer .load (lang , base_rule_id .. ' _cmd' )
65
- lex :embed (
66
- embedder ,
67
- lex :tag (lexer .FUNCTION_BUILTIN , ' \\ ' .. name ) * optparams * lex :tag (lexer .OPERATOR , ' {' ),
68
- lex :tag (lexer .OPERATOR , ' }' ))
69
- local env = lexer .load (lang , base_rule_id .. ' _env' )
79
+ -- Order matters: environments, commands with arguments, commands without arguments.
80
+ -- We need alt names for multiple embeddings and rules.
81
+ local base_rule_id = name .. ' _' .. lang
82
+
83
+ -- 3.1. Reserved environments.
84
+ -- Ex. \begin{lua} ... Lua code ... \end{lua}
85
+ local env_embedder = lexer .load (lang , base_rule_id .. ' _env' )
70
86
lex :embed (
71
- env ,
87
+ env_embedder ,
72
88
lex :tag (lexer .FUNCTION_BUILTIN , ' \\ begin' ) * optparams
73
89
* lex :tag (lexer .OPERATOR , ' {' ) * lex :tag (lexer .FUNCTION_BUILTIN , name ) * lex :tag (lexer .OPERATOR , ' }' ),
74
90
lex :tag (lexer .FUNCTION_BUILTIN , ' \\ end' )
75
91
* lex :tag (lexer .OPERATOR , ' {' ) * lex :tag (lexer .FUNCTION_BUILTIN , name ) * lex :tag (lexer .OPERATOR , ' }' ))
92
+
93
+ -- 3.2. Reserved commands.
94
+ -- Ex. \lua{... Lua code ...}
95
+ -- The hard trick here is that we want to want to keep track of the paired braces,
96
+ -- in order to exit the embedding on the right closing brace.
97
+ local cmd_embedder = lang == ' text'
98
+ and lexer .new (base_rule_id .. ' _cmd' ) -- pseudo-lexer for text
99
+ or lexer .load (lang , base_rule_id .. ' _cmd' ) -- real lexer for Lua, TeX, XML
100
+ if lang == ' lua' then
101
+ -- We hack the Lua lexer to intercept and handle the pairs of braces,
102
+ -- i.e. we remove them for the 'operator' rule and handle them separately.
103
+ cmd_embedder :modify_rule (' operator' , cmd_embedder :tag (lexer .OPERATOR , ' ..' + S (' +-*/%^#=<>&|~;:,.[]()' )))
104
+ cmd_embedder :add_rule (
105
+ ' sil_brace_open' ,
106
+ cmd_embedder :tag (lexer .OPERATOR , ' {' ) * increment_brace_level (1 )
107
+ )
108
+ cmd_embedder :add_rule (
109
+ ' sil_brace_close' ,
110
+ cmd_embedder :tag (lexer .OPERATOR , ' }' ) * increment_brace_level (- 1 )
111
+ )
112
+ elseif lang == ' tex' then
113
+ -- We hack the TeX math lexer to intercept and handle the pairs of braces,
114
+ -- i.e. we remove them for the 'operator' rule and handle them separately.
115
+ -- We also take the opportunity remove some operators not expected in math mode,
116
+ -- and add some extra operators for math mode.
117
+ cmd_embedder :modify_rule (' operator' , cmd_embedder :tag (lexer .OPERATOR , S (' &()[]' )))
118
+ cmd_embedder :add_rule (' operator_math' , cmd_embedder :tag (lexer .OPERATOR .. " .math" , S (' +-=^_' )))
119
+ cmd_embedder :add_rule (
120
+ ' sil_brace_open' ,
121
+ cmd_embedder :tag (lexer .OPERATOR , ' {' ) * increment_brace_level (1 )
122
+ )
123
+ cmd_embedder :add_rule (
124
+ ' sil_brace_close' ,
125
+ cmd_embedder :tag (lexer .OPERATOR , ' }' ) * increment_brace_level (- 1 )
126
+ )
127
+ else
128
+ -- We just need to keep track of the braces for the XML and text lexers,
129
+ -- without any special marking.
130
+ cmd_embedder :add_rule (
131
+ ' sil_brace_open' ,
132
+ P ' {' * increment_brace_level (1 )
133
+ )
134
+ cmd_embedder :add_rule (
135
+ ' sil_brace_close' ,
136
+ P ' }' * increment_brace_level (- 1 )
137
+ )
138
+ end
139
+ lex :embed (
140
+ cmd_embedder ,
141
+ lex :tag (lexer .FUNCTION_BUILTIN , ' \\ ' .. name ) * optparams * init_brace_level * lex :tag (lexer .FUNCTION_BUILTIN , ' {' ),
142
+ lex :tag (lexer .FUNCTION_BUILTIN , ' }' * is_exit_brace )
143
+ )
144
+
145
+ -- 3.3. Reserved commands without arguments (must come after the commands with arguments).
146
+ -- Ex. \use[module=packages.highlighter]
76
147
lex :add_rule (base_rule_id .. ' _cmd_no_arg' , lex :tag (lexer .FUNCTION_BUILTIN , P (' \\ ' .. name )) * optparams )
77
148
end
78
149
0 commit comments