1#!/usr/bin/env python
2# Copyright (c) 2016 Google Inc.
3
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Generates Vim syntax rules for SPIR-V assembly (.spvasm) files"""
16
17from __future__ import print_function
18
19import json
20
21PREAMBLE="""" Vim syntax file
22" Language:   spvasm
23" Generated by SPIRV-Tools
24
25if version < 600
26  syntax clear
27elseif exists("b:current_syntax")
28  finish
29endif
30
31syn case match
32"""
33
34POSTAMBLE="""
35
36syntax keyword spvasmTodo TODO FIXME contained
37
38syn match   spvasmIdNumber /%\d\+\>/
39
40" The assembler treats the leading minus sign as part of the number token.
41" This applies to integers, and to floats below.
42syn match   spvasmNumber /-\?\<\d\+\>/
43
44" Floating point literals.
45" In general, C++ requires at least digit in the mantissa, and the
46" floating point is optional.  This applies to both the regular decimal float
47" case and the hex float case.
48
49" First case: digits before the optional decimal, no trailing digits.
50syn match   spvasmFloat  /-\?\d\+\.\?\(e[+-]\d\+\)\?/
51" Second case: optional digits before decimal, trailing digits
52syn match   spvasmFloat  /-\?\d*\.\d\+\(e[+-]\d\+\)\?/
53
54" First case: hex digits before the optional decimal, no trailing hex digits.
55syn match   spvasmFloat  /-\?0[xX]\\x\+\.\?p[-+]\d\+/
56" Second case: optional hex digits before decimal, trailing hex digits
57syn match   spvasmFloat  /-\?0[xX]\\x*\.\\x\+p[-+]\d\+/
58
59syn match   spvasmComment /;.*$/ contains=spvasmTodo
60syn region  spvasmString start=/"/ skip=/\\\\"/ end=/"/
61syn match   spvasmId /%[a-zA-Z_][a-zA-Z_0-9]*/
62
63" Highlight unknown constants and statements as errors
64syn match   spvasmError /[a-zA-Z][a-zA-Z_0-9]*/
65
66
67if version >= 508 || !exists("did_c_syn_inits")
68  if version < 508
69    let did_c_syn_inits = 1
70    command -nargs=+ HiLink hi link <args>
71  else
72    command -nargs=+ HiLink hi def link <args>
73  endif
74
75  HiLink spvasmStatement Statement
76  HiLink spvasmNumber Number
77  HiLink spvasmComment Comment
78  HiLink spvasmString String
79  HiLink spvasmFloat Float
80  HiLink spvasmConstant Constant
81  HiLink spvasmIdNumber Identifier
82  HiLink spvasmId Identifier
83  HiLink spvasmTodo Todo
84
85  delcommand HiLink
86endif
87
88let b:current_syntax = "spvasm"
89"""
90
91# This list is taken from the description of OpSpecConstantOp in SPIR-V 1.1.
92# TODO(dneto): Propose that this information be embedded in the grammar file.
93SPEC_CONSTANT_OP_OPCODES = """
94        OpSConvert, OpFConvert
95        OpSNegate, OpNot
96        OpIAdd, OpISub
97        OpIMul, OpUDiv, OpSDiv, OpUMod, OpSRem, OpSMod
98        OpShiftRightLogical, OpShiftRightArithmetic, OpShiftLeftLogical
99        OpBitwiseOr, OpBitwiseXor, OpBitwiseAnd
100        OpVectorShuffle, OpCompositeExtract, OpCompositeInsert
101        OpLogicalOr, OpLogicalAnd, OpLogicalNot,
102        OpLogicalEqual, OpLogicalNotEqual
103        OpSelect
104        OpIEqual, OpINotEqual
105        OpULessThan, OpSLessThan
106        OpUGreaterThan, OpSGreaterThan
107        OpULessThanEqual, OpSLessThanEqual
108        OpUGreaterThanEqual, OpSGreaterThanEqual
109
110        OpQuantizeToF16
111
112        OpConvertFToS, OpConvertSToF
113        OpConvertFToU, OpConvertUToF
114        OpUConvert
115        OpConvertPtrToU, OpConvertUToPtr
116        OpGenericCastToPtr, OpPtrCastToGeneric
117        OpBitcast
118        OpFNegate
119        OpFAdd, OpFSub
120        OpFMul, OpFDiv
121        OpFRem, OpFMod
122        OpAccessChain, OpInBoundsAccessChain
123        OpPtrAccessChain, OpInBoundsPtrAccessChain"""
124
125
126def EmitAsStatement(name):
127    """Emits the given name as a statement token"""
128    print('syn keyword spvasmStatement', name)
129
130
131def EmitAsEnumerant(name):
132    """Emits the given name as an named operand token"""
133    print('syn keyword spvasmConstant', name)
134
135
136def main():
137    """Parses arguments, then generates the Vim syntax rules for SPIR-V assembly
138    on stdout."""
139    import argparse
140    parser = argparse.ArgumentParser(description='Generate SPIR-V info tables')
141    parser.add_argument('--spirv-core-grammar', metavar='<path>',
142                        type=str, required=True,
143                        help='input JSON grammar file for core SPIR-V '
144                        'instructions')
145    parser.add_argument('--extinst-glsl-grammar', metavar='<path>',
146                        type=str, required=False, default=None,
147                        help='input JSON grammar file for GLSL extended '
148                        'instruction set')
149    parser.add_argument('--extinst-opencl-grammar', metavar='<path>',
150                        type=str, required=False, default=None,
151                        help='input JSON grammar file for OpenGL extended '
152                        'instruction set')
153    parser.add_argument('--extinst-debuginfo-grammar', metavar='<path>',
154                        type=str, required=False, default=None,
155                        help='input JSON grammar file for DebugInfo extended '
156                        'instruction set')
157    args = parser.parse_args()
158
159    # Generate the syntax rules.
160    print(PREAMBLE)
161
162    core = json.loads(open(args.spirv_core_grammar).read())
163    print('\n" Core instructions')
164    for inst in core["instructions"]:
165        EmitAsStatement(inst['opname'])
166    print('\n" Core operand enums')
167    for operand_kind in core["operand_kinds"]:
168        if 'enumerants' in operand_kind:
169            for e in operand_kind['enumerants']:
170                EmitAsEnumerant(e['enumerant'])
171
172    if args.extinst_glsl_grammar is not None:
173        print('\n" GLSL.std.450 extended instructions')
174        glsl = json.loads(open(args.extinst_glsl_grammar).read())
175        # These opcodes are really enumerant operands for the OpExtInst
176        # instruction.
177        for inst in glsl["instructions"]:
178            EmitAsEnumerant(inst['opname'])
179
180    if args.extinst_opencl_grammar is not None:
181        print('\n" OpenCL.std extended instructions')
182        opencl = json.loads(open(args.extinst_opencl_grammar).read())
183        for inst in opencl["instructions"]:
184            EmitAsEnumerant(inst['opname'])
185
186    if args.extinst_debuginfo_grammar is not None:
187        print('\n" DebugInfo extended instructions')
188        debuginfo = json.loads(open(args.extinst_debuginfo_grammar).read())
189        for inst in debuginfo["instructions"]:
190            EmitAsEnumerant(inst['opname'])
191        print('\n" DebugInfo operand enums')
192        for operand_kind in debuginfo["operand_kinds"]:
193            if 'enumerants' in operand_kind:
194                for e in operand_kind['enumerants']:
195                    EmitAsEnumerant(e['enumerant'])
196
197    print('\n" OpSpecConstantOp opcodes')
198    for word in SPEC_CONSTANT_OP_OPCODES.split(' '):
199        stripped = word.strip('\n,')
200        if stripped != "":
201            # Treat as an enumerant, but without the leading "Op"
202            EmitAsEnumerant(stripped[2:])
203    print(POSTAMBLE)
204
205
206if __name__ == '__main__':
207    main()
208