xdrgen 0.0.1

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 (112) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +12 -0
  6. data/LICENSE.txt +202 -0
  7. data/README.md +79 -0
  8. data/Rakefile +6 -0
  9. data/bin/xdrgen +5 -0
  10. data/lib/xdrgen.rb +24 -0
  11. data/lib/xdrgen/ast.rb +81 -0
  12. data/lib/xdrgen/ast/concerns/contained.rb +32 -0
  13. data/lib/xdrgen/ast/concerns/has_children.rb +9 -0
  14. data/lib/xdrgen/ast/concerns/has_definitions.rb +90 -0
  15. data/lib/xdrgen/ast/concerns/named.rb +16 -0
  16. data/lib/xdrgen/ast/concerns/namespace.rb +7 -0
  17. data/lib/xdrgen/ast/concerns/nested_definition.rb +16 -0
  18. data/lib/xdrgen/ast/constant.rb +7 -0
  19. data/lib/xdrgen/ast/decimal_constant.rb +7 -0
  20. data/lib/xdrgen/ast/declarations/array.rb +15 -0
  21. data/lib/xdrgen/ast/declarations/base.rb +28 -0
  22. data/lib/xdrgen/ast/declarations/opaque.rb +11 -0
  23. data/lib/xdrgen/ast/declarations/optional.rb +5 -0
  24. data/lib/xdrgen/ast/declarations/simple.rb +7 -0
  25. data/lib/xdrgen/ast/declarations/string.rb +7 -0
  26. data/lib/xdrgen/ast/declarations/void.rb +7 -0
  27. data/lib/xdrgen/ast/definitions/base.rb +9 -0
  28. data/lib/xdrgen/ast/definitions/const.rb +12 -0
  29. data/lib/xdrgen/ast/definitions/enum.rb +17 -0
  30. data/lib/xdrgen/ast/definitions/enum_member.rb +44 -0
  31. data/lib/xdrgen/ast/definitions/namespace.rb +9 -0
  32. data/lib/xdrgen/ast/definitions/nested_enum.rb +7 -0
  33. data/lib/xdrgen/ast/definitions/nested_struct.rb +7 -0
  34. data/lib/xdrgen/ast/definitions/nested_union.rb +7 -0
  35. data/lib/xdrgen/ast/definitions/struct.rb +18 -0
  36. data/lib/xdrgen/ast/definitions/struct_body.rb +11 -0
  37. data/lib/xdrgen/ast/definitions/struct_member.rb +12 -0
  38. data/lib/xdrgen/ast/definitions/typedef.rb +19 -0
  39. data/lib/xdrgen/ast/definitions/union.rb +55 -0
  40. data/lib/xdrgen/ast/definitions/union_arm.rb +33 -0
  41. data/lib/xdrgen/ast/definitions/union_arm_case.rb +11 -0
  42. data/lib/xdrgen/ast/definitions/union_body.rb +22 -0
  43. data/lib/xdrgen/ast/definitions/union_default_arm.rb +19 -0
  44. data/lib/xdrgen/ast/fixed_size.rb +23 -0
  45. data/lib/xdrgen/ast/hexadecimal_constant.rb +7 -0
  46. data/lib/xdrgen/ast/identifier.rb +5 -0
  47. data/lib/xdrgen/ast/octal_constant.rb +7 -0
  48. data/lib/xdrgen/ast/top.rb +5 -0
  49. data/lib/xdrgen/ast/typespecs/base.rb +27 -0
  50. data/lib/xdrgen/ast/typespecs/bool.rb +5 -0
  51. data/lib/xdrgen/ast/typespecs/double.rb +5 -0
  52. data/lib/xdrgen/ast/typespecs/float.rb +5 -0
  53. data/lib/xdrgen/ast/typespecs/hyper.rb +6 -0
  54. data/lib/xdrgen/ast/typespecs/int.rb +6 -0
  55. data/lib/xdrgen/ast/typespecs/opaque.rb +10 -0
  56. data/lib/xdrgen/ast/typespecs/quadruple.rb +5 -0
  57. data/lib/xdrgen/ast/typespecs/simple.rb +14 -0
  58. data/lib/xdrgen/ast/typespecs/string.rb +9 -0
  59. data/lib/xdrgen/ast/typespecs/unsigned_hyper.rb +5 -0
  60. data/lib/xdrgen/ast/typespecs/unsigned_int.rb +5 -0
  61. data/lib/xdrgen/ast/var_size.rb +26 -0
  62. data/lib/xdrgen/ast/whitespace.rb +5 -0
  63. data/lib/xdrgen/cli.rb +31 -0
  64. data/lib/xdrgen/compilation.rb +31 -0
  65. data/lib/xdrgen/generators.rb +16 -0
  66. data/lib/xdrgen/generators/base.rb +11 -0
  67. data/lib/xdrgen/generators/elixir.rb +260 -0
  68. data/lib/xdrgen/generators/go.rb +578 -0
  69. data/lib/xdrgen/generators/java.rb +810 -0
  70. data/lib/xdrgen/generators/java/XdrDataInputStream.erb +122 -0
  71. data/lib/xdrgen/generators/java/XdrDataOutputStream.erb +96 -0
  72. data/lib/xdrgen/generators/java/XdrElement.erb +10 -0
  73. data/lib/xdrgen/generators/java/XdrString.erb +58 -0
  74. data/lib/xdrgen/generators/javascript.rb +248 -0
  75. data/lib/xdrgen/generators/ruby.rb +283 -0
  76. data/lib/xdrgen/grammar/base.treetop +71 -0
  77. data/lib/xdrgen/grammar/comments.treetop +15 -0
  78. data/lib/xdrgen/grammar/const.treetop +8 -0
  79. data/lib/xdrgen/grammar/declaration.treetop +99 -0
  80. data/lib/xdrgen/grammar/enum.treetop +46 -0
  81. data/lib/xdrgen/grammar/main.treetop +30 -0
  82. data/lib/xdrgen/grammar/namespace.treetop +12 -0
  83. data/lib/xdrgen/grammar/struct.treetop +32 -0
  84. data/lib/xdrgen/grammar/typedef.treetop +12 -0
  85. data/lib/xdrgen/grammar/union.treetop +63 -0
  86. data/lib/xdrgen/output.rb +37 -0
  87. data/lib/xdrgen/output_file.rb +87 -0
  88. data/lib/xdrgen/parser.rb +40 -0
  89. data/lib/xdrgen/version.rb +3 -0
  90. data/spec/fixtures/generator/block_comments.x +14 -0
  91. data/spec/fixtures/generator/const.x +4 -0
  92. data/spec/fixtures/generator/enum.x +36 -0
  93. data/spec/fixtures/generator/nesting.x +24 -0
  94. data/spec/fixtures/generator/optional.x +8 -0
  95. data/spec/fixtures/generator/struct.x +10 -0
  96. data/spec/fixtures/generator/test.x +77 -0
  97. data/spec/fixtures/generator/union.x +28 -0
  98. data/spec/fixtures/parser/block_comments.x +14 -0
  99. data/spec/fixtures/parser/const.x +1 -0
  100. data/spec/fixtures/parser/enum.x +35 -0
  101. data/spec/fixtures/parser/nesting.x +26 -0
  102. data/spec/fixtures/parser/optional.x +8 -0
  103. data/spec/fixtures/parser/struct.x +8 -0
  104. data/spec/fixtures/parser/test.x +77 -0
  105. data/spec/fixtures/parser/union.x +10 -0
  106. data/spec/lib/xdrgen/generator_spec.rb +30 -0
  107. data/spec/lib/xdrgen/output_file_spec.rb +33 -0
  108. data/spec/lib/xdrgen/parser_spec.rb +27 -0
  109. data/spec/spec_helper.rb +13 -0
  110. data/spec/support/fixtures.rb +27 -0
  111. data/xdrgen.gemspec +32 -0
  112. metadata +301 -0
