1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4#
5# Copyright 2017, Data61
6# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
7# ABN 41 687 119 230.
8#
9# This software may be distributed and modified according to the terms of
10# the BSD 2-Clause license. Note that NO WARRANTY is provided.
11# See "LICENSE_BSD2.txt" for details.
12#
13# @TAG(DATA61_BSD)
14#
15
16'''Generate railroad diagrams of the CAmkES syntax. Note that this needs a very
17up to date version of parcon to generate diagrams correctly. At time of writing,
18you need to get this from https://github.com/javawizard/parcon.
19'''
20
21from __future__ import absolute_import, division, print_function, \
22    unicode_literals
23
24import os, sys
25
26from parcon.railroad import Bullet, Loop, Nothing, Or, PRODUCTION, TEXT, Then, Token
27from parcon.railroad.raildraw import draw_to_png
28
29def Production(t):
30    return Token(PRODUCTION, t)
31def Text(t):
32    return Token(TEXT, t)
33
34def diag(*obj):
35    o = list(obj) + [Bullet()]
36    return Then(Bullet(), *o)
37
38DIAGRAMS = [
39    {'assembly':diag(
40        Text('assembly'),
41        Text('{'),
42        Production('composition'),
43        Or(Production('configuration'),
44            Nothing()),
45        Text('}'))
46    },
47    {'attribute':diag(
48        Production('type'),
49        Production('id'))
50    },
51    {'composition':diag(
52        Text('composition'),
53        Text('{'),
54        Loop(Or(Production('instance'),
55                Production('connection'),
56                Nothing()),
57            Nothing()),
58        Text('}'))
59    },
60    {'component':diag(
61        Text('component'),
62        Production('id'),
63        Text('{'),
64        Loop(Or(
65                Then(Text('control'), Text(';')),
66                Then(Text('uses'), Production('id'), Production('id')),
67                Then(Text('provides'), Production('id'), Production('id')),
68                Then(Text('consumes'), Production('id'), Production('id')),
69                Then(Text('emits'), Production('id'), Production('id')),
70                Then(Text('dataport'), Production('id'), Production('id')),
71                Then(Text('has'), Text('mutex'), Production('id')),
72                Then(Text('has'), Text('semaphore'), Production('id')),
73                Nothing()),
74            Nothing()),
75        Text('}'))
76    },
77    {'connection':diag(
78        Text('connection'),
79        Production('id'),
80        Production('id'),
81        Text('('),
82        Text('from'),
83        Production('id'),
84        Text('.'),
85        Production('id'),
86        Text(','),
87        Text('to'),
88        Production('id'),
89        Text('.'),
90        Production('id'),
91        Text(')'),
92        Text(';'))
93    },
94    {'dataport':diag(
95        Text('dataport'),
96        Production('id'),
97        Production('id'))
98    },
99    {'direction':diag(Or(
100        Text('refin'),
101        Text('in'),
102        Text('inout'),
103        Text('out')))
104    },
105    {'event':diag(
106        Text('event'),
107        Production('id'),
108        Text('='),
109        Production('number'))
110    },
111    {'instance':diag(
112        Text('component'),
113        Production('id'),
114        Production('id'),
115        Text(';'))
116    },
117    {'method':diag(
118        Or(
119            Text('void'),
120            Production('type')),
121        Production('id'),
122        Text('('),
123        Or(
124            Text('void'),
125            Loop(
126                Production('parameter'),
127                Nothing()),
128            Nothing()),
129        Text(')'),
130        Text(';'))
131    },
132    {'parameter':diag(
133        Production('direction'),
134        Production('type'),
135        Production('id'))
136    },
137    {'procedure':diag(
138        Or(
139            Loop(
140                Production('method'),
141                Nothing()),
142            Nothing()))
143    },
144    {'setting':diag(
145        Production('id'),
146        Text('.'),
147        Production('id'),
148        Text('='),
149        Or(Production('boolean'),
150            Production('number'),
151            Production('string'),
152            Production('id')))
153    },
154    {'type':diag(Or(
155        Text('int'),
156        Text('integer'),
157        Text('signed int'),
158        Text('unsigned int'),
159        Text('unsigned integer'),
160        Text('int8_t'),
161        Text('int16_t'),
162        Text('int32_t'),
163        Text('int64_t'),
164        Text('uint8_t'),
165        Text('uint16_t'),
166        Text('uint32_t'),
167        Text('uint64_t'),
168        Text('real'),
169        Text('double'),
170        Text('float'),
171        Text('uintptr_t'),
172        Text('char'),
173        Text('character'),
174        Text('boolean'),
175        Text('bool'),
176        Text('string')))
177    },
178]
179
180def main():
181    if len(sys.argv) == 2:
182        out = sys.argv[1]
183    elif len(sys.argv) == 1:
184        out = os.curdir
185    else:
186        sys.stderr.write('Usage: %s [output directory]\n' % sys.argv[0])
187        return -1
188
189    # Tweak options to hide the title we don't need.
190    options = {
191        'raildraw_title_hide':True,
192        'raildraw_title_after':0,
193    }
194
195    # Dump each diagram as a PNG.
196    for d in DIAGRAMS:
197        path = os.path.join(out, '%s.png' % list(d.keys())[0])
198        draw_to_png(d, options, path, True)
199
200    return 0
201
202if __name__ == '__main__':
203    sys.exit(main())
204