Subversion Repositories Kolibri OS

Rev

Rev 9407 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 9407 Rev 9408
Line 6... Line 6...
6
import hashlib
6
import hashlib
7
import difflib
7
import difflib
Line 8... Line 8...
8
 
8
 
9
# fasm keywords
9
# fasm keywords
154
    "xchg", "xend", "xgetbv", "xlat", "xlatb", "xor", "xorpd", "xorps", 
154
    "xchg", "xend", "xgetbv", "xlat", "xlatb", "xor", "xorpd", "xorps",
155
    "xrstor", "xrstors", "xsave", "xsavec", "xsaveopt", "xsaves", "xsetbv",
155
    "xrstor", "xrstors", "xsave", "xsavec", "xsaveopt", "xsaves", "xsetbv",
Line 156... Line 156...
156
]
156
]
157
 
157
 
Line 164... Line 164...
164
    "dq", "rq",
164
    "dq", "rq",
165
    "dt", "rt",
165
    "dt", "rt",
166
    "du",
166
    "du",
167
]
167
]
Line -... Line 168...
-
 
168
 
168
 
169
 
169
# Add kind flag to identifier in id2kind
170
# Add kind flag to identifier in id2kind
170
def id_add_kind(identifier, kind):
171
def id_add_kind(identifier, kind):
171
    if identifier not in id2kind:
172
    if identifier not in id2kind:
172
        id2kind[identifier] = ''
173
        id2kind[identifier] = ''
Line -... Line 174...
-
 
174
    id2kind[identifier] += kind
173
    id2kind[identifier] += kind
175
 
174
 
176
 
175
# Remove kind flag of identifier in id2kind
177
# Remove kind flag of identifier in id2kind
176
def id_remove_kind(identifier, kind):
178
def id_remove_kind(identifier, kind):
177
    if identifier in id2kind:
179
    if identifier in id2kind:
Line -... Line 180...
-
 
180
        if kind in id2kind[identifier]:
178
        if kind in id2kind[identifier]:
181
            id2kind[identifier] = id2kind[identifier].replace(kind, '')
179
            id2kind[identifier] = id2kind[identifier].replace(kind, '')
182
 
180
 
183
 
181
# Get kind of an identifier
184
# Get kind of an identifier
182
def id_get_kind(identifier):
185
def id_get_kind(identifier):
183
    if identifier in id2kind:
186
    if identifier in id2kind:
Line -... Line 187...
-
 
187
        return id2kind[identifier]
184
        return id2kind[identifier]
188
    else:
185
    else:
189
        return ''
186
        return ''
190
 
187
 
191
 
188
class LegacyAsmReader:
192
class LegacyAsmReader:
Line 194... Line 198...
194
 
198
 
195
    def currline(self):
199
    def currline(self):
Line 196... Line 200...
196
        return self.lines[self.line_idx]
200
        return self.lines[self.line_idx]
-
 
201
 
197
 
202
    def curr(self):
-
 
203
        try:
198
    def curr(self):
204
            return self.lines[self.line_idx][self.i]
Line 199... Line 205...
199
        try: return self.lines[self.line_idx][self.i]
205
        except:
200
        except: return ''
206
            return ''
201
 
207
 
202
    def step(self):
208
    def step(self):
Line 228... Line 234...
228
    def no_lines(self):
234
    def no_lines(self):
229
        if self.line_idx >= len(self.lines):
235
        if self.line_idx >= len(self.lines):
230
            return True
236
            return True
231
        return False
237
        return False
Line 232... Line 238...
232
 
238
 
233
    def location(self): 
239
    def location(self):
Line 234... Line 240...
234
        return f"{self.file}:{self.line_idx + 1}"
240
        return f"{self.file}:{self.line_idx + 1}"
235
 
241
 
236
    def skip_spaces(self):
242
    def skip_spaces(self):
Line -... Line 243...
-
 
243
        while self.curr().isspace():
237
        while self.curr().isspace():
244
            self.step()
238
            self.step()
245
 
239
 
246
 
240
class AsmReaderRecognizingStrings(LegacyAsmReader):
247
class AsmReaderRecognizingStrings(LegacyAsmReader):
241
    def __init__(self, file):
248
    def __init__(self, file):
Line 242... Line 249...
242
        super().__init__(file)
249
        super().__init__(file)
243
        self.in_string = None
250
        self.in_string = None
244
        self.should_recognize_strings = True
251
        self.should_recognize_strings = True
245
 
252
 
246
    def step(self):
253
    def step(self):
247
        c = super().step()
254
        c = super().step()
248
        if self.should_recognize_strings and (c == '"' or c == "'"):
255
        if self.should_recognize_strings and (c == '"' or c == "'"):
249
            # If just now we was at the double or single quotation mark
256
            # If just now we was at the double or single quotation mark
250
            # and we aren't in a string yet
257
            # and we aren't in a string yet then say
251
            # then say "we are in a string openned with this quotation mark now"
258
            # "we are in a string openned with this quotation mark now"
252
            if self.in_string == None:
259
            if self.in_string is None:
253
                self.in_string = c
260
                self.in_string = c
254
            # If just now we was at the double or single quotation mark
261
            # If just now we was at the double or single quotation mark
255
            # and we are in the string entered with the same quotation mark
262
            # and we are in the string entered with the same quotation mark
Line -... Line 263...
-
 
263
            # then say "we aren't in a string anymore"
256
            # then say "we aren't in a string anymore"
264
            elif self.in_string == c:
257
            elif self.in_string == c:
265
                self.in_string = None
258
                self.in_string = None
266
        return c
259
        return c
267
 
260
 
