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