xdrgen 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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