csound.py 16 KB

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