1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# SPDX-FileCopyrightText: 2014 Denis Steckelmacher <steckdenis@yahoo.fr>
5#
6# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
7
8from jsgenerator import *
9
10def license():
11    # Print the license of the generated file (the same as the one of this file)
12    print("""
13/*
14 * ==== FILE AUTOGENERATED FROM .idl FILES UNDER THE FOLLOWING LICENSE ====
15 *
16 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
20 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 *    notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 *    notice, this list of conditions and the following disclaimer in the
25 *    documentation and/or other materials provided with the distribution.
26 *
27 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
28 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
31 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
35 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39""")
40
41def get_type(decl):
42    typename = ' '.join(decl[0:-1])
43
44    if typename == 'void':
45        typename = ''
46    elif typename == 'any':
47        typename = '_mixed'
48    elif typename == 'boolean' or typename == 'bool':
49        typename = 'true'
50    elif typename == 'DOMString':
51        typename = "''"
52    elif typename == 'DOMObject':
53        typename = 'new Object()'
54    elif typename == 'DOMTimeStamp':
55        typename = 'new Date()'
56    elif 'unsigned' in typename or ' short' in typename or ' int' in typename or ' long' in typename or typename in ['short', 'int', 'long']:
57        typename = '1'
58    elif typename == 'float' or typename == 'double':
59        typename = '1.0'
60    elif typename == 'Array':
61        typename = '[]'
62    elif 'Callback' in typename or 'Handler' in typename:
63        typename = 'function(){}'
64    elif 'Constructor' in typename:
65        typename = typename.replace('Constructor', '')
66    else:
67        typename = 'new %s()' % typename
68
69    return typename
70
71def get_name(decl):
72    return decl[-1]
73
74member_decl = []
75param_decl = []
76params = []
77skip_end_of_line = False
78in_module = False
79in_interface = False
80in_inherit = False
81in_param = False
82cl = None
83
84def parse(token):
85    global member_decl, param_decl, params, skip_end_of_line, in_module, in_interface, in_inherit, in_param, cl
86
87    if token in ['in', 'readonly', 'optional', 'attribute', 'getter', 'setter', '{', '}']:
88        pass
89    elif token == 'raises':
90        skip_end_of_line = True
91    elif token == 'module':
92        in_module = True
93    elif token == 'interface':
94        in_module = False
95        in_interface = True
96    elif in_module:
97        # Skip the module declaration
98        pass
99    elif in_interface:
100        # Interface name
101        cl = Class(token)
102        in_interface = False
103
104        if token == 'DOMWindow':
105            # Export window, the only thing that should be exposed to the outside world
106            print('module.exports = DOMWindow;')
107
108    elif token == ';':
109        # End the current declaration
110        if len(member_decl) == 0:
111            # When the end of a class is reached, an empty declaration is produced
112            cl.print()
113            return
114
115        if in_param:
116            # Declare a method
117            cl.member(F(get_type(member_decl), get_name(member_decl), \
118                *[(get_name(p), get_type(p)) for p in params]
119            ))
120        else:
121            # Declare a member variable
122            if get_type(member_decl) is not None and get_name(member_decl)[0].isalpha():
123                cl.member(Var(get_type(member_decl), get_name(member_decl)))
124
125        member_decl.clear()
126        params.clear()
127        skip_end_of_line = False
128        in_param = False
129    elif skip_end_of_line:
130        # Skip everything until the colon
131        pass
132    elif token == ':':
133        in_inherit = True
134    elif in_inherit:
135        cl.prototype(token)
136        in_inherit = False
137    elif token == '(':
138        # Begin the parameter list
139        in_param = True
140    elif token == ')' or token == ',':
141        # End of a parameter
142        if len(param_decl) != 0:
143            params.append(param_decl[:])
144            param_decl.clear()
145
146        pass
147    elif in_param:
148        # Add a token to the parameter declaration
149        param_decl.append(token)
150    else:
151        # Add a token to the member declaration
152        member_decl.append(token)
153
154def tokenize(data):
155    token = ''
156    prev_c = ''
157    c = ''
158    next_c = ''
159    in_comment = 'none'
160    bracket_depth = 0
161
162    for i in range(len(data) + 1):
163        prev_c = c
164        c = next_c
165
166        if i < len(data):
167            next_c = data[i]
168
169        # Handle single-line and multi-line comments
170        if in_comment == 'singleline':
171            if c == '\n':
172                in_comment = 'none'
173            continue
174
175        if in_comment == 'multiline':
176            if prev_c == '*' and c == '/':
177                in_comment = 'none'
178            continue
179
180        if c == '/' and next_c == '*':
181            in_comment = 'multiline'
182            continue
183
184        if c == '/' and next_c == '/':
185            in_comment = 'singleline'
186            continue
187
188        if c == '#':
189            # Skip preprocessor macros: consider them as comments
190            in_comment = 'singleline'
191            continue
192
193        # Skip hints between brackets
194        if c == '[':
195            bracket_depth += 1
196            continue
197        elif c == ']':
198            bracket_depth -= 1
199            continue
200
201        if bracket_depth > 0:
202            continue
203
204        # Spaces are used to separate tokens
205        if not c.isalnum() or not token.isalnum():
206            if token != '':
207                parse(token)
208                token = ''
209
210            if c.isspace():
211                continue
212
213        token += c
214
215def main():
216    license()
217
218    for fl in sys.argv[1:]:
219        f = open(fl, 'r')
220        tokenize(f.read())
221        f.close()
222
223if __name__ == '__main__':
224    if len(sys.argv) < 2:
225        print('Usage: idltojs.py <idl...>')
226    else:
227        main()
228