csound.py 16 KB

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