1#!/usr/bin/env python3
2#
3# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
4#
5# SPDX-License-Identifier: BSD-2-Clause
6#
7
8from __future__ import print_function
9import sys
10import six
11from functools import reduce
12
13# We assume length tp > 0
14
15
16def parse_type(tps):
17    def helper(tps):
18        tp = tps[0]
19        rest = tps[1:]
20
21        if tp == 'Word':
22            return ('Word', rest[0]), rest[1:]
23
24        elif tp == 'Ptr':
25            tp2, rest = helper(rest)
26            return ('Ptr', tp2), rest
27
28        elif tp == 'Unit':
29            return ('Unit',), rest
30
31        elif tp == 'Array':
32            tp2, rest = helper(rest)
33            # arrays are Array ... sz
34            return ('Array', tp2, rest[0]), rest[1:]
35
36        else:
37            return ('Base', tp), rest
38
39    return helper(tps)[0]
40
41
42def splitBy(f, xs):
43    def fold(acc, v):
44        if f(v):
45            acc[0].append(acc[1])
46            return (acc[0], [])
47        else:
48            acc[1].append(v)
49            return acc
50
51    return (reduce(fold, xs, ([], [])))[0]
52
53
54def handle_one_struct(s):
55    def hdl_fld(f):
56        fl, tp = f.split(':')
57        return (fl.lstrip(), parse_type(tp.split(' ')))
58
59    name = s[0]
60    return (name, map(hdl_fld, s[1:]))
61
62
63def dict_from_list(ls):
64    a = {}
65    for k, v in ls:
66        a[k] = v
67
68    return a
69
70
71def is_base(x):
72    return (x[0] == 'Base')
73
74
75def base_name(x):
76    return x[1]
77
78# This assumes that membership is a DAG which is the case in C
79# We could memoize, doesn't seem worth it ...
80
81
82def paths_to_type(mp, f, start):
83    def handle_one(fld):
84        name, tp = fld
85        if f(tp):
86            return [([start + '.' + name], tp)]
87        elif is_base(tp):
88            res = paths_to_type(mp, f, base_name(tp))
89            # prepend paths by name (why no Cons in python? grrr)
90            map(lambda x: (x[0].insert(0, name)), res)
91            return res
92        else:
93            return []
94
95    # init case
96    start_tp = ('Base', start)
97    if f(start_tp):
98        return [([], start_tp)]
99    else:
100        res = map(handle_one, mp[start])
101        return (reduce(lambda x, y: x + y, res))
102
103
104def build_types(file):
105    in_file = open(file, 'r')
106
107    lines = map(lambda x: x.rstrip(), in_file.readlines())
108
109    in_file.close()
110
111    grps = splitBy(lambda x: x == '', lines)
112
113    # the order information will be important if we want _CL for all types
114    sts = dict_from_list(map(handle_one_struct, grps))
115
116    return sts
117
118
119def print_graph(filename, out_file):
120    mp = build_types(filename)
121
122    print('digraph types {', file=out_file)
123    for k, flds in six.iteritems(mp):
124        for fld, tp in flds:
125            # if is_base(tp):
126            print('\t "%s" -> "%s" [label="%s"]' % (k, base_name(tp), fld),
127                  file=out_file)
128    print('}', file=out_file)
129
130# Toplevel
131
132
133if __name__ == '__main__':
134    print_graph('umm_types.txt', sys.stdout)
135