|
1 # -*- coding: utf-8 -*- |
|
2 """ |
|
3 pygments.lexers.csound |
|
4 ~~~~~~~~~~~~~~~~~~~~~~ |
|
5 |
|
6 Lexers for Csound languages. |
|
7 |
|
8 :copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS. |
|
9 :license: BSD, see LICENSE for details. |
|
10 """ |
|
11 |
|
12 import re |
|
13 |
|
14 from pygments.lexer import RegexLexer, bygroups, default, include, using, words |
|
15 from pygments.token import Comment, Error, Keyword, Name, Number, Operator, Punctuation, \ |
|
16 String, Text, Whitespace |
|
17 from pygments.lexers._csound_builtins import OPCODES, DEPRECATED_OPCODES |
|
18 from pygments.lexers.html import HtmlLexer |
|
19 from pygments.lexers.python import PythonLexer |
|
20 from pygments.lexers.scripting import LuaLexer |
|
21 |
|
22 __all__ = ['CsoundScoreLexer', 'CsoundOrchestraLexer', 'CsoundDocumentLexer'] |
|
23 |
|
24 newline = (r'((?:(?:;|//).*)*)(\n)', bygroups(Comment.Single, Text)) |
|
25 |
|
26 |
|
27 class CsoundLexer(RegexLexer): |
|
28 tokens = { |
|
29 'whitespace': [ |
|
30 (r'[ \t]+', Text), |
|
31 (r'/[*](?:.|\n)*?[*]/', Comment.Multiline), |
|
32 (r'(?:;|//).*$', Comment.Single), |
|
33 (r'(\\)(\n)', bygroups(Whitespace, Text)) |
|
34 ], |
|
35 |
|
36 'preprocessor directives': [ |
|
37 (r'#(?:e(?:nd(?:if)?|lse)\b|##)|@@?[ \t]*\d+', Comment.Preproc), |
|
38 (r'#include', Comment.Preproc, 'include directive'), |
|
39 (r'#[ \t]*define', Comment.Preproc, 'define directive'), |
|
40 (r'#(?:ifn?def|undef)\b', Comment.Preproc, 'macro directive') |
|
41 ], |
|
42 |
|
43 'include directive': [ |
|
44 include('whitespace'), |
|
45 (r'([^ \t]).*?\1', String, '#pop') |
|
46 ], |
|
47 |
|
48 'define directive': [ |
|
49 (r'\n', Text), |
|
50 include('whitespace'), |
|
51 (r'([A-Z_a-z]\w*)(\()', bygroups(Comment.Preproc, Punctuation), |
|
52 ('#pop', 'macro parameter name list')), |
|
53 (r'[A-Z_a-z]\w*', Comment.Preproc, ('#pop', 'before macro body')) |
|
54 ], |
|
55 'macro parameter name list': [ |
|
56 include('whitespace'), |
|
57 (r'[A-Z_a-z]\w*', Comment.Preproc), |
|
58 (r"['#]", Punctuation), |
|
59 (r'\)', Punctuation, ('#pop', 'before macro body')) |
|
60 ], |
|
61 'before macro body': [ |
|
62 (r'\n', Text), |
|
63 include('whitespace'), |
|
64 (r'#', Punctuation, ('#pop', 'macro body')) |
|
65 ], |
|
66 'macro body': [ |
|
67 (r'(?:\\(?!#)|[^#\\]|\n)+', Comment.Preproc), |
|
68 (r'\\#', Comment.Preproc), |
|
69 (r'(?<!\\)#', Punctuation, '#pop') |
|
70 ], |
|
71 |
|
72 'macro directive': [ |
|
73 include('whitespace'), |
|
74 (r'[A-Z_a-z]\w*', Comment.Preproc, '#pop') |
|
75 ], |
|
76 |
|
77 'macro uses': [ |
|
78 (r'(\$[A-Z_a-z]\w*\.?)(\()', bygroups(Comment.Preproc, Punctuation), |
|
79 'macro parameter value list'), |
|
80 (r'\$[A-Z_a-z]\w*(?:\.|\b)', Comment.Preproc) |
|
81 ], |
|
82 'macro parameter value list': [ |
|
83 (r'(?:[^\'#"{()]|\{(?!\{))+', Comment.Preproc), |
|
84 (r"['#]", Punctuation), |
|
85 (r'"', String, 'macro parameter value quoted string'), |
|
86 (r'\{\{', String, 'macro parameter value braced string'), |
|
87 (r'\(', Comment.Preproc, 'macro parameter value parenthetical'), |
|
88 (r'\)', Punctuation, '#pop') |
|
89 ], |
|
90 'macro parameter value quoted string': [ |
|
91 (r"\\[#'()]", Comment.Preproc), |
|
92 (r"[#'()]", Error), |
|
93 include('quoted string') |
|
94 ], |
|
95 'macro parameter value braced string': [ |
|
96 (r"\\[#'()]", Comment.Preproc), |
|
97 (r"[#'()]", Error), |
|
98 include('braced string') |
|
99 ], |
|
100 'macro parameter value parenthetical': [ |
|
101 (r'(?:[^\\()]|\\\))+', Comment.Preproc), |
|
102 (r'\(', Comment.Preproc, '#push'), |
|
103 (r'\)', Comment.Preproc, '#pop') |
|
104 ], |
|
105 |
|
106 'whitespace and macro uses': [ |
|
107 include('whitespace'), |
|
108 include('macro uses') |
|
109 ], |
|
110 |
|
111 'numbers': [ |
|
112 (r'\d+[Ee][+-]?\d+|(\d+\.\d*|\d*\.\d+)([Ee][+-]?\d+)?', Number.Float), |
|
113 (r'(0[Xx])([0-9A-Fa-f]+)', bygroups(Keyword.Type, Number.Hex)), |
|
114 (r'\d+', Number.Integer) |
|
115 ], |
|
116 |
|
117 'braced string': [ |
|
118 # Do nothing. This must be defined in subclasses. |
|
119 ] |
|
120 } |
|
121 |
|
122 |
|
123 class CsoundScoreLexer(CsoundLexer): |
|
124 """ |
|
125 For `Csound <https://csound.github.io>`_ scores. |
|
126 |
|
127 .. versionadded:: 2.1 |
|
128 """ |
|
129 |
|
130 name = 'Csound Score' |
|
131 aliases = ['csound-score', 'csound-sco'] |
|
132 filenames = ['*.sco'] |
|
133 |
|
134 tokens = { |
|
135 'root': [ |
|
136 (r'\n', Text), |
|
137 include('whitespace and macro uses'), |
|
138 include('preprocessor directives'), |
|
139 |
|
140 (r'[abCdefiqstvxy]', Keyword), |
|
141 # There is also a w statement that is generated internally and should not be |
|
142 # used; see https://github.com/csound/csound/issues/750. |
|
143 |
|
144 (r'z', Keyword.Constant), |
|
145 # z is a constant equal to 800,000,000,000. 800 billion seconds is about |
|
146 # 25,367.8 years. See also |
|
147 # https://csound.github.io/docs/manual/ScoreTop.html and |
|
148 # https://github.com/csound/csound/search?q=stof+path%3AEngine+filename%3Asread.c. |
|
149 |
|
150 (r'([nNpP][pP])(\d+)', bygroups(Keyword, Number.Integer)), |
|
151 |
|
152 (r'[mn]', Keyword, 'mark statement'), |
|
153 |
|
154 include('numbers'), |
|
155 (r'[!+\-*/^%&|<>#~.]', Operator), |
|
156 (r'[()\[\]]', Punctuation), |
|
157 (r'"', String, 'quoted string'), |
|
158 (r'\{', Comment.Preproc, 'loop after left brace'), |
|
159 ], |
|
160 |
|
161 'mark statement': [ |
|
162 include('whitespace and macro uses'), |
|
163 (r'[A-Z_a-z]\w*', Name.Label), |
|
164 (r'\n', Text, '#pop') |
|
165 ], |
|
166 |
|
167 'quoted string': [ |
|
168 (r'"', String, '#pop'), |
|
169 (r'[^"$]+', String), |
|
170 include('macro uses'), |
|
171 (r'[$]', String) |
|
172 ], |
|
173 |
|
174 'loop after left brace': [ |
|
175 include('whitespace and macro uses'), |
|
176 (r'\d+', Number.Integer, ('#pop', 'loop after repeat count')), |
|
177 ], |
|
178 'loop after repeat count': [ |
|
179 include('whitespace and macro uses'), |
|
180 (r'[A-Z_a-z]\w*', Comment.Preproc, ('#pop', 'loop')) |
|
181 ], |
|
182 'loop': [ |
|
183 (r'\}', Comment.Preproc, '#pop'), |
|
184 include('root') |
|
185 ], |
|
186 |
|
187 # Braced strings are not allowed in Csound scores, but this is needed |
|
188 # because the superclass includes it. |
|
189 'braced string': [ |
|
190 (r'\}\}', String, '#pop'), |
|
191 (r'[^}]|\}(?!\})', String) |
|
192 ] |
|
193 } |
|
194 |
|
195 |
|
196 class CsoundOrchestraLexer(CsoundLexer): |
|
197 """ |
|
198 For `Csound <https://csound.github.io>`_ orchestras. |
|
199 |
|
200 .. versionadded:: 2.1 |
|
201 """ |
|
202 |
|
203 name = 'Csound Orchestra' |
|
204 aliases = ['csound', 'csound-orc'] |
|
205 filenames = ['*.orc', '*.udo'] |
|
206 |
|
207 user_defined_opcodes = set() |
|
208 |
|
209 def opcode_name_callback(lexer, match): |
|
210 opcode = match.group(0) |
|
211 lexer.user_defined_opcodes.add(opcode) |
|
212 yield match.start(), Name.Function, opcode |
|
213 |
|
214 def name_callback(lexer, match): |
|
215 name = match.group(1) |
|
216 if name in OPCODES or name in DEPRECATED_OPCODES: |
|
217 yield match.start(), Name.Builtin, name |
|
218 if match.group(2): |
|
219 yield match.start(2), Punctuation, match.group(2) |
|
220 yield match.start(3), Keyword.Type, match.group(3) |
|
221 elif name in lexer.user_defined_opcodes: |
|
222 yield match.start(), Name.Function, name |
|
223 else: |
|
224 nameMatch = re.search(r'^(g?[afikSw])(\w+)', name) |
|
225 if nameMatch: |
|
226 yield nameMatch.start(1), Keyword.Type, nameMatch.group(1) |
|
227 yield nameMatch.start(2), Name, nameMatch.group(2) |
|
228 else: |
|
229 yield match.start(), Name, name |
|
230 if match.group(2): |
|
231 yield match.start(2), Punctuation, match.group(2) |
|
232 yield match.start(3), Name, match.group(3) |
|
233 |
|
234 tokens = { |
|
235 'root': [ |
|
236 (r'\n', Text), |
|
237 |
|
238 (r'^([ \t]*)(\w+)(:)(?:[ \t]+|$)', bygroups(Text, Name.Label, Punctuation)), |
|
239 |
|
240 include('whitespace and macro uses'), |
|
241 include('preprocessor directives'), |
|
242 |
|
243 (r'\binstr\b', Keyword.Declaration, 'instrument numbers and identifiers'), |
|
244 (r'\bopcode\b', Keyword.Declaration, 'after opcode keyword'), |
|
245 (r'\b(?:end(?:in|op))\b', Keyword.Declaration), |
|
246 |
|
247 include('partial statements') |
|
248 ], |
|
249 |
|
250 'partial statements': [ |
|
251 (r'\b(?:0dbfs|A4|k(?:r|smps)|nchnls(?:_i)?|sr)\b', Name.Variable.Global), |
|
252 |
|
253 include('numbers'), |
|
254 |
|
255 (r'\+=|-=|\*=|/=|<<|>>|<=|>=|==|!=|&&|\|\||[~¬]|[=!+\-*/^%&|<>#?:]', Operator), |
|
256 (r'[(),\[\]]', Punctuation), |
|
257 |
|
258 (r'"', String, 'quoted string'), |
|
259 (r'\{\{', String, 'braced string'), |
|
260 |
|
261 (words(( |
|
262 'do', 'else', 'elseif', 'endif', 'enduntil', 'fi', 'if', 'ithen', 'kthen', |
|
263 'od', 'then', 'until', 'while', |
|
264 ), prefix=r'\b', suffix=r'\b'), Keyword), |
|
265 (words(('return', 'rireturn'), prefix=r'\b', suffix=r'\b'), Keyword.Pseudo), |
|
266 |
|
267 (r'\b[ik]?goto\b', Keyword, 'goto label'), |
|
268 (r'\b(r(?:einit|igoto)|tigoto)(\(|\b)', bygroups(Keyword.Pseudo, Punctuation), |
|
269 'goto label'), |
|
270 (r'\b(c(?:g|in?|k|nk?)goto)(\(|\b)', bygroups(Keyword.Pseudo, Punctuation), |
|
271 ('goto label', 'goto argument')), |
|
272 (r'\b(timout)(\(|\b)', bygroups(Keyword.Pseudo, Punctuation), |
|
273 ('goto label', 'goto argument', 'goto argument')), |
|
274 (r'\b(loop_[gl][et])(\(|\b)', bygroups(Keyword.Pseudo, Punctuation), |
|
275 ('goto label', 'goto argument', 'goto argument', 'goto argument')), |
|
276 |
|
277 (r'\bprintk?s\b', Name.Builtin, 'prints opcode'), |
|
278 (r'\b(?:readscore|scoreline(?:_i)?)\b', Name.Builtin, 'Csound score opcode'), |
|
279 (r'\bpyl?run[it]?\b', Name.Builtin, 'Python opcode'), |
|
280 (r'\blua_(?:exec|opdef)\b', Name.Builtin, 'Lua opcode'), |
|
281 (r'\bp\d+\b', Name.Variable.Instance), |
|
282 (r'\b([A-Z_a-z]\w*)(?:(:)([A-Za-z]))?\b', name_callback) |
|
283 ], |
|
284 |
|
285 'instrument numbers and identifiers': [ |
|
286 include('whitespace and macro uses'), |
|
287 (r'\d+|[A-Z_a-z]\w*', Name.Function), |
|
288 (r'[+,]', Punctuation), |
|
289 (r'\n', Text, '#pop') |
|
290 ], |
|
291 |
|
292 'after opcode keyword': [ |
|
293 include('whitespace and macro uses'), |
|
294 (r'[A-Z_a-z]\w*', opcode_name_callback, ('#pop', 'opcode type signatures')), |
|
295 (r'\n', Text, '#pop') |
|
296 ], |
|
297 'opcode type signatures': [ |
|
298 include('whitespace and macro uses'), |
|
299 |
|
300 # https://github.com/csound/csound/search?q=XIDENT+path%3AEngine+filename%3Acsound_orc.lex |
|
301 (r'0|[afijkKoOpPStV\[\]]+', Keyword.Type), |
|
302 |
|
303 (r',', Punctuation), |
|
304 (r'\n', Text, '#pop') |
|
305 ], |
|
306 |
|
307 'quoted string': [ |
|
308 (r'"', String, '#pop'), |
|
309 (r'[^\\"$%)]+', String), |
|
310 include('macro uses'), |
|
311 include('escape sequences'), |
|
312 include('format specifiers'), |
|
313 (r'[\\$%)]', String) |
|
314 ], |
|
315 'braced string': [ |
|
316 (r'\}\}', String, '#pop'), |
|
317 (r'(?:[^\\%)}]|\}(?!\}))+', String), |
|
318 include('escape sequences'), |
|
319 include('format specifiers'), |
|
320 (r'[\\%)]', String) |
|
321 ], |
|
322 'escape sequences': [ |
|
323 # https://github.com/csound/csound/search?q=unquote_string+path%3AEngine+filename%3Acsound_orc_compile.c |
|
324 (r'\\(?:[\\abnrt"]|[0-7]{1,3})', String.Escape) |
|
325 ], |
|
326 # Format specifiers are highlighted in all strings, even though only |
|
327 # fprintks https://csound.github.io/docs/manual/fprintks.html |
|
328 # fprints https://csound.github.io/docs/manual/fprints.html |
|
329 # printf/printf_i https://csound.github.io/docs/manual/printf.html |
|
330 # printks https://csound.github.io/docs/manual/printks.html |
|
331 # prints https://csound.github.io/docs/manual/prints.html |
|
332 # sprintf https://csound.github.io/docs/manual/sprintf.html |
|
333 # sprintfk https://csound.github.io/docs/manual/sprintfk.html |
|
334 # work with strings that contain format specifiers. In addition, these |
|
335 # opcodes’ handling of format specifiers is inconsistent: |
|
336 # - fprintks, fprints, printks, and prints do accept %a and %A |
|
337 # specifiers, but can’t accept %s specifiers. |
|
338 # - printf, printf_i, sprintf, and sprintfk don’t accept %a and %A |
|
339 # specifiers, but can accept %s specifiers. |
|
340 # See https://github.com/csound/csound/issues/747 for more information. |
|
341 'format specifiers': [ |
|
342 (r'%[#0\- +]*\d*(?:\.\d+)?[diuoxXfFeEgGaAcs]', String.Interpol), |
|
343 (r'%%', String.Escape) |
|
344 ], |
|
345 |
|
346 'goto argument': [ |
|
347 include('whitespace and macro uses'), |
|
348 (r',', Punctuation, '#pop'), |
|
349 include('partial statements') |
|
350 ], |
|
351 'goto label': [ |
|
352 include('whitespace and macro uses'), |
|
353 (r'\w+', Name.Label, '#pop'), |
|
354 default('#pop') |
|
355 ], |
|
356 |
|
357 'prints opcode': [ |
|
358 include('whitespace and macro uses'), |
|
359 (r'"', String, 'prints quoted string'), |
|
360 default('#pop') |
|
361 ], |
|
362 'prints quoted string': [ |
|
363 (r'\\\\[aAbBnNrRtT]', String.Escape), |
|
364 (r'%[!nNrRtT]|[~^]{1,2}', String.Escape), |
|
365 include('quoted string') |
|
366 ], |
|
367 |
|
368 'Csound score opcode': [ |
|
369 include('whitespace and macro uses'), |
|
370 (r'\{\{', String, 'Csound score'), |
|
371 (r'\n', Text, '#pop') |
|
372 ], |
|
373 'Csound score': [ |
|
374 (r'\}\}', String, '#pop'), |
|
375 (r'([^}]+)|\}(?!\})', using(CsoundScoreLexer)) |
|
376 ], |
|
377 |
|
378 'Python opcode': [ |
|
379 include('whitespace and macro uses'), |
|
380 (r'\{\{', String, 'Python'), |
|
381 (r'\n', Text, '#pop') |
|
382 ], |
|
383 'Python': [ |
|
384 (r'\}\}', String, '#pop'), |
|
385 (r'([^}]+)|\}(?!\})', using(PythonLexer)) |
|
386 ], |
|
387 |
|
388 'Lua opcode': [ |
|
389 include('whitespace and macro uses'), |
|
390 (r'\{\{', String, 'Lua'), |
|
391 (r'\n', Text, '#pop') |
|
392 ], |
|
393 'Lua': [ |
|
394 (r'\}\}', String, '#pop'), |
|
395 (r'([^}]+)|\}(?!\})', using(LuaLexer)) |
|
396 ] |
|
397 } |
|
398 |
|
399 |
|
400 class CsoundDocumentLexer(RegexLexer): |
|
401 """ |
|
402 For `Csound <https://csound.github.io>`_ documents. |
|
403 |
|
404 .. versionadded:: 2.1 |
|
405 """ |
|
406 |
|
407 name = 'Csound Document' |
|
408 aliases = ['csound-document', 'csound-csd'] |
|
409 filenames = ['*.csd'] |
|
410 |
|
411 # These tokens are based on those in XmlLexer in pygments/lexers/html.py. Making |
|
412 # CsoundDocumentLexer a subclass of XmlLexer rather than RegexLexer may seem like a |
|
413 # better idea, since Csound Document files look like XML files. However, Csound |
|
414 # Documents can contain Csound comments (preceded by //, for example) before and |
|
415 # after the root element, unescaped bitwise AND & and less than < operators, etc. In |
|
416 # other words, while Csound Document files look like XML files, they may not actually |
|
417 # be XML files. |
|
418 tokens = { |
|
419 'root': [ |
|
420 (r'/[*](.|\n)*?[*]/', Comment.Multiline), |
|
421 (r'(?:;|//).*$', Comment.Single), |
|
422 (r'[^/;<]+|/(?!/)', Text), |
|
423 |
|
424 (r'<\s*CsInstruments', Name.Tag, ('orchestra', 'tag')), |
|
425 (r'<\s*CsScore', Name.Tag, ('score', 'tag')), |
|
426 (r'<\s*[Hh][Tt][Mm][Ll]', Name.Tag, ('HTML', 'tag')), |
|
427 |
|
428 (r'<\s*[\w:.-]+', Name.Tag, 'tag'), |
|
429 (r'<\s*/\s*[\w:.-]+\s*>', Name.Tag) |
|
430 ], |
|
431 |
|
432 'orchestra': [ |
|
433 (r'<\s*/\s*CsInstruments\s*>', Name.Tag, '#pop'), |
|
434 (r'(.|\n)+?(?=<\s*/\s*CsInstruments\s*>)', using(CsoundOrchestraLexer)) |
|
435 ], |
|
436 'score': [ |
|
437 (r'<\s*/\s*CsScore\s*>', Name.Tag, '#pop'), |
|
438 (r'(.|\n)+?(?=<\s*/\s*CsScore\s*>)', using(CsoundScoreLexer)) |
|
439 ], |
|
440 'HTML': [ |
|
441 (r'<\s*/\s*[Hh][Tt][Mm][Ll]\s*>', Name.Tag, '#pop'), |
|
442 (r'(.|\n)+?(?=<\s*/\s*[Hh][Tt][Mm][Ll]\s*>)', using(HtmlLexer)) |
|
443 ], |
|
444 |
|
445 'tag': [ |
|
446 (r'\s+', Text), |
|
447 (r'[\w.:-]+\s*=', Name.Attribute, 'attr'), |
|
448 (r'/?\s*>', Name.Tag, '#pop') |
|
449 ], |
|
450 'attr': [ |
|
451 (r'\s+', Text), |
|
452 (r'".*?"', String, '#pop'), |
|
453 (r"'.*?'", String, '#pop'), |
|
454 (r'[^\s>]+', String, '#pop') |
|
455 ] |
|
456 } |