268
 
Line 280... Line 288...
280
 
288
 
281
    def status_set_has_code(self):
289
    def status_set_has_code(self):
Line 282... Line 290...
282
        self.status_has_code = True
290
        self.status_has_code = True
283
 
291
 
-
 
292
    def update_status(self):
284
    def update_status(self):
293
        # If we aren't in a comment and we aren't in a string -
-
 
294
        # say we are now in a comment if ';' met
-
 
295
        if (not self.status_has_comment and
285
        # If we aren't in a comment and we aren't in a string - say we are now in a comment if ';' met
296
            not self.in_string and
286
        if not self.status_has_comment and not self.in_string and self.curr() == ';':
297
            self.curr() == ';'):
287
            self.status_set_has_comment()
298
            self.status_set_has_comment()
288
        # Else if we are in a comment - collect the comment
299
        # Else if we are in a comment - collect the comment
289
        elif self.status_has_comment:
300
        elif self.status_has_comment:
Line 303... Line 314...
303
    def nextline(self):
314
    def nextline(self):
304
        prev_line = self.currline()
315
        prev_line = self.currline()
305
        super().nextline()
316
        super().nextline()
306
        # If the line we leave was not a comment-only line
317
        # If the line we leave was not a comment-only line
307
        # then forget the collected comment
318
        # then forget the collected comment
308
        # Otherwise the collected comment should be complemented by comment from next line in step()
319
        # Otherwise the collected comment should be complemented by
-
 
320
        # comment from next line in step()
309
        if self.status_has_code:
321
        if self.status_has_code:
310
            # But we should preserve comment for the next line
322
            # But we should preserve comment for the next line
311
            # If previous line set align (cause many functions re documented
323
            # If previous line set align (cause many functions re documented
312
            # right before align set, not before their labels)
324
            # right before align set, not before their labels)
313
            if not prev_line.startswith("align "):
325
            if not prev_line.startswith("align "):
314
                self.comment = ''
326
                self.comment = ''
315
        # Reset the line status (now it's the status of the new line)
327
        # Reset the line status (now it's the status of the new line)
316
        self.status_reset()
328
        self.status_reset()
317
        # Set new status for this line according to the first character in the line
329
        # Set new status for this line according to the
-
 
330
        # first character in the line
318
        self.update_status()
331
        self.update_status()
Line -... Line 332...
-
 
332
 
319
 
333
 
320
class AsmReaderFetchingIdentifiers(AsmReaderReadingComments):
334
class AsmReaderFetchingIdentifiers(AsmReaderReadingComments):
321
    def __init__(self, file):
335
    def __init__(self, file):
Line 322... Line 336...
322
        super().__init__(file)
336
        super().__init__(file)
Line 326... Line 340...
326
        result = ''
340
        result = ''
327
        while is_id(self.curr()):
341
        while is_id(self.curr()):
328
            result += self.step()
342
            result += self.step()
329
        return result
343
        return result
Line -... Line 344...
-
 
344
 
330
 
345
 
331
class AsmReader(AsmReaderFetchingIdentifiers):
346
class AsmReader(AsmReaderFetchingIdentifiers):
332
    def __init__(self, file):
347
    def __init__(self, file):
Line -... Line 348...
-
 
348
        super().__init__(file)
333
        super().__init__(file)
349
 
334
 
350
 
335
def append_file(full_path, contents):
351
def append_file(full_path, contents):
336
    if debug_mode:
352
    if debug_mode:
337
        if full_path not in output_files:
353
        if full_path not in output_files:
338
            output_files[full_path] = ""
354
            output_files[full_path] = ""
339
        output_files[full_path] += contents
355
        output_files[full_path] += contents
340
    else:
356
    else:
341
        f = open(full_path, "a")
357
        f = open(full_path, "a")
Line -... Line 358...
-
 
358
        f.write(contents)
342
        f.write(contents)
359
        f.close()
343
        f.close()
360
 
344
 
361
 
Line 345... Line 362...
345
class AsmElement:
362
class AsmElement:
-
 
363
    def __init__(self, location, name, comment):
346
    def __init__(self, location, name, comment):
364
        global warnings
347
        global warnings
365
 
348
 
366
        # If the element was constructed during this execution then
349
        # If the element was constructed during this execution then the element is new
367
        # the element is new
350
        self.new = True
368
        self.new = True
Line 359... Line 377...
359
 
377
 
360
    def dump(self):
378
    def dump(self):
361
        print(f"\n{self.location}: {self.name}")
379
        print(f"\n{self.location}: {self.name}")
Line 362... Line 380...
362
        print(f"{self.comment}")
380
        print(f"{self.comment}")
363
 
381
 
364
    def emit(self, dest, doxycomment = '', declaration = ''):
382
    def emit(self, dest, doxycomment='', declaration=''):
365
        # Do not emit anything if the symbol is marked as hidden in its comment
383
        # Do not emit anything if the symbol is marked as hidden in its comment
Line 366... Line 384...
366
        if '@dont_give_a_doxygen' in self.comment:
384
        if '@dont_give_a_doxygen' in self.comment:
Line 372... Line 390...
372
            declaration = f'#define {self.name}'
390
            declaration = f'#define {self.name}'
373
        # Check doxycomment
391
        # Check doxycomment
374
        if not doxycomment.endswith('\n'):
392
        if not doxycomment.endswith('\n'):
375
            doxycomment += '\n'
393
            doxycomment += '\n'
376
        if doxycomment.split('@brief ')[1][0].islower():
394
        if doxycomment.split('@brief ')[1][0].islower():
377
            warnings += f"{self.location}: Brief comment starting from lowercase\n"
