carbon-compiler 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +17 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +39 -0
  6. data/.travis.yml +5 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +41 -0
  11. data/Rakefile +9 -0
  12. data/Vagrantfile +84 -0
  13. data/carbon-compiler.gemspec +28 -0
  14. data/lib/carbon/compiler.rb +20 -0
  15. data/lib/carbon/compiler/directive.rb +48 -0
  16. data/lib/carbon/compiler/directive/import.rb +17 -0
  17. data/lib/carbon/compiler/errors.rb +7 -0
  18. data/lib/carbon/compiler/location.rb +136 -0
  19. data/lib/carbon/compiler/metanostic.rb +123 -0
  20. data/lib/carbon/compiler/metanostic/defaults.rb +41 -0
  21. data/lib/carbon/compiler/metanostic/defaults.yml +138 -0
  22. data/lib/carbon/compiler/metanostic/diagnostic.rb +112 -0
  23. data/lib/carbon/compiler/metanostic/list.rb +109 -0
  24. data/lib/carbon/compiler/metanostic/mode.rb +162 -0
  25. data/lib/carbon/compiler/metanostic/state.rb +174 -0
  26. data/lib/carbon/compiler/metanostic/template.erb +11 -0
  27. data/lib/carbon/compiler/node.rb +18 -0
  28. data/lib/carbon/compiler/node/base.rb +213 -0
  29. data/lib/carbon/compiler/node/definition.rb +19 -0
  30. data/lib/carbon/compiler/node/definition/class.rb +24 -0
  31. data/lib/carbon/compiler/node/definition/class/element.rb +18 -0
  32. data/lib/carbon/compiler/node/definition/directive.rb +22 -0
  33. data/lib/carbon/compiler/node/definition/directive/function.rb +18 -0
  34. data/lib/carbon/compiler/node/definition/enum.rb +20 -0
  35. data/lib/carbon/compiler/node/definition/enum/element.rb +17 -0
  36. data/lib/carbon/compiler/node/definition/function.rb +44 -0
  37. data/lib/carbon/compiler/node/definition/function/body.rb +17 -0
  38. data/lib/carbon/compiler/node/definition/function/name.rb +18 -0
  39. data/lib/carbon/compiler/node/definition/function/parameter.rb +18 -0
  40. data/lib/carbon/compiler/node/definition/function/parameters.rb +17 -0
  41. data/lib/carbon/compiler/node/definition/module.rb +23 -0
  42. data/lib/carbon/compiler/node/definition/struct.rb +24 -0
  43. data/lib/carbon/compiler/node/definition/struct/element.rb +18 -0
  44. data/lib/carbon/compiler/node/etype.rb +66 -0
  45. data/lib/carbon/compiler/node/etype/option.rb +25 -0
  46. data/lib/carbon/compiler/node/etype/star.rb +13 -0
  47. data/lib/carbon/compiler/node/expression.rb +18 -0
  48. data/lib/carbon/compiler/node/expression/assignment.rb +22 -0
  49. data/lib/carbon/compiler/node/expression/call.rb +22 -0
  50. data/lib/carbon/compiler/node/expression/call/access.rb +24 -0
  51. data/lib/carbon/compiler/node/expression/call/attribute.rb +23 -0
  52. data/lib/carbon/compiler/node/expression/call/enum.rb +25 -0
  53. data/lib/carbon/compiler/node/expression/call/module.rb +24 -0
  54. data/lib/carbon/compiler/node/expression/call/parameters.rb +17 -0
  55. data/lib/carbon/compiler/node/expression/call/self.rb +17 -0
  56. data/lib/carbon/compiler/node/expression/call/unified.rb +27 -0
  57. data/lib/carbon/compiler/node/expression/literal.rb +83 -0
  58. data/lib/carbon/compiler/node/expression/operation.rb +20 -0
  59. data/lib/carbon/compiler/node/expression/operation/and.rb +20 -0
  60. data/lib/carbon/compiler/node/expression/operation/neq.rb +21 -0
  61. data/lib/carbon/compiler/node/expression/operation/normal.rb +20 -0
  62. data/lib/carbon/compiler/node/expression/operation/or.rb +20 -0
  63. data/lib/carbon/compiler/node/expression/unit.rb +16 -0
  64. data/lib/carbon/compiler/node/name.rb +27 -0
  65. data/lib/carbon/compiler/node/root.rb +23 -0
  66. data/lib/carbon/compiler/node/statement.rb +24 -0
  67. data/lib/carbon/compiler/node/statement/catch.rb +20 -0
  68. data/lib/carbon/compiler/node/statement/condition.rb +14 -0
  69. data/lib/carbon/compiler/node/statement/else.rb +17 -0
  70. data/lib/carbon/compiler/node/statement/elsif.rb +18 -0
  71. data/lib/carbon/compiler/node/statement/finally.rb +17 -0
  72. data/lib/carbon/compiler/node/statement/for.rb +18 -0
  73. data/lib/carbon/compiler/node/statement/if.rb +18 -0
  74. data/lib/carbon/compiler/node/statement/let.rb +18 -0
  75. data/lib/carbon/compiler/node/statement/match.rb +14 -0
  76. data/lib/carbon/compiler/node/statement/return.rb +17 -0
  77. data/lib/carbon/compiler/node/statement/try.rb +19 -0
  78. data/lib/carbon/compiler/node/statement/while.rb +19 -0
  79. data/lib/carbon/compiler/parser.rb +63 -0
  80. data/lib/carbon/compiler/parser/common.rb +79 -0
  81. data/lib/carbon/compiler/parser/expressions.rb +39 -0
  82. data/lib/carbon/compiler/parser/expressions/precedence.rb +134 -0
  83. data/lib/carbon/compiler/parser/expressions/primary.rb +120 -0
  84. data/lib/carbon/compiler/parser/firsts.rb +74 -0
  85. data/lib/carbon/compiler/parser/helpers.rb +61 -0
  86. data/lib/carbon/compiler/parser/root.rb +57 -0
  87. data/lib/carbon/compiler/parser/root/class.rb +34 -0
  88. data/lib/carbon/compiler/parser/root/directive.rb +87 -0
  89. data/lib/carbon/compiler/parser/root/enum.rb +45 -0
  90. data/lib/carbon/compiler/parser/root/function.rb +90 -0
  91. data/lib/carbon/compiler/parser/root/struct.rb +34 -0
  92. data/lib/carbon/compiler/parser/root/trait.rb +44 -0
  93. data/lib/carbon/compiler/parser/statements.rb +86 -0
  94. data/lib/carbon/compiler/parser/statements/if.rb +50 -0
  95. data/lib/carbon/compiler/parser/statements/match.rb +39 -0
  96. data/lib/carbon/compiler/parser/statements/try.rb +49 -0
  97. data/lib/carbon/compiler/project.rb +37 -0
  98. data/lib/carbon/compiler/project/file.rb +64 -0
  99. data/lib/carbon/compiler/scanner.rb +82 -0
  100. data/lib/carbon/compiler/scanner/main.rb +76 -0
  101. data/lib/carbon/compiler/scanner/token.rb +58 -0
  102. data/lib/carbon/compiler/version.rb +8 -0
  103. data/lib/carbon/compiler/visitor.rb +13 -0
  104. data/lib/carbon/compiler/visitor/base.rb +52 -0
  105. data/lib/carbon/compiler/visitor/generation.rb +45 -0
  106. data/lib/carbon/compiler/visitor/generation/asserts.rb +30 -0
  107. data/lib/carbon/compiler/visitor/generation/class.rb +93 -0
  108. data/lib/carbon/compiler/visitor/generation/context.rb +75 -0
  109. data/lib/carbon/compiler/visitor/generation/expressions.rb +82 -0
  110. data/lib/carbon/compiler/visitor/generation/expressions/assignment.rb +105 -0
  111. data/lib/carbon/compiler/visitor/generation/expressions/calls.rb +89 -0
  112. data/lib/carbon/compiler/visitor/generation/function.rb +68 -0
  113. data/lib/carbon/compiler/visitor/generation/statements.rb +131 -0
  114. data/lib/carbon/compiler/visitor/generation/struct.rb +115 -0
  115. data/lib/carbon/compiler/visitor/preparation.rb +86 -0
  116. data/lib/carbon/compiler/visitor/preparation/expressions.rb +26 -0
  117. data/lib/carbon/compiler/visitor/preparation/function.rb +55 -0
  118. data/lib/carbon/compiler/visitor/preparation/statements.rb +73 -0
  119. data/lib/carbon/compiler/visitor/preparation/struct.rb +37 -0
  120. data/program.ca +16 -0
  121. data/test.rb +21 -0
  122. metadata +234 -0
