highway 0.0.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/highway.rb +8 -4
- data/lib/highway/compiler/analyze/analyzer.rb +249 -0
- data/lib/highway/compiler/analyze/tree/root.rb +95 -0
- data/lib/highway/compiler/analyze/tree/segments/text.rb +36 -0
- data/lib/highway/compiler/analyze/tree/segments/variable.rb +43 -0
- data/lib/highway/compiler/analyze/tree/stage.rb +48 -0
- data/lib/highway/compiler/analyze/tree/step.rb +69 -0
- data/lib/highway/compiler/analyze/tree/values/array.rb +45 -0
- data/lib/highway/compiler/analyze/tree/values/base.rb +67 -0
- data/lib/highway/compiler/analyze/tree/values/hash.rb +45 -0
- data/lib/highway/compiler/analyze/tree/values/primitive.rb +43 -0
- data/lib/highway/compiler/analyze/tree/variable.rb +48 -0
- data/lib/highway/compiler/build/builder.rb +154 -0
- data/lib/highway/compiler/build/output/invocation.rb +70 -0
- data/lib/highway/compiler/build/output/manifest.rb +52 -0
- data/lib/highway/compiler/parse/parser.rb +92 -0
- data/lib/highway/compiler/parse/tree/root.rb +73 -0
- data/lib/highway/compiler/parse/tree/step.rb +62 -0
- data/lib/highway/compiler/parse/tree/variable.rb +48 -0
- data/lib/highway/compiler/parse/versions/v1.rb +110 -0
- data/lib/highway/compiler/suite.rb +56 -0
- data/lib/highway/environment.rb +282 -0
- data/lib/highway/fastlane/action.rb +67 -0
- data/lib/highway/interface.rb +135 -0
- data/lib/highway/main.rb +173 -0
- data/lib/highway/runtime/context.rb +229 -0
- data/lib/highway/runtime/report.rb +80 -0
- data/lib/highway/runtime/runner.rb +286 -0
- data/lib/highway/steps/infrastructure.rb +20 -0
- data/lib/highway/steps/library/action.rb +42 -0
- data/lib/highway/steps/library/appcenter.rb +106 -0
- data/lib/highway/steps/library/appstore.rb +137 -0
- data/lib/highway/steps/library/carthage.rb +67 -0
- data/lib/highway/steps/library/cocoapods.rb +76 -0
- data/lib/highway/steps/library/copy_artifacts.rb +36 -0
- data/lib/highway/steps/library/lane.rb +42 -0
- data/lib/highway/steps/library/sh.rb +36 -0
- data/lib/highway/steps/library/slack.rb +381 -0
- data/lib/highway/steps/library/testflight.rb +105 -0
- data/lib/highway/steps/library/xcode_archive.rb +162 -0
- data/lib/highway/steps/library/xcode_test.rb +264 -0
- data/lib/highway/steps/parameters/base.rb +52 -0
- data/lib/highway/steps/parameters/compound.rb +141 -0
- data/lib/highway/steps/parameters/single.rb +74 -0
- data/lib/highway/steps/registry.rb +92 -0
- data/lib/highway/steps/step.rb +52 -0
- data/lib/highway/steps/types/any.rb +65 -0
- data/lib/highway/steps/types/anyof.rb +64 -0
- data/lib/highway/steps/types/array.rb +44 -0
- data/lib/highway/steps/types/bool.rb +36 -0
- data/lib/highway/steps/types/enum.rb +44 -0
- data/lib/highway/steps/types/hash.rb +45 -0
- data/lib/highway/steps/types/number.rb +38 -0
- data/lib/highway/steps/types/set.rb +39 -0
- data/lib/highway/steps/types/string.rb +47 -0
- data/lib/highway/steps/types/url.rb +35 -0
- data/lib/highway/utilities.rb +51 -0
- data/lib/highway/version.rb +9 -1
- metadata +194 -22
- data/.gitignore +0 -4
- data/Gemfile +0 -4
- data/Rakefile +0 -1
- data/highway.gemspec +0 -24
@@ -0,0 +1,48 @@
|
|
1
|
+
#
|
2
|
+
# stage.rb
|
3
|
+
# Copyright © 2019 Netguru S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
module Highway
|
7
|
+
module Compiler
|
8
|
+
module Analyze
|
9
|
+
module Tree
|
10
|
+
|
11
|
+
# This class represents a stage note in the semantic tree. It contains
|
12
|
+
# information about stage order and execution policy.
|
13
|
+
class Stage
|
14
|
+
|
15
|
+
public
|
16
|
+
|
17
|
+
# Initialize an instance.
|
18
|
+
#
|
19
|
+
# @param index [Integer] Index of the stage.
|
20
|
+
# @param name [String] Name of the stage.
|
21
|
+
# @param policy [Symbol] Execution policy of the stage.
|
22
|
+
def initialize(index:, name:, policy:)
|
23
|
+
@index = index
|
24
|
+
@name = name
|
25
|
+
@policy = policy
|
26
|
+
end
|
27
|
+
|
28
|
+
# Index of the stage.
|
29
|
+
#
|
30
|
+
# @return [Integer]
|
31
|
+
attr_reader :index
|
32
|
+
|
33
|
+
# Name of the stage.
|
34
|
+
#
|
35
|
+
# @return [String]
|
36
|
+
attr_reader :name
|
37
|
+
|
38
|
+
# Execution policy of the stage.
|
39
|
+
#
|
40
|
+
# @return [Symbol]
|
41
|
+
attr_reader :policy
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#
|
2
|
+
# step.rb
|
3
|
+
# Copyright © 2019 Netguru S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
module Highway
|
7
|
+
module Compiler
|
8
|
+
module Analyze
|
9
|
+
module Tree
|
10
|
+
|
11
|
+
# This class represents a step node in a parse tree. It contains
|
12
|
+
# information about a single step and its parameters.
|
13
|
+
class Step
|
14
|
+
|
15
|
+
public
|
16
|
+
|
17
|
+
# Initialize an instance.
|
18
|
+
#
|
19
|
+
# @param index [Integer] Index of step in its scope.
|
20
|
+
# @param name [String] Name of the step.
|
21
|
+
# @param step_class [Class] Definition class of the step.
|
22
|
+
# @param parameters [Highway::Compiler::Analyze::Tree::Values::Hash] The hash value of step parameters.
|
23
|
+
# @param preset [String] Parent preset of the step.
|
24
|
+
# @param stage [String] Parent stage of the step.
|
25
|
+
def initialize(index:, name:, step_class:, parameters:, preset:, stage:)
|
26
|
+
@index = index
|
27
|
+
@name = name
|
28
|
+
@step_class = step_class
|
29
|
+
@parameters = parameters
|
30
|
+
@preset = preset
|
31
|
+
@stage = stage
|
32
|
+
end
|
33
|
+
|
34
|
+
# Index of step in its scope.
|
35
|
+
#
|
36
|
+
# @return [Integer]
|
37
|
+
attr_reader :index
|
38
|
+
|
39
|
+
# Name of the step.
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
attr_reader :name
|
43
|
+
|
44
|
+
# Definition class of the step.
|
45
|
+
#
|
46
|
+
# @return [Class]
|
47
|
+
attr_reader :step_class
|
48
|
+
|
49
|
+
# The hash value of step parameters.
|
50
|
+
#
|
51
|
+
# @return [Highway::Compiler::Analyze::Tree::Values::Hash]
|
52
|
+
attr_reader :parameters
|
53
|
+
|
54
|
+
# Parent preset of the step.
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
attr_reader :preset
|
58
|
+
|
59
|
+
# Parent stage of the step.
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
attr_reader :stage
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# array.rb
|
3
|
+
# Copyright © 2019 Netguru S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
require "highway/compiler/analyze/tree/values/base"
|
7
|
+
|
8
|
+
module Highway
|
9
|
+
module Compiler
|
10
|
+
module Analyze
|
11
|
+
module Tree
|
12
|
+
module Values
|
13
|
+
|
14
|
+
# This class represents an array value in the semantic tree. It
|
15
|
+
# consists of an array of child values.
|
16
|
+
class Array < Values::Base
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
# Initialize an instance.
|
21
|
+
#
|
22
|
+
# @param children [Array<Highway::Compiler::Analyze::Tree::Values::*>] The array of child values.
|
23
|
+
def initialize(children)
|
24
|
+
@children = children
|
25
|
+
end
|
26
|
+
|
27
|
+
# The array of child values.
|
28
|
+
#
|
29
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Values::*>]
|
30
|
+
attr_reader :children
|
31
|
+
|
32
|
+
# The flat array of all segments.
|
33
|
+
#
|
34
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Segments::*>]
|
35
|
+
def flatten_segments
|
36
|
+
@children.flat_map(&:flatten_segments)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# base.rb
|
3
|
+
# Copyright © 2019 Netguru S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
module Highway
|
7
|
+
module Compiler
|
8
|
+
module Analyze
|
9
|
+
module Tree
|
10
|
+
module Values
|
11
|
+
|
12
|
+
# This class is a base abstract class for other classes in this
|
13
|
+
# module. You should not use it directly.
|
14
|
+
class Base
|
15
|
+
|
16
|
+
public
|
17
|
+
|
18
|
+
# Initialize an instance.
|
19
|
+
def initialize()
|
20
|
+
raise NotImplementedError.new("You must not call `#{__method__.to_s}` on `#{self.class.to_s}`.")
|
21
|
+
end
|
22
|
+
|
23
|
+
# A flat array of all segments.
|
24
|
+
#
|
25
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Segments::*>]
|
26
|
+
def flatten_segments
|
27
|
+
raise NotImplementedError.new("You must override `#{__method__.to_s}` in `#{self.class.to_s}`.")
|
28
|
+
end
|
29
|
+
|
30
|
+
# A flat array of all segments which satisty the given block.
|
31
|
+
#
|
32
|
+
# @param &block [Block] The selection block.
|
33
|
+
#
|
34
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Segments::*>]
|
35
|
+
def select_segments(&block)
|
36
|
+
flatten_segments.select(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
# The flat array of variable segments which satisfy the given block.
|
40
|
+
#
|
41
|
+
# @param &block [Block] The selection block.
|
42
|
+
#
|
43
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Segments::Variable>]
|
44
|
+
def select_variable_segments(&block)
|
45
|
+
if block_given?
|
46
|
+
select_segments { |s| s.is_a?(Segments::Variable) && block.call(s) }
|
47
|
+
else
|
48
|
+
select_segments { |s| s.is_a?(Segments::Variable) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# The flat array of variable segments with the given scope.
|
53
|
+
#
|
54
|
+
# @param &block [Symbol] The lookup scope.
|
55
|
+
#
|
56
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Segments::Variable>]
|
57
|
+
def select_variable_segments_with_scope(scope)
|
58
|
+
select_variable_segments { |s| s.scope == scope }
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# hash.rb
|
3
|
+
# Copyright © 2019 Netguru S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
require "highway/compiler/analyze/tree/values/base"
|
7
|
+
|
8
|
+
module Highway
|
9
|
+
module Compiler
|
10
|
+
module Analyze
|
11
|
+
module Tree
|
12
|
+
module Values
|
13
|
+
|
14
|
+
# This class represents a hash value in the semantic tree. It
|
15
|
+
# consists of an array of child values.
|
16
|
+
class Hash < Values::Base
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
# Initialize an instance.
|
21
|
+
#
|
22
|
+
# @param children [Hash<String, Highway::Compiler::Analyze::Tree::Values::*>] The array of child values.
|
23
|
+
def initialize(children)
|
24
|
+
@children = children
|
25
|
+
end
|
26
|
+
|
27
|
+
# The array of child values.
|
28
|
+
#
|
29
|
+
# @return [Hash<String, Highway::Compiler::Analyze::Tree::Values::*>]
|
30
|
+
attr_reader :children
|
31
|
+
|
32
|
+
# The flat array of all segments.
|
33
|
+
#
|
34
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Segments::*>]
|
35
|
+
def flatten_segments
|
36
|
+
@children.values.flat_map(&:flatten_segments)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# primitive.rb
|
3
|
+
# Copyright © 2019 Netguru S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
require "highway/compiler/analyze/tree/values/base"
|
7
|
+
|
8
|
+
module Highway
|
9
|
+
module Compiler
|
10
|
+
module Analyze
|
11
|
+
module Tree
|
12
|
+
module Values
|
13
|
+
|
14
|
+
# This class represents a primitive value in the semantic tree. It
|
15
|
+
# consists of an array of segments.
|
16
|
+
class Primitive < Values::Base
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
# Initialize an instance.
|
21
|
+
#
|
22
|
+
# @param segments [Array<Highway::Compiler::Analyze::Tree::Segments::*>] The array of segments.
|
23
|
+
def initialize(segments)
|
24
|
+
@segments = segments
|
25
|
+
end
|
26
|
+
|
27
|
+
# The array of segments.
|
28
|
+
#
|
29
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Segments::*>]
|
30
|
+
attr_reader :segments
|
31
|
+
|
32
|
+
# The flat array of all segments.
|
33
|
+
#
|
34
|
+
# @return [Array<Highway::Compiler::Analyze::Tree::Segments::*>]
|
35
|
+
alias :flatten_segments :segments
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#
|
2
|
+
# variable.rb
|
3
|
+
# Copyright © 2019 Netguru S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
module Highway
|
7
|
+
module Compiler
|
8
|
+
module Analyze
|
9
|
+
module Tree
|
10
|
+
|
11
|
+
# This class represents a variable node in a semantic tree. It contains
|
12
|
+
# information about a single variable.
|
13
|
+
class Variable
|
14
|
+
|
15
|
+
public
|
16
|
+
|
17
|
+
# Initialize an instance.
|
18
|
+
#
|
19
|
+
# @param name [String] Name of the variable.
|
20
|
+
# @param value [Highway::Compiler::Analyze::Tree::Values::*] Value of the variable.
|
21
|
+
# @param preset [String] Parent preset of the variable.
|
22
|
+
def initialize(name:, value:, preset:)
|
23
|
+
@name = name
|
24
|
+
@value = value
|
25
|
+
@preset = preset
|
26
|
+
end
|
27
|
+
|
28
|
+
# Name of the variable.
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
attr_reader :name
|
32
|
+
|
33
|
+
# Value of the variable.
|
34
|
+
#
|
35
|
+
# @return [Highway::Compiler::Analyze::Tree::Values::*]
|
36
|
+
attr_reader :value
|
37
|
+
|
38
|
+
# Parent preset of the variable.
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
attr_reader :preset
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
#
|
2
|
+
# builder.rb
|
3
|
+
# Copyright © 2019 Netguru S.A. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
require "highway/compiler/analyze/tree/root"
|
7
|
+
require "highway/compiler/build/output/manifest"
|
8
|
+
require "highway/utilities"
|
9
|
+
|
10
|
+
module Highway
|
11
|
+
module Compiler
|
12
|
+
module Build
|
13
|
+
|
14
|
+
# This class is responsible for manifest generation based on the semantic
|
15
|
+
# tree. This is the third and final phase of the compiler.
|
16
|
+
class Builder
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
# Initialize an instance.
|
21
|
+
#
|
22
|
+
# @param interface [Highway::Interface] The interface.
|
23
|
+
def initialize(interface:)
|
24
|
+
@interface = interface
|
25
|
+
end
|
26
|
+
|
27
|
+
# Build the manifst.
|
28
|
+
#
|
29
|
+
# The builder resolves variables and steps in the semantic tree for
|
30
|
+
# given preset and builds concrete invocations.
|
31
|
+
#
|
32
|
+
# The builder produces a manifest that is later executed by runner.
|
33
|
+
#
|
34
|
+
# @param sema_tree [Highway::Compiler::Analyze::Tree::Root] The semantic tree.
|
35
|
+
#
|
36
|
+
# return [Highway::Compiler::Build::Output::Manifest]
|
37
|
+
def build(sema_tree:, preset:)
|
38
|
+
|
39
|
+
manifest = Build::Output::Manifest.new()
|
40
|
+
|
41
|
+
manifest.preset = preset
|
42
|
+
|
43
|
+
variables = resolve_variables(sema_tree: sema_tree, preset: preset)
|
44
|
+
steps = resolve_steps(sema_tree: sema_tree, preset: preset)
|
45
|
+
|
46
|
+
build_invocations(stages: sema_tree.stages, variables: variables, steps: steps, manifest: manifest)
|
47
|
+
|
48
|
+
manifest
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def resolve_variables(sema_tree:, preset:)
|
55
|
+
|
56
|
+
exact_variables = sema_tree.variables.select { |variable|
|
57
|
+
variable.preset == preset
|
58
|
+
}
|
59
|
+
|
60
|
+
default_variables = sema_tree.variables.select { |variable|
|
61
|
+
variable.preset == sema_tree.default_preset && !exact_variables.any? { |exact_variable| exact_variable.name == variable.name }
|
62
|
+
}
|
63
|
+
|
64
|
+
default_variables + exact_variables
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def resolve_steps(sema_tree:, preset:)
|
69
|
+
|
70
|
+
stages = sema_tree.stages.sort_by { |stage| stage.index }
|
71
|
+
|
72
|
+
stages.flat_map { |stage|
|
73
|
+
|
74
|
+
exact_steps = sema_tree.steps.select { |step|
|
75
|
+
step.preset == preset && step.stage == stage.name
|
76
|
+
}
|
77
|
+
|
78
|
+
default_steps = sema_tree.steps.select { |step|
|
79
|
+
step.preset == sema_tree.default_preset && step.stage == stage.name
|
80
|
+
}
|
81
|
+
|
82
|
+
exact_steps = exact_steps.sort_by { |step| step.index }
|
83
|
+
default_steps = default_steps.sort_by { |step| step.index }
|
84
|
+
|
85
|
+
exact_steps = nil if exact_steps.empty?
|
86
|
+
default_steps = nil if default_steps.empty?
|
87
|
+
|
88
|
+
exact_steps || default_steps || Array.new()
|
89
|
+
|
90
|
+
}
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
def build_invocations(stages:, variables:, steps:, manifest:)
|
95
|
+
steps.each_with_index do |step, index|
|
96
|
+
stage = stages.find { |stage| stage.name == step.stage }
|
97
|
+
parameters = build_value(value: step.parameters, variables: variables)
|
98
|
+
keypath = [step.stage, step.preset, step.name]
|
99
|
+
manifest.add_invocation(index: index + 1, step_class: step.step_class, parameters: parameters, policy: stage.policy, keypath: keypath)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_value(value:, variables:)
|
104
|
+
if value.is_a?(Analyze::Tree::Values::Primitive)
|
105
|
+
Analyze::Tree::Values::Primitive.new(
|
106
|
+
build_value_segments(segments: value.segments, variables: variables)
|
107
|
+
)
|
108
|
+
elsif value.is_a?(Analyze::Tree::Values::Array)
|
109
|
+
Analyze::Tree::Values::Array.new(
|
110
|
+
value.children.map { |value|
|
111
|
+
build_value(value: value, variables: variables)
|
112
|
+
}
|
113
|
+
)
|
114
|
+
elsif value.is_a?(Analyze::Tree::Values::Hash)
|
115
|
+
Analyze::Tree::Values::Hash.new(
|
116
|
+
Utilities::hash_map(value.children) { |key, value|
|
117
|
+
[key, build_value(value: value, variables: variables)]
|
118
|
+
}
|
119
|
+
)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def build_value_segments(segments:, variables:)
|
124
|
+
|
125
|
+
resolved = segments.flat_map { |segment|
|
126
|
+
if segment.is_a?(Analyze::Tree::Segments::Variable) && segment.scope == :static
|
127
|
+
variable = variables.find { |variable| variable.name == segment.name }
|
128
|
+
build_value_segments(segments: variable.value.segments, variables: variables)
|
129
|
+
else
|
130
|
+
[segment]
|
131
|
+
end
|
132
|
+
}
|
133
|
+
|
134
|
+
reduced = resolved.reduce([]) { |memo, segment|
|
135
|
+
if last = memo.pop()
|
136
|
+
if last.is_a?(Analyze::Tree::Segments::Text) && segment.is_a?(Analyze::Tree::Segments::Text)
|
137
|
+
memo + [Analyze::Tree::Segments::Text.new(last.value + segment.value)]
|
138
|
+
else
|
139
|
+
memo + [last, segment]
|
140
|
+
end
|
141
|
+
else
|
142
|
+
[segment]
|
143
|
+
end
|
144
|
+
}
|
145
|
+
|
146
|
+
reduced
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|