carbon-compiler 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitattributes +17 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +39 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +9 -0
- data/Vagrantfile +84 -0
- data/carbon-compiler.gemspec +28 -0
- data/lib/carbon/compiler.rb +20 -0
- data/lib/carbon/compiler/directive.rb +48 -0
- data/lib/carbon/compiler/directive/import.rb +17 -0
- data/lib/carbon/compiler/errors.rb +7 -0
- data/lib/carbon/compiler/location.rb +136 -0
- data/lib/carbon/compiler/metanostic.rb +123 -0
- data/lib/carbon/compiler/metanostic/defaults.rb +41 -0
- data/lib/carbon/compiler/metanostic/defaults.yml +138 -0
- data/lib/carbon/compiler/metanostic/diagnostic.rb +112 -0
- data/lib/carbon/compiler/metanostic/list.rb +109 -0
- data/lib/carbon/compiler/metanostic/mode.rb +162 -0
- data/lib/carbon/compiler/metanostic/state.rb +174 -0
- data/lib/carbon/compiler/metanostic/template.erb +11 -0
- data/lib/carbon/compiler/node.rb +18 -0
- data/lib/carbon/compiler/node/base.rb +213 -0
- data/lib/carbon/compiler/node/definition.rb +19 -0
- data/lib/carbon/compiler/node/definition/class.rb +24 -0
- data/lib/carbon/compiler/node/definition/class/element.rb +18 -0
- data/lib/carbon/compiler/node/definition/directive.rb +22 -0
- data/lib/carbon/compiler/node/definition/directive/function.rb +18 -0
- data/lib/carbon/compiler/node/definition/enum.rb +20 -0
- data/lib/carbon/compiler/node/definition/enum/element.rb +17 -0
- data/lib/carbon/compiler/node/definition/function.rb +44 -0
- data/lib/carbon/compiler/node/definition/function/body.rb +17 -0
- data/lib/carbon/compiler/node/definition/function/name.rb +18 -0
- data/lib/carbon/compiler/node/definition/function/parameter.rb +18 -0
- data/lib/carbon/compiler/node/definition/function/parameters.rb +17 -0
- data/lib/carbon/compiler/node/definition/module.rb +23 -0
- data/lib/carbon/compiler/node/definition/struct.rb +24 -0
- data/lib/carbon/compiler/node/definition/struct/element.rb +18 -0
- data/lib/carbon/compiler/node/etype.rb +66 -0
- data/lib/carbon/compiler/node/etype/option.rb +25 -0
- data/lib/carbon/compiler/node/etype/star.rb +13 -0
- data/lib/carbon/compiler/node/expression.rb +18 -0
- data/lib/carbon/compiler/node/expression/assignment.rb +22 -0
- data/lib/carbon/compiler/node/expression/call.rb +22 -0
- data/lib/carbon/compiler/node/expression/call/access.rb +24 -0
- data/lib/carbon/compiler/node/expression/call/attribute.rb +23 -0
- data/lib/carbon/compiler/node/expression/call/enum.rb +25 -0
- data/lib/carbon/compiler/node/expression/call/module.rb +24 -0
- data/lib/carbon/compiler/node/expression/call/parameters.rb +17 -0
- data/lib/carbon/compiler/node/expression/call/self.rb +17 -0
- data/lib/carbon/compiler/node/expression/call/unified.rb +27 -0
- data/lib/carbon/compiler/node/expression/literal.rb +83 -0
- data/lib/carbon/compiler/node/expression/operation.rb +20 -0
- data/lib/carbon/compiler/node/expression/operation/and.rb +20 -0
- data/lib/carbon/compiler/node/expression/operation/neq.rb +21 -0
- data/lib/carbon/compiler/node/expression/operation/normal.rb +20 -0
- data/lib/carbon/compiler/node/expression/operation/or.rb +20 -0
- data/lib/carbon/compiler/node/expression/unit.rb +16 -0
- data/lib/carbon/compiler/node/name.rb +27 -0
- data/lib/carbon/compiler/node/root.rb +23 -0
- data/lib/carbon/compiler/node/statement.rb +24 -0
- data/lib/carbon/compiler/node/statement/catch.rb +20 -0
- data/lib/carbon/compiler/node/statement/condition.rb +14 -0
- data/lib/carbon/compiler/node/statement/else.rb +17 -0
- data/lib/carbon/compiler/node/statement/elsif.rb +18 -0
- data/lib/carbon/compiler/node/statement/finally.rb +17 -0
- data/lib/carbon/compiler/node/statement/for.rb +18 -0
- data/lib/carbon/compiler/node/statement/if.rb +18 -0
- data/lib/carbon/compiler/node/statement/let.rb +18 -0
- data/lib/carbon/compiler/node/statement/match.rb +14 -0
- data/lib/carbon/compiler/node/statement/return.rb +17 -0
- data/lib/carbon/compiler/node/statement/try.rb +19 -0
- data/lib/carbon/compiler/node/statement/while.rb +19 -0
- data/lib/carbon/compiler/parser.rb +63 -0
- data/lib/carbon/compiler/parser/common.rb +79 -0
- data/lib/carbon/compiler/parser/expressions.rb +39 -0
- data/lib/carbon/compiler/parser/expressions/precedence.rb +134 -0
- data/lib/carbon/compiler/parser/expressions/primary.rb +120 -0
- data/lib/carbon/compiler/parser/firsts.rb +74 -0
- data/lib/carbon/compiler/parser/helpers.rb +61 -0
- data/lib/carbon/compiler/parser/root.rb +57 -0
- data/lib/carbon/compiler/parser/root/class.rb +34 -0
- data/lib/carbon/compiler/parser/root/directive.rb +87 -0
- data/lib/carbon/compiler/parser/root/enum.rb +45 -0
- data/lib/carbon/compiler/parser/root/function.rb +90 -0
- data/lib/carbon/compiler/parser/root/struct.rb +34 -0
- data/lib/carbon/compiler/parser/root/trait.rb +44 -0
- data/lib/carbon/compiler/parser/statements.rb +86 -0
- data/lib/carbon/compiler/parser/statements/if.rb +50 -0
- data/lib/carbon/compiler/parser/statements/match.rb +39 -0
- data/lib/carbon/compiler/parser/statements/try.rb +49 -0
- data/lib/carbon/compiler/project.rb +37 -0
- data/lib/carbon/compiler/project/file.rb +64 -0
- data/lib/carbon/compiler/scanner.rb +82 -0
- data/lib/carbon/compiler/scanner/main.rb +76 -0
- data/lib/carbon/compiler/scanner/token.rb +58 -0
- data/lib/carbon/compiler/version.rb +8 -0
- data/lib/carbon/compiler/visitor.rb +13 -0
- data/lib/carbon/compiler/visitor/base.rb +52 -0
- data/lib/carbon/compiler/visitor/generation.rb +45 -0
- data/lib/carbon/compiler/visitor/generation/asserts.rb +30 -0
- data/lib/carbon/compiler/visitor/generation/class.rb +93 -0
- data/lib/carbon/compiler/visitor/generation/context.rb +75 -0
- data/lib/carbon/compiler/visitor/generation/expressions.rb +82 -0
- data/lib/carbon/compiler/visitor/generation/expressions/assignment.rb +105 -0
- data/lib/carbon/compiler/visitor/generation/expressions/calls.rb +89 -0
- data/lib/carbon/compiler/visitor/generation/function.rb +68 -0
- data/lib/carbon/compiler/visitor/generation/statements.rb +131 -0
- data/lib/carbon/compiler/visitor/generation/struct.rb +115 -0
- data/lib/carbon/compiler/visitor/preparation.rb +86 -0
- data/lib/carbon/compiler/visitor/preparation/expressions.rb +26 -0
- data/lib/carbon/compiler/visitor/preparation/function.rb +55 -0
- data/lib/carbon/compiler/visitor/preparation/statements.rb +73 -0
- data/lib/carbon/compiler/visitor/preparation/struct.rb +37 -0
- data/program.ca +16 -0
- data/test.rb +21 -0
- 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
|