395
            warnings += (f"{self.location}: Brief comment starting from " +
-
 
396
                         "lowercase\n")
378
        # Build contents to emit
397
        # Build contents to emit
379
        contents = ''
398
        contents = ''
380
        contents += '/**\n'
399
        contents += '/**\n'
381
        contents += doxycomment
400
        contents += doxycomment
382
        contents += (f"@par Source\n" +
401
        contents += (f"@par Source\n" +
-
 
402
                     f"{self.file}:{self.line}\n")
403
                     f"#line-{self.line}'>{self.file}:{self.line}\n")
384
        contents += '*/\n'
404
        contents += '*/\n'
385
        contents += declaration
405
        contents += declaration
386
        contents += '\n\n'
406
        contents += '\n\n'
387
        # Get path to file to emit this
407
        # Get path to file to emit this
388
        full_path = dest + '/' + self.file
408
        full_path = dest + '/' + self.file
389
        # Remove the file on first access if it was created by previous generation
409
        # Remove the file on first access if it was
-
 
410
        # created by previous generation
390
        if full_path not in created_files:
411
        if full_path not in created_files:
391
            if os.path.isfile(full_path):
412
            if os.path.isfile(full_path):
392
                os.remove(full_path)
413
                os.remove(full_path)
393
            created_files.append(full_path)
414
            created_files.append(full_path)
394
        # Create directories need for the file
415
        # Create directories need for the file
395
        os.makedirs(os.path.dirname(full_path), exist_ok=True)
416
        os.makedirs(os.path.dirname(full_path), exist_ok=True)
396
        contents = ''.join([i if ord(i) < 128 else '?' for i in contents])
417
        contents = ''.join([i if ord(i) < 128 else '?' for i in contents])
Line 397... Line 418...
397
 
418
 
Line -... Line 419...
-
 
419
        append_file(full_path, contents)
398
        append_file(full_path, contents)
420
 
399
 
421
 
400
class AsmVariable(AsmElement):
422
class AsmVariable(AsmElement):
401
    def __init__(self, location, name, comment, type, init):
423
    def __init__(self, location, name, comment, type, init):
402
        super().__init__(location, name, comment)
424
        super().__init__(location, name, comment)
Line 420... Line 442...
420
        var_type = self.type.replace(".", "_")
442
        var_type = self.type.replace(".", "_")
421
        declaration = f"{var_type} {name};"
443
        declaration = f"{var_type} {name};"
422
        # Emit this
444
        # Emit this
423
        super().emit(dest, doxycomment, declaration)
445
        super().emit(dest, doxycomment, declaration)
Line -... Line 446...
-
 
446
 
424
 
447
 
425
class AsmFunction(AsmElement):
448
class AsmFunction(AsmElement):
-
 
449
    def __init__(self, location, name, comment, calling_convention,
426
    def __init__(self, location, name, comment, calling_convention, args, used_regs):
450
                 args, used_regs):
427
        super().__init__(location, name, comment)
451
        super().__init__(location, name, comment)
428
        self.calling_convention = calling_convention
452
        self.calling_convention = calling_convention
429
        self.args = args
453
        self.args = args
Line 471... Line 495...
471
        name = self.name.replace(".", "_")
495
        name = self.name.replace(".", "_")
472
        declaration = f"void {name}{arg_list};"
496
        declaration = f"void {name}{arg_list};"
473
        # Emit this
497
        # Emit this
474
        super().emit(dest, doxycomment, declaration)
498
        super().emit(dest, doxycomment, declaration)
Line -... Line 499...
-
 
499
 
475
 
500
 
476
class AsmLabel(AsmElement):
501
class AsmLabel(AsmElement):
477
    def __init__(self, location, name, comment):
502
    def __init__(self, location, name, comment):
Line 478... Line 503...
478
        super().__init__(location, name, comment)
503
        super().__init__(location, name, comment)
Line 491... Line 516...
491
        name = self.name.replace(".", "_")
516
        name = self.name.replace(".", "_")
492
        declaration = f"label {name};"
517
        declaration = f"label {name};"
493
        # Emit this
518
        # Emit this
494
        super().emit(dest, doxycomment, declaration)
519
        super().emit(dest, doxycomment, declaration)
Line -... Line 520...
-
 
520
 
495
 
521
 
496
class AsmMacro(AsmElement):
522
class AsmMacro(AsmElement):
497
    def __init__(self, location, name, comment, args):
523
    def __init__(self, location, name, comment, args):
498
        super().__init__(location, name, comment)
524
        super().__init__(location, name, comment)
Line 524... Line 550...
524
        # Build declaration
550
        # Build declaration
525
        declaration = f"#define {self.name}{arg_list}"
551
        declaration = f"#define {self.name}{arg_list}"
526
        # Emit this
552
        # Emit this
527
        super().emit(dest, doxycomment, declaration)
553
        super().emit(dest, doxycomment, declaration)
Line -... Line 554...
-
 
554
 
528
 
555
 
529
class AsmStruct(AsmElement):
556
class AsmStruct(AsmElement):
530
    def __init__(self, location, name, comment, members):
557
    def __init__(self, location, name, comment, members):
531
        super().__init__(location, name, comment)
558
        super().__init__(location, name, comment)
Line 544... Line 571...
544
        doxycomment += '\n'
571
        doxycomment += '\n'
545
        # Build declaration
572
        # Build declaration
546
        declaration = f"struct {self.name}" + " {\n"
573
        declaration = f"struct {self.name}" + " {\n"
547
        for member in self.members:
574
        for member in self.members:
548
            if type(member) == AsmVariable:
