carbon-compiler 0.2.0

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.
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