| #!/usr/bin/env fuchsia-vendored-python |
| # Copyright 2020 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| example: |
| find . \ |
| -path ./out -prune -o \ |
| -path ./garnet/public/lib/fidl/fuzz/input_corpus -prune -o \ |
| -path ./prebuilt -prune -o \ |
| -type f \ |
| -name \*.fidl -print0 \ |
| | xargs -0 tools/fidl/scripts/format_unions.py |
| """ |
| |
| import sys |
| |
| KW_REPLACEMENTS = [ |
| ("xunion", "flexible union"), |
| ("union", "strict union"), |
| ("strict xunion", "strict union"), |
| # ('strict union', 'union') |
| ] |
| |
| |
| def starts_with_oneof(s, prefixes): |
| return any(s.startswith(p) for p in prefixes) |
| |
| |
| def format_file(path, in_place, format_lines): |
| with open(path, "r+") as f: |
| result = format_lines(f) |
| |
| if in_place: |
| with open(path, "w") as f: |
| f.writelines(result) |
| else: |
| print("".join(result)) |
| |
| |
| def has_explicit_xunion(path): |
| with open(path, "r") as f: |
| in_union = False |
| for line in f: |
| if starts_with_oneof(line, ["xunion ", "strict xunion "]): |
| in_union = True |
| elif in_union and line.startswith("}"): |
| in_union = False |
| elif in_union and is_explicit(line.split()): |
| return True |
| return False |
| |
| |
| def purge_xunion(in_lines): |
| out_lines = [] |
| for line in in_lines: |
| for before, after in KW_REPLACEMENTS: |
| if line.startswith(before): |
| without_keyword = slice(len(before), None) |
| updated = after + line[without_keyword] |
| out_lines.append(updated) |
| break |
| else: |
| out_lines.append(line) |
| return out_lines |
| |
| |
| def add_explicit_ordinals(in_lines): |
| out_lines = [] |
| current_union_lines = [] |
| in_union = False |
| for line in in_lines: |
| if starts_with_oneof(line, ["union ", "xunion ", "strict xunion "]): |
| in_union = True |
| out_lines.append(line) |
| elif line.startswith("}") and in_union: |
| out_lines += format_union_member_lines(current_union_lines) |
| out_lines.append(line) |
| current_union_lines = [] |
| in_union = False |
| elif in_union: |
| current_union_lines.append(line) |
| else: |
| out_lines.append(line) |
| return out_lines |
| |
| |
| def format_union_member_lines(lines): |
| out_lines = [] |
| ordinal_width = len( |
| str(len([l for l in lines if is_union_field_line(l.split())])) |
| ) |
| index = 1 |
| for line in lines: |
| tokens = line.split() |
| if not is_union_field_line(tokens): |
| out_lines.append(line) |
| elif is_explicit(tokens): |
| out_lines.append(line) |
| else: # implicit, update to be explicit |
| extra_spaces = (ordinal_width - len(str(index))) * " " |
| indent = (len(line) - len(line.lstrip())) * " " |
| out_lines.append( |
| "{0}{1}{2}: {3}".format( |
| indent, extra_spaces, index, line.lstrip() |
| ) |
| ) |
| index += 1 |
| return out_lines |
| |
| |
| def is_union_field_line(tokens): |
| return not ( |
| not tokens or tokens[0].startswith("//") or tokens[0].startswith("[") |
| ) |
| |
| |
| def is_explicit(tokens): |
| return tokens and tokens[0].endswith(":") and tokens[0][:-1].isnumeric() |
| |
| |
| def test(): |
| explicit_lines = [ |
| " 1: uint8 unused1;", |
| " 2: uint8 unused2;", |
| " 3: array<uint8>:6 variant;", |
| ] |
| print(format_union_member_lines(explicit_lines)) |
| |
| implicit_lines = [ |
| " uint32 before;", |
| " UnionSize12Alignment4 union;", |
| " uint32 after;", |
| ] |
| print(format_union_member_lines(implicit_lines)) |
| |
| many_lines = [ |
| " uint32 before;", |
| " UnionSize12Alignment4 union;", |
| " uint32 after;", |
| " uint32 before;", |
| " UnionSize12Alignment4 union;", |
| " uint32 after;", |
| " uint32 before;", |
| " UnionSize12Alignment4 union;", |
| " uint32 after;", |
| " uint32 before;", |
| " UnionSize12Alignment4 union;", |
| " uint32 after;", |
| ] |
| print(format_union_member_lines(many_lines)) |
| |
| # doesn't currently work |
| attribute_inline = [ |
| ' [Selector = "v2"] Foo foo;', |
| ] |
| print(format_union_member_lines(attribute_inline)) |
| |
| |
| if __name__ == "__main__": |
| for filename in sys.argv[1:]: |
| try: |
| format_file(filename, in_place=True, format_lines=purge_xunion) |
| # if (has_explicit_xunion(filename)): |
| # print(filename) |
| except: |
| print("error in file: {0}".format(filename)) |
| raise |