575
            if type(member) == AsmVariable:
549
                declaration += f'\t{member.type} {member.name}; /**< {member.comment} */\n'
576
                declaration += (f'\t{member.type} {member.name}; ' +
-
 
577
                                f'/**< {member.comment} */\n')
550
        declaration += '};'
578
        declaration += '};'
551
        # Emit this
579
        # Emit this
552
        super().emit(dest, doxycomment, declaration)
580
        super().emit(dest, doxycomment, declaration)
Line -... Line 581...
-
 
581
 
553
 
582
 
554
class AsmUnion(AsmElement):
583
class AsmUnion(AsmElement):
555
    def __init__(self, location, name, comment, members):
584
    def __init__(self, location, name, comment, members):
556
        super().__init__(location, name, comment)
585
        super().__init__(location, name, comment)
Line 569... Line 598...
569
        # Build declaration
598
        # Build declaration
570
        declaration = f"union {self.name}" + " {};"
599
        declaration = f"union {self.name}" + " {};"
571
        # Emit this
600
        # Emit this
572
        super().emit(dest, doxycomment, declaration)
601
        super().emit(dest, doxycomment, declaration)
Line -... Line 602...
-
 
602
 
573
 
603
 
574
class VariableNameIsMacroName:
604
class VariableNameIsMacroName:
575
    def __init__(self, name):
605
    def __init__(self, name):
Line -... Line 606...
-
 
606
        self.name = name
576
        self.name = name
607
 
577
 
608
 
Line -... Line 609...
-
 
609
def is_id(c):
578
def is_id(c):
610
    return c.isprintable() and c not in "+-/*=<>()[]{};:,|&~#`'\" \n\r\t\v"
579
    return c.isprintable() and c not in "+-/*=<>()[]{};:,|&~#`'\" \n\r\t\v"
611
 
Line -... Line 612...
-
 
612
 
580
 
613
def is_starts_as_id(s):
581
def is_starts_as_id(s):
614
    return not s[0].isdigit()
Line 582... Line 615...
582
    return not s[0].isdigit()
615
 
583
 
616
 
Line 618... Line 651...
618
        # Just skip whitespaces
651
        # Just skip whitespaces
619
        elif r.curr().isspace():
652
        elif r.curr().isspace():
620
            r.step()
653
            r.step()
621
        # Something unexpected
654
        # Something unexpected
622
        else:
655
        else:
623
            raise Exception(f"Unexpected symbol '{r.curr()}' at index #{r.i} " + 
656
            raise Exception(f"Unexpected symbol '{r.curr()}' " +
624
                            f"in the macro declaration at {location} " +
657
                            f"at index #{r.i} in the macro declaration " +
-
 
658
                            f"at {location} " +
625
                            f"(line: {r.lines[r.line_idx]})\n''")
659
                            f"(line: {r.lines[r.line_idx]})\n''")
626
    # Append the last argument
660
    # Append the last argument
627
    if arg != '':
661
    if arg != '':
628
        args.append(arg)
662
        args.append(arg)
629
    # Skip t spaces after the argument list
663
    # Skip t spaces after the argument list
630
    r.skip_spaces()
664
    r.skip_spaces()
631
    # Get a comment if it is: read till the end of the line and get the comment from the reader
665
    # Get a comment if it is: read till the end of the line and
-
 
666
    # get the comment from the reader
632
    while r.curr() != '':
667
    while r.curr() != '':
633
        r.step()
668
        r.step()
634
    comment = r.comment
669
    comment = r.comment
635
    # Find end of the macro
670
    # Find end of the macro
636
    prev = ''
671
    prev = ''
Line 643... Line 678...
643
            continue
678
            continue
644
        prev = r.step()
679
        prev = r.step()
645
    # Build the output
680
    # Build the output
646
    return AsmMacro(location, name, comment, args)
681
    return AsmMacro(location, name, comment, args)
Line -... Line 682...
-
 
682
 
647
 
683
 
648
def parse_variable(r, first_word = None):
684
def parse_variable(r, first_word=None):
649
    global warnings
685
    global warnings
Line 650... Line 686...
650
    location = r.location()
686
    location = r.location()
651
 
687
 
652
    # Skip spaces before variable name
688
    # Skip spaces before variable name
653
    r.skip_spaces()
689
    r.skip_spaces()
654
    # Get variable name
690
    # Get variable name
655
    name = ""
691
    name = ""
656
    # Read it if it was not supplied
692
    # Read it if it was not supplied
657
    if first_word == None:
693
    if first_word is None:
658
        while is_id(r.curr()):
694
        while is_id(r.curr()):
659
            name += r.step()
695
            name += r.step()
660
    # Or use the supplied one instead
696
    # Or use the supplied one instead
661
    else:
697
    else:
662
        name = first_word
698
        name = first_word
-
 
699
    # Check the name
663
    # Check the name
700
    # If it's 0 len, that means threr's something else than an
664
    # If it's 0 len, that means threr's something else than an identifier at the beginning
701
    # identifier at the beginning
665
    if len(name) == 0:
702
    if len(name) == 0:
666
        return None
703
        return None
667
    # If it starts from digit or othervice illegally it's illegal
704
    # If it starts from digit or othervice illegally it's illegal
Line 673... Line 710...
673
    if ID_KIND_KEYWORD in kind:
710
    if ID_KIND_KEYWORD in kind:
674
        return None
711
        return None
675
    # If it's a macro name, that's not a variable declaration
712
    # If it's a macro name, that's not a variable declaration
676
    if ID_KIND_MACRO_NAME in kind:
713
    if ID_KIND_MACRO_NAME in kind:
