Class: SoberSwag::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/sober_swag/parser.rb

Overview

Parses a Dry-Types Schema into a set of nodes we can use to compile. This is mostly because the visitor pattern sucks and catamorphisms are nice.

Do not use this class directly, as it is not part of the public api. Instead, use classes from the Compiler namespace.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(node) ⇒ Parser

Returns a new instance of Parser.



9
10
11
12
# File 'lib/sober_swag/parser.rb', line 9

def initialize(node)
  @node = node
  @found = Set.new
end

Instance Attribute Details

#foundObject (readonly)

What other types did we find while parsing this type?



77
78
79
# File 'lib/sober_swag/parser.rb', line 77

def found
  @found
end

Instance Method Details

#bind(other) ⇒ Object

Call .to_syntax on another node, putting any new classes it finds in the list of classes we found in the process.

If you're a big Haskell nerd, then this is >>=.



84
85
86
87
88
# File 'lib/sober_swag/parser.rb', line 84

def bind(other)
  result = other.to_syntax
  @found += other.found
  result
end

#run_parserObject



71
72
73
# File 'lib/sober_swag/parser.rb', line 71

def run_parser
  [to_syntax, found]
end

#to_syntaxSoberSwag::Nodes::Base

Compile to one of our internal nodes.

Returns:



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/sober_swag/parser.rb', line 17

def to_syntax # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
  case @node
  when Dry::Types::Default
    # we handle this elsewhere, so
    bind(Parser.new(@node.type))
  when Dry::Types::Array::Member
    Nodes::List.new(bind(Parser.new(@node.member)))
  when Dry::Types::Enum
    Nodes::Enum.new(@node.values)
  when Dry::Types::Schema
    Nodes::Object.new(
      @node.map { |attr| bind(Parser.new(attr)) }
    )
  when Dry::Types::Schema::Key
    Nodes::Attribute.new(
      @node.name,
      @node.required? && !@node.type.default?,
      bind(Parser.new(@node.type)),
      @node.meta
    )
  when Dry::Types::Sum
    left = bind(Parser.new(@node.left))
    right = bind(Parser.new(@node.right))
    # special case booleans to just return the left value
    # this is because modeling a boolean as a sum type of
    # TrueClass and FalseClass is kinda weird, because they're
    # considered different types instead of different constructors,
    # which we don't want to do
    is_bool = [left, right].all? do |e|
      e.respond_to?(:value) && [TrueClass, FalseClass].include?(e.value)
    end
    if is_bool
      left
    else
      Nodes::Sum.new(left, right)
    end
  when Dry::Types::Constrained
    bind(Parser.new(@node.type))
  when Dry::Types::Nominal
    if @node.respond_to?(:type) && @node.type.is_a?(Dry::Types::Constrained)
      bind(Parser.new(@node.type))
    else
      old_meta = @node.primitive.respond_to?(:meta) ? @node.primitive.meta : {}
      # start off with the moral equivalent of NodeTree[String]
      Nodes::Primitive.new(@node.primitive, old_meta.merge(@node.meta))
    end
  else
    # Inside of this case we have a class that is some user-defined type
    # We put it in our array of found types, and consider it a primitive
    @found.add(@node)
    Nodes::Primitive.new(@node, @node.respond_to?(:meta) ? @node.meta : {})
  end
end