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