677
        return VariableNameIsMacroName(name)
714
        return VariableNameIsMacroName(name)
678
    # If it's a datatype or a structure name that's not a variable declaration: that's just a data
715
    # If it's a datatype or a structure name that's not a
-
 
716
    # variable declaration: that's just a data
679
    # don't document just a data for now
717
    # don't document just a data for now
680
    if ID_KIND_STRUCT_NAME in kind or ID_KIND_FASM_TYPE in kind:
718
    if ID_KIND_STRUCT_NAME in kind or ID_KIND_FASM_TYPE in kind:
681
        return None
719
        return None
682
    # Skip spaces before type name
720
    # Skip spaces before type name
683
    r.skip_spaces()
721
    r.skip_spaces()
Line 712... Line 750...
712
    while r.curr() != '':
750
    while r.curr() != '':
713
        r.step()
751
        r.step()
714
    # Build the result
752
    # Build the result
715
    return AsmVariable(location, name, r.comment, var_type, value)
753
    return AsmVariable(location, name, r.comment, var_type, value)
Line -... Line 754...
-
 
754
 
716
 
755
 
717
def parse_after_struct(r, as_union = True):
756
def parse_after_struct(r, as_union=True):
718
    global warnings
757
    global warnings
Line 719... Line 758...
719
    location = r.location()
758
    location = r.location()
720
 
759
 
Line 738... Line 777...
738
        if type(var) == AsmVariable:
777
        if type(var) == AsmVariable:
739
            members.append(var)
778
            members.append(var)
740
        elif type(var) == str:
779
        elif type(var) == str:
741
            if var == 'union':
780
            if var == 'union':
742
                # Parse the union as a struct
781
                # Parse the union as a struct
743
                union = parse_after_struct(r, as_union = True)
782
                union = parse_after_struct(r, as_union=True)
744
                members.append(union)
783
                members.append(union)
745
                # Skip the ends of the union
784
                # Skip the ends of the union
746
                r.nextline()
785
                r.nextline()
747
            elif r.curr() == ':':
786
            elif r.curr() == ':':
748
                warnings += f"{r.location()}: Skept the label in the struct\n"
787
                warnings += f"{r.location()}: Skept the label in the struct\n"
749
            else:
788
            else:
750
                raise Exception(f"Garbage in struct member at {location} (got '{var}' identifier)")
789
                raise Exception(f"Garbage in struct member at {location} " +
-
 
790
                                f" (got '{var}' identifier)")
751
        elif type(var) == VariableNameIsMacroName:
791
        elif type(var) == VariableNameIsMacroName:
752
            if var.name == 'ends':
792
            if var.name == 'ends':
753
                break
793
                break
754
        r.nextline()
794
        r.nextline()
755
    # Return the result
795
    # Return the result
756
    if as_union:
796
    if as_union:
757
        return AsmStruct(location, name, comment, members)
797
        return AsmStruct(location, name, comment, members)
758
    else:
798
    else:
759
        return AsmUnion(location, name, comment, members)
799
        return AsmUnion(location, name, comment, members)
Line -... Line 800...
-
 
800
 
760
 
801
 
761
def parse_after_proc(r):
802
def parse_after_proc(r):
762
    # Get proc name
803
    # Get proc name
763
    name = r.fetch_identifier()
804
    name = r.fetch_identifier()
764
    # Next identifier after the proc name
805
    # Next identifier after the proc name
Line 811... Line 852...
811
    # Get to the end of the line and get a comment from the reader
852
    # Get to the end of the line and get a comment from the reader
812
    while r.curr() != '':
853
    while r.curr() != '':
813
        r.step()
854
        r.step()
814
    comment = r.comment
855
    comment = r.comment
815
    # Build the element
856
    # Build the element
816
    return AsmFunction(r.location(), name, comment, calling_convention, args, used_regs)
857
    return AsmFunction(r.location(), name, comment, calling_convention,
-
 
858
                       args, used_regs)
-
 
859
 
Line 817... Line 860...
817
 
860
 
818
def get_declarations(asm_file_contents, asm_file_name):
861
def get_declarations(asm_file_contents, asm_file_name):
Line 819... Line 862...
819
    r = AsmReader(asm_file_name)
862
    r = AsmReader(asm_file_name)
Line 855... Line 898...
855
        elif first_word == 'repeat':
898
        elif first_word == 'repeat':
856
            # Skip the repeat directive
899
            # Skip the repeat directive
857
            pass
900
            pass
858
        elif first_word == 'purge':
901
        elif first_word == 'purge':
859
            while True:
902
            while True:
-
 
903
                # Skip spaces after the 'purge' keyword or after
860
                # Skip spaces after the 'purge' keyword or after the comma what separated the previous macro name
904
                # the comma what separated the previous macro name
861
                r.skip_spaces()
905
                r.skip_spaces()
862
                # Get the purged macro name
906
                # Get the purged macro name
863
                name = ''
907
                name = ''
864
                while is_id(r.curr()):
908
                while is_id(r.curr()):
865
                    name += r.step()
909
                    name += r.step()
Line 868... Line 912...
868
                    id_remove_kind(name, ID_KIND_MACRO_NAME)
912
                    id_remove_kind(name, ID_KIND_MACRO_NAME)
869
                except:
913
                except:
870
                    pass
914
                    pass
871
                # Skip spaces after the name
915
                # Skip spaces after the name
872
                r.skip_spaces()
916
                r.skip_spaces()
873
                # If it's comma (',') after then that's not the last purged macro, continue purging
917
                # If it's comma (',') after then that's not the last purged
-
 
918
                # macro, continue purging
