1"""
2 pygments.lexers.qvt
3 ~~~~~~~~~~~~~~~~~~~
4
5 Lexer for QVT Operational language.
6
7 :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9"""
10
11from pygments.lexer import RegexLexer, bygroups, include, combined, default, \
12 words
13from pygments.token import Text, Comment, Operator, Keyword, Punctuation, \
14 Name, String, Number
15
16__all__ = ['QVToLexer']
17
18
19class QVToLexer(RegexLexer):
20 """
21 For the QVT Operational Mapping language.
22
23 Reference for implementing this: «Meta Object Facility (MOF) 2.0
24 Query/View/Transformation Specification», Version 1.1 - January 2011
25 (https://www.omg.org/spec/QVT/1.1/), see §8.4, «Concrete Syntax» in
26 particular.
27
28 Notable tokens assignments:
29
30 - Name.Class is assigned to the identifier following any of the following
31 keywords: metamodel, class, exception, primitive, enum, transformation
32 or library
33
34 - Name.Function is assigned to the names of mappings and queries
35
36 - Name.Builtin.Pseudo is assigned to the pre-defined variables 'this',
37 'self' and 'result'.
38 """
39 # With obvious borrowings & inspiration from the Java, Python and C lexers
40
41 name = 'QVTO'
42 aliases = ['qvto', 'qvt']
43 filenames = ['*.qvto']
44 url = 'https://www.omg.org/spec/QVT/1.1'
45 version_added = ''
46
47 tokens = {
48 'root': [
49 (r'\n', Text),
50 (r'[^\S\n]+', Text),
51 (r'(--|//)(\s*)(directive:)?(.*)$',
52 bygroups(Comment, Comment, Comment.Preproc, Comment)),
53 # Uncomment the following if you want to distinguish between
54 # '/*' and '/**', à la javadoc
55 # (r'/[*]{2}(.|\n)*?[*]/', Comment.Multiline),
56 (r'/[*](.|\n)*?[*]/', Comment.Multiline),
57 (r'\\\n', Text),
58 (r'(and|not|or|xor|##?)\b', Operator.Word),
59 (r'(:{1,2}=|[-+]=)\b', Operator.Word),
60 (r'(@|<<|>>)\b', Keyword), # stereotypes
61 (r'!=|<>|==|=|!->|->|>=|<=|[.]{3}|[+/*%=<>&|.~]', Operator),
62 (r'[]{}:(),;[]', Punctuation),
63 (r'(true|false|unlimited|null)\b', Keyword.Constant),
64 (r'(this|self|result)\b', Name.Builtin.Pseudo),
65 (r'(var)\b', Keyword.Declaration),
66 (r'(from|import)\b', Keyword.Namespace, 'fromimport'),
67 (r'(metamodel|class|exception|primitive|enum|transformation|'
68 r'library)(\s+)(\w+)',
69 bygroups(Keyword.Word, Text, Name.Class)),
70 (r'(exception)(\s+)(\w+)',
71 bygroups(Keyword.Word, Text, Name.Exception)),
72 (r'(main)\b', Name.Function),
73 (r'(mapping|helper|query)(\s+)',
74 bygroups(Keyword.Declaration, Text), 'operation'),
75 (r'(assert)(\s+)\b', bygroups(Keyword, Text), 'assert'),
76 (r'(Bag|Collection|Dict|OrderedSet|Sequence|Set|Tuple|List)\b',
77 Keyword.Type),
78 include('keywords'),
79 ('"', String, combined('stringescape', 'dqs')),
80 ("'", String, combined('stringescape', 'sqs')),
81 include('name'),
82 include('numbers'),
83 # (r'([a-zA-Z_]\w*)(::)([a-zA-Z_]\w*)',
84 # bygroups(Text, Text, Text)),
85 ],
86
87 'fromimport': [
88 (r'(?:[ \t]|\\\n)+', Text),
89 (r'[a-zA-Z_][\w.]*', Name.Namespace),
90 default('#pop'),
91 ],
92
93 'operation': [
94 (r'::', Text),
95 (r'(.*::)([a-zA-Z_]\w*)([ \t]*)(\()',
96 bygroups(Text, Name.Function, Text, Punctuation), '#pop')
97 ],
98
99 'assert': [
100 (r'(warning|error|fatal)\b', Keyword, '#pop'),
101 default('#pop'), # all else: go back
102 ],
103
104 'keywords': [
105 (words((
106 'abstract', 'access', 'any', 'assert', 'blackbox', 'break',
107 'case', 'collect', 'collectNested', 'collectOne', 'collectselect',
108 'collectselectOne', 'composes', 'compute', 'configuration',
109 'constructor', 'continue', 'datatype', 'default', 'derived',
110 'disjuncts', 'do', 'elif', 'else', 'end', 'endif', 'except',
111 'exists', 'extends', 'forAll', 'forEach', 'forOne', 'from', 'if',
112 'implies', 'in', 'inherits', 'init', 'inout', 'intermediate',
113 'invresolve', 'invresolveIn', 'invresolveone', 'invresolveoneIn',
114 'isUnique', 'iterate', 'late', 'let', 'literal', 'log', 'map',
115 'merges', 'modeltype', 'new', 'object', 'one', 'ordered', 'out',
116 'package', 'population', 'property', 'raise', 'readonly',
117 'references', 'refines', 'reject', 'resolve', 'resolveIn',
118 'resolveone', 'resolveoneIn', 'return', 'select', 'selectOne',
119 'sortedBy', 'static', 'switch', 'tag', 'then', 'try', 'typedef',
120 'unlimited', 'uses', 'when', 'where', 'while', 'with', 'xcollect',
121 'xmap', 'xselect'), suffix=r'\b'), Keyword),
122 ],
123
124 # There is no need to distinguish between String.Single and
125 # String.Double: 'strings' is factorised for 'dqs' and 'sqs'
126 'strings': [
127 (r'[^\\\'"\n]+', String),
128 # quotes, percents and backslashes must be parsed one at a time
129 (r'[\'"\\]', String),
130 ],
131 'stringescape': [
132 (r'\\([\\btnfr"\']|u[0-3][0-7]{2}|u[0-7]{1,2})', String.Escape)
133 ],
134 'dqs': [ # double-quoted string
135 (r'"', String, '#pop'),
136 (r'\\\\|\\"', String.Escape),
137 include('strings')
138 ],
139 'sqs': [ # single-quoted string
140 (r"'", String, '#pop'),
141 (r"\\\\|\\'", String.Escape),
142 include('strings')
143 ],
144 'name': [
145 (r'[a-zA-Z_]\w*', Name),
146 ],
147 # numbers: excerpt taken from the python lexer
148 'numbers': [
149 (r'(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?', Number.Float),
150 (r'\d+[eE][+-]?[0-9]+', Number.Float),
151 (r'\d+', Number.Integer)
152 ],
153 }