class Xdrgen::Generators::Go

Public Instance Methods

generate() click to toggle source
# File lib/xdrgen/generators/go.rb, line 6
def generate
  @already_rendered = []
  path = "#{@namespace}_generated.go"
  out = @output.open(path)

  render_top_matter out
  render_definitions(out, @top)
  render_bottom_matter out
end

Private Instance Methods

access_arm(arm) click to toggle source
# File lib/xdrgen/generators/go.rb, line 493
      def access_arm(arm)

        <<-EOS.strip_heredoc
          // Must#{name arm} retrieves the #{name arm} value from the union,
          // panicing if the value is not set.
          func (u #{name arm.union}) Must#{name arm}() #{reference arm.type} {
            val, ok := u.Get#{name arm}()

            if !ok {
              panic("arm #{name arm} is not set")
            }

            return val
          }

          // Get#{name arm} retrieves the #{name arm} value from the union,
          // returning ok if the union's switch indicated the value is valid.
          func (u #{name arm.union}) Get#{name arm}() (result #{reference arm.type}, ok bool) {
            armName, _ := u.ArmForSwitch(int32(u.#{name arm.union.discriminant}))

            if armName == "#{name arm}" {
              result = *u.#{name arm}
              ok = true
            }

            return
          }
        EOS
      end
escape_name(name) click to toggle source
# File lib/xdrgen/generators/go.rb, line 451
def escape_name(name)
  case name
  when "type" ; "aType"
  when "func" ; "aFunc"
  else ; name
  end
end
field_tag(struct, field) click to toggle source
# File lib/xdrgen/generators/go.rb, line 560
def field_tag(struct, field)
  size = nil

  case field.declaration
  when Xdrgen::AST::Declarations::Opaque
    size = field.declaration.resolved_size
  when Xdrgen::AST::Declarations::String
    size = field.declaration.resolved_size
  when Xdrgen::AST::Declarations::Array
    size = field.declaration.resolved_size unless field.declaration.fixed?
  end

  return "`xdrmaxsize:\"#{size}\"`" if size.present?
end
name(named) click to toggle source
# File lib/xdrgen/generators/go.rb, line 435
def name(named)
  parent = name named.parent_defn if named.is_a?(AST::Concerns::NestedDefinition)

  base = if named.respond_to?(:name)
    named.name
  else
    named.text_value
  end

  "#{parent}#{base.underscore.camelize}"
end
private_name(named) click to toggle source
# File lib/xdrgen/generators/go.rb, line 447
def private_name(named)
  escape_name named.name.underscore.camelize(:lower)
end
reference(type) click to toggle source
# File lib/xdrgen/generators/go.rb, line 377
def reference(type)
  baseReference = case type
  when AST::Typespecs::Bool
    "bool"
  when AST::Typespecs::Double
    "float64"
  when AST::Typespecs::Float
    "float32"
  when AST::Typespecs::Hyper
    "int64"
  when AST::Typespecs::Int
    "int32"
  when AST::Typespecs::Opaque
    if type.fixed?
      "[#{type.size}]byte"
    else
      "[]byte"
    end
  when AST::Typespecs::Quadruple
    raise "no quadruple support for go"
  when AST::Typespecs::String
    "string"
  when AST::Typespecs::UnsignedHyper
    "uint64"
  when AST::Typespecs::UnsignedInt
    "uint32"
  when AST::Typespecs::Simple
    name type
  when AST::Definitions::Base
    name type
  when AST::Concerns::NestedDefinition
    name type
  else
    raise "Unknown reference type: #{type.class.name}, #{type.class.ancestors}"
  end

  case type.sub_type
  when :simple
    baseReference
  when :optional
    "*#{baseReference}"
  when :array
    is_named, size = type.array_size

    # if named, lookup the const definition
    if is_named
      size = name @top.find_definition(size)
    end

    "[#{size}]#{baseReference}"
  when :var_array
    "[#{size}]#{baseReference}"
  else
    raise "Unknown sub_type: #{type.sub_type}"
  end

end
render_binary_interface(out, name) click to toggle source
# File lib/xdrgen/generators/go.rb, line 314
def render_binary_interface(out, name)
  out.puts "// MarshalBinary implements encoding.BinaryMarshaler."
  out.puts "func (s #{name}) MarshalBinary() ([]byte, error) {"
  out.puts "  b := new(bytes.Buffer)"
  out.puts "  _, err := Marshal(b, s)"
  out.puts "  return b.Bytes(), err"
  out.puts "}"
  out.break
  out.puts "// UnmarshalBinary implements encoding.BinaryUnmarshaler."
  out.puts "func (s *#{name}) UnmarshalBinary(inp []byte) error {"
  out.puts "  _, err := Unmarshal(bytes.NewReader(inp), s)"
  out.puts "  return err"
  out.puts "}"
  out.break
  out.puts "var ("
  out.puts "  _ encoding.BinaryMarshaler   = (*#{name})(nil)"
  out.puts "  _ encoding.BinaryUnmarshaler = (*#{name})(nil)"
  out.puts ")"
  out.break
end
render_bottom_matter(out) click to toggle source
# File lib/xdrgen/generators/go.rb, line 368
      def render_bottom_matter(out)
        out.puts <<-EOS
        var fmtTest = fmt.Sprint("this is a dummy usage of fmt")

        EOS
      end
render_const(out, const) click to toggle source
# File lib/xdrgen/generators/go.rb, line 135
def render_const(out, const)
  out.puts "const #{name const} = #{const.value}"
  out.break
end
render_definition(out, defn) click to toggle source
# File lib/xdrgen/generators/go.rb, line 150
def render_definition(out, defn)
  if @already_rendered.include? name(defn)

    unless defn.is_a?(AST::Definitions::Namespace)
      $stderr.puts "warn: #{name(defn)} is defined twice.  skipping"
    end

    return
  end

  render_nested_definitions(out, defn)
  render_source_comment(out, defn)

  @already_rendered << name(defn)

  case defn
  when AST::Definitions::Struct ;
    render_struct out, defn
    render_binary_interface out, name(defn)
  when AST::Definitions::Enum ;
    render_enum out, defn
    render_binary_interface out, name(defn)
  when AST::Definitions::Union ;
    render_union out, defn
    render_binary_interface out, name(defn)
  when AST::Definitions::Typedef ;
    render_typedef out, defn
    render_binary_interface out, name(defn)
  when AST::Definitions::Const ;
    render_const out, defn
  end
end
render_definitions(out, node) click to toggle source
# File lib/xdrgen/generators/go.rb, line 140
def render_definitions(out, node)
  node.definitions.each{|n| render_definition out, n }
  node.namespaces.each{|n| render_definitions out, n }
end
render_enum(out, enum) click to toggle source
# File lib/xdrgen/generators/go.rb, line 211
      def render_enum(out, enum)
        # render the "enum"
        out.puts "type #{name enum} int32"
        out.puts "const ("
        out.indent do
          first_member = enum.members.first
          out.puts "#{name enum}#{name first_member} #{name enum} = #{first_member.value}"

          rest_members = enum.members.drop(1)
          rest_members.each do |m|
            out.puts "#{name enum}#{name m} #{name enum} = #{m.value}"
          end
        end
        out.puts ")"

        # render the map used by xdr to decide valid values
        out.puts "var #{private_name enum}Map = map[int32]string{"
        out.indent do

          enum.members.each do |m|
            out.puts "#{m.value}: \"#{name enum}#{name m}\","
          end

        end
        out.puts "}"

        out.break

        out.puts <<-EOS.strip_heredoc
          // ValidEnum validates a proposed value for this enum.  Implements
          // the Enum interface for #{name enum}
          func (e #{name enum}) ValidEnum(v int32) bool {
            _, ok := #{private_name enum}Map[v]
            return ok
          }
        EOS

        out.puts <<-EOS.strip_heredoc
          // String returns the name of `e`
          func (e #{name enum}) String() string {
            name, _ := #{private_name enum}Map[int32(e)]
            return name
          }
        EOS

        out.break
      end
render_enum_typedef(out, typedef, enum) click to toggle source
# File lib/xdrgen/generators/go.rb, line 59
      def render_enum_typedef(out, typedef, enum)
        out.puts <<-EOS.strip_heredoc
          // ValidEnum validates a proposed value for this enum.  Implements
          // the Enum interface for #{name typedef}
          func (e #{name typedef}) ValidEnum(v int32) bool {
            return #{name enum}(e).ValidEnum(v)
          }
        EOS

        out.puts <<-EOS.strip_heredoc
          // String returns the name of `e`
          func (e #{name typedef}) String() string {
            return #{name enum}(e).String()
          }
        EOS

        out.break
      end
render_maxsize_method(out, typedef, size) click to toggle source
# File lib/xdrgen/generators/go.rb, line 48
      def render_maxsize_method(out, typedef, size)
        return if size.blank?

        out.puts <<-EOS.strip_heredoc
          // XDRMaxSize implements the Sized interface for #{name typedef}
          func (e #{name typedef}) XDRMaxSize() int {
            return #{size}
          }
        EOS
      end
render_nested_definitions(out, defn) click to toggle source
# File lib/xdrgen/generators/go.rb, line 145
def render_nested_definitions(out, defn)
  return unless defn.respond_to? :nested_definitions
  defn.nested_definitions.each{|ndefn| render_definition out, ndefn}
end
render_source_comment(out, defn) click to toggle source
# File lib/xdrgen/generators/go.rb, line 183
      def render_source_comment(out, defn)
        return if defn.is_a?(AST::Definitions::Namespace)

        out.puts <<-EOS.strip_heredoc
          // #{name defn} is an XDR #{defn.class.name.demodulize} defines as:
          //
        EOS

        out.puts "//   " + defn.text_value.split("\n").join("\n//    ")

        out.puts <<-EOS.strip_heredoc
          //
        EOS
      end
render_struct(out, struct) click to toggle source
# File lib/xdrgen/generators/go.rb, line 198
def render_struct(out, struct)
  out.puts "type #{name struct} struct {"
  out.indent do

    struct.members.each do |m|
      out.puts "#{name m} #{reference(m.declaration.type)} #{field_tag struct, m}"
    end

  end
  out.puts "}"
  out.break
end
render_top_matter(out) click to toggle source
# File lib/xdrgen/generators/go.rb, line 335
      def render_top_matter(out)
        out.puts <<-EOS.strip_heredoc
          // Package #{@namespace || "main"} is generated from:
          //
          //  #{@output.source_paths.join("\n//  ")}
          //
          // DO NOT EDIT or your changes may be overwritten
          package #{@namespace || "main"}

          import (
            "bytes"
            "encoding"
            "io"
            "fmt"

            "github.com/stellar/go-xdr/xdr3"
          )

          // Unmarshal reads an xdr element from `r` into `v`.
          func Unmarshal(r io.Reader, v interface{}) (int, error) {
            // delegate to xdr package's Unmarshal
                return xdr.Unmarshal(r, v)
          }

          // Marshal writes an xdr element `v` into `w`.
          func Marshal(w io.Writer, v interface{}) (int, error) {
            // delegate to xdr package's Marshal
            return xdr.Marshal(w, v)
          }
        EOS
        out.break
      end
render_typedef(out, typedef) click to toggle source
# File lib/xdrgen/generators/go.rb, line 18
def render_typedef(out, typedef)
  out.puts "type #{name typedef} #{reference typedef.declaration.type}"


  # write sizing restrictions
  case typedef.declaration
  when Xdrgen::AST::Declarations::String
    render_maxsize_method out, typedef, typedef.declaration.resolved_size
  when Xdrgen::AST::Declarations::Opaque
    render_maxsize_method out, typedef, typedef.declaration.resolved_size
  when Xdrgen::AST::Declarations::Array
    unless typedef.declaration.fixed?
      render_maxsize_method out, typedef, typedef.declaration.resolved_size
    end
  end

  return unless typedef.sub_type == :simple

  resolved = typedef.resolved_type

  case resolved
  when AST::Definitions::Enum
    render_enum_typedef out, typedef, resolved
  when AST::Definitions::Union
    render_union_typedef out, typedef, resolved
  end

  out.break
end
render_union(out, union) click to toggle source
# File lib/xdrgen/generators/go.rb, line 259
      def render_union(out, union)

        out.puts "type #{name union} struct{"
        out.indent do
          out.puts "#{name union.discriminant} #{reference union.discriminant.type}"

          union.arms.each do |arm|
            next if arm.void?
            out.puts "#{name arm} *#{reference arm.type} #{field_tag union, arm}"
          end
        end
        out.puts "}"
        out.break

        out.puts <<-EOS.strip_heredoc
          // SwitchFieldName returns the field name in which this union's
          // discriminant is stored
          func (u #{name union}) SwitchFieldName() string {
            return "#{name union.discriminant}"
          }
        EOS

        out.break

        out.puts <<-EOS.strip_heredoc
          // ArmForSwitch returns which field name should be used for storing
          // the value for an instance of #{name union}
          func (u #{name union}) ArmForSwitch(sw int32) (string, bool) {
        EOS

        switch_for(out, union, "sw") do |arm, kase|
          "return \"#{name arm unless arm.void?}\", true"
        end

        # when the default arm is not present, we must render the failure case
        unless union.default_arm.present?
          out.puts 'return "-", false'
        end

        out.puts "}"
        out.break

        # Add constructor of the form u := NewUnion(switch,val)
        render_union_constructor(out, union)

        # Add accessors for of form val, ok := union.GetArmName()
        # and val := union.MustArmName()
        union.arms.each do |arm|
          next if arm.void?
          out.puts access_arm(arm)
        end

        out.break
      end
render_union_constructor(out, union) click to toggle source
# File lib/xdrgen/generators/go.rb, line 459
      def render_union_constructor(out, union)
        constructor_name  = "New#{name union}"


        discriminant_arg = private_name union.discriminant
        discriminant_type = reference union.discriminant.type

        out.puts <<-EOS.strip_heredoc
          // #{constructor_name} creates a new  #{name union}.
          func #{constructor_name}(#{discriminant_arg} #{discriminant_type}, value interface{}) (result #{reference union}, err error) {
            result.#{name union.discriminant} = #{discriminant_arg}
        EOS

        switch_for(out, union, discriminant_arg) do |arm, kase|
          if arm.void?
            "// void"
          else
            <<-EOS
            tv, ok := value.(#{reference arm.type})
            if !ok {
              err = fmt.Errorf("invalid value, must be #{reference arm.type}")
              return
            }
            result.#{name arm} = &tv
            EOS
          end
        end

        out.puts <<-EOS.strip_heredoc
            return
          }
        EOS
      end
render_union_typedef(out, typedef, union) click to toggle source
# File lib/xdrgen/generators/go.rb, line 78
      def render_union_typedef(out, typedef, union)
        out.puts <<-EOS.strip_heredoc
          // SwitchFieldName returns the field name in which this union's
          // discriminant is stored
          func (u #{name typedef}) SwitchFieldName() string {
            return #{name union}(u).SwitchFieldName()
          }
        EOS

        out.break

        out.puts <<-EOS.strip_heredoc
          // ArmForSwitch returns which field name should be used for storing
          // the value for an instance of #{name union}
          func (u #{name typedef}) ArmForSwitch(sw int32) (string, bool) {
            return #{name union}(u).ArmForSwitch(sw)
          }
        EOS

        out.break

        constructor_name  = "New#{name typedef}"
        discriminant_arg = private_name union.discriminant
        discriminant_type = reference union.discriminant.type

        out.puts <<-EOS.strip_heredoc
          // #{constructor_name} creates a new  #{name typedef}.
          func #{constructor_name}(#{discriminant_arg} #{discriminant_type}, value interface{}) (result #{reference typedef}, err error) {
            u, err := New#{name union}(#{discriminant_arg}, value)
            result = #{name typedef}(u)
            return
          }
        EOS

        out.break

        # Add accessors for of form val, ok := union.GetArmName()
        # and val := union.MustArmName()
        union.arms.each do |arm|
          next if arm.void?
          out.puts   <<-EOS.strip_heredoc
            // Must#{name arm} retrieves the #{name arm} value from the union,
            // panicing if the value is not set.
            func (u #{name typedef}) Must#{name arm}() #{reference arm.type} {
              return #{name union}(u).Must#{name arm}()
            }

            // Get#{name arm} retrieves the #{name arm} value from the union,
            // returning ok if the union's switch indicated the value is valid.
            func (u #{name typedef}) Get#{name arm}() (result #{reference arm.type}, ok bool) {
              return #{name union}(u).Get#{name arm}()
            }
          EOS
        end

      end
size(size_s) click to toggle source
# File lib/xdrgen/generators/go.rb, line 523
def size(size_s)
  result = size_s
  result = "MaxXdrElements" if result.blank?
  result
end
switch_for(out, union, ident) click to toggle source
# File lib/xdrgen/generators/go.rb, line 529
def switch_for(out, union, ident)
  out.puts "switch #{reference union.discriminant.type}(#{ident}) {"

  union.normal_arms.each do |arm|
    arm.cases.each do |c|

      value = if c.value.is_a?(AST::Identifier)
                member = union.resolved_case(c)
                if union.discriminant_type.nil? then
                  "int32(#{name member.enum}#{name member})"
                else
                  "#{name union.discriminant_type}#{name member}"
                end
              else
                c.value.text_value
              end

      out.puts "    case #{value}:"
      out.puts "      #{yield arm, c}"
    end
  end

  if union.default_arm.present?
    arm = union.default_arm
    out.puts "    default:"
    out.puts "      #{yield arm, :default}"
  end

  out.puts "}"
end