874
                if r.curr() == ',':
919
                if r.curr() == ',':
875
                    r.step()
920
                    r.step()
876
                    continue
921
                    continue
877
                # Here we purged all the macros should be purged
922
                # Here we purged all the macros should be purged
878
                break
923
                break
Line 891... Line 936...
891
            # If it didn't match a type identifier after the word
936
            # If it didn't match a type identifier after the word
892
            elif type(var) == str:
937
            elif type(var) == str:
893
                name = var
938
                name = var
894
                # Match label beginning (':' after name)
939
                # Match label beginning (':' after name)
895
                if r.curr() == ':':
940
                if r.curr() == ':':
-
 
941
                    # Get to the end of the line and
896
                    # Get to the end of the line and get the coment from the reader
942
                    # get the coment from the reader
897
                    while r.curr() != '':
943
                    while r.curr() != '':
898
                        r.step()
944
                        r.step()
899
                    comment = r.comment
945
                    comment = r.comment
900
                    # Only handle non-local labels
946
                    # Only handle non-local labels
901
                    if name[0] != '.' and name != "@@" and name != "$Revision":
947
                    if name[0] != '.' and name != "@@" and name != "$Revision":
-
 
948
                        # Treate the label as function if there's @return or
-
 
949
                        # @param in its comment. Othervice it's just a variable
-
 
950
                        # with type `label` in generated doxygen C
902
                        if '@return' in comment or '@param' in comment:
951
                        if '@return' in comment or '@param' in comment:
903
                            element = AsmFunction(r.location(), name, comment, '', [], [])
952
                            element = AsmFunction(r.location(), name, comment,
-
 
953
                                                  '', [], [])
904
                        else:
954
                        else:
905
                            element = AsmLabel(r.location(), name, comment)
955
                            element = AsmLabel(r.location(), name, comment)
906
                        elements.append(element)
956
                        elements.append(element)
907
                elif r.curr() == '=':
957
                elif r.curr() == '=':
908
                    # Save the identifier as a set constant
958
                    # Save the identifier as a set constant
Line 912... Line 962...
912
                if word_two == 'equ':
962
                if word_two == 'equ':
913
                    # Save the identifier as an equated constant
963
                    # Save the identifier as an equated constant
914
                    id_add_kind(word_one, ID_KIND_EQUATED_CONSTANT)
964
                    id_add_kind(word_one, ID_KIND_EQUATED_CONSTANT)
915
        r.nextline()
965
        r.nextline()
Line -... Line 966...
-
 
966
 
916
 
967
 
917
def it_neds_to_be_parsed(source_file):
968
def it_neds_to_be_parsed(source_file):
918
    # If there's no symbols file saved - parse it anyway
969
    # If there's no symbols file saved - parse it anyway
919
    # cause we need to create the symbols file and use it
970
    # cause we need to create the symbols file and use it
920
    # if we gonna generate proper doxygen
971
    # if we gonna generate proper doxygen
Line 931... Line 982...
931
    # then the source should be recompiled (existing doxygen is old)
982
    # then the source should be recompiled (existing doxygen is old)
932
    if source_change_time > dest_change_file:
983
    if source_change_time > dest_change_file:
933
        return True
984
        return True
934
    return False
985
    return False
Line -... Line 986...
-
 
986
 
935
 
987
 
936
def handle_file(handled_files, asm_file_name, subdir = "."):
988
def handle_file(handled_files, asm_file_name, subdir="."):
937
    global elements
989
    global elements
938
    # Canonicalize the file path and get it relative to cwd
990
    # Canonicalize the file path and get it relative to cwd
939
    cwd = os.path.abspath(os.path.dirname(sys.argv[0]))
991
    cwd = os.path.abspath(os.path.dirname(sys.argv[0]))
940
    asm_file_name = os.path.realpath(asm_file_name)
992
    asm_file_name = os.path.realpath(asm_file_name)
Line 945... Line 997...
945
    # If the file was handled in this execution before - skip it
997
    # If the file was handled in this execution before - skip it
946
    if asm_file_name in handled_files:
998
    if asm_file_name in handled_files:
947
        return
999
        return
948
    # Say that the file was handled in this execution
1000
    # Say that the file was handled in this execution
949
    handled_files.append(asm_file_name)
1001
    handled_files.append(asm_file_name)
-
 
1002
    # Check if the file should be parsed
950
    # Check if the file should be parsed (if it was modified or wasn't parsed yet)
1003
    # (if it was modified or wasn't parsed yet)
951
    should_get_declarations = True
1004
    should_get_declarations = True
952
    if not it_neds_to_be_parsed(asm_file_name):
1005
    if not it_neds_to_be_parsed(asm_file_name):
953
        print(f"Skipping {asm_file_name} (already newest)")
1006
        print(f"Skipping {asm_file_name} (already newest)")
954
        should_get_declarations = False
1007
        should_get_declarations = False
955
    else:
1008
    else:
956
        print(f"Handling {asm_file_name}")
1009
        print(f"Handling {asm_file_name}")
957
        # Remove elements parsed from this file before if any
1010
        # Remove elements parsed from this file before if any
-
 
1011
        elements_to_remove = [
958
        elements_to_remove = [x for x in elements if x.location.split(':')[0] == asm_file_name]
1012
            x for x in elements if x.location.split(':')[0] == asm_file_name
-
 
1013
        ]
-
 
1014
        elements = [
959
        elements = [x for x in elements if x.location.split(':')[0] != asm_file_name]
1015
            x for x in elements if x.location.split(':')[0] != asm_file_name
-
 
1016
        ]
