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