class Carbon::Concrete::Type::Parse

Parses a type. This starts by scanning the string, then parsing it. Each instance should result in a different parsed value; a single instance cannot be re-used for parsing something else. **Not for public consumption.**

@api private @private

Constants

FUNC_NAME

Public Class Methods

new(scan) click to toggle source

Initialize the parser.

@param scan [::String] The string to parse.

# File lib/carbon/concrete/type/parse.rb, line 21
def initialize(scan)
  @string = scan
  @scanner = StringScanner.new(@string)
end

Public Instance Methods

value() click to toggle source

The resulting value. This should be a name.

@see parse @return [Type::Name]

# File lib/carbon/concrete/type/parse.rb, line 30
def value
  @_value ||= parse
end

Private Instance Methods

error(expected, given) click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 164
def error(expected, given)
  fail ArgumentError,
    "Unexpected #{given.inspect}, " \
    "expected #{expected.map(&:inspect).join(', ')} " \
    "(source #{@string})"
end
expect(*values) click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 52
def expect(*values)
  token = @tokens.next
  error(values, token[0]) unless values.include?(token[0])
  token
end
parse() click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 36
def parse
  @tokens = scan
  name = parse_name
end
parse_function_name() click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 73
def parse_function_name
  name = nil
  params = []
  expect :"."
  name =  case @tokens.peek[0]
          when :<   then expect(:<); "<"
          when :>   then expect(:>); ">"
          when :+   then expect(:+); "+"
          else expect(:FNAME)[1]
          end

  expect :"("
  until @tokens.peek[0] == :")"
    params << Type.new(parse_name)
    break unless @tokens.peek[0] == :","
    expect :","
  end
  expect :")"

  Function.new(name, params)
end
parse_generic() click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 113
def parse_generic
  name = Type.new(parse_name)
  implements = []
  if @tokens.peek[0] == :":"
    expect :":"
    implements << Type.new(parse_name)
    while @tokens.peek[0] == :+
      expect :+
      implements << Type.new(parse_name)
    end
  end

  Generic.new(name, implements)
end
parse_name() click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 58
def parse_name
  parts = [parse_name_part]
  func = nil

  while @tokens.peek[0] == :"::"
    expect :"::"
    parts << parse_name_part
  end

  parts = [*parts[0..-2].map(&:strip), parts[-1]]
  func = parse_function_name if @tokens.peek[0] == :"."

  Name.new(parts, func)
end
parse_name_part() click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 95
def parse_name_part
  part = expect(:MNAME)
  generics = []
  if @tokens.peek[0] == :<
    expect :<

    until @tokens.peek[0] == :>
      generics << parse_generic
      break unless @tokens.peek[0] == :","
      expect :","
    end

    expect :>
  end

  Part.new(part[1], generics)
end
scan() { |single| ... } click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 41
def scan
  return to_enum(:scan) unless block_given?

  until @scanner.eos?
    single = scan_single
    yield single if single
  end

  yield [:EOS]
end
scan_single() click to toggle source
# File lib/carbon/concrete/type/parse.rb, line 144
def scan_single
  case
  when @scanner.scan(/[A-Z][A-Za-z0-9_-]*/)
    [:MNAME, @scanner[0]]
  when @scanner.scan(FUNC_NAME)
    [:FNAME, @scanner[0]]
  when @scanner.scan(/::/) then [:"::"]
  when @scanner.scan(/</) then [:<]
  when @scanner.scan(/>/) then [:>]
  when @scanner.scan(/,/) then [:","]
  when @scanner.scan(/:/) then [:":"]
  when @scanner.scan(/\+/) then [:+]
  when @scanner.scan(/\./) then [:"."]
  when @scanner.scan(/\(/) then [:"("]
  when @scanner.scan(/\)/) then [:")"]
  when @scanner.scan(/\s+/)
  else error([], @string[@scanner.pos])
  end
end