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,123 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "carbon/compiler/metanostic/defaults"
|
5
|
+
require "carbon/compiler/metanostic/diagnostic"
|
6
|
+
require "carbon/compiler/metanostic/list"
|
7
|
+
require "carbon/compiler/metanostic/mode"
|
8
|
+
require "carbon/compiler/metanostic/state"
|
9
|
+
|
10
|
+
module Carbon
|
11
|
+
module Compiler
|
12
|
+
# A "Metanostic." A "metanostic" has information about what a diagnostic
|
13
|
+
# could be about. For example, a `Diagnostic/Unknown` diagnostic has
|
14
|
+
# metanostic information; the name itself, the default mode, and the
|
15
|
+
# allowed modes are a part of that metanostic information. Diagnostics
|
16
|
+
# are generated from both metanostic information and environmental
|
17
|
+
# information.
|
18
|
+
class Metanostic
|
19
|
+
include Comparable
|
20
|
+
# The name of the metanostic. This is used as a unique identifier for
|
21
|
+
# the metanostic. This is a string, and is normally represented as a
|
22
|
+
# path of CamelCase names. For example, a name might be
|
23
|
+
# `Diagnostic/Unknown`.
|
24
|
+
#
|
25
|
+
# @return [::String]
|
26
|
+
attr_reader :name
|
27
|
+
# The allowed modes of a metanostic or diagnostic. This limits how a
|
28
|
+
# diagnostic can be categorized. This is a bitfield of modes. If a
|
29
|
+
# metanostic can be any mode, this is `Metanostic::Mode::ALL`; otherwise,
|
30
|
+
# it is a union of all of the allowed modes of a diagnostic.
|
31
|
+
#
|
32
|
+
# @see Metanostic::Mode
|
33
|
+
# @return [::Integer]
|
34
|
+
attr_reader :allowed
|
35
|
+
# The default mode of derived diagnostics. This is and must be an
|
36
|
+
# allowed diagnostic.
|
37
|
+
#
|
38
|
+
# @see #allowed
|
39
|
+
# @see Metanostic::Mode
|
40
|
+
# @return [::Integer]
|
41
|
+
attr_reader :default
|
42
|
+
# The default message of derived diagnostics, if the diagnostic does not
|
43
|
+
# provide one. This is used to provide a generic message.
|
44
|
+
#
|
45
|
+
# @return [::String]
|
46
|
+
attr_reader :message
|
47
|
+
|
48
|
+
# Initialize the diagnostic with the given data.
|
49
|
+
#
|
50
|
+
# @param data [::Hash{::Symbol,::String => ::Object}]
|
51
|
+
# @option data [::String] :name The name of the metanostic.
|
52
|
+
# @option data [::Integer] :allowed The allowed modes of derived
|
53
|
+
# diagnostics.
|
54
|
+
# @option data [::Integer] :default The default mode of derived
|
55
|
+
# diagnostics.
|
56
|
+
# @option data [::String] :message The default message of derived
|
57
|
+
# diagnostics.
|
58
|
+
# @raise [::KeyError] If one of the option keys is not present.
|
59
|
+
def initialize(data)
|
60
|
+
@name = data.fetch(:name) { data.fetch("name") }
|
61
|
+
@allowed = data.fetch(:allowed) { data.fetch("allowed") }
|
62
|
+
self.default = data.fetch(:default) { data.fetch("default") }
|
63
|
+
@message = data.fetch(:message) { data.fetch("message") }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Compares this with another metanostic. This should only be used for
|
67
|
+
# equality; it makes no sense otherwise.
|
68
|
+
#
|
69
|
+
# @param other [Metanostic] The metanostic to compare to.
|
70
|
+
# @return [::Numeric]
|
71
|
+
def <=>(other)
|
72
|
+
fail ArgumentError, "Expected Metanostic" unless other.is_a?(Metanostic)
|
73
|
+
to_a <=> other.to_a
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns a numeric representation of this class for use of hashing.
|
77
|
+
#
|
78
|
+
# @return [::Numeric]
|
79
|
+
def hash
|
80
|
+
to_a.hash
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns an array representation of this class. This is frozen.
|
84
|
+
#
|
85
|
+
# @return [(::Class, ::String, ::Integer, ::Integer, ::String)]
|
86
|
+
def to_a
|
87
|
+
[self.class, @name, @allowed, @default, @message].freeze
|
88
|
+
end
|
89
|
+
|
90
|
+
# Pretty inspect.
|
91
|
+
#
|
92
|
+
# @return [::String]
|
93
|
+
def inspect
|
94
|
+
"#<#{self.class} #{@name}(#{Mode.to_s(@default)}) " \
|
95
|
+
"[#{Mode.to_s(@allowed)}]>"
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sets the default mode of the metanostic to the given mode. The
|
99
|
+
# metanostic must have the corresponding allowed mode bit set.
|
100
|
+
#
|
101
|
+
# @see Metanostic::Mode
|
102
|
+
# @raise [DiagnosticError] If the metanostic cannot have the given mode
|
103
|
+
# as the default.
|
104
|
+
# @return [::Integer] The new mode.
|
105
|
+
def default=(mode)
|
106
|
+
unless can_be?(mode)
|
107
|
+
fail DiagnosticError, "#{mode} is not allowed as a default"
|
108
|
+
end
|
109
|
+
@default = mode
|
110
|
+
end
|
111
|
+
|
112
|
+
# Checks to see if the metanostic or any derived diagnostics can have the
|
113
|
+
# given mode.
|
114
|
+
#
|
115
|
+
# @see Metanostic::Mode
|
116
|
+
# @param mode [Integer] The mode.
|
117
|
+
# @return [Boolean]
|
118
|
+
def can_be?(mode)
|
119
|
+
Mode.singular?(mode) && Mode.is?(allowed, mode)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module Carbon
|
6
|
+
module Compiler
|
7
|
+
class Metanostic
|
8
|
+
# Deals with loading default of metanostics. It mostly takes defaults
|
9
|
+
# from a defaults.yml file in the same directory as this file.
|
10
|
+
module Defaults
|
11
|
+
# Load a file and return the metanostics contained within the files.
|
12
|
+
#
|
13
|
+
# @param file [String] The file to load from. The file must contain
|
14
|
+
# a YAML-formatted list of diagnostics.
|
15
|
+
# @return [{String => Metanostic}]
|
16
|
+
def self.load_file(file)
|
17
|
+
data = YAML.load_file(file)
|
18
|
+
metanostics = {}
|
19
|
+
data["metanostics"].each do |name, metanostic|
|
20
|
+
meta = { name: name,
|
21
|
+
default: Mode.from(metanostic["default"]),
|
22
|
+
allowed: Mode.from(metanostic["allowed"]),
|
23
|
+
message: metanostic["message"] }
|
24
|
+
metanostics[name] = Metanostic.new(meta)
|
25
|
+
end
|
26
|
+
|
27
|
+
metanostics
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns a frozen hash of all the default metanostics. Since it is
|
31
|
+
# frozen, it is cached.
|
32
|
+
#
|
33
|
+
# @return [{String => Metanostic}]
|
34
|
+
def self.defaults
|
35
|
+
@_defaults ||=
|
36
|
+
load_file(::File.expand_path("../defaults.yml", __FILE__)).freeze
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
metanostics:
|
2
|
+
Test/All:
|
3
|
+
allowed: [all]
|
4
|
+
default: warning
|
5
|
+
Test/Ignore:
|
6
|
+
allowed: [all]
|
7
|
+
default: ignore
|
8
|
+
Test/Info:
|
9
|
+
allowed: [all]
|
10
|
+
default: info
|
11
|
+
Test/Warning:
|
12
|
+
allowed: [all]
|
13
|
+
default: warning
|
14
|
+
Test/Error:
|
15
|
+
allowed: [all]
|
16
|
+
default: error
|
17
|
+
Test/Panic:
|
18
|
+
allowed: [all]
|
19
|
+
default: panic
|
20
|
+
System/Error:
|
21
|
+
allowed: [panic]
|
22
|
+
default: panic
|
23
|
+
message: >-
|
24
|
+
The compiler encountered an unrecoverable error and must exit
|
25
|
+
note: >-
|
26
|
+
Results from internal usage of an unknown diagnostic so far.
|
27
|
+
Trace/Location:
|
28
|
+
allowed: [all]
|
29
|
+
default: warning
|
30
|
+
message: previous location is here
|
31
|
+
note: >-
|
32
|
+
Used for tracing the location of something.
|
33
|
+
Diagnostic/Unknown:
|
34
|
+
allowed: [all]
|
35
|
+
default: error
|
36
|
+
message: unknown diagnostic used
|
37
|
+
Diagnostic/Mode/Invalid:
|
38
|
+
allowed: [all]
|
39
|
+
default: error
|
40
|
+
message: an invalid mode was given
|
41
|
+
Syntax/Token/Unknown:
|
42
|
+
allowed: [panic]
|
43
|
+
default: panic
|
44
|
+
message: unknown token %s
|
45
|
+
Syntax/Token/Unexpected:
|
46
|
+
allowed: [panic]
|
47
|
+
default: panic
|
48
|
+
message: unexpected %s, expected one of %s
|
49
|
+
Module/Definition/MultipleData:
|
50
|
+
allowed: [error, panic]
|
51
|
+
default: error
|
52
|
+
message: too many data definitions in file
|
53
|
+
note: >-
|
54
|
+
The compiler preceeds with error checking using the first data
|
55
|
+
definition in the file. However, the compiler never preceeds to the
|
56
|
+
emitting stage.
|
57
|
+
Module/Definition/MultipleNames:
|
58
|
+
allowed: [error, panic]
|
59
|
+
default: error
|
60
|
+
message: too many module name definitions in file
|
61
|
+
note: >-
|
62
|
+
The compiler preceeds with error checking using the first name
|
63
|
+
definition in the file. However, the compiler never preceeds to the
|
64
|
+
emitting stage.
|
65
|
+
Module/Definition/MismatchedName:
|
66
|
+
allowed: [all]
|
67
|
+
default: error
|
68
|
+
message: expected name %s, found name %s
|
69
|
+
note: >-
|
70
|
+
The default behavior is to use the name in the file. If the mode
|
71
|
+
is anything under panic, the name in the file is set as the name of
|
72
|
+
the module.
|
73
|
+
Module/Name/Invalid:
|
74
|
+
allowed: [error, panic]
|
75
|
+
default: error
|
76
|
+
message: the module name had an invalid component
|
77
|
+
note: >-
|
78
|
+
This typically doesn't happen except in directives, where a directive
|
79
|
+
was expecting a "normal" module name instead of the directive's
|
80
|
+
"extended" module name.
|
81
|
+
Module/Name/InvalidExtended:
|
82
|
+
allowed: [error, panic]
|
83
|
+
default: error
|
84
|
+
message: the star component of a module name was in an invalid position
|
85
|
+
note: >-
|
86
|
+
This only happens in an extended module name for a directive. The
|
87
|
+
star of a module name must be at the end.
|
88
|
+
Module/Name/IgnoredGenerics:
|
89
|
+
allowed: [all]
|
90
|
+
default: warning
|
91
|
+
message: the module name includes generic information that will be discarded
|
92
|
+
Module/Name/UnexpectedGeneric:
|
93
|
+
allowed: [error, panic]
|
94
|
+
default: error
|
95
|
+
message: the module name includes generic information that was not expected
|
96
|
+
Diagnostic/Unknown:
|
97
|
+
allowed: [all]
|
98
|
+
default: error
|
99
|
+
message: an unknown directive was used
|
100
|
+
note: >-
|
101
|
+
The default behavior is to ignore the directive.
|
102
|
+
Directive/Parameter/Invalid:
|
103
|
+
allowed: [error, panic]
|
104
|
+
default: error
|
105
|
+
message: an invalid parameter for a directive was used
|
106
|
+
note: >-
|
107
|
+
Due to technical restraints, this also includes parameters that don't
|
108
|
+
exist.
|
109
|
+
Directive/Parameter/Excessive:
|
110
|
+
allowed: [warning, error, panic]
|
111
|
+
default: warning
|
112
|
+
message: expected %s, got %s
|
113
|
+
note: >-
|
114
|
+
The default behavior is to ignore the excessive parameters.
|
115
|
+
Enum/Definition/Duplicate:
|
116
|
+
allowed: [error, panic]
|
117
|
+
default: error
|
118
|
+
message: one or more elements in the enum were duplicated
|
119
|
+
Enum/Definition/ConflictingTypes:
|
120
|
+
allowed: [error, panic]
|
121
|
+
default: error
|
122
|
+
message: one or more elements had differing types
|
123
|
+
Struct/Definition/Duplicate:
|
124
|
+
allowed: [error, panic]
|
125
|
+
default: error
|
126
|
+
message: duplicate name %s
|
127
|
+
Function/Duplicate:
|
128
|
+
allowed: [error, panic]
|
129
|
+
default: error
|
130
|
+
message: a function was duplicated
|
131
|
+
Expression/Type/Mismatch:
|
132
|
+
allowed: [ignore, warning, error, panic]
|
133
|
+
default: error
|
134
|
+
message: found type %s, expected type %s
|
135
|
+
Statement/Let/Redefine:
|
136
|
+
allowed: [ignore, warning, error, panic]
|
137
|
+
default: error
|
138
|
+
message: attempted to redefine local %s
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Carbon
|
5
|
+
module Compiler
|
6
|
+
class Metanostic
|
7
|
+
# A diagnostic. This is an actual diagnostic, spawned off of a metanostic
|
8
|
+
# and an issue in compiling a file. This contains information about the
|
9
|
+
# related metanostic, the location of the diagnostic, an associated
|
10
|
+
# message, and the mode the diagnostic is considered.
|
11
|
+
class Diagnostic
|
12
|
+
TEMPLATESTR = File.read(File.expand_path("../template.erb", __FILE__))
|
13
|
+
.gsub(/\r\n|\r|\n/, "\n")
|
14
|
+
TEMPLATE = ERB.new(TEMPLATESTR, nil, ">")
|
15
|
+
|
16
|
+
include Comparable
|
17
|
+
# The associated metanostic. This contains information about
|
18
|
+
# information the name and the allowed modes of this diagnostic.
|
19
|
+
#
|
20
|
+
# @return [Metanostic]
|
21
|
+
attr_reader :metanostic
|
22
|
+
# The associated location. If the location is not given, it defaults
|
23
|
+
# to {Location.default}.
|
24
|
+
#
|
25
|
+
# @return [Location]
|
26
|
+
attr_reader :location
|
27
|
+
# The message for the diagnostic. If none is given, it defaults to the
|
28
|
+
# metanostic's message.
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
attr_reader :message
|
32
|
+
# The mode for the diagnostic. This changes how the diagnostic is
|
33
|
+
# treated.
|
34
|
+
#
|
35
|
+
# @return [Integer]
|
36
|
+
attr_reader :mode
|
37
|
+
|
38
|
+
# Initializes the diagnostic, and then freezes it.
|
39
|
+
#
|
40
|
+
# @param data [{Symbol => Object}] Origin date for the diagnostic.
|
41
|
+
# @option data [Metanostic] :meta The associated metanostic.
|
42
|
+
# See {#metanostic}.
|
43
|
+
# @option data [Location] :location The location. See {#location}.
|
44
|
+
# @option data [Integer] :mode The mode. See {#mode}.
|
45
|
+
# @option data [String?] :message The message. See {#message}.
|
46
|
+
# @raise [KeyError] If one of the above options was not present.
|
47
|
+
def initialize(data)
|
48
|
+
@metanostic = data.fetch(:meta)
|
49
|
+
@location = data.fetch(:location)
|
50
|
+
@mode = data.fetch(:mode)
|
51
|
+
@list = data.fetch(:list)
|
52
|
+
@stack = data.fetch(:stack) { caller[3..8] }
|
53
|
+
@message = data[:message] || @metanostic.message
|
54
|
+
to_a
|
55
|
+
freeze
|
56
|
+
end
|
57
|
+
|
58
|
+
# Outputs this diagnostic to the given IO. If the mode is
|
59
|
+
# {Mode::IGNORE}, and {Carbon.verbose} is less than `1`, then the
|
60
|
+
# diagnostic is not output.
|
61
|
+
#
|
62
|
+
# @param io [#<<]
|
63
|
+
# @return [void]
|
64
|
+
def output(io)
|
65
|
+
return if mode == Mode::IGNORE && Carbon.verbose < 1
|
66
|
+
io << TEMPLATE.result(binding) << "\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Compares this instance with another instance. It does so by comparing
|
70
|
+
# the array representations of both; therefore, this makes no sense
|
71
|
+
# other than equality.
|
72
|
+
#
|
73
|
+
# @param other [Diagnostic] The diagnostic to compare.
|
74
|
+
# @return [Numeric]
|
75
|
+
def <=>(other)
|
76
|
+
fail ArgumentError, "Expected Diagnostic" unless \
|
77
|
+
other.is_a?(Diagnostic)
|
78
|
+
to_a <=> other.to_a
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns a numeric representation of this class for use of hashing.
|
82
|
+
#
|
83
|
+
# @return [Numeric]
|
84
|
+
def hash
|
85
|
+
to_a.hash
|
86
|
+
end
|
87
|
+
|
88
|
+
# An array representation of this class. This is frozen and cached.
|
89
|
+
#
|
90
|
+
# @return [(Class, Metanostic, Location, Integer, String)]
|
91
|
+
def to_a
|
92
|
+
@array ||= [self.class, @metanostic, @location, @mode,
|
93
|
+
@message].freeze
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def distance
|
99
|
+
@location.column.end - @location.column.begin
|
100
|
+
end
|
101
|
+
|
102
|
+
def lines
|
103
|
+
(@location.line.begin - 1)..(@location.line.end - 1)
|
104
|
+
end
|
105
|
+
|
106
|
+
def file
|
107
|
+
@list.files[@location.file]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Carbon
|
5
|
+
module Compiler
|
6
|
+
class Metanostic
|
7
|
+
# A list of diagnostics. This handles setting up the modes of
|
8
|
+
# new, emitted diagnostics, and reporting diagnostic types.
|
9
|
+
class List
|
10
|
+
include Enumerable
|
11
|
+
extend Forwardable
|
12
|
+
# The current state. This contains information about what modes each
|
13
|
+
# diagnostic should inherit.
|
14
|
+
#
|
15
|
+
# @return [State]
|
16
|
+
attr_reader :state
|
17
|
+
# The default hash of metanostics.
|
18
|
+
#
|
19
|
+
# @see Defaults.defaults
|
20
|
+
# @return [{::Hash => Metanostic}]
|
21
|
+
attr_reader :defaults
|
22
|
+
# The project files for the metanostic list. This is to provide context
|
23
|
+
# for diagnostic errors.
|
24
|
+
#
|
25
|
+
# @retrun [{::String => Project::File}]
|
26
|
+
attr_reader :files
|
27
|
+
|
28
|
+
THRESHOLDS = {
|
29
|
+
"warnings" => 100,
|
30
|
+
"errors" => 20
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
delegate [:each, :size, :count, :length, :clear] => :@list
|
34
|
+
|
35
|
+
# Initialize the list.
|
36
|
+
def initialize
|
37
|
+
@defaults = Metanostic::Defaults.defaults
|
38
|
+
@state = State.new
|
39
|
+
@list = Concurrent::Array.new
|
40
|
+
@files = Concurrent::Hash.new
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a list of diagnostics that have the {Mode::PANIC} mode.
|
44
|
+
#
|
45
|
+
# @return [<Diagnostic>]
|
46
|
+
def panics
|
47
|
+
select { |d| d.metanostic.default == Mode::PANIC }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a list of diagnostics that have the {Mode::ERROR} mode.
|
51
|
+
#
|
52
|
+
# @return [<Diagnostic>]
|
53
|
+
def errors
|
54
|
+
select { |d| d.metanostic.default == Mode::ERROR }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a list of diagnostics that have the {Mode::WARNING} mode.
|
58
|
+
#
|
59
|
+
# @return [<Diagnostic>]
|
60
|
+
def warnings
|
61
|
+
select { |d| d.metanostic.default == Mode::WARNING }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Emits a diagnostic to the list. It sets up the data to pass to the
|
65
|
+
# diagnostic. If the diagnostic cannot be found, it instead emits a
|
66
|
+
# `System/Error` diagnostic.
|
67
|
+
def emit(name, location = Location.default, format = [])
|
68
|
+
mode, metanostic = @state.fetch(name) do
|
69
|
+
return emit("System/Error", location, [name])
|
70
|
+
end
|
71
|
+
|
72
|
+
message = format.is_a?(::String) ? format :
|
73
|
+
sprintf(metanostic.message, *format)
|
74
|
+
|
75
|
+
stack = caller[2..7]
|
76
|
+
diagnostic = Diagnostic.new(meta: metanostic, location: location,
|
77
|
+
message: message, mode: mode, list: self, stack: stack)
|
78
|
+
@list << diagnostic
|
79
|
+
diagnostic.output($stderr)
|
80
|
+
diagnostic_check
|
81
|
+
end
|
82
|
+
|
83
|
+
alias_method :<<, :emit
|
84
|
+
alias_method :push, :emit
|
85
|
+
|
86
|
+
# Outputs all of the diagnostics in this list. Does nothing if
|
87
|
+
# {Carbon.quiet?} is true.
|
88
|
+
#
|
89
|
+
# @see Diagnostic#output
|
90
|
+
# @param io [#<<] The IO device to output to.
|
91
|
+
# @return [void]
|
92
|
+
def output(io)
|
93
|
+
return if Carbon.quiet?
|
94
|
+
each { |i| i.output(io, @files) }
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def diagnostic_check
|
100
|
+
fail DiagnosticError, "Exceeded warning threshold" if \
|
101
|
+
warnings.size >= THRESHOLDS.fetch("warnings")
|
102
|
+
fail DiagnosticError, "Exceeded error threshold" if \
|
103
|
+
errors.size >= THRESHOLDS.fetch("errors")
|
104
|
+
fail DiagnosticError, "Exceeded panic threshold" if panics.size >= 1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|