960
        # Forget types of identifiers of names of the removed elements
1017
        # Forget types of identifiers of names of the removed elements
961
        for element in elements_to_remove:
1018
        for element in elements_to_remove:
962
            if type(element) == AsmStruct:
1019
            if type(element) == AsmStruct:
963
                id_remove_kind(element.name, ID_KIND_STRUCT_NAME)
1020
                id_remove_kind(element.name, ID_KIND_STRUCT_NAME)
964
            elif type(element) == AsmMacro:
1021
            elif type(element) == AsmMacro:
965
                id_remove_kind(element.name, ID_KIND_MACRO_NAME)
1022
                id_remove_kind(element.name, ID_KIND_MACRO_NAME)
966
    # Read the source
1023
    # Read the source
967
    asm_file_contents = open(asm_file_name, "r", encoding="utf-8").read()
1024
    asm_file_contents = open(asm_file_name, "r", encoding="utf-8").read()
968
    # Find includes, fix their paths and handle em recoursively
1025
    # Find includes, fix their paths and handle em recoursively
969
    includes = re.findall(r'^include (["\'])(.*)\1', asm_file_contents, flags=re.MULTILINE)
1026
    includes = re.findall(r'^include (["\'])(.*)\1', asm_file_contents,
-
 
1027
                          flags=re.MULTILINE)
970
    for include in includes:
1028
    for include in includes:
971
        include = include[1].replace('\\', '/');
1029
        include = include[1].replace('\\', '/')
972
        full_path = subdir + '/' + include;
1030
        full_path = subdir + '/' + include
973
        # If the path isn't valid, maybe that's not relative path
1031
        # If the path isn't valid, maybe that's not relative path
974
        if not os.path.isfile(full_path):
1032
        if not os.path.isfile(full_path):
975
            full_path = include
1033
            full_path = include
976
        new_subdir = full_path.rsplit('/', 1)[0]
1034
        new_subdir = full_path.rsplit('/', 1)[0]
977
        handle_file(handled_files, full_path, new_subdir)
1035
        handle_file(handled_files, full_path, new_subdir)
978
    # Only collect declarations from the file if it wasn't parsed before
1036
    # Only collect declarations from the file if it wasn't parsed before
979
    if should_get_declarations and not clean_generated_stuff:
1037
    if should_get_declarations and not clean_generated_stuff:
980
        get_declarations(asm_file_contents, asm_file_name)
1038
        get_declarations(asm_file_contents, asm_file_name)
Line 981... Line 1039...
981
 
1039
 
982
if __name__ == "__main__":
1040
if __name__ == "__main__":
-
 
1041
    link_root = "http://websvn.kolibrios.org/filedetails.php"
Line 983... Line 1042...
983
    link_root = "http://websvn.kolibrios.org/filedetails.php?repname=Kolibri+OS&path=/kernel/trunk"
1042
    link_root += "?repname=Kolibri+OS&path=/kernel/trunk"
984
 
1043
 
985
    # Dict where an identifier is assicoated with a string
1044
    # Dict where an identifier is assicoated with a string
986
    # The string contains characters specifying flags
1045
    # The string contains characters specifying flags
Line 1021... Line 1080...
1021
    enable_warnings = True
1080
    enable_warnings = True
Line 1022... Line 1081...
1022
 
1081
 
1023
    # Parse arguments
1082
    # Parse arguments
1024
    parser = argparse.ArgumentParser()
1083
    parser = argparse.ArgumentParser()
-
 
1084
    parser.add_argument("-o", help="Doxygen output folder")
1025
    parser.add_argument("-o", help="Doxygen output folder")
1085
    parser.add_argument("--clean",
-
 
1086
                        help="Remove generated files",
-
 
1087
                        action="store_true")
1026
    parser.add_argument("--clean", help="Remove generated files", action="store_true")
1088
    parser.add_argument("--dump",
-
 
1089
                        help="Dump all defined symbols",
-
 
1090
                        action="store_true")
1027
    parser.add_argument("--dump", help="Dump all defined symbols", action="store_true")
1091
    parser.add_argument("--stats",
-
 
1092
                        help="Print symbol stats",
-
 
1093
                        action="store_true")
1028
    parser.add_argument("--stats", help="Print symbol stats", action="store_true")
1094
    parser.add_argument("--nowarn",
-
 
1095
                        help="Do not write warnings file",
-
 
1096
                        action="store_true")
1029
    parser.add_argument("--nowarn", help="Do not write warnings file", action="store_true")
1097
    parser.add_argument("--noemit",
-
 
1098
                        help="Do not emit doxygen files (for testing)",
-
 
1099
                        action="store_true")
1030
    parser.add_argument("--noemit", help="Do not emit doxygen files (for testing)", action="store_true")
1100
    parser.add_argument("--debug",
-
 
1101
                        help="Show hashes of files (for testing)",
1031
    parser.add_argument("--debug", help="Show hashes of files (for testing)", action="store_true")
1102
                        action="store_true")
1032
    args = parser.parse_args()
1103
    args = parser.parse_args()
1033
    doxygen_src_path = args.o if args.o else 'docs/doxygen'
1104
    doxygen_src_path = args.o if args.o else 'docs/doxygen'
1034
    clean_generated_stuff = args.clean
1105
    clean_generated_stuff = args.clean
1035
    dump_symbols = args.dump
1106
    dump_symbols = args.dump
Line 1040... Line 1111...
1040
 
1111
 
1041
    # Variables, functions, labels, macros, structure types
1112
    # Variables, functions, labels, macros, structure types
1042
    elements = []
1113
    elements = []
1043
    created_files = []
1114
    created_files = []