@@ -0,0 +1,16 @@
1
+ module Xdrgen::Generators
2
+ extend ActiveSupport::Autoload
3
+
4
+ autoload :Base
5
+ autoload :Ruby
6
+ autoload :Go
7
+ autoload :Javascript
8
+ autoload :Java
9
+ autoload :Elixir
10
+
11
+ def self.for_language(language)
12
+ const_get language.to_s.classify
13
+ rescue NameError
14
+ raise ArgumentError, "Unsupported language: #{language}"
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ class Xdrgen::Generators::Base
2
+ def initialize(top, output, namespace=nil)
3
+ @top = top
4
+ @output = output
5
+ @namespace = namespace
6
+ end
7
+
8
+ def generate
9
+ raise NotImplementedError
10
+ end
11
+ end
@@ -0,0 +1,260 @@
1
+ module Xdrgen
2
+ module Generators
3
+ class Elixir < Xdrgen::Generators::Base
4
+ MAX_INT = (2**31) - 1
5
+ def generate
6
+ path = "#{@namespace}_generated.ex"
7
+ out = @output.open(path)
8
+
9
+ render_define_block(out) do
10
+ out.indent() do
11
+ render_definitions(out, @top)
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+ def render_definitions(out, node)
18
+ node.definitions.each{|n| render_definition out, n }
19
+ node.namespaces.each{|n| render_definitions out, n }
20
+ end
21
+
22
+ def render_nested_definitions(out, defn)
23
+ return unless defn.respond_to? :nested_definitions
24
+ defn.nested_definitions.each{|ndefn| render_definition out, ndefn}
25
+ end
26
+
27
+ def render_definition(out, defn)
28
+ render_nested_definitions(out, defn)
29
+ render_source_comment(out, defn)
30
+
31
+ case defn
32
+ when AST::Definitions::Struct ;
33
+ render_struct out, defn
34
+ when AST::Definitions::Enum ;
35
+ render_enum out, defn
36
+ when AST::Definitions::Union ;
37
+ render_union out, defn
38
+ when AST::Definitions::Typedef ;
39
+ render_typedef out, defn
40
+ when AST::Definitions::Const ;
41
+ render_const out, defn
42
+ end
43
+
44
+ out.break
45
+ end
46
+
47
+ def render_source_comment(out, defn)
48
+ return if defn.is_a?(AST::Definitions::Namespace)
49
+
50
+ out.puts <<-EOS.strip_heredoc
51
+ comment ~S"""
52
+ === xdr source ============================================================
53
+
54
+ EOS
55
+
56
+ out.puts " " + defn.text_value.split("\n").join("\n ")
57
+
58
+ out.puts <<-EOS.strip_heredoc
59
+
60
+ ===========================================================================
61
+ """
62
+ EOS
63
+ end
64
+
65
+ def render_moduledoc(out)
66
+ out.puts <<-EOS.strip_heredoc
67
+ @moduledoc """
68
+ Automatically generated on #{Time.now.iso8601}
69
+ DO NOT EDIT or your changes may be overwritten
70
+
71
+ Target implementation: exdr at https://hex.pm/packages/exdr
72
+ """
73
+ EOS
74
+ out.break
75
+ end
76
+
77
+ def render_define_block(out)
78
+ out.puts "defmodule #{@namespace} do"
79
+ out.indent do
80
+ render_moduledoc(out)
81
+ out.puts "use XDR.Base\n\n"
82
+ end
83
+ yield
84
+ ensure
85
+ out.puts "end"
86
+ out.break
87
+ end
88
+
89
+
90
+ def render_typedef(out, typedef)
91
+ out.puts "define_type(\"#{name typedef}\", #{build_type_args typedef.declaration.type})"
92
+ end
93
+
94
+ def render_const(out, const)
95
+ out.puts "define_type(\"#{const_name const}\", Const, #{const.value});"
96
+ end
97
+
98
+ def render_struct(out, struct)
99
+ out.puts "define_type(\"#{name struct}\", Struct,"
100
+ out.indent do
101
+ struct.members.each_with_index do |m, i|
102
+ out.puts "#{member_name m}: #{type_reference m.type}#{comma_unless_last(i, struct.members)}"
103
+ end
104
+ end
105
+ out.puts ")"
106
+ end
107
+
108
+ def render_enum(out, enum)
109
+ out.puts "define_type(\"#{name enum}\", Enum,"
110
+ out.indent do
111
+ enum.members.each_with_index do |m, i|
112
+ out.puts "#{member_name m}: #{m.value}#{comma_unless_last(i, enum.members)}"
113
+ end
114
+ end
115
+ out.puts ")"
116
+ end
117
+
118
+ def render_union(out, union)
119
+ out.puts "define_type(\"#{name union}\", Union,"
120
+ out.indent do
121
+ out.puts "switch_type: #{type_reference union.discriminant.type},"
122
+ out.puts "switch_name: :#{member_name union.discriminant},"
123
+
124
+ out.puts "switches: ["
125
+ out.indent do
126
+ union.normal_arms.each do |arm|
127
+ arm_name = arm.void? ? "XDR.Type.Void" : ":#{member_name(arm)}"
128
+
129
+ arm.cases.each do |acase|
130
+ switch = if acase.value.is_a?(AST::Identifier)
131
+ ":#{member_name(acase.value)}"
132
+ else
133
+ acase.value.text_value
134
+ end
135
+
136
+ out.puts "{#{switch}, #{arm_name}},"
137
+ end
138
+ end
139
+ end
140
+ out.puts "],"
141
+
142
+ out.puts "arms: ["
143
+ out.indent do
144
+ union.arms.each do |arm|
145
+ next if arm.void?
146
+ out.puts "#{member_name arm}: #{type_reference arm.type},"
147
+ end
148
+ end
149
+ out.puts union.default_arm.present? ? "]," : "]"
150
+
151
+ if union.default_arm.present?
152
+ arm = union.default_arm
153
+ arm_name = arm.void? ? "XDR.Type.Void" : member_name(arm)
154
+ out.puts "default_arm: #{arm_name},"
155
+ end
156
+ end
157
+ out.puts ")"
158
+ end
159
+
160
+ private
161
+ def name(named)
162
+ return nil unless named.respond_to?(:name)
163
+
164
+ parent = name named.parent_defn if named.is_a?(AST::Concerns::NestedDefinition)
165
+
166
+ # NOTE: classify will strip plurality, so we restore it if necessary
167
+ plural = named.name.pluralize == named.name
168
+ base = named.name.underscore.classify
169
+ result = plural ? base.pluralize : base
170
+
171
+ "#{parent}#{result}"
172
+ end
173
+
174
+ def const_name(named)
175
+ # NOTE: classify will strip plurality, so we restore it if necessary
176
+ plural = named.name.pluralize == named.name
177
+ base = named.name.underscore.upcase
178
+ plural ? base.pluralize : base
179
+ end
180
+
181
+ def member_name(member)
182
+ name(member).underscore
183
+ end
184
+
185
+ # this can be a string to reference a custom type
186
+ # or a build_type call like build_type(VariableOpaque, 100)
187
+ # args for build_type can be created with build_type_args
188
+ def type_reference(type)
189
+ build_args = build_type_args(type)
190
+
191
+ build_args === "\"#{name type}\"" ? build_args : "build_type(#{build_args})"
192
+ end
193
+
194
+ def comma_unless_last(index, collection)
195
+ if index + 1 >= collection.length
196
+ ""
197
+ else
198
+ ","
199
+ end
200
+ end
201
+
202
+ # the args to supply build_type (or define_type(name, ...args))
203
+ def build_type_args(type)
204
+ base_ref = case type
205
+ when AST::Typespecs::Bool
206
+ "Bool"
207
+ when AST::Typespecs::Double
208
+ "Double"
209
+ when AST::Typespecs::Float
210
+ "Float"
211
+ when AST::Typespecs::Hyper
212
+ "HyperInt"
213
+ when AST::Typespecs::Int
214
+ "Int"
215
+ when AST::Typespecs::Opaque
216
+ if type.fixed?
217
+ "Opaque, #{type.size}"
218
+ else
219
+ type.size ? "VariableOpaque, #{type.size}" : "VariableOpaque"
220
+ end
221
+ when AST::Typespecs::Quadruple
222
+ raise "no quadruple support in elixir"
223
+ when AST::Typespecs::String
224
+ "XDR.Type.String, #{type.size}"
225
+ when AST::Typespecs::UnsignedHyper
226
+ "UnsignedHyperInt"
227
+ when AST::Typespecs::UnsignedInt
228
+ "UnsignedInt"
229
+ when AST::Typespecs::Simple
230
+ "\"#{name type}\""
231
+ when AST::Definitions::Base
232
+ "\"#{name type}\""
233
+ when AST::Concerns::NestedDefinition
234
+ "\"#{name type}\""
235
+ else
236
+ raise "Unknown reference type: #{type.class.name}, #{type.class.ancestors}"
237
+ end
238
+
239
+ base_type = base_ref === "\"#{name type}\"" ? base_ref : "buid_type(base_ref)"
240
+
241
+ case type.sub_type
242
+ when :simple
243
+ base_ref
244
+ when :optional
245
+ "Optional, #{base_type}"
246
+ when :array
247
+ is_named, size = type.array_size
248
+ size = is_named ? "\"#{size}\"" : size
249
+ "Array, length: #{size}, type: #{base_type}"
250
+ when :var_array
251
+ is_named, size = type.array_size
252
+ size = is_named ? "\"#{size}\"" : (size || MAX_INT)
253
+ "VariableArray, max_length: #{size}, type: #{base_type}"
254
+ else
255
+ raise "Unknown sub_type: #{type.sub_type}"
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,578 @@
1
+ module Xdrgen
2
+ module Generators
3
+
4
+ class Go < Xdrgen::Generators::Base
5
+
6
+ def generate
7
+ @already_rendered = []
8
+ path = "#{@namespace}_generated.go"
9
+ out = @output.open(path)
10
+
11
+ render_top_matter out
12
+ render_definitions(out, @top)
13
+ render_bottom_matter out
14
+ end
15
+
16
+ private
17
+
18
+ def render_typedef(out, typedef)
19
+ out.puts "type #{name typedef} #{reference typedef.declaration.type}"
20
+
21
+
22
+ # write sizing restrictions
23
+ case typedef.declaration
24
+ when Xdrgen::AST::Declarations::String
25
+ render_maxsize_method out, typedef, typedef.declaration.resolved_size
26
+ when Xdrgen::AST::Declarations::Opaque
27
+ render_maxsize_method out, typedef, typedef.declaration.resolved_size
28
+ when Xdrgen::AST::Declarations::Array
29
+ unless typedef.declaration.fixed?
30
+ render_maxsize_method out, typedef, typedef.declaration.resolved_size
31
+ end
32
+ end
33
+
34
+ return unless typedef.sub_type == :simple
35
+
36
+ resolved = typedef.resolved_type
37
+
38
+ case resolved
39
+ when AST::Definitions::Enum
40
+ render_enum_typedef out, typedef, resolved
41
+ when AST::Definitions::Union
42
+ render_union_typedef out, typedef, resolved
43
+ end
44
+
45
+ out.break
46
+ end
47
+
48
+ def render_maxsize_method(out, typedef, size)
49
+ return if size.blank?
50
+
51
+ out.puts <<-EOS.strip_heredoc
52
+ // XDRMaxSize implements the Sized interface for #{name typedef}
53
+ func (e #{name typedef}) XDRMaxSize() int {
54
+ return #{size}
55
+ }
56
+ EOS
57
+ end
58
+
59
+ def render_enum_typedef(out, typedef, enum)
60
+ out.puts <<-EOS.strip_heredoc
61
+ // ValidEnum validates a proposed value for this enum. Implements
62
+ // the Enum interface for #{name typedef}
63
+ func (e #{name typedef}) ValidEnum(v int32) bool {
64
+ return #{name enum}(e).ValidEnum(v)
65
+ }
66
+ EOS
67
+
68
+ out.puts <<-EOS.strip_heredoc
69
+ // String returns the name of `e`
70
+ func (e #{name typedef}) String() string {
71
+ return #{name enum}(e).String()
72
+ }
73
+ EOS
74
+
75
+ out.break
76
+ end
77
+
78
+ def render_union_typedef(out, typedef, union)
79
+ out.puts <<-EOS.strip_heredoc
80
+ // SwitchFieldName returns the field name in which this union's
81
+ // discriminant is stored
82
+ func (u #{name typedef}) SwitchFieldName() string {
83
+ return #{name union}(u).SwitchFieldName()
84
+ }
85
+ EOS
86
+
87
+ out.break
88
+
89
+ out.puts <<-EOS.strip_heredoc
90
+ // ArmForSwitch returns which field name should be used for storing
91
+ // the value for an instance of #{name union}
92
+ func (u #{name typedef}) ArmForSwitch(sw int32) (string, bool) {
93
+ return #{name union}(u).ArmForSwitch(sw)
94
+ }
95
+ EOS
96
+
97
+ out.break
98
+
99
+ constructor_name = "New#{name typedef}"
100
+ discriminant_arg = private_name union.discriminant
101
+ discriminant_type = reference union.discriminant.type
102
+
103
+ out.puts <<-EOS.strip_heredoc
104
+ // #{constructor_name} creates a new #{name typedef}.
105
+ func #{constructor_name}(#{discriminant_arg} #{discriminant_type}, value interface{}) (result #{reference typedef}, err error) {
106
+ u, err := New#{name union}(#{discriminant_arg}, value)
107
+ result = #{name typedef}(u)
108
+ return
109
+ }
110
+ EOS
111
+
112
+ out.break
113
+
114
+ # Add accessors for of form val, ok := union.GetArmName()
115
+ # and val := union.MustArmName()
116
+ union.arms.each do |arm|
117
+ next if arm.void?
118
+ out.puts <<-EOS.strip_heredoc
119
+ // Must#{name arm} retrieves the #{name arm} value from the union,
120
+ // panicing if the value is not set.
121
+ func (u #{name typedef}) Must#{name arm}() #{reference arm.type} {
122
+ return #{name union}(u).Must#{name arm}()
123
+ }
124
+
125
+ // Get#{name arm} retrieves the #{name arm} value from the union,
126
+ // returning ok if the union's switch indicated the value is valid.
127
+ func (u #{name typedef}) Get#{name arm}() (result #{reference arm.type}, ok bool) {
128
+ return #{name union}(u).Get#{name arm}()
129
+ }
130
+ EOS
131
+ end
132
+
133
+ end
134
+
135
+ def render_const(out, const)
136
+ out.puts "const #{name const} = #{const.value}"
137
+ out.break
138
+ end
139
+
140
+ def render_definitions(out, node)
141
+ node.definitions.each{|n| render_definition out, n }
142
+ node.namespaces.each{|n| render_definitions out, n }
143
+ end
144
+
145
+ def render_nested_definitions(out, defn)
146
+ return unless defn.respond_to? :nested_definitions
147
+ defn.nested_definitions.each{|ndefn| render_definition out, ndefn}
148
+ end
149
+
150
+ def render_definition(out, defn)
151
+ if @already_rendered.include? name(defn)
152
+
153
+ unless defn.is_a?(AST::Definitions::Namespace)
154
+ $stderr.puts "warn: #{name(defn)} is defined twice. skipping"
155
+ end
156
+
157
+ return
158
+ end
159
+
160
+ render_nested_definitions(out, defn)
161
+ render_source_comment(out, defn)
162
+
163
+ @already_rendered << name(defn)
164
+
165
+ case defn
166
+ when AST::Definitions::Struct ;
167
+ render_struct out, defn
168
+ render_binary_interface out, name(defn)
169
+ when AST::Definitions::Enum ;
170
+ render_enum out, defn
171
+ render_binary_interface out, name(defn)
172
+ when AST::Definitions::Union ;
173
+ render_union out, defn
174
+ render_binary_interface out, name(defn)
175
+ when AST::Definitions::Typedef ;
176
+ render_typedef out, defn
177
+ render_binary_interface out, name(defn)
178
+ when AST::Definitions::Const ;
179
+ render_const out, defn
180
+ end
181
+ end
182
+
183
+ def render_source_comment(out, defn)
184
+ return if defn.is_a?(AST::Definitions::Namespace)
185
+
186
+ out.puts <<-EOS.strip_heredoc
187
+ // #{name defn} is an XDR #{defn.class.name.demodulize} defines as:
188
+ //
189
+ EOS
190
+
191
+ out.puts "// " + defn.text_value.split("\n").join("\n// ")
192
+
193
+ out.puts <<-EOS.strip_heredoc
194
+ //
195
+ EOS
196
+ end
197
+
198
+ def render_struct(out, struct)
199
+ out.puts "type #{name struct} struct {"
200
+ out.indent do
201
+
202
+ struct.members.each do |m|
203
+ out.puts "#{name m} #{reference(m.declaration.type)} #{field_tag struct, m}"
204
+ end
205
+
206
+ end
207
+ out.puts "}"
208
+ out.break
209
+ end
210
+
211
+ def render_enum(out, enum)
212
+ # render the "enum"
213
+ out.puts "type #{name enum} int32"
214
+ out.puts "const ("
215
+ out.indent do
216
+ first_member = enum.members.first
217
+ out.puts "#{name enum}#{name first_member} #{name enum} = #{first_member.value}"
218
+
219
+ rest_members = enum.members.drop(1)
220
+ rest_members.each do |m|
221
+ out.puts "#{name enum}#{name m} #{name enum} = #{m.value}"
222
+ end
223
+ end
224
+ out.puts ")"
225
+
226
+ # render the map used by xdr to decide valid values
227
+ out.puts "var #{private_name enum}Map = map[int32]string{"
228
+ out.indent do
229
+
230
+ enum.members.each do |m|
231
+ out.puts "#{m.value}: \"#{name enum}#{name m}\","
232
+ end
233
+
234
+ end
235
+ out.puts "}"
236
+
237
+ out.break
238
+
239
+ out.puts <<-EOS.strip_heredoc
240
+ // ValidEnum validates a proposed value for this enum. Implements
241
+ // the Enum interface for #{name enum}
242
+ func (e #{name enum}) ValidEnum(v int32) bool {
243
+ _, ok := #{private_name enum}Map[v]
244
+ return ok
245
+ }
246
+ EOS
247
+
248
+ out.puts <<-EOS.strip_heredoc
249
+ // String returns the name of `e`
250
+ func (e #{name enum}) String() string {
251
+ name, _ := #{private_name enum}Map[int32(e)]
252
+ return name
253
+ }
254
+ EOS
255
+
256
+ out.break
257
+ end
258
+
259
+ def render_union(out, union)
260
+
261
+ out.puts "type #{name union} struct{"
262
+ out.indent do
263
+ out.puts "#{name union.discriminant} #{reference union.discriminant.type}"
264
+
265
+ union.arms.each do |arm|
266
+ next if arm.void?
267
+ out.puts "#{name arm} *#{reference arm.type} #{field_tag union, arm}"
268
+ end
269
+ end
270
+ out.puts "}"
271
+ out.break
272
+
273
+ out.puts <<-EOS.strip_heredoc
274
+ // SwitchFieldName returns the field name in which this union's
275
+ // discriminant is stored
276
+ func (u #{name union}) SwitchFieldName() string {
277
+ return "#{name union.discriminant}"
278
+ }
279
+ EOS
280
+
281
+ out.break
282
+
283
+ out.puts <<-EOS.strip_heredoc
284
+ // ArmForSwitch returns which field name should be used for storing
285
+ // the value for an instance of #{name union}
286
+ func (u #{name union}) ArmForSwitch(sw int32) (string, bool) {
287
+ EOS
288
+
289
+ switch_for(out, union, "sw") do |arm, kase|
290
+ "return \"#{name arm unless arm.void?}\", true"
291
+ end
292
+
293
+ # when the default arm is not present, we must render the failure case
294
+ unless union.default_arm.present?
295
+ out.puts 'return "-", false'
296
+ end
297
+
298
+ out.puts "}"
299
+ out.break
300
+
301
+ # Add constructor of the form u := NewUnion(switch,val)
302
+ render_union_constructor(out, union)
303
+
304
+ # Add accessors for of form val, ok := union.GetArmName()
305
+ # and val := union.MustArmName()
306
+ union.arms.each do |arm|
307
+ next if arm.void?
308
+ out.puts access_arm(arm)
309
+ end
310
+
311
+ out.break
312
+ end
313
+
314
+ def render_binary_interface(out, name)
315
+ out.puts "// MarshalBinary implements encoding.BinaryMarshaler."
316
+ out.puts "func (s #{name}) MarshalBinary() ([]byte, error) {"
317
+ out.puts " b := new(bytes.Buffer)"
318
+ out.puts " _, err := Marshal(b, s)"
319
+ out.puts " return b.Bytes(), err"
320
+ out.puts "}"
321
+ out.break
322
+ out.puts "// UnmarshalBinary implements encoding.BinaryUnmarshaler."
323
+ out.puts "func (s *#{name}) UnmarshalBinary(inp []byte) error {"
324
+ out.puts " _, err := Unmarshal(bytes.NewReader(inp), s)"
325
+ out.puts " return err"
326
+ out.puts "}"
327
+ out.break
328
+ out.puts "var ("
329
+ out.puts " _ encoding.BinaryMarshaler = (*#{name})(nil)"
330
+ out.puts " _ encoding.BinaryUnmarshaler = (*#{name})(nil)"
331
+ out.puts ")"
332
+ out.break
333
+ end
334
+
335
+ def render_top_matter(out)
336
+ out.puts <<-EOS.strip_heredoc
337
+ // Package #{@namespace || "main"} is generated from:
338
+ //
339
+ // #{@output.source_paths.join("\n// ")}
340
+ //
341
+ // DO NOT EDIT or your changes may be overwritten
342
+ package #{@namespace || "main"}
343
+
344
+ import (
345
+ "bytes"
346
+ "encoding"
347
+ "io"
348
+ "fmt"
349
+
350
+ "github.com/stellar/go-xdr/xdr3"
351
+ )
352
+
353
+ // Unmarshal reads an xdr element from `r` into `v`.
354
+ func Unmarshal(r io.Reader, v interface{}) (int, error) {
355
+ // delegate to xdr package's Unmarshal
356
+ return xdr.Unmarshal(r, v)
357
+ }
358
+
359
+ // Marshal writes an xdr element `v` into `w`.
360
+ func Marshal(w io.Writer, v interface{}) (int, error) {
361
+ // delegate to xdr package's Marshal
362
+ return xdr.Marshal(w, v)
363
+ }
364
+ EOS
365
+ out.break
366
+ end
367
+
368
+ def render_bottom_matter(out)
369
+ out.puts <<-EOS
370
+ var fmtTest = fmt.Sprint("this is a dummy usage of fmt")
371
+
372
+ EOS
373
+ end
374
+
375
+ private
376
+
377
+ def reference(type)
378
+ baseReference = case type
379
+ when AST::Typespecs::Bool
380
+ "bool"
381
+ when AST::Typespecs::Double
382
+ "float64"
383
+ when AST::Typespecs::Float
384
+ "float32"
385
+ when AST::Typespecs::Hyper
386
+ "int64"
387
+ when AST::Typespecs::Int
388
+ "int32"
389
+ when AST::Typespecs::Opaque
390
+ if type.fixed?
391
+ "[#{type.size}]byte"
392
+ else
393
+ "[]byte"
394
+ end
395
+ when AST::Typespecs::Quadruple
396
+ raise "no quadruple support for go"
397
+ when AST::Typespecs::String
398
+ "string"
399
+ when AST::Typespecs::UnsignedHyper
400
+ "uint64"
401
+ when AST::Typespecs::UnsignedInt
402
+ "uint32"
403
+ when AST::Typespecs::Simple
404
+ name type
405
+ when AST::Definitions::Base
406
+ name type
407
+ when AST::Concerns::NestedDefinition
408
+ name type
409
+ else
410
+ raise "Unknown reference type: #{type.class.name}, #{type.class.ancestors}"
411
+ end
412
+
413
+ case type.sub_type
414
+ when :simple
415
+ baseReference
416
+ when :optional
417
+ "*#{baseReference}"
418
+ when :array
419
+ is_named, size = type.array_size
420
+
421
+ # if named, lookup the const definition
422
+ if is_named
423
+ size = name @top.find_definition(size)
424
+ end
425
+
426
+ "[#{size}]#{baseReference}"
427
+ when :var_array
428
+ "[#{size}]#{baseReference}"
429
+ else
430
+ raise "Unknown sub_type: #{type.sub_type}"
431
+ end
432
+
433
+ end
434
+
435
+ def name(named)
436
+ parent = name named.parent_defn if named.is_a?(AST::Concerns::NestedDefinition)
437
+
438
+ base = if named.respond_to?(:name)
439
+ named.name
440
+ else
441
+ named.text_value
442
+ end
443
+
444
+ "#{parent}#{base.underscore.camelize}"
445
+ end
446
+
447
+ def private_name(named)
448
+ escape_name named.name.underscore.camelize(:lower)
449
+ end
450
+
451
+ def escape_name(name)
452
+ case name
453
+ when "type" ; "aType"
454
+ when "func" ; "aFunc"
455
+ else ; name
456
+ end
457
+ end
458
+
459
+ def render_union_constructor(out, union)
460
+ constructor_name = "New#{name union}"
461
+
462
+
463
+ discriminant_arg = private_name union.discriminant
464
+ discriminant_type = reference union.discriminant.type
465
+
466
+ out.puts <<-EOS.strip_heredoc
467
+ // #{constructor_name} creates a new #{name union}.
468
+ func #{constructor_name}(#{discriminant_arg} #{discriminant_type}, value interface{}) (result #{reference union}, err error) {
469
+ result.#{name union.discriminant} = #{discriminant_arg}
470
+ EOS
471
+
472
+ switch_for(out, union, discriminant_arg) do |arm, kase|
473
+ if arm.void?
474
+ "// void"
475
+ else
476
+ <<-EOS
477
+ tv, ok := value.(#{reference arm.type})
478
+ if !ok {
479
+ err = fmt.Errorf("invalid value, must be #{reference arm.type}")
480
+ return
481
+ }
482
+ result.#{name arm} = &tv
483
+ EOS
484
+ end
485
+ end
486
+
487
+ out.puts <<-EOS.strip_heredoc
488
+ return
489
+ }
490
+ EOS
491
+ end
492
+
493
+ def access_arm(arm)
494
+
495
+ <<-EOS.strip_heredoc
496
+ // Must#{name arm} retrieves the #{name arm} value from the union,
497
+ // panicing if the value is not set.
498
+ func (u #{name arm.union}) Must#{name arm}() #{reference arm.type} {
499
+ val, ok := u.Get#{name arm}()
500
+
501
+ if !ok {
502
+ panic("arm #{name arm} is not set")
503
+ }
504
+
505
+ return val
506
+ }
507
+
508
+ // Get#{name arm} retrieves the #{name arm} value from the union,
509
+ // returning ok if the union's switch indicated the value is valid.
510
+ func (u #{name arm.union}) Get#{name arm}() (result #{reference arm.type}, ok bool) {
511
+ armName, _ := u.ArmForSwitch(int32(u.#{name arm.union.discriminant}))
512
+
513
+ if armName == "#{name arm}" {
514
+ result = *u.#{name arm}
515
+ ok = true
516
+ }
517
+
518
+ return
519
+ }
520
+ EOS
521
+ end
522
+
523
+ def size(size_s)
524
+ result = size_s
525
+ result = "MaxXdrElements" if result.blank?
526
+ result
527
+ end
528
+
529
+ def switch_for(out, union, ident)
530
+ out.puts "switch #{reference union.discriminant.type}(#{ident}) {"
531
+
532
+ union.normal_arms.each do |arm|
533
+ arm.cases.each do |c|
534
+
535
+ value = if c.value.is_a?(AST::Identifier)
536
+ member = union.resolved_case(c)
537
+ if union.discriminant_type.nil? then
538
+ "int32(#{name member.enum}#{name member})"
539
+ else
540
+ "#{name union.discriminant_type}#{name member}"
541
+ end
542
+ else
543
+ c.value.text_value
544
+ end
545
+
546
+ out.puts " case #{value}:"
547
+ out.puts " #{yield arm, c}"
548
+ end
549
+ end
550
+
551
+ if union.default_arm.present?
552
+ arm = union.default_arm
553
+ out.puts " default:"
554
+ out.puts " #{yield arm, :default}"
555
+ end
556
+
557
+ out.puts "}"
558
+ end
559
+
560
+ def field_tag(struct, field)
561
+ size = nil
562
+
563
+ case field.declaration
564
+ when Xdrgen::AST::Declarations::Opaque
565
+ size = field.declaration.resolved_size
566
+ when Xdrgen::AST::Declarations::String
567
+ size = field.declaration.resolved_size
568
+ when Xdrgen::AST::Declarations::Array
569
+ size = field.declaration.resolved_size unless field.declaration.fixed?
570
+ end
571
+
572
+ return "`xdrmaxsize:\"#{size}\"`" if size.present?
573
+ end
574
+
575
+ end
576
+
577
+ end
578
+ end