jarib/webidl

View on GitHub
lib/webidl/generator/ruby_sexp_visitor.rb

Summary

Maintainability
B
5 hrs
Test Coverage
module WebIDL
  class RubySexpVisitor

    def visit_module(mod)
      [:block,
        [:module, classify(mod.name),
          [:scope,
            [:block] + mod.definitions.map { |d| d.accept(self) }
          ]
        ]
      ]
    end

    def visit_interface(intf)
      [:module, classify(intf.name),
        ([:scope, [:block] + intf.inherits.map { |inherit| [:call, nil, :include, [:arglist, inherit.accept(self)]] }] unless intf.inherits.empty?),
        [:scope,
          [:block] + intf.members.map { |m| m.accept(self) }
        ]
      ].compact
    end

    def visit_dictionary(dict)
      [:module, classify(dict.name),
        ([:scope, [:block] + dict.inherits.map { |inherit| [:call, nil, :include, [:arglist, inherit.accept(self)]] }] unless dict.inherits.empty?),
        [:scope,
          [:block] + dict.members.map { |m| m.accept(self) }
        ]
      ].compact
    end

    def visit_exception(ex)
      [:class, classify(ex.name), [:const, :StandardError],
        [:scope,
          [:block] + ex.members.map { |m| m.accept(self)}
        ]
      ]
    end

    def visit_const(const)
      [:cdecl, const.name, [:lit, const.value]] # FIXME: won't always be literals - need Literal AST node?
    end

    def visit_enum(enum)
      [:cdecl, enum.name, [:lit, enum.values]]
    end

    def visit_field(field)
      [:call, nil, :attr_accessor, [:arglist, [:lit, field.name.to_sym]]]
    end

    def visit_attribute(attribute)
      func = attribute.readonly? ? :attr_reader : :attr_accessor
      [:call, nil, func, [:arglist, [:lit, attrify(attribute.name).to_sym]]]
    end

    def visit_dictionary_member(mem)
      [:call, nil, :attr_accessor, [:arglist, [:lit, attrify(mem.name).to_sym]]]
    end

    def visit_callback(callback)
      arguments = callback.arguments.map { |a| [:lit, attrify(a.name).to_sym] }

      [:defn, callback.name,
        [:args] + callback.arguments.map { |a| a.accept(self) },
        [:scope,
          [:block,
            [:call, nil, :raise,
              [:arglist,
                [:const, :NotImplementedError]
              ]
            ]
          ]
        ]
      ]
    end

    def visit_operation(operation)
      if operation.name.empty?
        if operation.setter?
          meth = :[]=
        elsif operation.getter?
          meth = :[]
        elsif operation.creator?
          meth = :initialize
        elsif operation.stringifier?
          meth = :to_s
        elsif operation.deleter?
          meth = :delete!
        elsif operation.legacycaller?
          meth = operation.parent.name
        else
          raise "no name for operation #{operation.inspect}"
        end
      else
        meth = attrify(operation.name)
        meth << "=" if operation.setter?
      end

      [:defn, meth.to_sym,
        [:args] + operation.args.map { |a| a.accept(self) },
        [:scope,
          [:block,
            [:call, nil, :raise,
              [:arglist,
                [:const, :NotImplementedError]
              ]
            ]
          ]
        ]
      ]
    end

    def visit_argument(argument)
      name = attrify(argument.name)
      arg = argument.variadic? ? "*#{name}" : name

      arg.to_sym
    end

    def visit_type_def(typedef)
      # don't care
    end

    def visit_implements_statement(impls)
      [:module, classify(impls.implementor),
        [:scope,
          [:call, nil, :include,
            [:arglist,
              [:const, classify(impls.implementee)]
            ]
          ]
        ]
      ]
    end

    def visit_includes_statement(incls)
      [:module, classify(incls.includer),
        [:scope,
          [:call, nil, :include,
            [:arglist,
              [:const, classify(impls.includee)]
            ]
          ]
        ]
      ]
    end

    def visit_scoped_name(sn)
      [:const, classify(sn.qualified_name)]
    end

    private

    def classify(string)
      s = string.to_s

      if s.include?("::")
        s.split("::").map { |e| classify(e) unless e.empty? }.compact.join("::").to_sym
      else
        "#{s.slice(0,1).upcase}#{s[1..-1]}".to_sym
      end
    end

    RESERVED = %w[
      BEGIN    do    next    then
      END    else    nil    true
      alias    elsif    not    undef
      and    end    or    unless
      begin    ensure    redo    until
      break    false    rescue    when
      case    for    retry    while
      class    if    return    while
      def    in    self    __FILE__
      defined?    module    super    __LINE__
    ]

    def attrify(string)
      attr = string.snake_case
      attr += '_' if RESERVED.include?(attr)

      attr
    end

  end # RubySexpVisitor
end # WebIDL