1044
    kernel_files = []
1115
    kernel_files = []
Line 1045... Line 1116...
1045
    output_files = {} # If --debug then all the files are written here
1116
    output_files = {}  # If --debug then all the files are written here
1046
 
1117
 
1047
    # Load remembered list of symbols
1118
    # Load remembered list of symbols
1048
    if os.path.isfile('asmxygen.elements.pickle'):
1119
    if os.path.isfile('asmxygen.elements.pickle'):
-
 
1120
        print('Reading existing dump of symbols')
-
 
1121
        pickle_file = open('asmxygen.elements.pickle', 'rb')
Line 1049... Line 1122...
1049
        print('Reading existing dump of symbols')
1122
        (elements, id2kind) = pickle.load(pickle_file)
Line 1050... Line 1123...
1050
        (elements, id2kind) = pickle.load(open('asmxygen.elements.pickle', 'rb'))
1123
        pickle_file.close()
1051
 
1124
 
1052
    handle_file(kernel_files, "./kernel.asm");
1125
    handle_file(kernel_files, "./kernel.asm")
1053
 
1126
 
1054
    if dump_symbols:
1127
    if dump_symbols:
1055
        stdout = sys.stdout
1128
        stdout = sys.stdout
Line 1056... Line 1129...
1056
        sys.stdout = open('asmxygen.dump.txt', 'w', encoding = 'utf-8')
1129
        sys.stdout = open('asmxygen.dump.txt', 'w', encoding='utf-8')
1057
        for asm_element in elements:
1130
        for asm_element in elements:
1058
            asm_element.dump()
1131
            asm_element.dump()
1059
        sys.stdout = stdout
1132
        sys.stdout = stdout
1060
 
1133
 
1061
    if clean_generated_stuff:
1134
    if clean_generated_stuff:
1062
        kernel_files_set = set(kernel_files)
1135
        kernel_files_set = set(kernel_files)
1063
        for file in kernel_files:
1136
        for file in kernel_files:
1064
            doxygen_file = f"{doxygen_src_path}/{file}"
1137
            doxygen_file = f"{doxygen_src_path}/{file}"
1065
            if (os.path.isfile(doxygen_file)):
1138
            if (os.path.isfile(doxygen_file)):
Line 1066... Line 1139...
1066
                print(f"Removing {file}... ", end = '')
1139
                print(f"Removing {file}... ", end='')
1067
                os.remove(doxygen_file)
1140
                os.remove(doxygen_file)
1068
                print("Done.")
1141
                print("Done.")
-
 
1142
    elif not noemit:
1069
    elif not noemit:
1143
        print(f"Writing doumented sources to {doxygen_src_path}")
1070
        print(f"Writing doumented sources to {doxygen_src_path}")
1144
 
1071
 
1145
        i = 0
Line 1072... Line 1146...
1072
        i = 0
1146
        new_elements = [x for x in elements if x.new]
Line 1073... Line 1147...
1073
        new_elements = [x for x in elements if x.new]
1147
        for element in new_elements:
-
 
1148
            counter = f"[{i + 1}/{len(new_elements)}]"
1074
        for element in new_elements:
1149
            print(f"{counter} Emitting {element.name} from {element.location}")
1075
            print(f"[{i + 1}/{len(new_elements)}] Emitting {element.name} from {element.location}")
1150
            element.emit(doxygen_src_path)
1076
            element.emit(doxygen_src_path)
1151
            i += 1
-
 
1152
 
-
 
1153
        print(f"Writing dump of symbols to asmxygen.elements.pickle")
Line 1077... Line 1154...
1077
            i += 1
1154
 
1078
 
1155
        # Now when the new elements already was written, there's no new
1079
        print(f"Writing dump of symbols to asmxygen.elements.pickle")
1156
        # elements anymore
1080
 
1157
        for element in elements:
Line 1109... Line 1186...
1109
        print(f'Parsed function count: {fun_count}')
1186
        print(f'Parsed function count: {fun_count}')
1110
        print(f'Parsed union type count: {uni_count}')
1187
        print(f'Parsed union type count: {uni_count}')
1111
        print(f'Parsed structure type count: {str_count}')
1188
        print(f'Parsed structure type count: {str_count}')
Line 1112... Line 1189...
1112
 
1189
 
1113
    if enable_warnings:
1190
    if enable_warnings:
Line 1114... Line 1191...
1114
        open('asmxygen.txt', "w", encoding = "utf-8").write(warnings)
1191
        open('asmxygen.txt', "w", encoding="utf-8").write(warnings)
1115
 
1192
 
1116
    if debug_mode:
1193
    if debug_mode:
1117
        hash_per_file = ""
1194
        hash_per_file = ""
Line 1122... Line 1199...
1122
            open("asmxygen_hash_per_file.txt", "w").write(hash_per_file)
1199
            open("asmxygen_hash_per_file.txt", "w").write(hash_per_file)
1123
            print("NEW")
1200
            print("NEW")
1124
        else:
1201
        else:
1125
            reference_hash_per_file = open("asmxygen_hash_per_file.txt").read()
1202
            reference_hash_per_file = open("asmxygen_hash_per_file.txt").read()
1126
            if reference_hash_per_file != hash_per_file:
1203
            if reference_hash_per_file != hash_per_file:
1127
                print(''.join(difflib.ndiff(reference_hash_per_file, hash_per_file)))
1204
                diffs = difflib.ndiff(reference_hash_per_file, hash_per_file)
-
 
1205
                print(''.join(diffs))
1128
            else:
1206
            else:
1129
                print("SUCCESS")
1207
                print("SUCCESS")