carbon-compiler 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +17 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +39 -0
  6. data/.travis.yml +5 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +41 -0
  11. data/Rakefile +9 -0
  12. data/Vagrantfile +84 -0
  13. data/carbon-compiler.gemspec +28 -0
  14. data/lib/carbon/compiler.rb +20 -0
  15. data/lib/carbon/compiler/directive.rb +48 -0
  16. data/lib/carbon/compiler/directive/import.rb +17 -0
  17. data/lib/carbon/compiler/errors.rb +7 -0
  18. data/lib/carbon/compiler/location.rb +136 -0
  19. data/lib/carbon/compiler/metanostic.rb +123 -0
  20. data/lib/carbon/compiler/metanostic/defaults.rb +41 -0
  21. data/lib/carbon/compiler/metanostic/defaults.yml +138 -0
  22. data/lib/carbon/compiler/metanostic/diagnostic.rb +112 -0
  23. data/lib/carbon/compiler/metanostic/list.rb +109 -0
  24. data/lib/carbon/compiler/metanostic/mode.rb +162 -0
  25. data/lib/carbon/compiler/metanostic/state.rb +174 -0
  26. data/lib/carbon/compiler/metanostic/template.erb +11 -0
  27. data/lib/carbon/compiler/node.rb +18 -0
  28. data/lib/carbon/compiler/node/base.rb +213 -0
  29. data/lib/carbon/compiler/node/definition.rb +19 -0
  30. data/lib/carbon/compiler/node/definition/class.rb +24 -0
  31. data/lib/carbon/compiler/node/definition/class/element.rb +18 -0
  32. data/lib/carbon/compiler/node/definition/directive.rb +22 -0
  33. data/lib/carbon/compiler/node/definition/directive/function.rb +18 -0
  34. data/lib/carbon/compiler/node/definition/enum.rb +20 -0
  35. data/lib/carbon/compiler/node/definition/enum/element.rb +17 -0
  36. data/lib/carbon/compiler/node/definition/function.rb +44 -0
  37. data/lib/carbon/compiler/node/definition/function/body.rb +17 -0
  38. data/lib/carbon/compiler/node/definition/function/name.rb +18 -0
  39. data/lib/carbon/compiler/node/definition/function/parameter.rb +18 -0
  40. data/lib/carbon/compiler/node/definition/function/parameters.rb +17 -0
  41. data/lib/carbon/compiler/node/definition/module.rb +23 -0
  42. data/lib/carbon/compiler/node/definition/struct.rb +24 -0
  43. data/lib/carbon/compiler/node/definition/struct/element.rb +18 -0
  44. data/lib/carbon/compiler/node/etype.rb +66 -0
  45. data/lib/carbon/compiler/node/etype/option.rb +25 -0
  46. data/lib/carbon/compiler/node/etype/star.rb +13 -0
  47. data/lib/carbon/compiler/node/expression.rb +18 -0
  48. data/lib/carbon/compiler/node/expression/assignment.rb +22 -0
  49. data/lib/carbon/compiler/node/expression/call.rb +22 -0
  50. data/lib/carbon/compiler/node/expression/call/access.rb +24 -0
  51. data/lib/carbon/compiler/node/expression/call/attribute.rb +23 -0
  52. data/lib/carbon/compiler/node/expression/call/enum.rb +25 -0
  53. data/lib/carbon/compiler/node/expression/call/module.rb +24 -0
  54. data/lib/carbon/compiler/node/expression/call/parameters.rb +17 -0
  55. data/lib/carbon/compiler/node/expression/call/self.rb +17 -0
  56. data/lib/carbon/compiler/node/expression/call/unified.rb +27 -0
  57. data/lib/carbon/compiler/node/expression/literal.rb +83 -0
  58. data/lib/carbon/compiler/node/expression/operation.rb +20 -0
  59. data/lib/carbon/compiler/node/expression/operation/and.rb +20 -0
  60. data/lib/carbon/compiler/node/expression/operation/neq.rb +21 -0
  61. data/lib/carbon/compiler/node/expression/operation/normal.rb +20 -0
  62. data/lib/carbon/compiler/node/expression/operation/or.rb +20 -0
  63. data/lib/carbon/compiler/node/expression/unit.rb +16 -0
  64. data/lib/carbon/compiler/node/name.rb +27 -0
  65. data/lib/carbon/compiler/node/root.rb +23 -0
  66. data/lib/carbon/compiler/node/statement.rb +24 -0
  67. data/lib/carbon/compiler/node/statement/catch.rb +20 -0
  68. data/lib/carbon/compiler/node/statement/condition.rb +14 -0
  69. data/lib/carbon/compiler/node/statement/else.rb +17 -0
  70. data/lib/carbon/compiler/node/statement/elsif.rb +18 -0
  71. data/lib/carbon/compiler/node/statement/finally.rb +17 -0
  72. data/lib/carbon/compiler/node/statement/for.rb +18 -0
  73. data/lib/carbon/compiler/node/statement/if.rb +18 -0
  74. data/lib/carbon/compiler/node/statement/let.rb +18 -0
  75. data/lib/carbon/compiler/node/statement/match.rb +14 -0
  76. data/lib/carbon/compiler/node/statement/return.rb +17 -0
  77. data/lib/carbon/compiler/node/statement/try.rb +19 -0
  78. data/lib/carbon/compiler/node/statement/while.rb +19 -0
  79. data/lib/carbon/compiler/parser.rb +63 -0
  80. data/lib/carbon/compiler/parser/common.rb +79 -0
  81. data/lib/carbon/compiler/parser/expressions.rb +39 -0
  82. data/lib/carbon/compiler/parser/expressions/precedence.rb +134 -0
  83. data/lib/carbon/compiler/parser/expressions/primary.rb +120 -0
  84. data/lib/carbon/compiler/parser/firsts.rb +74 -0
  85. data/lib/carbon/compiler/parser/helpers.rb +61 -0
  86. data/lib/carbon/compiler/parser/root.rb +57 -0
  87. data/lib/carbon/compiler/parser/root/class.rb +34 -0
  88. data/lib/carbon/compiler/parser/root/directive.rb +87 -0
  89. data/lib/carbon/compiler/parser/root/enum.rb +45 -0
  90. data/lib/carbon/compiler/parser/root/function.rb +90 -0
  91. data/lib/carbon/compiler/parser/root/struct.rb +34 -0
  92. data/lib/carbon/compiler/parser/root/trait.rb +44 -0
  93. data/lib/carbon/compiler/parser/statements.rb +86 -0
  94. data/lib/carbon/compiler/parser/statements/if.rb +50 -0
  95. data/lib/carbon/compiler/parser/statements/match.rb +39 -0
  96. data/lib/carbon/compiler/parser/statements/try.rb +49 -0
  97. data/lib/carbon/compiler/project.rb +37 -0
  98. data/lib/carbon/compiler/project/file.rb +64 -0
  99. data/lib/carbon/compiler/scanner.rb +82 -0
  100. data/lib/carbon/compiler/scanner/main.rb +76 -0
  101. data/lib/carbon/compiler/scanner/token.rb +58 -0
  102. data/lib/carbon/compiler/version.rb +8 -0
  103. data/lib/carbon/compiler/visitor.rb +13 -0
  104. data/lib/carbon/compiler/visitor/base.rb +52 -0
  105. data/lib/carbon/compiler/visitor/generation.rb +45 -0
  106. data/lib/carbon/compiler/visitor/generation/asserts.rb +30 -0
  107. data/lib/carbon/compiler/visitor/generation/class.rb +93 -0
  108. data/lib/carbon/compiler/visitor/generation/context.rb +75 -0
  109. data/lib/carbon/compiler/visitor/generation/expressions.rb +82 -0
  110. data/lib/carbon/compiler/visitor/generation/expressions/assignment.rb +105 -0
  111. data/lib/carbon/compiler/visitor/generation/expressions/calls.rb +89 -0
  112. data/lib/carbon/compiler/visitor/generation/function.rb +68 -0
  113. data/lib/carbon/compiler/visitor/generation/statements.rb +131 -0
  114. data/lib/carbon/compiler/visitor/generation/struct.rb +115 -0
  115. data/lib/carbon/compiler/visitor/preparation.rb +86 -0
  116. data/lib/carbon/compiler/visitor/preparation/expressions.rb +26 -0
  117. data/lib/carbon/compiler/visitor/preparation/function.rb +55 -0
  118. data/lib/carbon/compiler/visitor/preparation/statements.rb +73 -0
  119. data/lib/carbon/compiler/visitor/preparation/struct.rb +37 -0
  120. data/program.ca +16 -0
  121. data/test.rb +21 -0
  122. metadata +234 -0
@@ -0,0 +1,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