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