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.
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,39 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ class Parser
7
+ module Statements
8
+ # Parses a match statement.
9
+ module Match
10
+ protected
11
+
12
+ # TODO: Fix
13
+
14
+ def parse_statement_match
15
+ expect :match
16
+ expect :"("
17
+ source = parse_expression
18
+ expect :")"
19
+ expect :do
20
+ children = []
21
+ children << parse_match_element until peek? :end
22
+ expect :end
23
+
24
+ elements = Node.new(:statement_match_elements, children)
25
+ Node.new(:statement_match, [source, elements])
26
+ end
27
+
28
+ def parse_match_element
29
+ pattern = parse_expression
30
+ expect :"=>"
31
+ body = parse_statement
32
+
33
+ Node.new(:statement_match_element, [pattern, body])
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ class Parser
7
+ module Statements
8
+ # Parses a try statement.
9
+ module Try
10
+ protected
11
+
12
+ def parse_statement_try
13
+ expect :try
14
+ body = parse_statement
15
+ continued = parse_statement_try_continued
16
+
17
+ Node::Statement::Try.new([body, continued])
18
+ end
19
+
20
+ def parse_statement_try_continued
21
+ case peek.type
22
+ when :catch then parse_statement_try_catch
23
+ when :finally then parse_statement_try_finally
24
+ end
25
+ end
26
+
27
+ def parse_statement_try_catch
28
+ expect :catch
29
+ expect :"("
30
+ name = parse_name
31
+ expect :":"
32
+ type = parse_type
33
+ expect :")"
34
+ body = parse_statement
35
+ follow = parse_statement_try_continued
36
+
37
+ Node::Statement::Catch.new([name, type, body, follow])
38
+ end
39
+
40
+ def parse_statement_try_finally
41
+ expect :finally
42
+
43
+ Node::Statement::Finally.new([parse_statement])
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "carbon/compiler/project/file"
5
+ require "llvm/target"
6
+ require "llvm/execution_engine"
7
+
8
+ module Carbon
9
+ module Compiler
10
+ class Project
11
+ attr_reader :diagnostics
12
+ attr_reader :items
13
+ attr_reader :main
14
+
15
+ def initialize(files, root, libraries: [])
16
+ @root = Pathname.new(root).expand_path(Dir.pwd)
17
+ @files = files.map { |f| Project::File.new(f, @root, self) }
18
+ @diagnostics = Metanostic::List.new
19
+ @libraries = []
20
+ @files.each { |f| @diagnostics.files[f.short] = f }
21
+ end
22
+
23
+ def call
24
+ @index ||= begin
25
+ @index = Concrete::Index.new
26
+ @files.each { |f| f.call(@index, @diagnostics.clone) }
27
+ # load libraries...
28
+ @libraries.each do |library|
29
+ @index.link(Carbon::Concrete.load(::File.read(library, "rb")))
30
+ end
31
+ @index.link(Carbon::Core.index)
32
+ @index.finalize
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ class Project
7
+ class File
8
+ attr_reader :name
9
+ attr_reader :root
10
+ attr_reader :path
11
+ attr_reader :short
12
+ attr_reader :aliases
13
+
14
+ def initialize(path, root, project)
15
+ @root = Pathname.new(root)
16
+ @path = Pathname.new(path).expand_path(@root)
17
+ @short = @path.relative_path_from(@root)
18
+ @name = @short.sub_ext("").to_s
19
+ @project = project
20
+ @aliases = {}
21
+ end
22
+
23
+ def module
24
+ @module ||= begin
25
+ mname = @name
26
+ .gsub(/\A([a-z])/, &:upcase)
27
+ .gsub(/(\/|\\)([a-z])/) { |m| "::#{m[1].upcase}" }
28
+ .gsub(/_([a-z])/) { |m| m[1].upcase }
29
+ .gsub(/\/|\\/, "::")
30
+ Carbon::Type(mname)
31
+ end
32
+ end
33
+
34
+ attr_writer :module
35
+
36
+ def emit(*p)
37
+ @diagnostics.emit(*p)
38
+ end
39
+
40
+ def call(index, diagnostics)
41
+ @diagnostics = diagnostics
42
+ root = Visitor::Preparation.new(self, index).call(parser.root)
43
+ Visitor::Generation.new(self, index).call(root)
44
+ end
45
+
46
+ def parser
47
+ @parser ||= Compiler::Parser.new(scanner)
48
+ end
49
+
50
+ def scanner
51
+ @scanner ||= Compiler::Scanner.new(self)
52
+ end
53
+
54
+ def read
55
+ @_read ||= @path.read
56
+ end
57
+
58
+ def lines
59
+ @_lines ||= @path.each_line
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require "strscan"
4
+ require "carbon/compiler/scanner/main"
5
+ require "carbon/compiler/scanner/token"
6
+
7
+ module Carbon
8
+ module Compiler
9
+ # Scans over the file, taking a squence of characters and turning them into
10
+ # tokens.
11
+ class Scanner
12
+ include Main
13
+ include Enumerable
14
+
15
+ # The file that the scanner will run over.
16
+ #
17
+ # @return [Project::File]
18
+ attr_reader :file
19
+
20
+ # Initialize the scanner. Takes a file, which contains information about
21
+ # the file, and can read from the file.
22
+ #
23
+ # @param file [Project::File]
24
+ def initialize(file)
25
+ @file = file
26
+ @scanner = StringScanner.new(@file.read)
27
+ @line = 1
28
+ @last = 0
29
+ end
30
+
31
+ # Pretty inspect.
32
+ #
33
+ # @return [void]
34
+ def inspect
35
+ "#<#{self.class} #{@file.name}>"
36
+ end
37
+
38
+ # Yields each token to the block, if one is given. Returns an
39
+ # enumerator that enumerates over all generated tokens.
40
+ #
41
+ # @yield [token]
42
+ # @yieldparam token [Token]
43
+ # @return [Enumerator<Token>]
44
+ def each
45
+ return to_enum unless block_given?
46
+ until @scanner.eos?
47
+ token = scan
48
+ yield token if token.is_a?(Token)
49
+ end
50
+
51
+ yield Token.new(:EOF, "", location)
52
+ @scanner.reset
53
+ end
54
+
55
+ private
56
+
57
+ def match(string, name = :"#{string}")
58
+ if string.is_a?(::String)
59
+ string = /#{::Regexp.escape(string.to_s)}/
60
+ elsif string.is_a?(::Symbol)
61
+ string = /#{::Regexp.escape(string.to_s)}(?![\w])/
62
+ end
63
+
64
+ if @scanner.scan(string)
65
+ Token.new(name, @scanner[0], location(@scanner[0].length))
66
+ end
67
+ end
68
+
69
+ def location(length = 0)
70
+ lines = @line..@line
71
+ start = @scanner.charpos - @last
72
+ columns = (start - length)..start
73
+ Location.new(lines, columns, @file.short)
74
+ end
75
+
76
+ def error!
77
+ @file.emit("Syntax/Token/Unknown", location,
78
+ [@scanner.string[@scanner.charpos]])
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ class Scanner
7
+ # Performs the scanning.
8
+ module Main
9
+ protected
10
+
11
+ def scan
12
+ false ||
13
+ scan_keywords ||
14
+ scan_identifiers ||
15
+ scan_operators ||
16
+ scan_comment ||
17
+ scan_whitespace ||
18
+ error!
19
+ end
20
+
21
+ # rubocop:disable Metrics/CyclomaticComplexity
22
+ def scan_identifiers
23
+ false ||
24
+ match(/[a-z][A-Za-z0-9_-]*[!?=]?/, :IDENTIFIER) ||
25
+ match(/(0[xX][0-9a-fA-F]+)(_?[ui][0-9]+)?/, :NUMBER) ||
26
+ match(/(0|[1-9][0-9]*)(_?[ui][0-9]+)?/, :NUMBER) ||
27
+ match(/[A-Z][A-Za-z0-9_-]*/, :MNAME) ||
28
+ match(/"(([^"\\]|\\.)*)"/, :STRING) ||
29
+ false
30
+ end
31
+
32
+ KEYWORDS = %i(
33
+ finally return struct module catch elsif match trait while
34
+ self else enum for def end let try do if _
35
+ ).sort_by(&:size).reverse.freeze
36
+
37
+ def scan_keywords
38
+ KEYWORDS.find do |keyword|
39
+ m = match(/#{Regexp.escape(keyword)}(?![a-zA-Z0-9!?=_-])/, keyword)
40
+ return m if m
41
+ end || false
42
+ end
43
+
44
+ OPERATORS = %w(
45
+ === :: => == && || != << >> <= >= + - ~ ! * / % : ; & | [ ] ( ) { } <
46
+ > . , = ^ [] []=
47
+ ).sort_by(&:size).reverse.freeze
48
+
49
+ def scan_operators
50
+ OPERATORS.find do |operator|
51
+ m = match(operator)
52
+ return m if m
53
+ end || false
54
+ end
55
+
56
+ def scan_comment
57
+ if @scanner.scan(/\#.+?(\r\n|\r|\n)/)
58
+ @line += 1
59
+ @last = @scanner.charpos - 1
60
+ true
61
+ end
62
+ end
63
+
64
+ def scan_whitespace
65
+ if @scanner.scan(/\r\n|\r|\n/)
66
+ @line += 1
67
+ @last = @scanner.charpos - 1
68
+ true
69
+ elsif @scanner.scan(/[ \v\t\f]/)
70
+ true
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ class Scanner
7
+ # A token from the scanner.
8
+ class Token
9
+ # The type of the token. For any explicit matches (e.g. `"module"`),
10
+ # this is the exact match (e.g. `:module`). For any regular matches
11
+ # (e.g. an identifier), this is in all caps (e.g. `:IDENTIFIER`).
12
+ #
13
+ # @return [Symbol]
14
+ attr_reader :type
15
+ # The lexeme that this token is associated. This is what the token
16
+ # matched directly from the source.
17
+ #
18
+ # @return [String]
19
+ attr_reader :value
20
+ # The exact location the token was taken from. This only spans one
21
+ # line.
22
+ #
23
+ # @return [Location]
24
+ attr_reader :location
25
+
26
+ # Initialize the token. Freezes it after initializing.
27
+ #
28
+ # @param type [Symbol] The type. See {#type}.
29
+ # @param value [String] The value. See {#value}.
30
+ # @param location [Location] The location. See {#location}.
31
+ def initialize(type, value, location)
32
+ @type = type
33
+ @value = value
34
+ @location = location
35
+ @children = []
36
+ to_a
37
+ freeze
38
+ end
39
+
40
+ # Creates an array representation of this class. This is cached and
41
+ # frozen, since nothing about this class changes.
42
+ #
43
+ # @return [(Class, Symbol, String, Location)]
44
+ def to_a
45
+ @array ||= [self.class, @type, @value, @location].freeze
46
+ end
47
+
48
+ # Creates a numeric representation of this class for hashing.
49
+ #
50
+ # @see #to_a
51
+ # @return [Numeric]
52
+ def hash
53
+ to_a.hash
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ VERSION = "0.2.0".freeze
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "carbon/compiler/visitor/base"
5
+ require "carbon/compiler/visitor/generation"
6
+ require "carbon/compiler/visitor/preparation"
7
+
8
+ module Carbon
9
+ module Compiler
10
+ module Visitor
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Carbon
5
+ module Compiler
6
+ module Visitor
7
+ # A visitor base.
8
+ module Base
9
+ # The class methods.
10
+ module ClassMethods
11
+ def matches
12
+ @_matches ||= Concurrent::Hash.new
13
+ end
14
+
15
+ def on(nodes)
16
+ matches.merge!(nodes)
17
+ end
18
+ end
19
+
20
+ # The instance methods.
21
+ module InstanceMethods
22
+ def accept(node, *params)
23
+ visit(node, *params)
24
+ end
25
+
26
+ def visit(node, *params)
27
+ return unless node
28
+ begin
29
+ action = self.class.matches.fetch(node.class)
30
+ rescue KeyError
31
+ visit_missing(node, *params)
32
+ end
33
+ public_send(action, node, *params)
34
+ end
35
+
36
+ def visit_missing(node, *)
37
+ fail ArgumentError, "Cannot visit #{node.class}"
38
+ end
39
+
40
+ def visit_ignore(node)
41
+ node
42
+ end
43
+ end
44
+
45
+ def self.included(base)
46
+ base.include InstanceMethods
47
+ base.extend ClassMethods
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end