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'''
17Stage 6 parser. The following parser is designed to accept a stage 5 parser,
18whose output it consumes. This parser's purpose is to combine multiple assembly
19entities into a single top-level assembly.
20'''
21
22from __future__ import absolute_import, division, print_function, \
23    unicode_literals
24from camkes.internal.seven import cmp, filter, map, zip
25
26from .base import Transformer
27from camkes.ast import Assembly, Composition, Configuration, Group, \
28    TraversalAction
29from .exception import ParseError
30
31def precondition(ast_lifted):
32    '''
33    Precondition of this parser. No groups should be present in the AST.
34    '''
35    return all(not isinstance(x, Group) for x in ast_lifted)
36
37def postcondition(ast_lifted):
38    '''
39    Postcondition of the stage 6 parser. Only a single assembly should remain.
40    '''
41    class Post(TraversalAction):
42        def __init__(self):
43            self.count = 0
44        def __call__(self, item):
45            if isinstance(item, Assembly):
46                self.count += 1
47            return item
48
49    p = Post()
50    ast_lifted.postorder(p)
51
52    return p.count <= 1
53
54def compose_assemblies(ast):
55    assembly = ast.assembly
56
57    if assembly is None:
58        raise ParseError('no assembly found in input specification')
59
60    # collect pieces from all assemblies
61    for a in [x for x in ast.items if isinstance(x, Assembly) and
62            not x is assembly]:
63
64        assembly.composition.instances.extend(a.composition.instances)
65        assembly.composition.connections.extend(a.composition.connections)
66
67        if a.configuration is not None:
68            assembly.configuration.settings.extend(a.configuration.settings)
69
70    # Ensure AST consistency.
71    assembly.composition.claim_children()
72    assembly.configuration.claim_children()
73
74    # Remove all other assemblies from AST.
75    ast.filter(lambda x: not isinstance(x, Assembly) or x is assembly)
76
77class Parse6(Transformer):
78    def precondition(self, ast_lifted, _):
79        return precondition(ast_lifted)
80
81    def postcondition(self, ast_lifted, _):
82        return postcondition(ast_lifted)
83
84    def transform(self, ast_lifted, read):
85        compose_assemblies(ast_lifted)
86        return ast_lifted, read
87