1"""
2 pygments.lexers.modeling
3 ~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Lexers for modeling languages.
6
7 :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9"""
10
11import re
12
13from pygments.lexer import RegexLexer, include, bygroups, using, default, words
14from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
15 Number, Punctuation, Whitespace
16
17from pygments.lexers.html import HtmlLexer
18from pygments.lexers import _stan_builtins
19
20__all__ = ['ModelicaLexer', 'BugsLexer', 'JagsLexer', 'StanLexer']
21
22
23class ModelicaLexer(RegexLexer):
24 """
25 For Modelica source code.
26 """
27 name = 'Modelica'
28 url = 'http://www.modelica.org/'
29 aliases = ['modelica']
30 filenames = ['*.mo']
31 mimetypes = ['text/x-modelica']
32 version_added = '1.1'
33
34 flags = re.DOTALL | re.MULTILINE
35
36 _name = r"(?:'(?:[^\\']|\\.)+'|[a-zA-Z_]\w*)"
37
38 tokens = {
39 'whitespace': [
40 (r'[\s\ufeff]+', Text),
41 (r'//[^\n]*\n?', Comment.Single),
42 (r'/\*.*?\*/', Comment.Multiline)
43 ],
44 'root': [
45 include('whitespace'),
46 (r'"', String.Double, 'string'),
47 (r'[()\[\]{},;]+', Punctuation),
48 (r'\.?[*^/+-]|\.|<>|[<>:=]=?', Operator),
49 (r'\d+(\.?\d*[eE][-+]?\d+|\.\d*)', Number.Float),
50 (r'\d+', Number.Integer),
51 (words(('abs', 'acos', 'actualStream', 'array', 'asin', 'assert',
52 'AssertionLevel', 'atan', 'atan2',
53 'backSample', 'Boolean', 'cardinality',
54 'cat', 'ceil', 'change', 'Clock',
55 'Connections', 'cos', 'cosh', 'cross',
56 'delay', 'diagonal', 'div', 'edge', 'exp',
57 'ExternalObject', 'fill', 'floor',
58 'getInstanceName', 'hold', 'homotopy',
59 'identity', 'inStream', 'integer',
60 'Integer', 'interval', 'inverse',
61 'isPresent', 'linspace', 'log', 'log10',
62 'matrix', 'max', 'min', 'mod', 'ndims',
63 'noClock', 'noEvent', 'ones',
64 'outerProduct', 'pre', 'previous',
65 'product', 'Real', 'reinit', 'rem',
66 'rooted', 'sample', 'scalar', 'semiLinear',
67 'shiftSample', 'sign', 'sin', 'sinh',
68 'size', 'skew', 'smooth',
69 'spatialDistribution', 'sqrt',
70 'StateSelect', 'String', 'subSample', 'sum',
71 'superSample', 'symmetric', 'tan', 'tanh',
72 'terminal', 'terminate', 'time',
73 'transpose', 'vector', 'zeros'), suffix=r'\b'), Name.Builtin),
74 (words(('algorithm', 'annotation', 'break', 'connect', 'constant',
75 'constrainedby', 'der', 'discrete', 'each',
76 'else', 'elseif', 'elsewhen',
77 'encapsulated', 'enumeration', 'equation',
78 'exit', 'expandable', 'extends', 'external',
79 'firstTick', 'final', 'flow', 'for', 'if',
80 'import', 'impure', 'in', 'initial',
81 'inner', 'input', 'interval', 'loop',
82 'nondiscrete', 'outer', 'output',
83 'parameter', 'partial', 'protected',
84 'public', 'pure', 'redeclare',
85 'replaceable', 'return', 'stream', 'then',
86 'when', 'while'), suffix=r'\b'),
87 Keyword.Reserved),
88 (r'(and|not|or)\b', Operator.Word),
89 (words(('block', 'class', 'connector', 'end', 'function', 'model',
90 'operator', 'package', 'record', 'type'), suffix=r'\b'), Keyword.Reserved, 'class'),
91 (r'(false|true)\b', Keyword.Constant),
92 (r'within\b', Keyword.Reserved, 'package-prefix'),
93 (_name, Name)
94 ],
95 'class': [
96 include('whitespace'),
97 (r'(function|record)\b', Keyword.Reserved),
98 (r'(if|for|when|while)\b', Keyword.Reserved, '#pop'),
99 (_name, Name.Class, '#pop'),
100 default('#pop')
101 ],
102 'package-prefix': [
103 include('whitespace'),
104 (_name, Name.Namespace, '#pop'),
105 default('#pop')
106 ],
107 'string': [
108 (r'"', String.Double, '#pop'),
109 (r'\\[\'"?\\abfnrtv]', String.Escape),
110 (r'(?i)<\s*html\s*>([^\\"]|\\.)+?(<\s*/\s*html\s*>|(?="))',
111 using(HtmlLexer)),
112 (r'<|\\?[^"\\<]+', String.Double)
113 ]
114 }
115
116
117class BugsLexer(RegexLexer):
118 """
119 Pygments Lexer for OpenBugs and WinBugs
120 models.
121 """
122
123 name = 'BUGS'
124 aliases = ['bugs', 'winbugs', 'openbugs']
125 filenames = ['*.bug']
126 url = 'https://www.mrc-bsu.cam.ac.uk/software/bugs/openbugs'
127 version_added = '1.6'
128
129 _FUNCTIONS = (
130 # Scalar functions
131 'abs', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctanh',
132 'cloglog', 'cos', 'cosh', 'cumulative', 'cut', 'density', 'deviance',
133 'equals', 'expr', 'gammap', 'ilogit', 'icloglog', 'integral', 'log',
134 'logfact', 'loggam', 'logit', 'max', 'min', 'phi', 'post.p.value',
135 'pow', 'prior.p.value', 'probit', 'replicate.post', 'replicate.prior',
136 'round', 'sin', 'sinh', 'solution', 'sqrt', 'step', 'tan', 'tanh',
137 'trunc',
138 # Vector functions
139 'inprod', 'interp.lin', 'inverse', 'logdet', 'mean', 'eigen.vals',
140 'ode', 'prod', 'p.valueM', 'rank', 'ranked', 'replicate.postM',
141 'sd', 'sort', 'sum',
142 # Special
143 'D', 'I', 'F', 'T', 'C')
144 """ OpenBUGS built-in functions
145
146 From http://www.openbugs.info/Manuals/ModelSpecification.html#ContentsAII
147
148 This also includes
149
150 - T, C, I : Truncation and censoring.
151 ``T`` and ``C`` are in OpenBUGS. ``I`` in WinBUGS.
152 - D : ODE
153 - F : Functional http://www.openbugs.info/Examples/Functionals.html
154
155 """
156
157 _DISTRIBUTIONS = ('dbern', 'dbin', 'dcat', 'dnegbin', 'dpois',
158 'dhyper', 'dbeta', 'dchisqr', 'ddexp', 'dexp',
159 'dflat', 'dgamma', 'dgev', 'df', 'dggamma', 'dgpar',
160 'dloglik', 'dlnorm', 'dlogis', 'dnorm', 'dpar',
161 'dt', 'dunif', 'dweib', 'dmulti', 'ddirch', 'dmnorm',
162 'dmt', 'dwish')
163 """ OpenBUGS built-in distributions
164
165 Functions from
166 http://www.openbugs.info/Manuals/ModelSpecification.html#ContentsAI
167 """
168
169 tokens = {
170 'whitespace': [
171 (r"\s+", Text),
172 ],
173 'comments': [
174 # Comments
175 (r'#.*$', Comment.Single),
176 ],
177 'root': [
178 # Comments
179 include('comments'),
180 include('whitespace'),
181 # Block start
182 (r'(model)(\s+)(\{)',
183 bygroups(Keyword.Namespace, Text, Punctuation)),
184 # Reserved Words
185 (r'(for|in)(?![\w.])', Keyword.Reserved),
186 # Built-in Functions
187 (r'({})(?=\s*\()'.format(r'|'.join(_FUNCTIONS + _DISTRIBUTIONS)),
188 Name.Builtin),
189 # Regular variable names
190 (r'[A-Za-z][\w.]*', Name),
191 # Number Literals
192 (r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', Number),
193 # Punctuation
194 (r'\[|\]|\(|\)|:|,|;', Punctuation),
195 # Assignment operators
196 # SLexer makes these tokens Operators.
197 (r'<-|~', Operator),
198 # Infix and prefix operators
199 (r'\+|-|\*|/', Operator),
200 # Block
201 (r'[{}]', Punctuation),
202 ]
203 }
204
205 def analyse_text(text):
206 if re.search(r"^\s*model\s*{", text, re.M):
207 return 0.7
208 else:
209 return 0.0
210
211
212class JagsLexer(RegexLexer):
213 """
214 Pygments Lexer for JAGS.
215 """
216
217 name = 'JAGS'
218 aliases = ['jags']
219 filenames = ['*.jag', '*.bug']
220 url = 'https://mcmc-jags.sourceforge.io'
221 version_added = '1.6'
222
223 # JAGS
224 _FUNCTIONS = (
225 'abs', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctanh',
226 'cos', 'cosh', 'cloglog',
227 'equals', 'exp', 'icloglog', 'ifelse', 'ilogit', 'log', 'logfact',
228 'loggam', 'logit', 'phi', 'pow', 'probit', 'round', 'sin', 'sinh',
229 'sqrt', 'step', 'tan', 'tanh', 'trunc', 'inprod', 'interp.lin',
230 'logdet', 'max', 'mean', 'min', 'prod', 'sum', 'sd', 'inverse',
231 'rank', 'sort', 't', 'acos', 'acosh', 'asin', 'asinh', 'atan',
232 # Truncation/Censoring (should I include)
233 'T', 'I')
234 # Distributions with density, probability and quartile functions
235 _DISTRIBUTIONS = tuple(f'[dpq]{x}' for x in
236 ('bern', 'beta', 'dchiqsqr', 'ddexp', 'dexp',
237 'df', 'gamma', 'gen.gamma', 'logis', 'lnorm',
238 'negbin', 'nchisqr', 'norm', 'par', 'pois', 'weib'))
239 # Other distributions without density and probability
240 _OTHER_DISTRIBUTIONS = (
241 'dt', 'dunif', 'dbetabin', 'dbern', 'dbin', 'dcat', 'dhyper',
242 'ddirch', 'dmnorm', 'dwish', 'dmt', 'dmulti', 'dbinom', 'dchisq',
243 'dnbinom', 'dweibull', 'ddirich')
244
245 tokens = {
246 'whitespace': [
247 (r"\s+", Text),
248 ],
249 'names': [
250 # Regular variable names
251 (r'[a-zA-Z][\w.]*\b', Name),
252 ],
253 'comments': [
254 # do not use stateful comments
255 (r'(?s)/\*.*?\*/', Comment.Multiline),
256 # Comments
257 (r'#.*$', Comment.Single),
258 ],
259 'root': [
260 # Comments
261 include('comments'),
262 include('whitespace'),
263 # Block start
264 (r'(model|data)(\s+)(\{)',
265 bygroups(Keyword.Namespace, Text, Punctuation)),
266 (r'var(?![\w.])', Keyword.Declaration),
267 # Reserved Words
268 (r'(for|in)(?![\w.])', Keyword.Reserved),
269 # Builtins
270 # Need to use lookahead because . is a valid char
271 (r'({})(?=\s*\()'.format(r'|'.join(_FUNCTIONS
272 + _DISTRIBUTIONS
273 + _OTHER_DISTRIBUTIONS)),
274 Name.Builtin),
275 # Names
276 include('names'),
277 # Number Literals
278 (r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', Number),
279 (r'\[|\]|\(|\)|:|,|;', Punctuation),
280 # Assignment operators
281 (r'<-|~', Operator),
282 # # JAGS includes many more than OpenBUGS
283 (r'\+|-|\*|\/|\|\|[&]{2}|[<>=]=?|\^|%.*?%', Operator),
284 (r'[{}]', Punctuation),
285 ]
286 }
287
288 def analyse_text(text):
289 if re.search(r'^\s*model\s*\{', text, re.M):
290 if re.search(r'^\s*data\s*\{', text, re.M):
291 return 0.9
292 elif re.search(r'^\s*var', text, re.M):
293 return 0.9
294 else:
295 return 0.3
296 else:
297 return 0
298
299
300class StanLexer(RegexLexer):
301 """Pygments Lexer for Stan models.
302
303 The Stan modeling language is specified in the *Stan Modeling Language
304 User's Guide and Reference Manual, v2.17.0*,
305 `pdf <https://github.com/stan-dev/stan/releases/download/v2.17.0/stan-reference-2.17.0.pdf>`__.
306 """
307
308 name = 'Stan'
309 aliases = ['stan']
310 filenames = ['*.stan']
311 url = 'https://mc-stan.org'
312 version_added = '1.6'
313
314 tokens = {
315 'whitespace': [
316 (r"\s+", Text),
317 ],
318 'comments': [
319 (r'(?s)/\*.*?\*/', Comment.Multiline),
320 # Comments
321 (r'(//|#).*$', Comment.Single),
322 ],
323 'root': [
324 (r'"[^"]*"', String),
325 # Comments
326 include('comments'),
327 # block start
328 include('whitespace'),
329 # Block start
330 (r'({})(\s*)(\{{)'.format(r'|'.join(('functions', 'data', r'transformed\s+?data',
331 'parameters', r'transformed\s+parameters',
332 'model', r'generated\s+quantities'))),
333 bygroups(Keyword.Namespace, Text, Punctuation)),
334 # target keyword
335 (r'target\s*\+=', Keyword),
336 # jacobian += statement
337 (r'jacobian\s*\+=', Keyword),
338 # Reserved Words
339 (r'({})\b'.format(r'|'.join(_stan_builtins.KEYWORDS)), Keyword),
340 # Truncation
341 (r'T(?=\s*\[)', Keyword),
342 # Data types
343 (r'({})\b'.format(r'|'.join(_stan_builtins.TYPES)), Keyword.Type),
344 # < should be punctuation, but elsewhere I can't tell if it is in
345 # a range constraint
346 (r'(<)(\s*)(upper|lower|offset|multiplier)(\s*)(=)',
347 bygroups(Operator, Whitespace, Keyword, Whitespace, Punctuation)),
348 (r'(,)(\s*)(upper)(\s*)(=)',
349 bygroups(Punctuation, Whitespace, Keyword, Whitespace, Punctuation)),
350 # Punctuation
351 (r"[;,\[\]()]", Punctuation),
352 # Builtin
353 (r'({})(?=\s*\()'.format('|'.join(_stan_builtins.FUNCTIONS)), Name.Builtin),
354 (r'(~)(\s*)({})(?=\s*\()'.format('|'.join(_stan_builtins.DISTRIBUTIONS)),
355 bygroups(Operator, Whitespace, Name.Builtin)),
356 # Special names ending in __, like lp__
357 (r'[A-Za-z]\w*__\b', Name.Builtin.Pseudo),
358 (r'({})\b'.format(r'|'.join(_stan_builtins.RESERVED)), Keyword.Reserved),
359 # user-defined functions
360 (r'[A-Za-z]\w*(?=\s*\()]', Name.Function),
361 # Imaginary Literals
362 (r'[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?i', Number.Float),
363 (r'\.[0-9]+([eE][+-]?[0-9]+)?i', Number.Float),
364 (r'[0-9]+i', Number.Float),
365 # Real Literals
366 (r'[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?', Number.Float),
367 (r'\.[0-9]+([eE][+-]?[0-9]+)?', Number.Float),
368 # Integer Literals
369 (r'[0-9]+', Number.Integer),
370 # Regular variable names
371 (r'[A-Za-z]\w*\b', Name),
372 # Assignment operators
373 (r'<-|(?:\+|-|\.?/|\.?\*|=)?=|~', Operator),
374 # Infix, prefix and postfix operators (and = )
375 (r"\+|-|\.?\*|\.?/|\\|'|\.?\^|!=?|<=?|>=?|\|\||&&|%|\?|:|%/%|!", Operator),
376 # Block delimiters
377 (r'[{}]', Punctuation),
378 # Distribution |
379 (r'\|', Punctuation)
380 ]
381 }
382
383 def analyse_text(text):
384 if re.search(r'^\s*parameters\s*\{', text, re.M):
385 return 1.0
386 else:
387 return 0.0