|
1 # -*- coding: utf-8 -*- |
|
2 """ |
|
3 pygments.lexers.csound |
|
4 ~~~~~~~~~~~~~~~~~~~~~~ |
|
5 |
|
6 Lexers for CSound languages. |
|
7 |
|
8 :copyright: Copyright 2006-2015 by the Pygments team, see AUTHORS. |
|
9 :license: BSD, see LICENSE for details. |
|
10 """ |
|
11 |
|
12 import copy, re |
|
13 |
|
14 from pygments.lexer import RegexLexer, bygroups, default, include, using, words |
|
15 from pygments.token import Comment, Keyword, Name, Number, Operator, Punctuation, \ |
|
16 String, Text |
|
17 from pygments.lexers._csound_builtins import 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 # Subclasses must define a 'single-line string' state. |
|
29 tokens = { |
|
30 'whitespace': [ |
|
31 (r'[ \t]+', Text), |
|
32 (r'\\\n', Text), |
|
33 (r'/[*](.|\n)*?[*]/', Comment.Multiline) |
|
34 ], |
|
35 |
|
36 'macro call': [ |
|
37 (r'(\$\w+\.?)(\()', bygroups(Comment.Preproc, Punctuation), |
|
38 'function macro call'), |
|
39 (r'\$\w+(\.|\b)', Comment.Preproc) |
|
40 ], |
|
41 'function macro call': [ |
|
42 (r"((?:\\['\)]|[^'\)])+)(')", bygroups(Comment.Preproc, Punctuation)), |
|
43 (r"([^'\)]+)(\))", bygroups(Comment.Preproc, Punctuation), '#pop') |
|
44 ], |
|
45 |
|
46 'whitespace or macro call': [ |
|
47 include('whitespace'), |
|
48 include('macro call') |
|
49 ], |
|
50 |
|
51 'preprocessor directives': [ |
|
52 (r'#(e(nd(if)?|lse)|ifn?def|undef)\b|##', Comment.Preproc), |
|
53 (r'#include\b', Comment.Preproc, 'include'), |
|
54 (r'#[ \t]*define\b', Comment.Preproc, 'macro name'), |
|
55 (r'@+[ \t]*\d*', Comment.Preproc) |
|
56 ], |
|
57 |
|
58 'include': [ |
|
59 include('whitespace'), |
|
60 (r'"', String, 'single-line string') |
|
61 ], |
|
62 |
|
63 'macro name': [ |
|
64 include('whitespace'), |
|
65 (r'(\w+)(\()', bygroups(Comment.Preproc, Text), |
|
66 'function macro argument list'), |
|
67 (r'\w+', Comment.Preproc, 'object macro definition after name') |
|
68 ], |
|
69 'object macro definition after name': [ |
|
70 include('whitespace'), |
|
71 (r'#', Punctuation, 'object macro replacement text') |
|
72 ], |
|
73 'object macro replacement text': [ |
|
74 (r'(\\#|[^#])+', Comment.Preproc), |
|
75 (r'#', Punctuation, '#pop:3') |
|
76 ], |
|
77 'function macro argument list': [ |
|
78 (r"(\w+)(['#])", bygroups(Comment.Preproc, Punctuation)), |
|
79 (r'(\w+)(\))', bygroups(Comment.Preproc, Punctuation), |
|
80 'function macro definition after name') |
|
81 ], |
|
82 'function macro definition after name': [ |
|
83 (r'[ \t]+', Text), |
|
84 (r'#', Punctuation, 'function macro replacement text') |
|
85 ], |
|
86 'function macro replacement text': [ |
|
87 (r'(\\#|[^#])+', Comment.Preproc), |
|
88 (r'#', Punctuation, '#pop:4') |
|
89 ] |
|
90 } |
|
91 |
|
92 |
|
93 class CsoundScoreLexer(CsoundLexer): |
|
94 """ |
|
95 For `Csound <http://csound.github.io>`_ scores. |
|
96 |
|
97 .. versionadded:: 2.1 |
|
98 """ |
|
99 |
|
100 name = 'Csound Score' |
|
101 aliases = ['csound-score', 'csound-sco'] |
|
102 filenames = ['*.sco'] |
|
103 |
|
104 tokens = { |
|
105 'partial statement': [ |
|
106 include('preprocessor directives'), |
|
107 (r'\d+e[+-]?\d+|(\d+\.\d*|\d*\.\d+)(e[+-]?\d+)?', Number.Float), |
|
108 (r'0[xX][a-fA-F0-9]+', Number.Hex), |
|
109 (r'\d+', Number.Integer), |
|
110 (r'"', String, 'single-line string'), |
|
111 (r'[+\-*/%^!=<>|&#~.]', Operator), |
|
112 (r'[]()[]', Punctuation), |
|
113 (r'\w+', Comment.Preproc) |
|
114 ], |
|
115 |
|
116 'statement': [ |
|
117 include('whitespace or macro call'), |
|
118 newline + ('#pop',), |
|
119 include('partial statement') |
|
120 ], |
|
121 |
|
122 'root': [ |
|
123 newline, |
|
124 include('whitespace or macro call'), |
|
125 (r'[{}]', Punctuation, 'statement'), |
|
126 (r'[abefimq-tv-z]|[nN][pP]?', Keyword, 'statement') |
|
127 ], |
|
128 |
|
129 'single-line string': [ |
|
130 (r'"', String, '#pop'), |
|
131 (r'[^\\"]+', String) |
|
132 ] |
|
133 } |
|
134 |
|
135 |
|
136 class CsoundOrchestraLexer(CsoundLexer): |
|
137 """ |
|
138 For `Csound <http://csound.github.io>`_ orchestras. |
|
139 |
|
140 .. versionadded:: 2.1 |
|
141 """ |
|
142 |
|
143 name = 'Csound Orchestra' |
|
144 aliases = ['csound', 'csound-orc'] |
|
145 filenames = ['*.orc'] |
|
146 |
|
147 user_defined_opcodes = set() |
|
148 |
|
149 def opcode_name_callback(lexer, match): |
|
150 opcode = match.group(0) |
|
151 lexer.user_defined_opcodes.add(opcode) |
|
152 yield match.start(), Name.Function, opcode |
|
153 |
|
154 def name_callback(lexer, match): |
|
155 name = match.group(0) |
|
156 if re.match('p\d+$', name) or name in OPCODES: |
|
157 yield match.start(), Name.Builtin, name |
|
158 elif name in lexer.user_defined_opcodes: |
|
159 yield match.start(), Name.Function, name |
|
160 else: |
|
161 nameMatch = re.search(r'^(g?[aikSw])(\w+)', name) |
|
162 if nameMatch: |
|
163 yield nameMatch.start(1), Keyword.Type, nameMatch.group(1) |
|
164 yield nameMatch.start(2), Name, nameMatch.group(2) |
|
165 else: |
|
166 yield match.start(), Name, name |
|
167 |
|
168 tokens = { |
|
169 'label': [ |
|
170 (r'\b(\w+)(:)', bygroups(Name.Label, Punctuation)) |
|
171 ], |
|
172 |
|
173 'partial expression': [ |
|
174 include('preprocessor directives'), |
|
175 (r'\b(0dbfs|k(r|smps)|nchnls(_i)?|sr)\b', Name.Variable.Global), |
|
176 (r'\d+e[+-]?\d+|(\d+\.\d*|\d*\.\d+)(e[+-]?\d+)?', Number.Float), |
|
177 (r'0[xX][a-fA-F0-9]+', Number.Hex), |
|
178 (r'\d+', Number.Integer), |
|
179 (r'"', String, 'single-line string'), |
|
180 (r'{{', String, 'multi-line string'), |
|
181 (r'[+\-*/%^!=&|<>#~¬]', Operator), |
|
182 (r'[](),?:[]', Punctuation), |
|
183 (words(( |
|
184 # Keywords |
|
185 'do', 'else', 'elseif', 'endif', 'enduntil', 'fi', 'if', 'ithen', 'kthen', |
|
186 'od', 'then', 'until', 'while', |
|
187 # Opcodes that act as control structures |
|
188 'return', 'timout' |
|
189 ), prefix=r'\b', suffix=r'\b'), Keyword), |
|
190 (words(('goto', 'igoto', 'kgoto', 'rigoto', 'tigoto'), |
|
191 prefix=r'\b', suffix=r'\b'), Keyword, 'goto label'), |
|
192 (words(('cggoto', 'cigoto', 'cingoto', 'ckgoto', 'cngoto'), |
|
193 prefix=r'\b', suffix=r'\b'), Keyword, |
|
194 ('goto label', 'goto expression')), |
|
195 (words(('loop_ge', 'loop_gt', 'loop_le', 'loop_lt'), |
|
196 prefix=r'\b', suffix=r'\b'), Keyword, |
|
197 ('goto label', 'goto expression', 'goto expression', 'goto expression')), |
|
198 (r'\bscoreline(_i)?\b', Name.Builtin, 'scoreline opcode'), |
|
199 (r'\bpyl?run[it]?\b', Name.Builtin, 'python opcode'), |
|
200 (r'\blua_(exec|opdef)\b', Name.Builtin, 'lua opcode'), |
|
201 (r'\b[a-zA-Z_]\w*\b', name_callback) |
|
202 ], |
|
203 |
|
204 'expression': [ |
|
205 include('whitespace or macro call'), |
|
206 newline + ('#pop',), |
|
207 include('partial expression') |
|
208 ], |
|
209 |
|
210 'root': [ |
|
211 newline, |
|
212 include('whitespace or macro call'), |
|
213 (r'\binstr\b', Keyword, ('instrument block', 'instrument name list')), |
|
214 (r'\bopcode\b', Keyword, ('opcode block', 'opcode parameter list', |
|
215 'opcode types', 'opcode types', 'opcode name')), |
|
216 include('label'), |
|
217 default('expression') |
|
218 ], |
|
219 |
|
220 'instrument name list': [ |
|
221 include('whitespace or macro call'), |
|
222 (r'\d+|\+?[a-zA-Z_]\w*', Name.Function), |
|
223 (r',', Punctuation), |
|
224 newline + ('#pop',) |
|
225 ], |
|
226 'instrument block': [ |
|
227 newline, |
|
228 include('whitespace or macro call'), |
|
229 (r'\bendin\b', Keyword, '#pop'), |
|
230 include('label'), |
|
231 default('expression') |
|
232 ], |
|
233 |
|
234 'opcode name': [ |
|
235 include('whitespace or macro call'), |
|
236 (r'[a-zA-Z_]\w*', opcode_name_callback, '#pop') |
|
237 ], |
|
238 'opcode types': [ |
|
239 include('whitespace or macro call'), |
|
240 (r'0|[]afijkKoOpPStV[]+', Keyword.Type, '#pop'), |
|
241 (r',', Punctuation) |
|
242 ], |
|
243 'opcode parameter list': [ |
|
244 include('whitespace or macro call'), |
|
245 newline + ('#pop',) |
|
246 ], |
|
247 'opcode block': [ |
|
248 newline, |
|
249 include('whitespace or macro call'), |
|
250 (r'\bendop\b', Keyword, '#pop'), |
|
251 include('label'), |
|
252 default('expression') |
|
253 ], |
|
254 |
|
255 'goto label': [ |
|
256 include('whitespace or macro call'), |
|
257 (r'\w+', Name.Label, '#pop'), |
|
258 default('#pop') |
|
259 ], |
|
260 'goto expression': [ |
|
261 include('whitespace or macro call'), |
|
262 (r',', Punctuation, '#pop'), |
|
263 include('partial expression') |
|
264 ], |
|
265 |
|
266 'single-line string': [ |
|
267 include('macro call'), |
|
268 (r'"', String, '#pop'), |
|
269 # From https://github.com/csound/csound/blob/develop/Opcodes/fout.c#L1405 |
|
270 (r'%\d*(\.\d+)?[cdhilouxX]', String.Interpol), |
|
271 (r'%[!%nNrRtT]|[~^]|\\([\\aAbBnNrRtT"]|[0-7]{1,3})', String.Escape), |
|
272 (r'[^\\"~$%\^\n]+', String), |
|
273 (r'[\\"~$%\^\n]', String) |
|
274 ], |
|
275 'multi-line string': [ |
|
276 (r'}}', String, '#pop'), |
|
277 (r'[^\}]+|\}(?!\})', String) |
|
278 ], |
|
279 |
|
280 'scoreline opcode': [ |
|
281 include('whitespace or macro call'), |
|
282 (r'{{', String, 'scoreline'), |
|
283 default('#pop') |
|
284 ], |
|
285 'scoreline': [ |
|
286 (r'}}', String, '#pop'), |
|
287 (r'([^\}]+)|\}(?!\})', using(CsoundScoreLexer)) |
|
288 ], |
|
289 |
|
290 'python opcode': [ |
|
291 include('whitespace or macro call'), |
|
292 (r'{{', String, 'python'), |
|
293 default('#pop') |
|
294 ], |
|
295 'python': [ |
|
296 (r'}}', String, '#pop'), |
|
297 (r'([^\}]+)|\}(?!\})', using(PythonLexer)) |
|
298 ], |
|
299 |
|
300 'lua opcode': [ |
|
301 include('whitespace or macro call'), |
|
302 (r'"', String, 'single-line string'), |
|
303 (r'{{', String, 'lua'), |
|
304 (r',', Punctuation), |
|
305 default('#pop') |
|
306 ], |
|
307 'lua': [ |
|
308 (r'}}', String, '#pop'), |
|
309 (r'([^\}]+)|\}(?!\})', using(LuaLexer)) |
|
310 ] |
|
311 } |
|
312 |
|
313 |
|
314 class CsoundDocumentLexer(RegexLexer): |
|
315 """ |
|
316 For `Csound <http://csound.github.io>`_ documents. |
|
317 |
|
318 |
|
319 """ |
|
320 |
|
321 name = 'Csound Document' |
|
322 aliases = ['csound-document', 'csound-csd'] |
|
323 filenames = ['*.csd'] |
|
324 |
|
325 # These tokens are based on those in XmlLexer in pygments/lexers/html.py. Making |
|
326 # CsoundDocumentLexer a subclass of XmlLexer rather than RegexLexer may seem like a |
|
327 # better idea, since Csound Document files look like XML files. However, Csound |
|
328 # Documents can contain Csound comments (preceded by //, for example) before and |
|
329 # after the root element, unescaped bitwise AND & and less than < operators, etc. In |
|
330 # other words, while Csound Document files look like XML files, they may not actually |
|
331 # be XML files. |
|
332 tokens = { |
|
333 'root': [ |
|
334 newline, |
|
335 (r'/[*](.|\n)*?[*]/', Comment.Multiline), |
|
336 (r'[^<&;/]+', Text), |
|
337 (r'<\s*CsInstruments', Name.Tag, ('orchestra', 'tag')), |
|
338 (r'<\s*CsScore', Name.Tag, ('score', 'tag')), |
|
339 (r'<\s*[hH][tT][mM][lL]', Name.Tag, ('HTML', 'tag')), |
|
340 (r'<\s*[\w:.-]+', Name.Tag, 'tag'), |
|
341 (r'<\s*/\s*[\w:.-]+\s*>', Name.Tag) |
|
342 ], |
|
343 'orchestra': [ |
|
344 (r'<\s*/\s*CsInstruments\s*>', Name.Tag, '#pop'), |
|
345 (r'(.|\n)+?(?=<\s*/\s*CsInstruments\s*>)', using(CsoundOrchestraLexer)) |
|
346 ], |
|
347 'score': [ |
|
348 (r'<\s*/\s*CsScore\s*>', Name.Tag, '#pop'), |
|
349 (r'(.|\n)+?(?=<\s*/\s*CsScore\s*>)', using(CsoundScoreLexer)) |
|
350 ], |
|
351 'HTML': [ |
|
352 (r'<\s*/\s*[hH][tT][mM][lL]\s*>', Name.Tag, '#pop'), |
|
353 (r'(.|\n)+?(?=<\s*/\s*[hH][tT][mM][lL]\s*>)', using(HtmlLexer)) |
|
354 ], |
|
355 'tag': [ |
|
356 (r'\s+', Text), |
|
357 (r'[\w.:-]+\s*=', Name.Attribute, 'attr'), |
|
358 (r'/?\s*>', Name.Tag, '#pop') |
|
359 ], |
|
360 'attr': [ |
|
361 (r'\s+', Text), |
|
362 (r'".*?"', String, '#pop'), |
|
363 (r"'.*?'", String, '#pop'), |
|
364 (r'[^\s>]+', String, '#pop') |
|
365 ] |
|
366 } |