@@ -0,0 +1,162 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "rainbow"
5
+
6
+ module Carbon
7
+ module Compiler
8
+ class Metanostic
9
+ # The "mode" of a metanostic or diagnostic. This determines how a
10
+ # diagnostic should be treated.
11
+ module Mode
12
+ # No mode. This is rarely used.
13
+ #
14
+ # @return [Integer]
15
+ NONE = 0x0
16
+ # Ignore mode. This means that by default, the diagnostic should be
17
+ # ignored by the compiler; with a default or less verbose level, the
18
+ # diagnostic is not output to the compiler.
19
+ #
20
+ # @return [Integer]
21
+ IGNORE = 0b1
22
+ # Informational mode. This means that the diagnostic is output for the
23
+ # purpose of informing a developer of a certain thing; for example,
24
+ # the original definition of a function. At the default verbose level,
25
+ # it is output to the compiler.
26
+ #
27
+ # @return [Integer]
28
+ INFO = 0b10
29
+ # Warning mode. This means that the diagnostic is output to inform the
30
+ # developer of a potential issue. Enough diagnostic warnings causes
31
+ # a panic. At the default verbose level, it is output to the compiler.
32
+ #
33
+ # @return [Integer]
34
+ WARNING = 0b100
35
+ # Error mode. This means that the diagnostic is output to inform the
36
+ # developer of a definite issue. The compiler will not be able to
37
+ # finish, and enough diagnostic errors causes a panic. At the default
38
+ # verbose level, it is output to the compiler.
39
+ #
40
+ # @return [Integer]
41
+ ERROR = 0b1000
42
+ # Panic mode. This means that the diagnostic is output to inform the
43
+ # developer of a fatal issue. Once the panic is emitted, all execution
44
+ # stops, and all diagnostics output.
45
+ #
46
+ # @return [Integer]
47
+ PANIC = 0b10000
48
+ # All of the above. This is mostly used for metanostics to note that
49
+ # all of the above are allowable modes for a given diagnostic.
50
+ #
51
+ # @return [Integer]
52
+ ALL = PANIC | ERROR | WARNING | INFO | IGNORE
53
+
54
+ # An associative list of modes to convert from strings.
55
+ #
56
+ # @return [{String => Integer}]
57
+ MODES = {
58
+ "none" => NONE, "ignore" => IGNORE, "info" => INFO,
59
+ "warning" => WARNING, "error" => ERROR, "panic" => PANIC,
60
+ "all" => ALL
61
+ }.freeze
62
+
63
+ # Converts from a given value into a mode. If the value is an array,
64
+ # the return value of the function is the result of converting each
65
+ # element into a mode and unioning the result. If the value is an array,
66
+ # {MODES} is used to convert the string. If the value is numeric,
67
+ # it is returned.
68
+ #
69
+ # @raise [ArgumentError] If value is not an Array, a String, or a
70
+ # Numeric.
71
+ # @param value [Array, String, Numeric]
72
+ # @return [Integer]
73
+ def self.from(value)
74
+ if value.is_a?(::Array)
75
+ from_array(value)
76
+ elsif value.is_a?(::String)
77
+ from_string(value)
78
+ elsif value.is_a?(::Numeric)
79
+ value
80
+ else
81
+ fail ArgumentError, "Unexpected value #{value.class}"
82
+ end
83
+ end
84
+
85
+ # Converts the given Array value into a mode. It does this by converting
86
+ # each element into a mode, and unioning the modes.
87
+ #
88
+ # @param value [<String, Numeric>]
89
+ # @return [Integer]
90
+ def self.from_array(value)
91
+ value.inject(NONE) { |a, e| a | from(e) }
92
+ end
93
+
94
+ # Converts the given String value into a mode. It does this by fetching
95
+ # the mode from the {MODES} constant.
96
+ #
97
+ # @raise KeyError If value is not a valid mode.
98
+ # @param value [String]
99
+ # @return [Integer]
100
+ def self.from_string(value)
101
+ MODES.fetch(value.downcase)
102
+ end
103
+
104
+ # Checks if the given type is a mode. This is done by performing a
105
+ # bitwise and, and comparing the result against zero. The order of the
106
+ # arguments does not matter.
107
+ #
108
+ # @param mode [Integer] The mode to check against. This might be the
109
+ # allowed modes of a {Metanostic}.
110
+ # @param type [Integer] The mode to check for. This might be the new
111
+ # default value of a {Metanostic}.
112
+ # @return [Boolean]
113
+ def self.is?(mode, type)
114
+ (mode & type) != 0
115
+ end
116
+
117
+ # Determines if a given mode is a singular mode (i.e. only one of
118
+ # {IGNORE}. {INFO}, {WARNING}, {ERROR}, or {PANIC}). It does this
119
+ # via doing a population count.
120
+ #
121
+ # @param mode [Integer]
122
+ # @return [Boolean]
123
+ def self.singular?(mode)
124
+ count = 0
125
+ ((count += mode & 1) && mode >>= 1) until mode == 0
126
+ count < 2
127
+ end
128
+
129
+ # Converts the given mode into a string. If the value is {ALL}, it
130
+ # retuns `"All"`; if the value is {NONE}, it returns `"None"`;
131
+ # otherwise, it attempts to find all of the modes that are represented
132
+ # by the value.
133
+ #
134
+ # @return [String]
135
+ def self.to_s(value)
136
+ return "All" if value == ALL
137
+ return "None" if value == NONE
138
+ MODES.select { |_, v| is?(value, v) && v != ALL }
139
+ .map(&:first)
140
+ .map(&:capitalize)
141
+ .join(", ")
142
+ end
143
+
144
+ # Creates a rainbow representation of the given mode. The mode must
145
+ # be singular (see {.singular?}).
146
+ #
147
+ # @see https://github.com/sickill/rainbow
148
+ # @param mode [Integer]
149
+ # @return [Rainbow]
150
+ def self.to_rainbow(mode)
151
+ case mode
152
+ when IGNORE then Rainbow("Ignore").color(:white).bright
153
+ when INFO then Rainbow("Info").color(:blue).bright
154
+ when WARNING then Rainbow("Warning").color(:yellow).bright
155
+ when ERROR then Rainbow("Error").color(:red).bright
156
+ when PANIC then Rainbow("Panic").color(:magenta).bright
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,174 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ class Metanostic
7
+ # A "State" object. This manages the state of metanostic modes at any
8
+ # point in the program. This is useful for something like the diagnostic
9
+ # directives, in which `pop`/`push`/`set` operations need to be available
10
+ # for the metanostic modes. This manages the metanostic modes.
11
+ #
12
+ # The {Metanostic}s are first loaded using {Defaults.defaults}. They
13
+ # are then used to generate a "beginning" state or "blank" state. Their
14
+ # modes are extracted, resulting in a hash of
15
+ # `{String => (Integer, Metanostic)}`. Then, a "current" state is
16
+ # generated; this is the same hash type as the "beginning" state,
17
+ # and initially, the same contents. However, when the `push` operation
18
+ # occurs, a new "state" is added to the stack, and a new "current" state
19
+ # is generated, using the beginning state and the stack as a reference.
20
+ # Whenever a set occurs, it sets to the last state added to the stack,
21
+ # and a new current state is generated.
22
+ #
23
+ # The point of all this is to have a hash at all times that accurately
24
+ # represents the set modes of all diagnostics.
25
+ class State
26
+ extend Forwardable
27
+ include Enumerable
28
+
29
+ # @!method [](name)
30
+ # Indexes the state. Returns an array containing information about
31
+ # the mode and the metanostic for the corresponding name, otherwise
32
+ # it returns `nil`.
33
+ #
34
+ # @param name [String] The name of the metanostic.
35
+ # @return [(Integer, Metanostic)?] The integer is the current mode for
36
+ # any diagnostics emitted that derives from the given {Metanostic}.
37
+ def_delegator :current, :[]
38
+ # @!method fetch(name, default = CANARY)
39
+ # Fetches the {Metanostic} information with the name `name`. If
40
+ # no key exists with the given name, the method will attempt to
41
+ # do the following: if a block is given, yield to that; otherwise,
42
+ # if a default is given, return that; otherwise, throw a
43
+ # `KeyError`.
44
+ #
45
+ # @raise [KeyError] If no block is given and no default is given
46
+ # and there is no key `name`.
47
+ # @param name [String] The name of the metanostic to look up.
48
+ # @return [(Integer, Metanostic), Object] The integer is the current
49
+ # mode for any diagnostics emitted that derives from the given
50
+ # {Metanostic}.
51
+ def_delegator :current, :fetch
52
+ # @!method each
53
+ # Iterates over each key-value pair in the current state.
54
+ #
55
+ # @yield [name, data]
56
+ # @yieldparam name [String] The name, or key, of the key pair.
57
+ # @yieldparam data [(Integer, Metanostic)] The data, or value, of the
58
+ # key pair.
59
+ # @return [Enumerator]
60
+ def_delegator :current, :each
61
+
62
+ # Initialize the state. It sets the defaults using {Defaults.defaults},
63
+ # and generates the beginning state.
64
+ def initialize
65
+ @monitor = Monitor.new
66
+ @defaults = Defaults.defaults
67
+ @stack = Concurrent::Array.new([Concurrent::Hash.new])
68
+ generate_beginning
69
+ end
70
+
71
+ # Initializes a copy of the given state. This is called by `#clone`
72
+ # and `#dup`, and as such should not be used outside of either of these
73
+ # things.
74
+ #
75
+ # @param state [State] The state to copy from.
76
+ def initialize_copy(state)
77
+ @defaults = state.defaults
78
+ @stack = state.stack.map(&:clone)
79
+ @monitor = Monitor.new
80
+ @beginning = state.beginning
81
+ end
82
+
83
+ # Pushes to the stack. This pushes a clean state to the stack.
84
+ # This does not change the current state at all, since the new state
85
+ # is empty.
86
+ #
87
+ # @return [void]
88
+ def push
89
+ @monitor.synchronize do
90
+ @stack.push(Concurrent::Hash.new)
91
+ end
92
+ end
93
+
94
+ # Pops the last state from the stack. This could potentially change
95
+ # the state, since the last state may have a new diagnostic setting
96
+ # inside of it.
97
+ #
98
+ # @note This _may_ change the current state.
99
+ # @return [void]
100
+ def pop
101
+ @monitor.synchronize do
102
+ clear! if @stack.pop.any?
103
+ end
104
+ end
105
+
106
+ # Sets the given metanostic name to the given metanostic value in the
107
+ # last state in the stack. If the metanostic is not found in any of
108
+ # the defaults, a `KeyError` is raised. If the new mode is not allowed
109
+ # for the given metanostic, a {DiagnosticError} is raised.
110
+ #
111
+ # @note This changes the current state.
112
+ # @raise [KeyError] If the metanostic cannot be found.
113
+ # @raise [DiagnosticError] If the diagnostic could not be set to the
114
+ # given mode.
115
+ # @return [void]
116
+ def set(name, mode)
117
+ @monitor.synchronize do
118
+ mode = Mode.from(mode)
119
+ metanostic = @defaults.fetch(name)
120
+ unless metanostic.can_be?(mode)
121
+ fail DiagnosticError,
122
+ "Diagnostic #{name} cannot be #{Mode.to_s(mode)}"
123
+ end
124
+ @stack.last[metanostic.name] = [mode, metanostic]
125
+ clear!
126
+ end
127
+ end
128
+
129
+ # This resets the given metanostic to its default metanostic mode.
130
+ # It does this by taking the definition from the beginning state and
131
+ # copying it to the last stack frame.
132
+ #
133
+ # @see #set
134
+ # @note This changes the current state.
135
+ # @raise (see #set)
136
+ # @param name [String] The name of the metanostic to reset.
137
+ # @return (see #set)
138
+ def reset(name)
139
+ set(name, @beginning.fetch(name))
140
+ end
141
+
142
+ # The current state. This is frozen and cached, since it is a derived
143
+ # value of the beginning state and the stack.
144
+ #
145
+ # @return [{String => (Integer, Metanostic)}]
146
+ def current
147
+ @_current ||= @monitor.synchronize do
148
+ @stack.inject(@beginning) { |a, e| a.merge(e) }.freeze
149
+ end
150
+ end
151
+
152
+ protected
153
+
154
+ attr_reader :stack
155
+ attr_reader :defaults
156
+ attr_reader :beginning
157
+
158
+ private
159
+
160
+ def clear!
161
+ @_current = nil
162
+ end
163
+
164
+ def generate_beginning
165
+ @beginning = {}
166
+ @defaults.each do |name, metanostic|
167
+ @beginning[name] = [metanostic.default, metanostic].freeze
168
+ end
169
+ @beginning.freeze
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,11 @@
1
+ <% if file %>
2
+ <%= file.lines.to_a[lines].join("\n") %><%=
3
+ " " * [@location.column.begin - 1, 0].max %>^<%=
4
+ (("-" * (distance - 2) + "^") if distance > 2) %>
5
+
6
+ <% end %><%= Metanostic::Mode.to_rainbow(mode) %>: <%=
7
+ Rainbow(metanostic.name).color(:cyan).bright %>: <%=
8
+ Rainbow(location).color(:white).bright %>: <%= message %>
9
+ <% if Carbon.verbose > 1 %><% @stack.each do |frame| %>
10
+
11
+ <%= frame %><% end %><% end %>
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "carbon/compiler/node/base"
5
+ require "carbon/compiler/node/definition"
6
+ require "carbon/compiler/node/etype"
7
+ require "carbon/compiler/node/expression"
8
+ require "carbon/compiler/node/name"
9
+ require "carbon/compiler/node/root"
10
+ require "carbon/compiler/node/statement"
11
+
12
+ module Carbon
13
+ module Compiler
14
+ # The nodes.
15
+ module Node
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,213 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ # A base-level node. Contains all of the basic behaviors that a node
7
+ # needs.
8
+ module Node
9
+ # A base-level node. Contains all of the basic behaviors that a node
10
+ # needs.
11
+ class Base
12
+ # Creates a "mapping." This contructs a mapping between a child
13
+ # and a name. For example, a for loop has four children: `initial`,
14
+ # `condition`, `increment`, and `body`. This creates the association
15
+ # between the numerical index of the child and the symbolic name
16
+ # of the child.
17
+ #
18
+ # @example
19
+ # Statement::For.mapping(initial: 0, condition: 1, increment: 2,
20
+ # body: 3)
21
+ # Statement::For.new(children).initial # => children[0]
22
+ # @param data [Hash?] The data for the mapping. If this is nil,
23
+ # the function returns the mapping instead of setting it.
24
+ # @return [Hash, void]
25
+ def self.mapping(data = nil)
26
+ if data.nil?
27
+ @mapping ||= Concurrent::Hash.new
28
+ else
29
+ @mapping = Concurrent::Hash.new.merge(data)
30
+
31
+ data.each do |key, value|
32
+ fail if key == :klass
33
+ define_method(key) { @children[value] }
34
+ end
35
+ end
36
+ end
37
+
38
+ class << self
39
+ alias_method :attribute, :mapping
40
+ alias_method :attributes, :mapping
41
+ end
42
+
43
+ include Enumerable
44
+ extend Forwardable
45
+
46
+ # The node's children. This is most often an array of nodes, but can
47
+ # sometimes include Scanner tokens as well.
48
+ #
49
+ # @return [Array<Node, Scanner::Token>]
50
+ attr_reader :children
51
+
52
+ # The location describing the node. This is derived solely from the
53
+ # children that make up the node, and so is considered auxillary.
54
+ #
55
+ # @return [Location]
56
+ attr_reader :location
57
+
58
+ # The attributes of a node. This is mainly used for associating
59
+ # directives to definitions. This is empty for most of the lifetime
60
+ # of a node.
61
+ #
62
+ # @return [Array]
63
+ attr_reader :attributes
64
+
65
+ # @!method [](index)
66
+ # Access a specific child at the given index. If a child exists,
67
+ # it is returned; otherwise, `nil` is returned.
68
+ #
69
+ # @return [Node, Scanner::Token, nil]
70
+ def_delegator :children, :[]
71
+
72
+ # @!method each
73
+ # Yields all children one by one, if a block is given. It
74
+ # then returns an enumerator that enumerates over all of the
75
+ # children.
76
+ #
77
+ # @yield [child]
78
+ # @yieldparam child [Node, Scanner::Token]
79
+ # @return [Enumerator<Node, Scanner::Token>]
80
+ def_delegator :children, :each
81
+
82
+ def_delegator :children, :to_a
83
+
84
+ # Initialize the node with the given children and the data.
85
+ #
86
+ # @param children [Array<Base>, Scanner::Token] The children of this
87
+ # node.
88
+ # @param data [Hash] The associated data for this node.
89
+ # @option data [Type] :type (nil) The type of the node.
90
+ # @option data [Array] :attributes ([]) The attributes of the node.
91
+ # @option data [Location] :location (nil) The location of the node.
92
+ # @option data [Array] :components (children) The nodes to derive
93
+ # the location of the node from.
94
+ def initialize(children, data = {})
95
+ @children = children.dup.freeze
96
+ @type = data[:type]
97
+ @attributes = data.fetch(:attributes, [])
98
+
99
+ if data.key?(:location)
100
+ @location = data[:location]
101
+ else
102
+ derive_location(data.fetch(:components, children))
103
+ end
104
+
105
+ freeze
106
+ end
107
+
108
+ # Sets the attributes of a copy of this node to the given attributes.
109
+ # It then returns the copy.
110
+ #
111
+ # @param attributes [Array]
112
+ # @return [Base]
113
+ def attributes!(attributes)
114
+ self.class.new(children, data.merge(attributes: attributes))
115
+ end
116
+
117
+ # Sets the type of a copy of this node to the given type.
118
+ # It then returns the copy.
119
+ #
120
+ # @param type [Type]
121
+ # @return [Base]
122
+ def type!(type)
123
+ self.class.new(children, data.merge(type: type))
124
+ end
125
+
126
+ # Sets the children of a copy of this node to the given children.
127
+ # It then returns the copy.
128
+ #
129
+ # @param children [Array]
130
+ # @return [Base]
131
+ def update!(children)
132
+ self.class.new(children, data)
133
+ end
134
+ alias_method :update, :update!
135
+
136
+ # Maps the children using the given block. It then returns a copy of
137
+ # the node with the new children.
138
+ #
139
+ # @param children [::Array]
140
+ # @return [Base]
141
+ def map!
142
+ update!(children.map(&Proc.new))
143
+ end
144
+
145
+ # Using the {ClassMethods#mapping}, it merges the given hash into
146
+ # the children.
147
+ #
148
+ # @example
149
+ # Statement::For.mapping(initial: 0, condition: 1, increment: 2,
150
+ # body: 3)
151
+ # stmt = Statement::For.new(children)
152
+ # stmt.merge!(initial: initial).initial # => initial
153
+ # @param options [{Symbol => Base}]
154
+ # @return [Base]
155
+ def merge!(options)
156
+ merged = @children.dup
157
+ options.each do |key, value|
158
+ merged[attributes.fetch(key)] = value
159
+ end
160
+
161
+ update!(merged)
162
+ end
163
+
164
+ alias_method :merge, :merge!
165
+
166
+ # Accepts the current visitor unto itself.
167
+ #
168
+ # @param visitor [#visit]
169
+ # @return [Object]
170
+ def accept(visitor, *arguments)
171
+ visitor.visit(self, *arguments)
172
+ end
173
+
174
+ # Returns true if this node is a data definition.
175
+ #
176
+ # @return [Boolean]
177
+ def data?
178
+ false
179
+ end
180
+
181
+ # Returns true if this node is a behavior definition.
182
+ #
183
+ # @return [Boolean]
184
+ def behavior?
185
+ false
186
+ end
187
+
188
+ # Returns true if this node is an identity definition.
189
+ #
190
+ # @return [Boolean]
191
+ def identity?
192
+ false
193
+ end
194
+
195
+ def data
196
+ { location: @location, attributes: @attributes, type: @type }
197
+ end
198
+
199
+ private
200
+
201
+ def attributes
202
+ self.class.attributes
203
+ end
204
+
205
+ def derive_location(children)
206
+ @location = children.flatten.compact
207
+ .select { |c| c.respond_to?(:location) }
208
+ .map(&:location).inject(:|)
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end