From 2225ac6819db1f49480ab4cb340d255e606d8a14 Mon Sep 17 00:00:00 2001 From: Nathaniel Bauernfeind Date: Wed, 23 May 2018 23:28:27 -0400 Subject: [PATCH 01/20] Add Scala generation support. --- README.md | 3 +- lib/xdrgen/generators.rb | 3 +- lib/xdrgen/generators/scala.rb | 475 ++++++++++++++++++ .../generators/scala/XdrDataInputStream.erb | 59 +++ .../generators/scala/XdrDataOutputStream.erb | 53 ++ spec/lib/xdrgen/generator_spec.rb | 2 +- 6 files changed, 592 insertions(+), 3 deletions(-) create mode 100644 lib/xdrgen/generators/scala.rb create mode 100644 lib/xdrgen/generators/scala/XdrDataInputStream.erb create mode 100644 lib/xdrgen/generators/scala/XdrDataOutputStream.erb diff --git a/README.md b/README.md index 47902910c..c7bf799ac 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,13 @@ Xdrgen is a very early project. Aside from the test fixtures in are the .x files used for the [stellar-core project](https://github.com/stellar/stellar-core). -Xdrgen presently supports three output languages: ruby, javacript, java, and golang: +Xdrgen presently supports the following output languages: - ruby: complete support - javascript: complete support - java: complete support - golang: currently using a fork of go-xdr, but has complete support +- scala: complete support Testing is _very_ sparse, but will improve over time. diff --git a/lib/xdrgen/generators.rb b/lib/xdrgen/generators.rb index 9bd8719fd..0420000f9 100644 --- a/lib/xdrgen/generators.rb +++ b/lib/xdrgen/generators.rb @@ -6,10 +6,11 @@ module Xdrgen::Generators autoload :Go autoload :Javascript autoload :Java + autoload :Scala def self.for_language(language) const_get language.to_s.classify rescue NameError raise ArgumentError, "Unsupported language: #{language}" end -end \ No newline at end of file +end diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb new file mode 100644 index 000000000..6467cced7 --- /dev/null +++ b/lib/xdrgen/generators/scala.rb @@ -0,0 +1,475 @@ +module Xdrgen + module Generators + + class Scala < Xdrgen::Generators::Base + + def generate + render_lib + render_definitions(@top) + end + + def render_lib + template = IO.read(__dir__ + "/scala/XdrDataInputStream.erb") + result = ERB.new(template).result binding + @output.write "XdrDataInputStream.scala", result + + template = IO.read(__dir__ + "/scala/XdrDataOutputStream.erb") + result = ERB.new(template).result binding + @output.write "XdrDataOutputStream.scala", result + end + + def render_definitions(node) + node.namespaces.each{|n| render_definitions n } + node.definitions.each(&method(:render_definition)) + end + + def render_definition(defn) + case defn + when AST::Definitions::Struct ; + render_file defn do |out| + render_struct defn, out + end + when AST::Definitions::Enum ; + render_file defn do |out| + render_enum defn, out + end + when AST::Definitions::Union ; + render_file defn do |out| + render_union defn, out + end + when AST::Definitions::Typedef ; + render_file defn do |out| + render_typedef defn, out + end + end + end + + def render_file(element) + path = element.name.camelize + ".scala" + out = @output.open(path) + render_top_matter out + render_source_comment out, element + + yield out + end + + def render_nested_definitions(defn, out) + return unless defn.respond_to? :nested_definitions + unless defn.nested_definitions.empty? + out.puts "" + end + defn.nested_definitions.each{|ndefn| + case ndefn + when AST::Definitions::Struct ; + name = name ndefn + render_struct ndefn, out + when AST::Definitions::Enum ; + name = name ndefn + render_enum ndefn, out + when AST::Definitions::Union ; + name = name ndefn + render_union ndefn, out + when AST::Definitions::Typedef ; + name = name ndefn + render_typedef ndefn, out + end + } + end + + def render_enum(enum, out) + name = name_string enum.name + out.puts <<-EOS.strip_heredoc + sealed class #{name} (val i: Int) { + def encode(stream: XdrDataOutputStream) = stream.writeInt(i) + } + + object #{name} { + def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { + EOS + out.indent do + out.indent do + enum.members.each do |em| + out.puts("case #{em.value} => #{name_string em.name}") + end + out.puts("case i => throw new IllegalArgumentException(s\"#{name} value $i is invalid\")") + end + out.puts "}" + out.puts "" + enum.members.each do |em| + out.puts <<-EOS.strip_heredoc + case object #{name_string em.name} extends #{name}(#{em.value}) + EOS + end + + render_nested_definitions enum, out + end + out.puts "}" + end + + def render_struct(element, out) + name = name_string element.name + out.puts "case class #{name} (" + out.indent do + len = element.members.length + element.members.each do |m| + out.puts "#{sanitize m.name}: " + (is_nested?(m.type) ? "#{name}." : "") + "#{decl_string m.declaration}" + (len > 1 ? ", " : "") + len -= 1 + end + end + out.puts ") {" + out.indent do + out.puts "def encode(stream: XdrDataOutputStream): Unit = {" + out.indent do + element.members.each do |m| + encode_member m, out + end + end + end + out.puts " }" + out.puts "}" + out.puts "" + out.puts "object #{name} {" + out.puts " def decode(stream: XdrDataInputStream): #{name} = #{name element}(" + out.indent do + out.indent do + len = element.members.length + element.members.each do |m| + decode_member m, out, len == 1 + len -= 1 + end + end + out.puts ")" + render_nested_definitions element, out + end + out.puts "}" + end + + def render_typedef(typedef, out) + name = name_string typedef.name + out.puts <<-EOS.strip_heredoc + case class #{name} (self: #{decl_string typedef.declaration}) { + def encode(stream: XdrDataOutputStream): Unit = { + EOS + out.indent(2) do + encode_decl typedef.declaration, "self", false, out + end + out.puts <<-EOS.strip_heredoc + } + } + object #{name} { + def decode(stream: XdrDataInputStream): #{name} = #{name}(#{decode_decl typedef.declaration}) + } + EOS + end + + def render_union(union, out) + name = name_string union.name + def render_arms(union) + union.arms.each do |arm| + if arm.is_a? AST::Definitions::UnionDefaultArm + yield arm.declaration, "d", "Default", true + else + arm.cases.each do |kase| + dtype = type_string union.discriminant.type + ktype = + if kase.value.is_a?(AST::Identifier) + "#{dtype}.#{name_string kase.value.name}" + elsif union.discriminant.type.is_a?(AST::Typespecs::Simple) + "#{dtype}(#{kase.value.value})" + else + "#{kase.value.value}" + end + rtype = kase.value.is_a?(AST::Identifier) ? "#{name_string kase.value.name}" : "R_#{kase.value.value}" + yield arm.declaration, ktype, rtype, false + end + end + end + end + + out.puts <<-EOS.strip_heredoc + sealed trait #{name} { + def encode(stream: XdrDataOutputStream): Unit + } + + object #{name} { + def decode(stream: XdrDataInputStream): #{name} = #{decode_type union.discriminant.type} match { + EOS + out.indent do + out.indent do + the_default = "case d => throw new IllegalArgumentException(s\"#{type_string union.discriminant.type} value $d is invalid\")" + render_arms(union) do |decl, ktype, rtype, is_default| + decode = "#{decode_decl decl}" + if is_default + the_default = "case #{ktype} => #{rtype}(d" + (is_void?(decl) ? "" : ", #{decode}") + ")" + else + out.puts "case #{ktype} => #{rtype}#{is_void?(decl) ? "" : "(" + decode + ")" }" + end + end + out.puts the_default + end + out.puts "}" + out.puts "" + render_arms(union) do |decl, ktype, rtype, is_default| + type = "class" + args = is_default ? "(d: #{type_string union.discriminant.type}" : "" + unless is_void? decl + args += (args.empty? ? "(" : ", ") + "x: #{decl_string decl})" + else + args += args.empty? ? "" : ")" + end + if !is_default && is_void?(decl) + type = "object" + args = "" + end + out.puts "case #{type} #{rtype}#{args} extends #{name} {" + out.indent do + out.puts "def encode(stream: XdrDataOutputStream): Unit = {" + out.indent do + encode_decl union.discriminant, ktype, false, out + unless is_void? decl + encode_decl decl, "x", false, out + end + end + out.puts "}" + end + out.puts "}" + end + + render_nested_definitions union, out + end + out.puts "}" + end + + def render_top_matter(out) + out.puts <<-EOS.strip_heredoc + // Automatically generated by xdrgen + // DO NOT EDIT or your changes may be overwritten + + package #{@namespace || "main"} + + import stellar.sdk._ + + EOS + end + + def render_source_comment(out, defn) + return if defn.is_a?(AST::Definitions::Namespace) + + out.puts <<-EOS.strip_heredoc + // === xdr source ============================================================ + + EOS + + out.puts "// " + defn.text_value.split("\n").join("\n// ") + + out.puts <<-EOS.strip_heredoc + + // =========================================================================== + + EOS + end + + def encode_member(member, out) + encode_decl member.declaration, member.name, member.type.sub_type == :optional, out + end + + def encode_decl(decl, name, isOption, out) + case decl + when AST::Declarations::Void + return + end + + if isOption + out.puts "#{sanitize name} match {" + out.indent do + out.puts "case Some(x) => " + out.indent do + out.puts "stream.writeInt(1)" + encode_decl_inner(decl, "x", out) + end + out.puts "case None => stream.writeInt(0)" + end + out.puts "}" + else + encode_decl_inner(decl, sanitize(name), out) + end + end + + def encode_decl_inner(decl, name, out) + case decl + when AST::Declarations::Opaque ; + unless decl.fixed? + out.puts "stream.writeInt(#{name}.length)" + end + out.puts "stream.write(#{name}, 0, #{name}.length)" + when AST::Declarations::Array ; + unless decl.fixed? + out.puts "stream.writeInt(#{name}.length)" + end + out.puts "#{name}.foreach { #{encode_type decl.type, "_"} }" + else + out.puts "#{encode_type decl.type, "#{name}"}" + end + end + + def encode_type(type, value) + case type + when AST::Typespecs::Int ; + "stream.writeInt(#{value})" + when AST::Typespecs::UnsignedInt ; + "stream.writeInt(#{value})" + when AST::Typespecs::Hyper ; + "stream.writeLong(#{value})" + when AST::Typespecs::UnsignedHyper ; + "stream.writeLong(#{value})" + when AST::Typespecs::Float ; + "stream.writeFloat(#{value})" + when AST::Typespecs::Double ; + "stream.writeDouble(#{value})" + when AST::Typespecs::Quadruple ; + raise "cannot render quadruple in scala" + when AST::Typespecs::Bool ; + "stream.writeInt(if (#{value}) 1 else 0)" + when AST::Typespecs::String ; + "stream.writeString(#{value})" + when AST::Typespecs::Simple ; + "#{value}.encode(stream)" + when AST::Concerns::NestedDefinition ; + "#{value}.encode(stream)" + else + raise "Unknown typespec: #{type.class.name}" + end + end + + def decode_member(member, out, final) + case member.declaration + when AST::Declarations::Void ; + return + end + str = decode_decl (member.declaration) + if member.type.sub_type == :optional + out.puts "if (stream.readInt == 0) None else Some(" + str + ")" + (final ? "" : ",") + else + out.puts str + (final ? "" : ",") + end + end + + def decode_decl(decl) + case decl + when AST::Declarations::Void ; + return + when AST::Declarations::Opaque ; + size = decl.fixed? ? decl.size : "stream.readInt" + return "stream.readBytes(#{size})" + when AST::Declarations::Array ; + size = decl.fixed? ? decl.size : "stream.readInt" + return "(0 until #{size}).map(_ => #{decode_type decl.type}).toArray" + else + return decode_type decl.type + end + end + + def decode_type(type) + case type + when AST::Typespecs::Int ; + "stream.readInt" + when AST::Typespecs::UnsignedInt ; + "stream.readInt" + when AST::Typespecs::Hyper ; + "stream.readLong" + when AST::Typespecs::UnsignedHyper ; + "stream.readLong" + when AST::Typespecs::Float ; + "stream.readFloat" + when AST::Typespecs::Double ; + "stream.readDouble" + when AST::Typespecs::Quadruple ; + raise "cannot render quadruple in scala" + when AST::Typespecs::Bool ; + "stream.readInt == 1" + when AST::Typespecs::String ; + "stream.readString" + when AST::Typespecs::Simple ; + "#{name type.resolved_type}.decode(stream)" + when AST::Concerns::NestedDefinition ; + "#{name type}.decode(stream)" + else + raise "Unknown typespec: #{type.class.name}" + end + end + + def decl_string(decl) + case decl + when AST::Declarations::Opaque ; + "Array[Byte]" + when AST::Declarations::String ; + "String" + when AST::Declarations::Array ; + "Array[#{type_string decl.type}]" + when AST::Declarations::Optional ; + "Option[#{type_string(decl.type)}]" + when AST::Declarations::Simple ; + type_string(decl.type) + when AST::Declarations::Void ; + "Unit" + else + raise "Unknown declaration type: #{decl.class.name}" + end + end + + def type_string(type) + case type + when AST::Typespecs::Int ; + "Int" + when AST::Typespecs::UnsignedInt ; + "Int" + when AST::Typespecs::Hyper ; + "Long" + when AST::Typespecs::UnsignedHyper ; + "Long" + when AST::Typespecs::Float ; + "Float" + when AST::Typespecs::Double ; + "Double" + when AST::Typespecs::Quadruple ; + raise "cannot render quadruple in scala" + when AST::Typespecs::Bool ; + "Boolean" + when AST::Typespecs::Opaque ; + "Array.ofDim[Byte](#{type.size})" + when AST::Typespecs::Simple ; + name type.resolved_type + when AST::Concerns::NestedDefinition ; + name type + else + raise "Unknown typespec: #{type.class.name}" + end + end + + def is_nested?(defn) + defn.is_a?(AST::Concerns::NestedDefinition) + end + + def is_void?(decl) + decl.is_a?(AST::Declarations::Void) + end + + def name(named) + name_string named.name + end + + def name_string(name) + # ensure that first letters are capitalized, but leave ENUM_LIKE_THINGS alone + name["_"] || !name[/[^A-Z]/] ? name : name.camelize + end + + def sanitize(name) + if name == "type" + "`type`" + else + name + end + end + end + end +end diff --git a/lib/xdrgen/generators/scala/XdrDataInputStream.erb b/lib/xdrgen/generators/scala/XdrDataInputStream.erb new file mode 100644 index 000000000..e41b3f58a --- /dev/null +++ b/lib/xdrgen/generators/scala/XdrDataInputStream.erb @@ -0,0 +1,59 @@ +package stellar.sdk + +import java.io.{DataInputStream, IOException, InputStream} +import java.nio.charset.Charset + +class XdrDataInputStream(in: InputStream) extends DataInputStream(new XdrInputStream(in)) { + + def readString: String = { + val bytes = Array.ofDim[Byte](readInt) + read(bytes) + new String(bytes, Charset.forName("UTF-8")) + } + + def readIntArray: Array[Int] = readIntArray(readInt) + + def readIntArray(size: Int): Array[Int] = (0 until size).map(_ => readInt).toArray + + def readFloatArray: Array[Float] = readFloatArray(readInt) + + def readFloatArray(size: Int): Array[Float] = (0 until size).map(_ => readFloat).toArray + + def readDoubleArray: Array[Double] = readDoubleArray(readInt) + + def readDoubleArray(size: Int): Array[Double] = (0 until size).map(_ => readDouble).toArray + + def readBytes(size: Int): Array[Byte] = { + val bytes = Array.ofDim[Byte](size) + read(bytes) + return bytes + } +} + +class XdrInputStream(in: InputStream) extends InputStream { + + private var count = 0 + + override def read: Int = { + val read = in.read + if (read >= 0) count += 1 + read + } + + override def read(bs: Array[Byte]): Int = read(bs, off = 0, len = bs.length) + + override def read(bs: Array[Byte], off: Int, len: Int): Int = { + val read = in.read(bs, off, len) + count += read + pad() + read + } + + private def pad(): Unit = { + val mod = count % 4 + val pad = if (mod > 0) 4 - mod else 0 + (0 until pad).foreach { _ => + if (read != 0) throw new IOException("non-zero padding") + } + } +} diff --git a/lib/xdrgen/generators/scala/XdrDataOutputStream.erb b/lib/xdrgen/generators/scala/XdrDataOutputStream.erb new file mode 100644 index 000000000..50860dd4f --- /dev/null +++ b/lib/xdrgen/generators/scala/XdrDataOutputStream.erb @@ -0,0 +1,53 @@ +package stellar.sdk + +import java.io.{DataOutputStream, OutputStream} +import java.nio.charset.Charset + +class XdrDataOutputStream(out: XdrOutputStream) extends DataOutputStream(new XdrOutputStream(out)) { + + def writeString(s: String): Unit = { + val chars = s.getBytes(Charset.forName("UTF-8")) + writeInt(chars.length) + write(chars) + } + + def writeIntArray(a: Array[Int]): Unit = { + writeInt(a.length) + a.foreach(writeInt) + } + + def writeFloatArray(a: Array[Float]): Unit = { + writeInt(a.length) + a.foreach(writeFloat) + } + + def writeDoubleArray(a: Array[Double]): Unit = { + writeInt(a.length) + a.foreach(writeDouble) + } +} + +class XdrOutputStream(out: OutputStream) extends OutputStream { + + private var count = 0 + + override def write(b: Int): Unit = { + out.write(b) + count += 1 + } + + override def write(bs: Array[Byte]): Unit = write(bs, 0, bs.length) + + override def write(bs: Array[Byte], offset: Int, len: Int): Unit = { + out.write(bs, offset, len) + count += len + pad() + } + + private def pad(): Unit = { + val mod = count % 4 + val pad = if (mod > 0) 4 - mod else 0 + (0 until pad).foreach(_ => write(0)) + } + +} diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index 2d0c3d2aa..0dc8969e1 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Xdrgen::Generators do - languages = %w(ruby javascript go java) + languages = %w(ruby javascript go java scala) focus_language = "" #"go" focus_basename = "" #"optional.x" From 086ebef146f1c317eb05d08b1119ff7df8a3dc98 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Fri, 7 Dec 2018 17:22:36 +1000 Subject: [PATCH 02/20] Fix compilation errors against test fixtures --- lib/xdrgen/generators/scala.rb | 50 +++++++++++++++---- .../generators/scala/XdrDataInputStream.erb | 2 +- .../generators/scala/XdrDataOutputStream.erb | 2 +- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 6467cced7..0947e6690 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -5,17 +5,32 @@ class Scala < Xdrgen::Generators::Base def generate render_lib + render_package_file do |out| + render_constants(@top, out) + end render_definitions(@top) end + private def render_lib template = IO.read(__dir__ + "/scala/XdrDataInputStream.erb") result = ERB.new(template).result binding - @output.write "XdrDataInputStream.scala", result + @output.write "#{@namespace.downcase}/XdrDataInputStream.scala", result template = IO.read(__dir__ + "/scala/XdrDataOutputStream.erb") result = ERB.new(template).result binding - @output.write "XdrDataOutputStream.scala", result + @output.write "#{@namespace.downcase}/XdrDataOutputStream.scala", result + end + + def render_constants(node, out) + node.namespaces.each{|n| render_constants(n, out) } + node.definitions.select{|c| c.is_a? AST::Definitions::Const}.each { |c| render_const out, c } + end + + def render_const(out, const) + out.indent do + out.puts "val #{const.name.downcase.camelize} = #{const.value.downcase.camelize}" + end end def render_definitions(node) @@ -44,8 +59,23 @@ def render_definition(defn) end end + def render_package_file + path = "#{@namespace.downcase}/#{@namespace.downcase}.scala" + out = @output.open(path) + out.puts <<-EOS.strip_heredoc + // Automatically generated by xdrgen + // DO NOT EDIT or your changes may be overwritten + + package object #{@namespace.downcase} { + EOS + + yield out + + out.puts("}") + end + def render_file(element) - path = element.name.camelize + ".scala" + path = "#{@namespace.downcase}/#{element.name.camelize}.scala" out = @output.open(path) render_top_matter out render_source_comment out, element @@ -151,13 +181,17 @@ def render_typedef(typedef, out) def encode(stream: XdrDataOutputStream): Unit = { EOS out.indent(2) do - encode_decl typedef.declaration, "self", false, out + encode_decl typedef.declaration, "self", typedef.type.sub_type == :optional, out end out.puts <<-EOS.strip_heredoc } } + EOS + decode_str = decode_decl typedef.declaration + decode_statement = typedef.type.sub_type == :optional ? "Option(#{decode_str})" : decode_str + out.puts <<-EOS.strip_heredoc object #{name} { - def decode(stream: XdrDataInputStream): #{name} = #{name}(#{decode_decl typedef.declaration}) + def decode(stream: XdrDataInputStream): #{name} = #{name}(#{decode_statement}) } EOS end @@ -245,9 +279,7 @@ def render_top_matter(out) // Automatically generated by xdrgen // DO NOT EDIT or your changes may be overwritten - package #{@namespace || "main"} - - import stellar.sdk._ + package #{@namespace.downcase || "main"} EOS end @@ -362,7 +394,7 @@ def decode_decl(decl) size = decl.fixed? ? decl.size : "stream.readInt" return "stream.readBytes(#{size})" when AST::Declarations::Array ; - size = decl.fixed? ? decl.size : "stream.readInt" + size = decl.fixed? ? decl.size.downcase.camelize : "stream.readInt" return "(0 until #{size}).map(_ => #{decode_type decl.type}).toArray" else return decode_type decl.type diff --git a/lib/xdrgen/generators/scala/XdrDataInputStream.erb b/lib/xdrgen/generators/scala/XdrDataInputStream.erb index e41b3f58a..03fe710d9 100644 --- a/lib/xdrgen/generators/scala/XdrDataInputStream.erb +++ b/lib/xdrgen/generators/scala/XdrDataInputStream.erb @@ -1,4 +1,4 @@ -package stellar.sdk +package <%= @namespace.downcase %> import java.io.{DataInputStream, IOException, InputStream} import java.nio.charset.Charset diff --git a/lib/xdrgen/generators/scala/XdrDataOutputStream.erb b/lib/xdrgen/generators/scala/XdrDataOutputStream.erb index 50860dd4f..307925d61 100644 --- a/lib/xdrgen/generators/scala/XdrDataOutputStream.erb +++ b/lib/xdrgen/generators/scala/XdrDataOutputStream.erb @@ -1,4 +1,4 @@ -package stellar.sdk +package <%= @namespace.downcase %> import java.io.{DataOutputStream, OutputStream} import java.nio.charset.Charset From d3f39e29445ff4b4404551922652ab338fc9933a Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Fri, 7 Dec 2018 22:06:04 +1000 Subject: [PATCH 03/20] Flatten union structs --- lib/xdrgen/generators/scala.rb | 105 ++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 0947e6690..67509dfa6 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -136,7 +136,7 @@ def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { out.puts "}" end - def render_struct(element, out) + def render_struct(element, out, superclass=nil) name = name_string element.name out.puts "case class #{name} (" out.indent do @@ -146,10 +146,11 @@ def render_struct(element, out) len -= 1 end end - out.puts ") {" + out.puts ") #{superclass ? "extends #{superclass} " : ""}{" out.indent do out.puts "def encode(stream: XdrDataOutputStream): Unit = {" out.indent do + out.puts "#{superclass}.encode(this, stream)" if superclass element.members.each do |m| encode_member m, out end @@ -172,12 +173,13 @@ def render_struct(element, out) render_nested_definitions element, out end out.puts "}" + out.puts "" end - def render_typedef(typedef, out) + def render_typedef(typedef, out, superclass=nil) name = name_string typedef.name out.puts <<-EOS.strip_heredoc - case class #{name} (self: #{decl_string typedef.declaration}) { + case class #{name} (self: #{decl_string typedef.declaration}) #{superclass ? "extends #{superclass} " : ""} { def encode(stream: XdrDataOutputStream): Unit = { EOS out.indent(2) do @@ -198,6 +200,7 @@ def decode(stream: XdrDataInputStream): #{name} = #{name}(#{decode_statement}) def render_union(union, out) name = name_string union.name + def render_arms(union) union.arms.each do |arm| if arm.is_a? AST::Definitions::UnionDefaultArm @@ -213,7 +216,7 @@ def render_arms(union) else "#{kase.value.value}" end - rtype = kase.value.is_a?(AST::Identifier) ? "#{name_string kase.value.name}" : "R_#{kase.value.value}" + rtype = kase.value.is_a?(AST::Identifier) ? "#{kase.value.name.downcase.camelize}" : "R_#{kase.value.value}" yield arm.declaration, ktype, rtype, false end end @@ -228,6 +231,8 @@ def encode(stream: XdrDataOutputStream): Unit object #{name} { def decode(stream: XdrDataInputStream): #{name} = #{decode_type union.discriminant.type} match { EOS + + # decode out.indent do out.indent do the_default = "case d => throw new IllegalArgumentException(s\"#{type_string union.discriminant.type} value $d is invalid\")" @@ -236,40 +241,82 @@ def decode(stream: XdrDataInputStream): #{name} = #{decode_type union.discrimina if is_default the_default = "case #{ktype} => #{rtype}(d" + (is_void?(decl) ? "" : ", #{decode}") + ")" else - out.puts "case #{ktype} => #{rtype}#{is_void?(decl) ? "" : "(" + decode + ")" }" + out.puts "case #{ktype} => #{is_void?(decl) ? rtype : decode}" # #{rtype}#{is_void?(decl) ? "" : "(" + decode + ")" }" end end out.puts the_default end - out.puts "}" - out.puts "" - render_arms(union) do |decl, ktype, rtype, is_default| - type = "class" - args = is_default ? "(d: #{type_string union.discriminant.type}" : "" - unless is_void? decl - args += (args.empty? ? "(" : ", ") + "x: #{decl_string decl})" - else - args += args.empty? ? "" : ")" - end - if !is_default && is_void?(decl) - type = "object" - args = "" - end - out.puts "case #{type} #{rtype}#{args} extends #{name} {" + + out.puts <<-EOS.strip_heredoc + } + + def encode(x: #{name}, stream: XdrDataOutputStream): Unit = x match { + EOS + + #encode out.indent do - out.puts "def encode(stream: XdrDataOutputStream): Unit = {" - out.indent do - encode_decl union.discriminant, ktype, false, out - unless is_void? decl - encode_decl decl, "x", false, out + render_arms(union) do |decl, ktype, rtype, is_default| + if is_void?(decl) + out.puts "case #{rtype} => #{ktype}.encode(stream)" + else + out.puts "case _: #{rtype} => #{ktype}.encode(stream)" end end - out.puts "}" end - out.puts "}" + + out.puts "}" + out.puts "" + + + # render_arms(union) do |decl, ktype, rtype, is_default| + # type = "class" + # args = is_default ? "(d: #{type_string union.discriminant.type}" : "" + # if is_void? decl + # args += args.empty? ? "" : ")" + # else + # args += (args.empty? ? "(" : ", ") + "x: #{decl_string decl})" + # end + # if !is_default && is_void?(decl) + # type = "object" + # args = "" + # end + # + # out.puts "case #{type} #{rtype}#{args} extends #{name} {" + # out.indent do + # out.puts "def encode(stream: XdrDataOutputStream): Unit = {" + # out.indent do + # encode_decl union.discriminant, ktype, false, out + # unless is_void? decl + # encode_decl decl, "x", false, out + # end + # end + # out.puts "}" + # end + + + union.nested_definitions.each { |ndefn| + case ndefn + when AST::Definitions::Struct ; + render_struct ndefn, out, name + when AST::Definitions::Enum ; + render_enum ndefn, out + when AST::Definitions::Union ; + render_union ndefn, out + when AST::Definitions::Typedef ; + render_typedef ndefn, out, name + end + } + + render_arms(union) do |decl, ktype, rtype, is_default| + if is_void? decl + out.puts <<-EOS.strip_heredoc + object #{rtype} extends #{name} { + def encode(stream: XdrDataOutputStream): Unit = #{ktype}.encode(stream) + } + EOS + end end - render_nested_definitions union, out end out.puts "}" end From 0b295f5ae998e3037498ef15d007edf7e777da40 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sat, 8 Dec 2018 22:07:04 +1000 Subject: [PATCH 04/20] Scala generator re-write: block_comments --- lib/xdrgen/generators/scala.rb | 82 +++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 67509dfa6..9a76e0388 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -3,14 +3,63 @@ module Generators class Scala < Xdrgen::Generators::Base + class Enum + attr_accessor :name, :members, :element + + def initialize(name, members, element) + @name = name + @members = members + @element = element + end + + def to_s + "#{@name}: #{@members.inspect}" + end + end + def generate + enums = parse_enums @top render_lib - render_package_file do |out| - render_constants(@top, out) + enums.each{|e| write_enum_file e } + end + + def parse_enums(ns) + ns.definitions.select{|d| d.is_a? AST::Definitions::Enum }.map do |e| + hash = e.members.reduce({}) do |hash, member| + hash.store(member.name.downcase.camelize, member.value) + hash + end + Enum.new(name(e), hash, e) end - render_definitions(@top) end + def write_enum_file(e) + path = "#{@namespace}/#{e.name}.scala" + out = @output.open(path) + render_top_matter out + render_enum out, e + end + + def render_enum(out, e) + render_source_comment out, e.element + out.puts <<-EOS.strip_heredoc + sealed class #{e.name}(val i: Int) { + def encode(stream: XdrDataOutputStream) = stream.writeInt(i) + } + + object #{e.name} { + def decode(stream: XdrDataInputStream): #{e.name} = stream.readInt() match { + #{e.members.map{|k,v| "case #{v} => #{k}" }.join("\n ")} + case i => throw new IllegalArgumentException(s"#{e.name} value $i is invalid") + } + + #{e.members.map{|k,v| "case object #{k} extends #{e.name}(#{v})"}.join("\n ")} + } + EOS + end + + # ---- + private def render_lib template = IO.read(__dir__ + "/scala/XdrDataInputStream.erb") @@ -106,7 +155,7 @@ def render_nested_definitions(defn, out) } end - def render_enum(enum, out) + def render_enum_old(enum, out) name = name_string enum.name out.puts <<-EOS.strip_heredoc sealed class #{name} (val i: Int) { @@ -119,7 +168,7 @@ def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { out.indent do out.indent do enum.members.each do |em| - out.puts("case #{em.value} => #{name_string em.name}") + out.puts("case #{em.value} => #{em.name.downcase.camelize}") end out.puts("case i => throw new IllegalArgumentException(s\"#{name} value $i is invalid\")") end @@ -127,7 +176,7 @@ def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { out.puts "" enum.members.each do |em| out.puts <<-EOS.strip_heredoc - case object #{name_string em.name} extends #{name}(#{em.value}) + case object #{em.name.downcase.camelize} extends #{name}(#{em.value}) EOS end @@ -179,7 +228,7 @@ def render_struct(element, out, superclass=nil) def render_typedef(typedef, out, superclass=nil) name = name_string typedef.name out.puts <<-EOS.strip_heredoc - case class #{name} (self: #{decl_string typedef.declaration}) #{superclass ? "extends #{superclass} " : ""} { + case class #{name}(self: #{decl_string typedef.declaration}) #{superclass ? "extends #{superclass} " : ""} { def encode(stream: XdrDataOutputStream): Unit = { EOS out.indent(2) do @@ -188,6 +237,7 @@ def encode(stream: XdrDataOutputStream): Unit = { out.puts <<-EOS.strip_heredoc } } + EOS decode_str = decode_decl typedef.declaration decode_statement = typedef.type.sub_type == :optional ? "Option(#{decode_str})" : decode_str @@ -229,7 +279,7 @@ def encode(stream: XdrDataOutputStream): Unit } object #{name} { - def decode(stream: XdrDataInputStream): #{name} = #{decode_type union.discriminant.type} match { + def decode(stream: XdrDataInputStream): Any = #{decode_type union.discriminant.type} match { EOS # decode @@ -333,19 +383,9 @@ def render_top_matter(out) def render_source_comment(out, defn) return if defn.is_a?(AST::Definitions::Namespace) - - out.puts <<-EOS.strip_heredoc - // === xdr source ============================================================ - - EOS - - out.puts "// " + defn.text_value.split("\n").join("\n// ") - - out.puts <<-EOS.strip_heredoc - - // =========================================================================== - - EOS + out.puts "/*" + out.puts " #{defn.text_value.split("\n").join("\n ")}" + out.puts "*/" end def encode_member(member, out) From ec65523b53917b3acfa76c3b8db1ada02127b7ee Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 9 Dec 2018 21:32:50 +1000 Subject: [PATCH 05/20] wip: nesting --- lib/xdrgen/generators/scala.rb | 574 ++++++++++++++++++++++----------- 1 file changed, 381 insertions(+), 193 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 9a76e0388..e5d3d7bc8 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -11,247 +11,171 @@ def initialize(name, members, element) @members = members @element = element end + end + + class Constant + attr_accessor :name, :value + + def initialize(name, value) + @name = name + @value = value + end + end - def to_s - "#{@name}: #{@members.inspect}" + class TypeDef + attr_accessor :name, :declaration, :is_optional, :element + + def initialize(name, declaration, element, is_optional = false) + @name = name + @declaration = declaration + @is_optional = is_optional + @element = element end end + # class Union + # attr_accessor :name + # + # def initialize(name) + # @name = name + # end + # end + def generate enums = parse_enums @top + constants = parse_constants @top + typedefs = parse_typedefs @top + unions = parse_unions @top + structs = parse_structs @top + render_lib - enums.each{|e| write_enum_file e } + write_package_class constants + enums.each(&method(:write_enum_file)) + typedefs.each(&method(:write_typedef_file)) + unions.each(&method(:write_union_file)) + structs.each(&method(:write_struct_file)) end def parse_enums(ns) ns.definitions.select{|d| d.is_a? AST::Definitions::Enum }.map do |e| hash = e.members.reduce({}) do |hash, member| - hash.store(member.name.downcase.camelize, member.value) + hash.store(member.name, member.value) hash end Enum.new(name(e), hash, e) end end - def write_enum_file(e) - path = "#{@namespace}/#{e.name}.scala" - out = @output.open(path) - render_top_matter out - render_enum out, e + def parse_constants(ns) + ns.definitions.select{|d| d.is_a? AST::Definitions::Const }.map do |c| + Constant.new(c.name, c.value) + end end - def render_enum(out, e) - render_source_comment out, e.element - out.puts <<-EOS.strip_heredoc - sealed class #{e.name}(val i: Int) { - def encode(stream: XdrDataOutputStream) = stream.writeInt(i) - } - - object #{e.name} { - def decode(stream: XdrDataInputStream): #{e.name} = stream.readInt() match { - #{e.members.map{|k,v| "case #{v} => #{k}" }.join("\n ")} - case i => throw new IllegalArgumentException(s"#{e.name} value $i is invalid") - } - - #{e.members.map{|k,v| "case object #{k} extends #{e.name}(#{v})"}.join("\n ")} - } - EOS + def parse_typedefs(ns) + ns.definitions.select{|d| d.is_a? AST::Definitions::Typedef }.map do |td| + TypeDef.new(td.name.camelize, td.declaration, td, td.type.sub_type == :optional) + end end - # ---- - - private - def render_lib - template = IO.read(__dir__ + "/scala/XdrDataInputStream.erb") - result = ERB.new(template).result binding - @output.write "#{@namespace.downcase}/XdrDataInputStream.scala", result - - template = IO.read(__dir__ + "/scala/XdrDataOutputStream.erb") - result = ERB.new(template).result binding - @output.write "#{@namespace.downcase}/XdrDataOutputStream.scala", result + def parse_unions(ns) + ns.definitions.select{|d| d.is_a? AST::Definitions::Union } end - def render_constants(node, out) - node.namespaces.each{|n| render_constants(n, out) } - node.definitions.select{|c| c.is_a? AST::Definitions::Const}.each { |c| render_const out, c } + def parse_structs(ns) + ns.definitions.select{|d| d.is_a? AST::Definitions::Struct } end - def render_const(out, const) - out.indent do - out.puts "val #{const.name.downcase.camelize} = #{const.value.downcase.camelize}" + def write_enum_file(e) + write_class_file e.name do |out| + render_enum out, e end end - def render_definitions(node) - node.namespaces.each{|n| render_definitions n } - node.definitions.each(&method(:render_definition)) + def write_typedef_file(td) + write_class_file td.name do |out| + render_typedef out, td + end end - def render_definition(defn) - case defn - when AST::Definitions::Struct ; - render_file defn do |out| - render_struct defn, out - end - when AST::Definitions::Enum ; - render_file defn do |out| - render_enum defn, out - end - when AST::Definitions::Union ; - render_file defn do |out| - render_union defn, out - end - when AST::Definitions::Typedef ; - render_file defn do |out| - render_typedef defn, out - end + def write_union_file(u) + write_class_file u.name do |out| + render_union out, u end end - def render_package_file - path = "#{@namespace.downcase}/#{@namespace.downcase}.scala" - out = @output.open(path) - out.puts <<-EOS.strip_heredoc - // Automatically generated by xdrgen - // DO NOT EDIT or your changes may be overwritten - - package object #{@namespace.downcase} { - EOS - - yield out - - out.puts("}") + def write_struct_file(s) + write_class_file s.name do |out| + render_struct out, s + end end - def render_file(element) - path = "#{@namespace.downcase}/#{element.name.camelize}.scala" + def write_class_file(name) + path = "#{@namespace.downcase}/#{name.camelize}.scala" out = @output.open(path) render_top_matter out - render_source_comment out, element - + render_package out yield out end - def render_nested_definitions(defn, out) - return unless defn.respond_to? :nested_definitions - unless defn.nested_definitions.empty? - out.puts "" - end - defn.nested_definitions.each{|ndefn| - case ndefn - when AST::Definitions::Struct ; - name = name ndefn - render_struct ndefn, out - when AST::Definitions::Enum ; - name = name ndefn - render_enum ndefn, out - when AST::Definitions::Union ; - name = name ndefn - render_union ndefn, out - when AST::Definitions::Typedef ; - name = name ndefn - render_typedef ndefn, out - end + def write_package_class(constants) + return if constants.empty? + path = "#{@namespace.downcase}/#{@namespace.downcase}.scala" + out = @output.open(path) + render_top_matter out + out.puts <<-EOS.strip_heredoc + package object #{@namespace.downcase} { + #{constants.map{|c| "val #{c.name} = #{c.value}"}.join("\n ")} } + EOS end - def render_enum_old(enum, out) - name = name_string enum.name - out.puts <<-EOS.strip_heredoc - sealed class #{name} (val i: Int) { - def encode(stream: XdrDataOutputStream) = stream.writeInt(i) + def render_enum(out, e) + render_source_comment out, e.element + out.puts <<~EOS.strip_heredoc + sealed class #{e.name}(val i: Int) { + def encode(stream: XdrDataOutputStream) = stream.writeInt(i) + } + + object #{e.name} { + def decode(stream: XdrDataInputStream): #{e.name} = stream.readInt() match { + #{e.members.map{|k,v| "case #{v} => #{k}" }.join("\n ")} + case i => throw new IllegalArgumentException(s"#{e.name} value $i is invalid") } - object #{name} { - def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { + #{e.members.map{|k,v| "case object #{k} extends #{e.name}(#{v})"}.join("\n ")} + } EOS - out.indent do - out.indent do - enum.members.each do |em| - out.puts("case #{em.value} => #{em.name.downcase.camelize}") - end - out.puts("case i => throw new IllegalArgumentException(s\"#{name} value $i is invalid\")") - end - out.puts "}" - out.puts "" - enum.members.each do |em| - out.puts <<-EOS.strip_heredoc - case object #{em.name.downcase.camelize} extends #{name}(#{em.value}) - EOS - end - - render_nested_definitions enum, out - end - out.puts "}" end - def render_struct(element, out, superclass=nil) - name = name_string element.name - out.puts "case class #{name} (" - out.indent do - len = element.members.length - element.members.each do |m| - out.puts "#{sanitize m.name}: " + (is_nested?(m.type) ? "#{name}." : "") + "#{decl_string m.declaration}" + (len > 1 ? ", " : "") - len -= 1 - end - end - out.puts ") #{superclass ? "extends #{superclass} " : ""}{" - out.indent do - out.puts "def encode(stream: XdrDataOutputStream): Unit = {" - out.indent do - out.puts "#{superclass}.encode(this, stream)" if superclass - element.members.each do |m| - encode_member m, out - end - end - end - out.puts " }" - out.puts "}" - out.puts "" - out.puts "object #{name} {" - out.puts " def decode(stream: XdrDataInputStream): #{name} = #{name element}(" - out.indent do - out.indent do - len = element.members.length - element.members.each do |m| - decode_member m, out, len == 1 - len -= 1 - end - end - out.puts ")" - render_nested_definitions element, out - end - out.puts "}" - out.puts "" - end + def render_typedef(out, td) + render_source_comment out, td.element - def render_typedef(typedef, out, superclass=nil) - name = name_string typedef.name out.puts <<-EOS.strip_heredoc - case class #{name}(self: #{decl_string typedef.declaration}) #{superclass ? "extends #{superclass} " : ""} { - def encode(stream: XdrDataOutputStream): Unit = { + case class #{td.name}(self: #{decl_string td.declaration}) { + def encode(stream: XdrDataOutputStream): Unit = { EOS out.indent(2) do - encode_decl typedef.declaration, "self", typedef.type.sub_type == :optional, out + encode_decl td.declaration, "self", td.is_optional, out end out.puts <<-EOS.strip_heredoc - } } + } EOS - decode_str = decode_decl typedef.declaration - decode_statement = typedef.type.sub_type == :optional ? "Option(#{decode_str})" : decode_str + decode_str = decode_decl td.declaration + decode_statement = td.is_optional ? "Option(#{decode_str})" : decode_str out.puts <<-EOS.strip_heredoc - object #{name} { - def decode(stream: XdrDataInputStream): #{name} = #{name}(#{decode_statement}) + object #{td.name} { + def decode(stream: XdrDataInputStream): #{td.name} = #{td.name}(#{decode_statement}) } EOS end - def render_union(union, out) + def render_union(out, union) name = name_string union.name - def render_arms(union) + def render_arms(union, name) union.arms.each do |arm| if arm.is_a? AST::Definitions::UnionDefaultArm yield arm.declaration, "d", "Default", true @@ -259,15 +183,28 @@ def render_arms(union) arm.cases.each do |kase| dtype = type_string union.discriminant.type ktype = - if kase.value.is_a?(AST::Identifier) - "#{dtype}.#{name_string kase.value.name}" - elsif union.discriminant.type.is_a?(AST::Typespecs::Simple) - "#{dtype}(#{kase.value.value})" - else - "#{kase.value.value}" - end - rtype = kase.value.is_a?(AST::Identifier) ? "#{kase.value.name.downcase.camelize}" : "R_#{kase.value.value}" - yield arm.declaration, ktype, rtype, false + if kase.value.is_a?(AST::Identifier) + "#{dtype}.#{name_string kase.value.name}" + elsif union.discriminant.type.is_a?(AST::Typespecs::Simple) + "#{dtype}(#{kase.value.value})" + else + "#{kase.value.value}" + end + rtype = + if kase.value.is_a?(AST::Identifier) + if is_void?(arm.declaration) + "#{name}#{name_string kase.value.name.downcase}" + elsif arm.declaration.type.sub_type == :simple + "#{name}#{arm.declaration.type.name.camelize}" + elsif arm.declaration.type.sub_type == :optional + "#{name}#{arm.declaration.type.name.camelize}Opt" + else + "#{name}#{arm.declaration.type.name.camelize}Array" + end + else + "#{name}#{kase.value.value}" + end + yield arm.declaration, ktype, rtype end end end @@ -277,11 +214,30 @@ def render_arms(union) sealed trait #{name} { def encode(stream: XdrDataOutputStream): Unit } + EOS - object #{name} { - def decode(stream: XdrDataInputStream): Any = #{decode_type union.discriminant.type} match { + render_arms(union, name) do |decl, ktype, rtype| + out.puts decl_wrapper(rtype, name, decl) + end + + out.puts <<-EOS.strip_heredoc + + object #{name} { + def decode(stream: XdrDataInputStream): #{name} = #{decode_type union.discriminant.type} match { + EOS + out.indent(2) do + render_arms(union, name) do |decl, ktype, rtype| + decode = "#{decode_decl decl}" + out.puts "case #{ktype} => #{rtype}#{is_void?(decl) ? "" : "(#{decode})"}" + end + out.puts "case d => throw new IllegalArgumentException(s\"#{type_string union.discriminant.type} value $d is invalid\")" + end + out.puts <<-EOS.strip_heredoc + } + } EOS +=begin # decode out.indent do out.indent do @@ -299,8 +255,8 @@ def decode(stream: XdrDataInputStream): Any = #{decode_type union.discriminant.t out.puts <<-EOS.strip_heredoc } - - def encode(x: #{name}, stream: XdrDataOutputStream): Unit = x match { + + def encode(x: Result, stream: XdrDataOutputStream): Unit = x match { EOS #encode @@ -369,6 +325,7 @@ def encode(stream: XdrDataOutputStream): Unit = #{ktype}.encode(stream) end out.puts "}" +=end end def render_top_matter(out) @@ -376,7 +333,12 @@ def render_top_matter(out) // Automatically generated by xdrgen // DO NOT EDIT or your changes may be overwritten - package #{@namespace.downcase || "main"} + EOS + end + + def render_package(out) + out.puts <<-EOS.strip_heredoc + package #{@namespace.downcase || "main"} EOS end @@ -388,17 +350,241 @@ def render_source_comment(out, defn) out.puts "*/" end + def render_lib + template = IO.read(__dir__ + "/scala/XdrDataInputStream.erb") + result = ERB.new(template).result binding + @output.write "#{@namespace.downcase}/XdrDataInputStream.scala", result + + template = IO.read(__dir__ + "/scala/XdrDataOutputStream.erb") + result = ERB.new(template).result binding + @output.write "#{@namespace.downcase}/XdrDataOutputStream.scala", result + end + + def decl_wrapper(name, super_name, decl) + case decl + when AST::Declarations::Opaque ; + <<-EOS.strip_heredoc + case class #{name}(bs: Array[Byte]) extends #{super_name} { + def encode(stream: XdrDataOutputStream) = { + stream.writeInt(bs.length) + stream.write(bs, 0, bs.length) + } + } + EOS + when AST::Declarations::String ; + <<-EOS.strip_heredoc + case class #{name}(s: String) extends #{super_name} { + def encode(stream: XdrDataOutputStream) = stream.writeString(s) + } + EOS + when AST::Declarations::Array ; + <<-EOS.strip_heredoc + case class #{name}(xs: Array[#{type_string decl.type}]) extends #{super_name} { + def encode(stream: XdrDataOutputStream) = { + stream.writeInt(xs.length) + xs.foreach(_.encode(stream)) + } + } + EOS + when AST::Declarations::Optional ; + <<-EOS.strip_heredoc + case class #{name}(m: Option[#{type_string(decl.type)}]) extends #{super_name} { + def encode(stream: XdrDataOutputStream) = m match { + case None => stream.writeInt(0) + case Some(x) => + stream.writeInt(1) + x.encode(stream) + } + } + EOS + when AST::Declarations::Simple ; + + # todo - how to get name of enum here? + + <<-EOS.strip_heredoc + case class #{name}(x: #{type_string(decl.type)}) extends #{super_name} { + def encode(stream: XdrDataOutputStream) = x.encode(stream) + } + EOS + when AST::Declarations::Void ; + <<-EOS.strip_heredoc + case object #{name} extends #{super_name} { + def encode(stream: XdrDataOutputStream) = Unit + } + EOS + else + raise "Unknown declaration type: #{decl.class.name}" + end + end + + # ---- + + + # def render_constants(node, out) + # node.namespaces.each{|n| render_constants(n, out) } + # node.definitions.select{|c| c.is_a? AST::Definitions::Const}.each { |c| render_const out, c } + # end + # + # def render_const(out, const) + # out.indent do + # out.puts "val #{const.name.downcase.camelize} = #{const.value.downcase.camelize}" + # end + # end + + def render_definitions(node) + node.namespaces.each{|n| render_definitions n } + node.definitions.each(&method(:render_definition)) + end + + def render_definition(defn) + case defn + when AST::Definitions::Struct ; + render_file defn do |out| + render_struct defn, out + end + when AST::Definitions::Enum ; + render_file defn do |out| + render_enum defn, out + end + when AST::Definitions::Union ; + render_file defn do |out| + render_union defn, out + end + when AST::Definitions::Typedef ; + render_file defn do |out| + render_typedef defn, out + end + end + end + + # def render_package_file + # path = "#{@namespace.downcase}/#{@namespace.downcase}.scala" + # out = @output.open(path) + # out.puts <<-EOS.strip_heredoc + # // Automatically generated by xdrgen + # // DO NOT EDIT or your changes may be overwritten + # + # package object #{@namespace.downcase} { + # EOS + # + # yield out + # + # out.puts("}") + # end + + def render_file(element) + path = "#{@namespace.downcase}/#{element.name.camelize}.scala" + out = @output.open(path) + render_top_matter out + render_source_comment out, element + + yield out + end + + def render_nested_definitions(defn, out) + return unless defn.respond_to? :nested_definitions + unless defn.nested_definitions.empty? + out.puts "" + end + defn.nested_definitions.each{|ndefn| + case ndefn + when AST::Definitions::Struct ; + name = name ndefn + render_struct ndefn, out + when AST::Definitions::Enum ; + name = name ndefn + render_enum ndefn, out + when AST::Definitions::Union ; + name = name ndefn + render_union ndefn, out + when AST::Definitions::Typedef ; + name = name ndefn + render_typedef ndefn, out + end + } + end + + def render_enum_old(enum, out) + name = name_string enum.name + out.puts <<-EOS.strip_heredoc + sealed class #{name} (val i: Int) { + def encode(stream: XdrDataOutputStream) = stream.writeInt(i) + } + + object #{name} { + def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { + EOS + out.indent do + out.indent do + enum.members.each do |em| + out.puts("case #{em.value} => #{em.name.downcase.camelize}") + end + out.puts("case i => throw new IllegalArgumentException(s\"#{name} value $i is invalid\")") + end + out.puts "}" + out.puts "" + enum.members.each do |em| + out.puts <<-EOS.strip_heredoc + case object #{em.name.downcase.camelize} extends #{name}(#{em.value}) + EOS + end + + render_nested_definitions enum, out + end + out.puts "}" + end + + def render_struct(out, element, superclass=nil) + name = name_string element.name.camelize + out.puts "case class #{name} (" + out.indent do + len = element.members.length + element.members.each do |m| + out.puts "#{sanitize m.name}: " + (is_nested?(m.type) ? "#{name}." : "") + "#{decl_string m.declaration}" + (len > 1 ? ", " : "") + len -= 1 + end + end + out.puts ") #{superclass ? "extends #{superclass} " : ""}{" + out.indent do + out.puts "def encode(stream: XdrDataOutputStream): Unit = {" + out.indent do + out.puts "#{superclass}.encode(this, stream)" if superclass + element.members.each do |m| + encode_member m, out + end + end + end + out.puts " }" + out.puts "}" + out.puts "" + out.puts "object #{name} {" + out.puts " def decode(stream: XdrDataInputStream): #{name} = #{name element}(" + out.indent do + out.indent do + len = element.members.length + element.members.each do |m| + decode_member m, out, len == 1 + len -= 1 + end + end + out.puts ")" + render_nested_definitions element, out + end + out.puts "}" + out.puts "" + end + def encode_member(member, out) encode_decl member.declaration, member.name, member.type.sub_type == :optional, out end - def encode_decl(decl, name, isOption, out) + def encode_decl(decl, name, is_option, out) case decl when AST::Declarations::Void return end - if isOption + if is_option out.puts "#{sanitize name} match {" out.indent do out.puts "case Some(x) => " @@ -481,7 +667,7 @@ def decode_decl(decl) size = decl.fixed? ? decl.size : "stream.readInt" return "stream.readBytes(#{size})" when AST::Declarations::Array ; - size = decl.fixed? ? decl.size.downcase.camelize : "stream.readInt" + size = decl.fixed? ? decl.size : "stream.readInt" return "(0 until #{size}).map(_ => #{decode_type decl.type}).toArray" else return decode_type decl.type @@ -559,6 +745,8 @@ def type_string(type) when AST::Typespecs::Simple ; name type.resolved_type when AST::Concerns::NestedDefinition ; + # todo - in some circumstances a nested defn should reference the enum type also + # "#{type.parent_defn.name}#{name type}" name type else raise "Unknown typespec: #{type.class.name}" From 4be3cf805f14f8bd50559f48fef265a8512dc123 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sat, 15 Dec 2018 16:08:55 +1000 Subject: [PATCH 06/20] tmp: nesting works, but union is now broken --- lib/xdrgen/generators/scala.rb | 471 ++++++++++++++++++------------ spec/lib/xdrgen/generator_spec.rb | 2 +- 2 files changed, 284 insertions(+), 189 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index e5d3d7bc8..baa09cf5b 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -57,7 +57,7 @@ def generate end def parse_enums(ns) - ns.definitions.select{|d| d.is_a? AST::Definitions::Enum }.map do |e| + ns.definitions.select {|d| d.is_a? AST::Definitions::Enum}.map do |e| hash = e.members.reduce({}) do |hash, member| hash.store(member.name, member.value) hash @@ -67,23 +67,23 @@ def parse_enums(ns) end def parse_constants(ns) - ns.definitions.select{|d| d.is_a? AST::Definitions::Const }.map do |c| + ns.definitions.select {|d| d.is_a? AST::Definitions::Const}.map do |c| Constant.new(c.name, c.value) end end def parse_typedefs(ns) - ns.definitions.select{|d| d.is_a? AST::Definitions::Typedef }.map do |td| + ns.definitions.select {|d| d.is_a? AST::Definitions::Typedef}.map do |td| TypeDef.new(td.name.camelize, td.declaration, td, td.type.sub_type == :optional) end end def parse_unions(ns) - ns.definitions.select{|d| d.is_a? AST::Definitions::Union } + ns.definitions.select {|d| d.is_a? AST::Definitions::Union} end def parse_structs(ns) - ns.definitions.select{|d| d.is_a? AST::Definitions::Struct } + ns.definitions.select {|d| d.is_a? AST::Definitions::Struct} end def write_enum_file(e) @@ -112,7 +112,7 @@ def write_struct_file(s) def write_class_file(name) path = "#{@namespace.downcase}/#{name.camelize}.scala" - out = @output.open(path) + out = @output.open(path) render_top_matter out render_package out yield out @@ -125,7 +125,7 @@ def write_package_class(constants) render_top_matter out out.puts <<-EOS.strip_heredoc package object #{@namespace.downcase} { - #{constants.map{|c| "val #{c.name} = #{c.value}"}.join("\n ")} + #{constants.map {|c| "val #{c.name} = #{c.value}"}.join("\n ")} } EOS end @@ -139,11 +139,11 @@ def encode(stream: XdrDataOutputStream) = stream.writeInt(i) object #{e.name} { def decode(stream: XdrDataInputStream): #{e.name} = stream.readInt() match { - #{e.members.map{|k,v| "case #{v} => #{k}" }.join("\n ")} + #{e.members.map {|k, v| "case #{v} => #{k}"}.join("\n ")} case i => throw new IllegalArgumentException(s"#{e.name} value $i is invalid") } - #{e.members.map{|k,v| "case object #{k} extends #{e.name}(#{v})"}.join("\n ")} + #{e.members.map {|k, v| "case object #{k} extends #{e.name}(#{v})"}.join("\n ")} } EOS end @@ -172,7 +172,155 @@ def decode(stream: XdrDataInputStream): #{td.name} = #{td.name}(#{decode_stateme EOS end + def decode_discriminant(disc) + case disc.type_s + when Xdrgen::AST::Identifier; + "#{disc.type.name}.decode(stream)" + else + "stream.readInt()" + end + end + + # def as_arguments(decl) + # if is_void?(decl) + # "" + # else + # decl_string(decl) + # end + # end + # + + def decode_member_string(member, final) + case member.declaration + when AST::Declarations::Void; + return + end + str = decode_decl(member.declaration) + if member.type.sub_type == :optional + "if (stream.readInt == 0) None else Some(" + str + ")" + (final ? "" : ",") + else + str + (final ? "" : ",") + end + end + + def encode_member_string(member) + encode_decl_string(member.declaration, member.declaration.name, member.type.sub_type == :optional) + end + + + def match_arm(arm, union) + def match_case(kase, arm, union) + union_name = "#{name_string union.name}" + + case kase.value + when Xdrgen::AST::Identifier; + case_match = "#{union.discriminant.type.name}.#{kase.value.name}" + + constructor_params = + if is_void?(arm.declaration) + "" + else # simple + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + member_count = arm.declaration.type_s.members.size + decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| + decode_member_string(m, i == member_count) + }.join(" ") + "(#{decoded_args})" + else + arm.declaration.name + end + end + + + "case #{case_match} => #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params}" + else + "case #{kase.value.value} => #{union_name}#{kase.value.value}" + end + end + + arm.cases.map {|c| match_case(c, arm, union)}.join("\n") + end + + def class_arm(arm, union) + def match_case(kase, arm, union) + union_name = "#{name_string union.name}" + + case kase.value + when Xdrgen::AST::Identifier; + class_or_object = is_void?(arm.declaration) ? "object" : "class" + constructor_params = + if is_void?(arm.declaration) + "" + else # simple + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + member_count = arm.declaration.type_s.members.size + decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| + "#{m.name}: #{decl_string m.declaration}#{(i==member_count) ? "" : ","}" + }.join(" ") + "(#{decoded_args})" + else + "()" + end + end + + member_encode_statements = + if is_void?(arm.declaration) + "" + else # simple + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + "\n " + arm.declaration.type_s.members.map(&method(:encode_member_string)).join("\n ") + else + "" + end + end + + <<~EOS.strip_heredoc + case #{class_or_object} #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params} extends #{union_name} { + def encode(stream: XdrDataOutputStream): Unit = { + #{union.discriminant.type.name}.#{kase.value.name}.encode(stream)#{member_encode_statements} + } + } + EOS + else + <<~EOS.strip_heredoc + case object #{union_name}#{kase.value.value} extends #{union_name} { + // todo + } + EOS + end + end + + arm.cases.map {|c| match_case(c, arm, union)}.join("\n") + end + def render_union(out, union) + union_name = name_string union.name + + render_source_comment out, union + + out.puts <<-EOS.strip_heredoc + object #{union_name} { + def decode(stream: XdrDataInputStream): #{union_name} = #{decode_discriminant union.discriminant} match { + #{union.arms.map {|a| match_arm(a, union)}.join("\n ")} + } + } + + EOS + + out.puts <<-EOS.strip_heredoc + sealed trait #{union_name} { + def encode(stream: XdrDataOutputStream): Unit + } + EOS + + union.arms.map{|arm| class_arm(arm, union)}.each {|s| out.puts(s) } + end + + def render_union_start_again(out, union) + render_source_comment out, union name = name_string union.name def render_arms(union, name) @@ -204,7 +352,7 @@ def render_arms(union, name) else "#{name}#{kase.value.value}" end - yield arm.declaration, ktype, rtype + yield arm.declaration, dtype, ktype, rtype end end end @@ -216,8 +364,8 @@ def encode(stream: XdrDataOutputStream): Unit } EOS - render_arms(union, name) do |decl, ktype, rtype| - out.puts decl_wrapper(rtype, name, decl) + render_arms(union, name) do |decl, dtype, ktype, rtype| + out.puts decl_wrapper(rtype, dtype, name, decl) end out.puts <<-EOS.strip_heredoc @@ -226,7 +374,7 @@ def encode(stream: XdrDataOutputStream): Unit def decode(stream: XdrDataInputStream): #{name} = #{decode_type union.discriminant.type} match { EOS out.indent(2) do - render_arms(union, name) do |decl, ktype, rtype| + render_arms(union, name) do |decl, dtype, ktype, rtype| decode = "#{decode_decl decl}" out.puts "case #{ktype} => #{rtype}#{is_void?(decl) ? "" : "(#{decode})"}" end @@ -236,96 +384,6 @@ def decode(stream: XdrDataInputStream): #{name} = #{decode_type union.discrimina } } EOS - -=begin - # decode - out.indent do - out.indent do - the_default = "case d => throw new IllegalArgumentException(s\"#{type_string union.discriminant.type} value $d is invalid\")" - render_arms(union) do |decl, ktype, rtype, is_default| - decode = "#{decode_decl decl}" - if is_default - the_default = "case #{ktype} => #{rtype}(d" + (is_void?(decl) ? "" : ", #{decode}") + ")" - else - out.puts "case #{ktype} => #{is_void?(decl) ? rtype : decode}" # #{rtype}#{is_void?(decl) ? "" : "(" + decode + ")" }" - end - end - out.puts the_default - end - - out.puts <<-EOS.strip_heredoc - } - - def encode(x: Result, stream: XdrDataOutputStream): Unit = x match { - EOS - - #encode - out.indent do - render_arms(union) do |decl, ktype, rtype, is_default| - if is_void?(decl) - out.puts "case #{rtype} => #{ktype}.encode(stream)" - else - out.puts "case _: #{rtype} => #{ktype}.encode(stream)" - end - end - end - - out.puts "}" - out.puts "" - - - # render_arms(union) do |decl, ktype, rtype, is_default| - # type = "class" - # args = is_default ? "(d: #{type_string union.discriminant.type}" : "" - # if is_void? decl - # args += args.empty? ? "" : ")" - # else - # args += (args.empty? ? "(" : ", ") + "x: #{decl_string decl})" - # end - # if !is_default && is_void?(decl) - # type = "object" - # args = "" - # end - # - # out.puts "case #{type} #{rtype}#{args} extends #{name} {" - # out.indent do - # out.puts "def encode(stream: XdrDataOutputStream): Unit = {" - # out.indent do - # encode_decl union.discriminant, ktype, false, out - # unless is_void? decl - # encode_decl decl, "x", false, out - # end - # end - # out.puts "}" - # end - - - union.nested_definitions.each { |ndefn| - case ndefn - when AST::Definitions::Struct ; - render_struct ndefn, out, name - when AST::Definitions::Enum ; - render_enum ndefn, out - when AST::Definitions::Union ; - render_union ndefn, out - when AST::Definitions::Typedef ; - render_typedef ndefn, out, name - end - } - - render_arms(union) do |decl, ktype, rtype, is_default| - if is_void? decl - out.puts <<-EOS.strip_heredoc - object #{rtype} extends #{name} { - def encode(stream: XdrDataOutputStream): Unit = #{ktype}.encode(stream) - } - EOS - end - end - - end - out.puts "}" -=end end def render_top_matter(out) @@ -360,9 +418,9 @@ def render_lib @output.write "#{@namespace.downcase}/XdrDataOutputStream.scala", result end - def decl_wrapper(name, super_name, decl) + def decl_wrapper(name, discriminator_name, super_name, decl) case decl - when AST::Declarations::Opaque ; + when AST::Declarations::Opaque; <<-EOS.strip_heredoc case class #{name}(bs: Array[Byte]) extends #{super_name} { def encode(stream: XdrDataOutputStream) = { @@ -371,13 +429,13 @@ def encode(stream: XdrDataOutputStream) = { } } EOS - when AST::Declarations::String ; + when AST::Declarations::String; <<-EOS.strip_heredoc case class #{name}(s: String) extends #{super_name} { def encode(stream: XdrDataOutputStream) = stream.writeString(s) } EOS - when AST::Declarations::Array ; + when AST::Declarations::Array; <<-EOS.strip_heredoc case class #{name}(xs: Array[#{type_string decl.type}]) extends #{super_name} { def encode(stream: XdrDataOutputStream) = { @@ -386,9 +444,9 @@ def encode(stream: XdrDataOutputStream) = { } } EOS - when AST::Declarations::Optional ; + when AST::Declarations::Optional; <<-EOS.strip_heredoc - case class #{name}(m: Option[#{type_string(decl.type)}]) extends #{super_name} { + case class #{name}(m: Option[#{discriminator_name}]) extends #{super_name} { def encode(stream: XdrDataOutputStream) = m match { case None => stream.writeInt(0) case Some(x) => @@ -397,16 +455,13 @@ def encode(stream: XdrDataOutputStream) = m match { } } EOS - when AST::Declarations::Simple ; - - # todo - how to get name of enum here? - + when AST::Declarations::Simple; <<-EOS.strip_heredoc - case class #{name}(x: #{type_string(decl.type)}) extends #{super_name} { + case class #{name}(x: #{discriminator_name}) extends #{super_name} { def encode(stream: XdrDataOutputStream) = x.encode(stream) } EOS - when AST::Declarations::Void ; + when AST::Declarations::Void; <<-EOS.strip_heredoc case object #{name} extends #{super_name} { def encode(stream: XdrDataOutputStream) = Unit @@ -432,25 +487,25 @@ def encode(stream: XdrDataOutputStream) = Unit # end def render_definitions(node) - node.namespaces.each{|n| render_definitions n } + node.namespaces.each {|n| render_definitions n} node.definitions.each(&method(:render_definition)) end def render_definition(defn) case defn - when AST::Definitions::Struct ; + when AST::Definitions::Struct; render_file defn do |out| render_struct defn, out end - when AST::Definitions::Enum ; + when AST::Definitions::Enum; render_file defn do |out| render_enum defn, out end - when AST::Definitions::Union ; + when AST::Definitions::Union; render_file defn do |out| render_union defn, out end - when AST::Definitions::Typedef ; + when AST::Definitions::Typedef; render_file defn do |out| render_typedef defn, out end @@ -474,7 +529,7 @@ def render_definition(defn) def render_file(element) path = "#{@namespace.downcase}/#{element.name.camelize}.scala" - out = @output.open(path) + out = @output.open(path) render_top_matter out render_source_comment out, element @@ -486,18 +541,18 @@ def render_nested_definitions(defn, out) unless defn.nested_definitions.empty? out.puts "" end - defn.nested_definitions.each{|ndefn| + defn.nested_definitions.each {|ndefn| case ndefn - when AST::Definitions::Struct ; + when AST::Definitions::Struct; name = name ndefn render_struct ndefn, out - when AST::Definitions::Enum ; + when AST::Definitions::Enum; name = name ndefn render_enum ndefn, out - when AST::Definitions::Union ; + when AST::Definitions::Union; name = name ndefn render_union ndefn, out - when AST::Definitions::Typedef ; + when AST::Definitions::Typedef; name = name ndefn render_typedef ndefn, out end @@ -517,7 +572,7 @@ def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { out.indent do out.indent do enum.members.each do |em| - out.puts("case #{em.value} => #{em.name.downcase.camelize}") + out.puts("case #{em.value} => #{em.name.downcase.camelize}") end out.puts("case i => throw new IllegalArgumentException(s\"#{name} value $i is invalid\")") end @@ -534,22 +589,22 @@ def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { out.puts "}" end - def render_struct(out, element, superclass=nil) - name = name_string element.name.camelize + def render_struct(out, struct, superclass = nil) + name = name_string struct.name.camelize out.puts "case class #{name} (" out.indent do - len = element.members.length - element.members.each do |m| - out.puts "#{sanitize m.name}: " + (is_nested?(m.type) ? "#{name}." : "") + "#{decl_string m.declaration}" + (len > 1 ? ", " : "") - len -= 1 - end + len = struct.members.length + struct.members.each do |m| + out.puts "#{sanitize m.name}: " + (is_nested?(m.type) ? "#{name}." : "") + "#{decl_string m.declaration}" + (len > 1 ? ", " : "") + len -= 1 + end end out.puts ") #{superclass ? "extends #{superclass} " : ""}{" out.indent do out.puts "def encode(stream: XdrDataOutputStream): Unit = {" out.indent do out.puts "#{superclass}.encode(this, stream)" if superclass - element.members.each do |m| + struct.members.each do |m| encode_member m, out end end @@ -558,17 +613,17 @@ def render_struct(out, element, superclass=nil) out.puts "}" out.puts "" out.puts "object #{name} {" - out.puts " def decode(stream: XdrDataInputStream): #{name} = #{name element}(" + out.puts " def decode(stream: XdrDataInputStream): #{name} = #{name struct}(" out.indent do out.indent do - len = element.members.length - element.members.each do |m| + len = struct.members.length + struct.members.each do |m| decode_member m, out, len == 1 len -= 1 end end out.puts ")" - render_nested_definitions element, out + render_nested_definitions struct, out end out.puts "}" out.puts "" @@ -578,10 +633,50 @@ def encode_member(member, out) encode_decl member.declaration, member.name, member.type.sub_type == :optional, out end + def encode_decl_string(decl, name, is_option) + + def inner(decl, name) + case decl + when AST::Declarations::Opaque; + <<~EOS.strip_heredoc + #{"stream.writeInt(#{name}.length)" unless decl.fixed?} + stream.write(#{name}, 0, #{name}.length) + EOS + + when AST::Declarations::Array; + <<~EOS.strip_heredoc + #{"stream.writeInt(#{name}.length)" unless decl.fixed?} + #{name}.foreach { #{encode_type decl.type, "_"} } + EOS + + else + "#{encode_type decl.type, "#{name}"}" + end + end + + case decl + when AST::Declarations::Void; + "" + else + if is_option + <<~EOS.strip_heredoc + #{sanitize name} match { + case Some(x) => + stream.writeInt(1) + #{inner(decl, "x")} + case None => stream.writeInt(0) + } + EOS + else + inner(decl, sanitize(name)) + end + end + end + def encode_decl(decl, name, is_option, out) case decl - when AST::Declarations::Void - return + when AST::Declarations::Void + return end if is_option @@ -602,12 +697,12 @@ def encode_decl(decl, name, is_option, out) def encode_decl_inner(decl, name, out) case decl - when AST::Declarations::Opaque ; + when AST::Declarations::Opaque; unless decl.fixed? out.puts "stream.writeInt(#{name}.length)" end out.puts "stream.write(#{name}, 0, #{name}.length)" - when AST::Declarations::Array ; + when AST::Declarations::Array; unless decl.fixed? out.puts "stream.writeInt(#{name}.length)" end @@ -619,27 +714,27 @@ def encode_decl_inner(decl, name, out) def encode_type(type, value) case type - when AST::Typespecs::Int ; + when AST::Typespecs::Int; "stream.writeInt(#{value})" - when AST::Typespecs::UnsignedInt ; + when AST::Typespecs::UnsignedInt; "stream.writeInt(#{value})" - when AST::Typespecs::Hyper ; + when AST::Typespecs::Hyper; "stream.writeLong(#{value})" - when AST::Typespecs::UnsignedHyper ; + when AST::Typespecs::UnsignedHyper; "stream.writeLong(#{value})" - when AST::Typespecs::Float ; + when AST::Typespecs::Float; "stream.writeFloat(#{value})" - when AST::Typespecs::Double ; + when AST::Typespecs::Double; "stream.writeDouble(#{value})" - when AST::Typespecs::Quadruple ; + when AST::Typespecs::Quadruple; raise "cannot render quadruple in scala" - when AST::Typespecs::Bool ; + when AST::Typespecs::Bool; "stream.writeInt(if (#{value}) 1 else 0)" - when AST::Typespecs::String ; + when AST::Typespecs::String; "stream.writeString(#{value})" - when AST::Typespecs::Simple ; + when AST::Typespecs::Simple; "#{value}.encode(stream)" - when AST::Concerns::NestedDefinition ; + when AST::Concerns::NestedDefinition; "#{value}.encode(stream)" else raise "Unknown typespec: #{type.class.name}" @@ -648,7 +743,7 @@ def encode_type(type, value) def decode_member(member, out, final) case member.declaration - when AST::Declarations::Void ; + when AST::Declarations::Void; return end str = decode_decl (member.declaration) @@ -661,12 +756,12 @@ def decode_member(member, out, final) def decode_decl(decl) case decl - when AST::Declarations::Void ; + when AST::Declarations::Void; return - when AST::Declarations::Opaque ; + when AST::Declarations::Opaque; size = decl.fixed? ? decl.size : "stream.readInt" return "stream.readBytes(#{size})" - when AST::Declarations::Array ; + when AST::Declarations::Array; size = decl.fixed? ? decl.size : "stream.readInt" return "(0 until #{size}).map(_ => #{decode_type decl.type}).toArray" else @@ -676,27 +771,27 @@ def decode_decl(decl) def decode_type(type) case type - when AST::Typespecs::Int ; + when AST::Typespecs::Int; "stream.readInt" - when AST::Typespecs::UnsignedInt ; + when AST::Typespecs::UnsignedInt; "stream.readInt" - when AST::Typespecs::Hyper ; + when AST::Typespecs::Hyper; "stream.readLong" - when AST::Typespecs::UnsignedHyper ; + when AST::Typespecs::UnsignedHyper; "stream.readLong" - when AST::Typespecs::Float ; + when AST::Typespecs::Float; "stream.readFloat" - when AST::Typespecs::Double ; + when AST::Typespecs::Double; "stream.readDouble" - when AST::Typespecs::Quadruple ; + when AST::Typespecs::Quadruple; raise "cannot render quadruple in scala" - when AST::Typespecs::Bool ; + when AST::Typespecs::Bool; "stream.readInt == 1" - when AST::Typespecs::String ; + when AST::Typespecs::String; "stream.readString" - when AST::Typespecs::Simple ; + when AST::Typespecs::Simple; "#{name type.resolved_type}.decode(stream)" - when AST::Concerns::NestedDefinition ; + when AST::Concerns::NestedDefinition; "#{name type}.decode(stream)" else raise "Unknown typespec: #{type.class.name}" @@ -705,18 +800,18 @@ def decode_type(type) def decl_string(decl) case decl - when AST::Declarations::Opaque ; + when AST::Declarations::Opaque; "Array[Byte]" - when AST::Declarations::String ; + when AST::Declarations::String; "String" - when AST::Declarations::Array ; + when AST::Declarations::Array; "Array[#{type_string decl.type}]" - when AST::Declarations::Optional ; + when AST::Declarations::Optional; "Option[#{type_string(decl.type)}]" - when AST::Declarations::Simple ; + when AST::Declarations::Simple; type_string(decl.type) - when AST::Declarations::Void ; - "Unit" + when AST::Declarations::Void; + "Unit" else raise "Unknown declaration type: #{decl.class.name}" end @@ -724,27 +819,27 @@ def decl_string(decl) def type_string(type) case type - when AST::Typespecs::Int ; + when AST::Typespecs::Int; "Int" - when AST::Typespecs::UnsignedInt ; + when AST::Typespecs::UnsignedInt; "Int" - when AST::Typespecs::Hyper ; + when AST::Typespecs::Hyper; "Long" - when AST::Typespecs::UnsignedHyper ; + when AST::Typespecs::UnsignedHyper; "Long" - when AST::Typespecs::Float ; + when AST::Typespecs::Float; "Float" - when AST::Typespecs::Double ; + when AST::Typespecs::Double; "Double" - when AST::Typespecs::Quadruple ; + when AST::Typespecs::Quadruple; raise "cannot render quadruple in scala" - when AST::Typespecs::Bool ; + when AST::Typespecs::Bool; "Boolean" - when AST::Typespecs::Opaque ; + when AST::Typespecs::Opaque; "Array.ofDim[Byte](#{type.size})" - when AST::Typespecs::Simple ; + when AST::Typespecs::Simple; name type.resolved_type - when AST::Concerns::NestedDefinition ; + when AST::Concerns::NestedDefinition; # todo - in some circumstances a nested defn should reference the enum type also # "#{type.parent_defn.name}#{name type}" name type @@ -774,7 +869,7 @@ def sanitize(name) if name == "type" "`type`" else - name + name end end end diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index 0dc8969e1..0ba9ee11c 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -2,7 +2,7 @@ describe Xdrgen::Generators do languages = %w(ruby javascript go java scala) - focus_language = "" #"go" + focus_language = "scala" #"go" focus_basename = "" #"optional.x" generator_fixture_paths.each do |path| From 52ea349267b899e315956735b1d8e57922671faf Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sat, 15 Dec 2018 16:27:51 +1000 Subject: [PATCH 07/20] encode for enum unions --- lib/xdrgen/generators/scala.rb | 45 ++++++---------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index baa09cf5b..efb89d2c8 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -133,7 +133,7 @@ def write_package_class(constants) def render_enum(out, e) render_source_comment out, e.element out.puts <<~EOS.strip_heredoc - sealed class #{e.name}(val i: Int) { + sealed abstract class #{e.name}(val i: Int) { def encode(stream: XdrDataOutputStream) = stream.writeInt(i) } @@ -228,11 +228,10 @@ def match_case(kase, arm, union) }.join(" ") "(#{decoded_args})" else - arm.declaration.name + "(#{decode_decl arm.declaration})" end end - "case #{case_match} => #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params}" else "case #{kase.value.value} => #{union_name}#{kase.value.value}" @@ -261,7 +260,7 @@ def match_case(kase, arm, union) }.join(" ") "(#{decoded_args})" else - "()" + "(#{arm.declaration.name}: #{decl_string arm.declaration})" end end @@ -273,7 +272,7 @@ def match_case(kase, arm, union) when Xdrgen::AST::Definitions::NestedStruct; "\n " + arm.declaration.type_s.members.map(&method(:encode_member_string)).join("\n ") else - "" + "\n #{encode_decl_string(arm.declaration, arm.declaration.name, arm.declaration.type.sub_type == :optional)}" end end @@ -287,7 +286,7 @@ def encode(stream: XdrDataOutputStream): Unit = { else <<~EOS.strip_heredoc case object #{union_name}#{kase.value.value} extends #{union_name} { - // todo + todo } EOS end @@ -559,36 +558,6 @@ def render_nested_definitions(defn, out) } end - def render_enum_old(enum, out) - name = name_string enum.name - out.puts <<-EOS.strip_heredoc - sealed class #{name} (val i: Int) { - def encode(stream: XdrDataOutputStream) = stream.writeInt(i) - } - - object #{name} { - def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { - EOS - out.indent do - out.indent do - enum.members.each do |em| - out.puts("case #{em.value} => #{em.name.downcase.camelize}") - end - out.puts("case i => throw new IllegalArgumentException(s\"#{name} value $i is invalid\")") - end - out.puts "}" - out.puts "" - enum.members.each do |em| - out.puts <<-EOS.strip_heredoc - case object #{em.name.downcase.camelize} extends #{name}(#{em.value}) - EOS - end - - render_nested_definitions enum, out - end - out.puts "}" - end - def render_struct(out, struct, superclass = nil) name = name_string struct.name.camelize out.puts "case class #{name} (" @@ -646,7 +615,7 @@ def inner(decl, name) when AST::Declarations::Array; <<~EOS.strip_heredoc #{"stream.writeInt(#{name}.length)" unless decl.fixed?} - #{name}.foreach { #{encode_type decl.type, "_"} } + #{name}.foreach(#{encode_type decl.type, "_"}) EOS else @@ -706,7 +675,7 @@ def encode_decl_inner(decl, name, out) unless decl.fixed? out.puts "stream.writeInt(#{name}.length)" end - out.puts "#{name}.foreach { #{encode_type decl.type, "_"} }" + out.puts "#{name}.foreach(#{encode_type decl.type, "_"})" else out.puts "#{encode_type decl.type, "#{name}"}" end From 100631e01adee397da0aa62bb57fc0bb7b8a5248 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sat, 15 Dec 2018 16:44:41 +1000 Subject: [PATCH 08/20] encode for int unions --- lib/xdrgen/generators/scala.rb | 96 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index efb89d2c8..44ea6706c 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -212,29 +212,28 @@ def match_arm(arm, union) def match_case(kase, arm, union) union_name = "#{name_string union.name}" + constructor_params = + if is_void?(arm.declaration) + "" + else # simple + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + member_count = arm.declaration.type_s.members.size + decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| + decode_member_string(m, i == member_count) + }.join(" ") + "(#{decoded_args})" + else + "(#{decode_decl arm.declaration})" + end + end + case kase.value when Xdrgen::AST::Identifier; case_match = "#{union.discriminant.type.name}.#{kase.value.name}" - - constructor_params = - if is_void?(arm.declaration) - "" - else # simple - case arm.declaration.type_s - when Xdrgen::AST::Definitions::NestedStruct; - member_count = arm.declaration.type_s.members.size - decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| - decode_member_string(m, i == member_count) - }.join(" ") - "(#{decoded_args})" - else - "(#{decode_decl arm.declaration})" - end - end - "case #{case_match} => #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params}" else - "case #{kase.value.value} => #{union_name}#{kase.value.value}" + "case #{kase.value.value} => #{union_name}#{kase.value.value}#{constructor_params}" end end @@ -244,38 +243,37 @@ def match_case(kase, arm, union) def class_arm(arm, union) def match_case(kase, arm, union) union_name = "#{name_string union.name}" - - case kase.value - when Xdrgen::AST::Identifier; - class_or_object = is_void?(arm.declaration) ? "object" : "class" - constructor_params = - if is_void?(arm.declaration) - "" - else # simple - case arm.declaration.type_s - when Xdrgen::AST::Definitions::NestedStruct; - member_count = arm.declaration.type_s.members.size - decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| - "#{m.name}: #{decl_string m.declaration}#{(i==member_count) ? "" : ","}" - }.join(" ") - "(#{decoded_args})" - else - "(#{arm.declaration.name}: #{decl_string arm.declaration})" - end + class_or_object = is_void?(arm.declaration) ? "object" : "class" + constructor_params = + if is_void?(arm.declaration) + "" + else # simple + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + member_count = arm.declaration.type_s.members.size + decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| + "#{m.name}: #{decl_string m.declaration}#{(i==member_count) ? "" : ","}" + }.join(" ") + "(#{decoded_args})" + else + "(#{arm.declaration.name}: #{decl_string arm.declaration})" end + end - member_encode_statements = - if is_void?(arm.declaration) - "" - else # simple - case arm.declaration.type_s - when Xdrgen::AST::Definitions::NestedStruct; - "\n " + arm.declaration.type_s.members.map(&method(:encode_member_string)).join("\n ") - else - "\n #{encode_decl_string(arm.declaration, arm.declaration.name, arm.declaration.type.sub_type == :optional)}" - end + member_encode_statements = + if is_void?(arm.declaration) + "" + else # simple + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + "\n " + arm.declaration.type_s.members.map(&method(:encode_member_string)).join("\n ") + else + "\n #{encode_decl_string(arm.declaration, arm.declaration.name, arm.declaration.type.sub_type == :optional)}" end + end + case kase.value + when Xdrgen::AST::Identifier; <<~EOS.strip_heredoc case #{class_or_object} #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params} extends #{union_name} { def encode(stream: XdrDataOutputStream): Unit = { @@ -285,8 +283,10 @@ def encode(stream: XdrDataOutputStream): Unit = { EOS else <<~EOS.strip_heredoc - case object #{union_name}#{kase.value.value} extends #{union_name} { - todo + case #{class_or_object} #{union_name}#{kase.value.value}#{constructor_params} extends #{union_name} { + def encode(stream: XdrDataOutputStream): Unit = { + stream.writeInt(#{kase.value.value})#{member_encode_statements} + } } EOS end From d05249da44d50aaad1a864e14764fa0dfa939853 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sat, 15 Dec 2018 16:54:10 +1000 Subject: [PATCH 09/20] add source comment for constants; --- lib/xdrgen/generators/scala.rb | 48 +++++++++++-------------------- spec/lib/xdrgen/generator_spec.rb | 2 +- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 44ea6706c..694cc4a58 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -14,11 +14,12 @@ def initialize(name, members, element) end class Constant - attr_accessor :name, :value + attr_accessor :name, :value, :element - def initialize(name, value) + def initialize(name, value, element) @name = name @value = value + @element = element end end @@ -33,14 +34,6 @@ def initialize(name, declaration, element, is_optional = false) end end - # class Union - # attr_accessor :name - # - # def initialize(name) - # @name = name - # end - # end - def generate enums = parse_enums @top constants = parse_constants @top @@ -68,7 +61,7 @@ def parse_enums(ns) def parse_constants(ns) ns.definitions.select {|d| d.is_a? AST::Definitions::Const}.map do |c| - Constant.new(c.name, c.value) + Constant.new(c.name, c.value, c) end end @@ -123,11 +116,19 @@ def write_package_class(constants) path = "#{@namespace.downcase}/#{@namespace.downcase}.scala" out = @output.open(path) render_top_matter out - out.puts <<-EOS.strip_heredoc - package object #{@namespace.downcase} { - #{constants.map {|c| "val #{c.name} = #{c.value}"}.join("\n ")} - } - EOS + out.puts "package object #{@namespace.downcase} {" + constants.each do |c| + out.indent(1) do + out.puts <<~EOS.strip_heredoc + + /* + #{c.element.text_value.split("\n").join("\n ")} + */ + val #{c.name} = #{c.value} + EOS + end + end + out.puts "}" end def render_enum(out, e) @@ -511,21 +512,6 @@ def render_definition(defn) end end - # def render_package_file - # path = "#{@namespace.downcase}/#{@namespace.downcase}.scala" - # out = @output.open(path) - # out.puts <<-EOS.strip_heredoc - # // Automatically generated by xdrgen - # // DO NOT EDIT or your changes may be overwritten - # - # package object #{@namespace.downcase} { - # EOS - # - # yield out - # - # out.puts("}") - # end - def render_file(element) path = "#{@namespace.downcase}/#{element.name.camelize}.scala" out = @output.open(path) diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index 0ba9ee11c..0dc8969e1 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -2,7 +2,7 @@ describe Xdrgen::Generators do languages = %w(ruby javascript go java scala) - focus_language = "scala" #"go" + focus_language = "" #"go" focus_basename = "" #"optional.x" generator_fixture_paths.each do |path| From ab9e53a034b9cef4cc54786052549788873899f9 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sat, 15 Dec 2018 20:35:06 +1000 Subject: [PATCH 10/20] source comment for structs --- lib/xdrgen/generators/scala.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 694cc4a58..8993c25c9 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -545,6 +545,7 @@ def render_nested_definitions(defn, out) end def render_struct(out, struct, superclass = nil) + render_source_comment out, struct name = name_string struct.name.camelize out.puts "case class #{name} (" out.indent do @@ -619,7 +620,8 @@ def inner(decl, name) case Some(x) => stream.writeInt(1) #{inner(decl, "x")} - case None => stream.writeInt(0) + case None => + stream.writeInt(0) } EOS else @@ -642,7 +644,10 @@ def encode_decl(decl, name, is_option, out) out.puts "stream.writeInt(1)" encode_decl_inner(decl, "x", out) end - out.puts "case None => stream.writeInt(0)" + out.puts "case None =>" + out.indent do + out.puts "stream.writeInt(0)" + end end out.puts "}" else From 3dcd1589ca7eae43298780e02d43f6ff4def366f Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 10:06:22 +1000 Subject: [PATCH 11/20] add struct default decode --- lib/xdrgen/generators/scala.rb | 162 +++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 70 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 8993c25c9..fa4288ac6 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -35,27 +35,62 @@ def initialize(name, declaration, element, is_optional = false) end def generate - enums = parse_enums @top - constants = parse_constants @top - typedefs = parse_typedefs @top - unions = parse_unions @top - structs = parse_structs @top - render_lib + generate_for @top + end + + def generate_for(node) + enums = parse_enums node + constants = parse_constants node + typedefs = parse_typedefs node + unions = parse_unions node + structs = parse_structs node + write_package_class constants enums.each(&method(:write_enum_file)) typedefs.each(&method(:write_typedef_file)) unions.each(&method(:write_union_file)) structs.each(&method(:write_struct_file)) + + node.namespaces.each(&method(:generate_for)) + end + + # def render_definitions(node) + # node.namespaces.each {|n| render_definitions n} + # node.definitions.each(&method(:render_definition)) + # end + + # def render_definition(defn) + # case defn + # when AST::Definitions::Struct; + # render_file defn do |out| + # render_struct defn, out + # end + # when AST::Definitions::Enum; + # render_file defn do |out| + # render_enum defn, out + # end + # when AST::Definitions::Union; + # render_file defn do |out| + # render_union defn, out + # end + # when AST::Definitions::Typedef; + # render_file defn do |out| + # render_typedef defn, out + # end + # end + # end + + def enum_members(e) + hash = e.members.reduce({}) do |hash, member| + hash.store(member.name, member.value) + hash + end end def parse_enums(ns) ns.definitions.select {|d| d.is_a? AST::Definitions::Enum}.map do |e| - hash = e.members.reduce({}) do |hash, member| - hash.store(member.name, member.value) - hash - end - Enum.new(name(e), hash, e) + Enum.new(name(e), enum_members(e), e) end end @@ -81,7 +116,7 @@ def parse_structs(ns) def write_enum_file(e) write_class_file e.name do |out| - render_enum out, e + render_enum out, e.element, e.name, e.members end end @@ -131,20 +166,20 @@ def write_package_class(constants) out.puts "}" end - def render_enum(out, e) - render_source_comment out, e.element + def render_enum(out, enum, name, members) + render_source_comment out, enum out.puts <<~EOS.strip_heredoc - sealed abstract class #{e.name}(val i: Int) { + sealed abstract class #{name}(val i: Int) { def encode(stream: XdrDataOutputStream) = stream.writeInt(i) } - object #{e.name} { - def decode(stream: XdrDataInputStream): #{e.name} = stream.readInt() match { - #{e.members.map {|k, v| "case #{v} => #{k}"}.join("\n ")} - case i => throw new IllegalArgumentException(s"#{e.name} value $i is invalid") + object #{name} { + def decode(stream: XdrDataInputStream): #{name} = stream.readInt() match { + #{members.map {|k, v| "case #{v} => #{k}"}.join("\n ")} + case i => throw new IllegalArgumentException(s"#{name} value $i is invalid") } - #{e.members.map {|k, v| "case object #{k} extends #{e.name}(#{v})"}.join("\n ")} + #{members.map {|k, v| "case object #{k} extends #{name}(#{v})"}.join("\n ")} } EOS end @@ -210,35 +245,43 @@ def encode_member_string(member) def match_arm(arm, union) + def constructor_params(arm) + if is_void?(arm.declaration) + "" + else # simple + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + member_count = arm.declaration.type_s.members.size + decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| + decode_member_string(m, i == member_count) + }.join(" ") + "(#{decoded_args})" + else + "(#{decode_decl arm.declaration})" + end + end + end + def match_case(kase, arm, union) union_name = "#{name_string union.name}" - constructor_params = - if is_void?(arm.declaration) - "" - else # simple - case arm.declaration.type_s - when Xdrgen::AST::Definitions::NestedStruct; - member_count = arm.declaration.type_s.members.size - decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| - decode_member_string(m, i == member_count) - }.join(" ") - "(#{decoded_args})" - else - "(#{decode_decl arm.declaration})" - end - end - case kase.value when Xdrgen::AST::Identifier; case_match = "#{union.discriminant.type.name}.#{kase.value.name}" - "case #{case_match} => #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params}" + "case #{case_match} => #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params(arm)}" else - "case #{kase.value.value} => #{union_name}#{kase.value.value}#{constructor_params}" + "case #{kase.value.value} => #{union_name}#{kase.value.value}#{constructor_params(arm)}" end end - arm.cases.map {|c| match_case(c, arm, union)}.join("\n") + case arm + when Xdrgen::AST::Definitions::UnionDefaultArm; + # todo - here how to get the constructor params? Perhaps make a fake case? + # todo also - need to define the class + "case _ => #{name_string union.name}Default#{constructor_params(arm)}" + else + arm.cases.map {|c| match_case(c, arm, union)}.join("\n") + end end def class_arm(arm, union) @@ -293,7 +336,12 @@ def encode(stream: XdrDataOutputStream): Unit = { end end - arm.cases.map {|c| match_case(c, arm, union)}.join("\n") + case arm + when Xdrgen::AST::Definitions::UnionDefaultArm; + "sdf" + else + arm.cases.map {|c| match_case(c, arm, union)}.join("\n") + end end def render_union(out, union) @@ -486,32 +534,6 @@ def encode(stream: XdrDataOutputStream) = Unit # end # end - def render_definitions(node) - node.namespaces.each {|n| render_definitions n} - node.definitions.each(&method(:render_definition)) - end - - def render_definition(defn) - case defn - when AST::Definitions::Struct; - render_file defn do |out| - render_struct defn, out - end - when AST::Definitions::Enum; - render_file defn do |out| - render_enum defn, out - end - when AST::Definitions::Union; - render_file defn do |out| - render_union defn, out - end - when AST::Definitions::Typedef; - render_file defn do |out| - render_typedef defn, out - end - end - end - def render_file(element) path = "#{@namespace.downcase}/#{element.name.camelize}.scala" out = @output.open(path) @@ -530,16 +552,16 @@ def render_nested_definitions(defn, out) case ndefn when AST::Definitions::Struct; name = name ndefn - render_struct ndefn, out + render_struct out, ndefn when AST::Definitions::Enum; name = name ndefn - render_enum ndefn, out + render_enum out, ndefn, name, enum_members(ndefn) when AST::Definitions::Union; name = name ndefn - render_union ndefn, out + render_union out, ndefn when AST::Definitions::Typedef; name = name ndefn - render_typedef ndefn, out + render_typedef out, ndefn end } end From 88e6fb5a33c9d2bf27cbfb88ce35ac1993863279 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 10:42:18 +1000 Subject: [PATCH 12/20] add discriminant to constructor of default switch case --- lib/xdrgen/generators/scala.rb | 67 ++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index fa4288ac6..4e36354b7 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -245,7 +245,7 @@ def encode_member_string(member) def match_arm(arm, union) - def constructor_params(arm) + def constructor_params_construct(arm, default) if is_void?(arm.declaration) "" else # simple @@ -255,9 +255,9 @@ def constructor_params(arm) decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| decode_member_string(m, i == member_count) }.join(" ") - "(#{decoded_args})" + "(#{default ? "discriminant, " : ""}#{decoded_args})" else - "(#{decode_decl arm.declaration})" + "(#{default ? "discriminant, " : ""}#{decode_decl arm.declaration})" end end end @@ -268,41 +268,47 @@ def match_case(kase, arm, union) case kase.value when Xdrgen::AST::Identifier; case_match = "#{union.discriminant.type.name}.#{kase.value.name}" - "case #{case_match} => #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params(arm)}" + "case #{case_match} => #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params_construct(arm, false)}" else - "case #{kase.value.value} => #{union_name}#{kase.value.value}#{constructor_params(arm)}" + "case #{kase.value.value} => #{union_name}#{kase.value.value}#{constructor_params_construct(arm, false)}" end end case arm when Xdrgen::AST::Definitions::UnionDefaultArm; - # todo - here how to get the constructor params? Perhaps make a fake case? - # todo also - need to define the class - "case _ => #{name_string union.name}Default#{constructor_params(arm)}" + "case discriminant => #{name_string union.name}Default#{constructor_params_construct(arm, true)}" else arm.cases.map {|c| match_case(c, arm, union)}.join("\n") end end def class_arm(arm, union) + + def constructor_params_call(arm, union, default) + if is_void?(arm.declaration) + "" + else # simple + discriminant_type = union.discriminant.type_s.is_a?(AST::Identifier) ? union.discriminant.type.name : "Int" + discriminant_param = default ? "discriminant: #{discriminant_type}, " : "" + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + member_count = arm.declaration.type_s.members.size + decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| + "#{m.name}: #{decl_string m.declaration}#{(i==member_count) ? "" : ","}" + }.join(" ") + "(#{discriminant_param}#{decoded_args})" + else + "(#{discriminant_param}#{arm.declaration.name}: #{decl_string arm.declaration})" + end + end + end + + def class_or_object(arm) + is_void?(arm.declaration) ? "object" : "class" + end + def match_case(kase, arm, union) union_name = "#{name_string union.name}" - class_or_object = is_void?(arm.declaration) ? "object" : "class" - constructor_params = - if is_void?(arm.declaration) - "" - else # simple - case arm.declaration.type_s - when Xdrgen::AST::Definitions::NestedStruct; - member_count = arm.declaration.type_s.members.size - decoded_args = arm.declaration.type_s.members.map.with_index(1) {|m,i| - "#{m.name}: #{decl_string m.declaration}#{(i==member_count) ? "" : ","}" - }.join(" ") - "(#{decoded_args})" - else - "(#{arm.declaration.name}: #{decl_string arm.declaration})" - end - end member_encode_statements = if is_void?(arm.declaration) @@ -319,7 +325,7 @@ def match_case(kase, arm, union) case kase.value when Xdrgen::AST::Identifier; <<~EOS.strip_heredoc - case #{class_or_object} #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params} extends #{union_name} { + case #{class_or_object(arm)} #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params_call(arm, union, false)} extends #{union_name} { def encode(stream: XdrDataOutputStream): Unit = { #{union.discriminant.type.name}.#{kase.value.name}.encode(stream)#{member_encode_statements} } @@ -327,7 +333,7 @@ def encode(stream: XdrDataOutputStream): Unit = { EOS else <<~EOS.strip_heredoc - case #{class_or_object} #{union_name}#{kase.value.value}#{constructor_params} extends #{union_name} { + case #{class_or_object(arm)} #{union_name}#{kase.value.value}#{constructor_params_call(arm, union, false)} extends #{union_name} { def encode(stream: XdrDataOutputStream): Unit = { stream.writeInt(#{kase.value.value})#{member_encode_statements} } @@ -338,7 +344,14 @@ def encode(stream: XdrDataOutputStream): Unit = { case arm when Xdrgen::AST::Definitions::UnionDefaultArm; - "sdf" + union_name = "#{name_string union.name}" + <<~EOS.strip_heredoc + case #{class_or_object(arm)} #{union_name}Default#{constructor_params_call(arm, union, true)} extends #{union_name} { + def encode(stream: XdrDataOutputStream): Unit = { + stream.({kase.value.value}){member_encode_statements} + } + } + EOS else arm.cases.map {|c| match_case(c, arm, union)}.join("\n") end From 9b2c892d2dc84c6eba8ee4a9683e34b21d3115e5 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 10:47:20 +1000 Subject: [PATCH 13/20] add encode for default switch value --- lib/xdrgen/generators/scala.rb | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 4e36354b7..789f647de 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -307,27 +307,28 @@ def class_or_object(arm) is_void?(arm.declaration) ? "object" : "class" end + def member_encode_statements(arm) + if is_void?(arm.declaration) + "" + else # simple + case arm.declaration.type_s + when Xdrgen::AST::Definitions::NestedStruct; + "\n " + arm.declaration.type_s.members.map(&method(:encode_member_string)).join("\n ") + else + "\n #{encode_decl_string(arm.declaration, arm.declaration.name, arm.declaration.type.sub_type == :optional)}" + end + end + end + def match_case(kase, arm, union) union_name = "#{name_string union.name}" - member_encode_statements = - if is_void?(arm.declaration) - "" - else # simple - case arm.declaration.type_s - when Xdrgen::AST::Definitions::NestedStruct; - "\n " + arm.declaration.type_s.members.map(&method(:encode_member_string)).join("\n ") - else - "\n #{encode_decl_string(arm.declaration, arm.declaration.name, arm.declaration.type.sub_type == :optional)}" - end - end - case kase.value when Xdrgen::AST::Identifier; <<~EOS.strip_heredoc case #{class_or_object(arm)} #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params_call(arm, union, false)} extends #{union_name} { def encode(stream: XdrDataOutputStream): Unit = { - #{union.discriminant.type.name}.#{kase.value.name}.encode(stream)#{member_encode_statements} + #{union.discriminant.type.name}.#{kase.value.name}.encode(stream)#{member_encode_statements(arm)} } } EOS @@ -335,7 +336,7 @@ def encode(stream: XdrDataOutputStream): Unit = { <<~EOS.strip_heredoc case #{class_or_object(arm)} #{union_name}#{kase.value.value}#{constructor_params_call(arm, union, false)} extends #{union_name} { def encode(stream: XdrDataOutputStream): Unit = { - stream.writeInt(#{kase.value.value})#{member_encode_statements} + stream.writeInt(#{kase.value.value})#{member_encode_statements(arm)} } } EOS @@ -348,7 +349,7 @@ def encode(stream: XdrDataOutputStream): Unit = { <<~EOS.strip_heredoc case #{class_or_object(arm)} #{union_name}Default#{constructor_params_call(arm, union, true)} extends #{union_name} { def encode(stream: XdrDataOutputStream): Unit = { - stream.({kase.value.value}){member_encode_statements} + #{encode_decl_string union.discriminant, "discriminant", false}#{member_encode_statements(arm)} } } EOS From ed65f526c26c6a87772beace803aa3b2b9b209e1 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 11:27:55 +1000 Subject: [PATCH 14/20] add void default and non-void default variants to union test --- spec/fixtures/generator/union.x | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/fixtures/generator/union.x b/spec/fixtures/generator/union.x index 6c02ba621..c7b12110a 100644 --- a/spec/fixtures/generator/union.x +++ b/spec/fixtures/generator/union.x @@ -26,3 +26,19 @@ union IntUnion switch (int type) }; typedef IntUnion IntUnion2; + +union VoidDefaultUnion switch (int type) +{ + case 0: + int anInt; + default: + void; +}; + +union SomeDefaultUnion switch (int type) +{ + case 0: + int anInt; + default: + string str<64>; +}; From 11c01c512e9b48b43f7b2bc8d4dc23c417ba97b8 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 11:39:33 +1000 Subject: [PATCH 15/20] support void default decoding --- lib/xdrgen/generators/scala.rb | 11 ++++++++--- spec/lib/xdrgen/generator_spec.rb | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 789f647de..5c0e63bd4 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -246,7 +246,9 @@ def encode_member_string(member) def match_arm(arm, union) def constructor_params_construct(arm, default) - if is_void?(arm.declaration) + if is_void?(arm.declaration) && default + "(discriminant)" + elsif is_void?(arm.declaration) "" else # simple case arm.declaration.type_s @@ -285,7 +287,10 @@ def match_case(kase, arm, union) def class_arm(arm, union) def constructor_params_call(arm, union, default) - if is_void?(arm.declaration) + if is_void?(arm.declaration) && default + discriminant_type = union.discriminant.type_s.is_a?(AST::Identifier) ? union.discriminant.type.name : "Int" + "(discriminant: #{discriminant_type})" + elsif is_void?(arm.declaration) "" else # simple discriminant_type = union.discriminant.type_s.is_a?(AST::Identifier) ? union.discriminant.type.name : "Int" @@ -347,7 +352,7 @@ def encode(stream: XdrDataOutputStream): Unit = { when Xdrgen::AST::Definitions::UnionDefaultArm; union_name = "#{name_string union.name}" <<~EOS.strip_heredoc - case #{class_or_object(arm)} #{union_name}Default#{constructor_params_call(arm, union, true)} extends #{union_name} { + case class #{union_name}Default#{constructor_params_call(arm, union, true)} extends #{union_name} { def encode(stream: XdrDataOutputStream): Unit = { #{encode_decl_string union.discriminant, "discriminant", false}#{member_encode_statements(arm)} } diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index 0dc8969e1..1b91b7978 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -2,8 +2,8 @@ describe Xdrgen::Generators do languages = %w(ruby javascript go java scala) - focus_language = "" #"go" - focus_basename = "" #"optional.x" + focus_language = "scala" #"go" + focus_basename = "union.x" #"optional.x" generator_fixture_paths.each do |path| languages.each do |lang| From cb222cdf8032684e6810097e7462736ee3a001f4 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 12:15:46 +1000 Subject: [PATCH 16/20] render union decode case when discriminant is type alias for an int --- lib/xdrgen/generators/scala.rb | 102 ++---------------------------- spec/fixtures/generator/test.x | 2 - spec/fixtures/generator/union.x | 8 +++ spec/lib/xdrgen/generator_spec.rb | 4 +- 4 files changed, 17 insertions(+), 99 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 5c0e63bd4..d9f8e7349 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -55,32 +55,6 @@ def generate_for(node) node.namespaces.each(&method(:generate_for)) end - # def render_definitions(node) - # node.namespaces.each {|n| render_definitions n} - # node.definitions.each(&method(:render_definition)) - # end - - # def render_definition(defn) - # case defn - # when AST::Definitions::Struct; - # render_file defn do |out| - # render_struct defn, out - # end - # when AST::Definitions::Enum; - # render_file defn do |out| - # render_enum defn, out - # end - # when AST::Definitions::Union; - # render_file defn do |out| - # render_union defn, out - # end - # when AST::Definitions::Typedef; - # render_file defn do |out| - # render_typedef defn, out - # end - # end - # end - def enum_members(e) hash = e.members.reduce({}) do |hash, member| hash.store(member.name, member.value) @@ -211,7 +185,7 @@ def decode(stream: XdrDataInputStream): #{td.name} = #{td.name}(#{decode_stateme def decode_discriminant(disc) case disc.type_s when Xdrgen::AST::Identifier; - "#{disc.type.name}.decode(stream)" + "#{name disc.type}.decode(stream)" else "stream.readInt()" end @@ -272,7 +246,12 @@ def match_case(kase, arm, union) case_match = "#{union.discriminant.type.name}.#{kase.value.name}" "case #{case_match} => #{union_name}#{kase.value.name.downcase.camelize}#{constructor_params_construct(arm, false)}" else - "case #{kase.value.value} => #{union_name}#{kase.value.value}#{constructor_params_construct(arm, false)}" + case union.discriminant.type + when Xdrgen::AST::Identifier; + "case #{name union.discriminant.type}(#{kase.value.value}) => #{union_name}#{kase.value.value}#{constructor_params_construct(arm, false)}" + else + "case #{kase.value.value} => #{union_name}#{kase.value.value}#{constructor_params_construct(arm, false)}" + end end end @@ -386,73 +365,6 @@ def encode(stream: XdrDataOutputStream): Unit union.arms.map{|arm| class_arm(arm, union)}.each {|s| out.puts(s) } end - def render_union_start_again(out, union) - render_source_comment out, union - name = name_string union.name - - def render_arms(union, name) - union.arms.each do |arm| - if arm.is_a? AST::Definitions::UnionDefaultArm - yield arm.declaration, "d", "Default", true - else - arm.cases.each do |kase| - dtype = type_string union.discriminant.type - ktype = - if kase.value.is_a?(AST::Identifier) - "#{dtype}.#{name_string kase.value.name}" - elsif union.discriminant.type.is_a?(AST::Typespecs::Simple) - "#{dtype}(#{kase.value.value})" - else - "#{kase.value.value}" - end - rtype = - if kase.value.is_a?(AST::Identifier) - if is_void?(arm.declaration) - "#{name}#{name_string kase.value.name.downcase}" - elsif arm.declaration.type.sub_type == :simple - "#{name}#{arm.declaration.type.name.camelize}" - elsif arm.declaration.type.sub_type == :optional - "#{name}#{arm.declaration.type.name.camelize}Opt" - else - "#{name}#{arm.declaration.type.name.camelize}Array" - end - else - "#{name}#{kase.value.value}" - end - yield arm.declaration, dtype, ktype, rtype - end - end - end - end - - out.puts <<-EOS.strip_heredoc - sealed trait #{name} { - def encode(stream: XdrDataOutputStream): Unit - } - EOS - - render_arms(union, name) do |decl, dtype, ktype, rtype| - out.puts decl_wrapper(rtype, dtype, name, decl) - end - - out.puts <<-EOS.strip_heredoc - - object #{name} { - def decode(stream: XdrDataInputStream): #{name} = #{decode_type union.discriminant.type} match { - EOS - out.indent(2) do - render_arms(union, name) do |decl, dtype, ktype, rtype| - decode = "#{decode_decl decl}" - out.puts "case #{ktype} => #{rtype}#{is_void?(decl) ? "" : "(#{decode})"}" - end - out.puts "case d => throw new IllegalArgumentException(s\"#{type_string union.discriminant.type} value $d is invalid\")" - end - out.puts <<-EOS.strip_heredoc - } - } - EOS - end - def render_top_matter(out) out.puts <<-EOS.strip_heredoc // Automatically generated by xdrgen diff --git a/spec/fixtures/generator/test.x b/spec/fixtures/generator/test.x index 8f78e84c5..1d8268368 100644 --- a/spec/fixtures/generator/test.x +++ b/spec/fixtures/generator/test.x @@ -70,8 +70,6 @@ struct Nester default: int blah2; } nestedUnion; - - }; } diff --git a/spec/fixtures/generator/union.x b/spec/fixtures/generator/union.x index c7b12110a..f71e4ac47 100644 --- a/spec/fixtures/generator/union.x +++ b/spec/fixtures/generator/union.x @@ -42,3 +42,11 @@ union SomeDefaultUnion switch (int type) default: string str<64>; }; + +union TypedIntDiscriminatedUnion switch (Error type) { + case 0: + void; + default: + string str<64>; +}; + diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index 1b91b7978..0dc8969e1 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -2,8 +2,8 @@ describe Xdrgen::Generators do languages = %w(ruby javascript go java scala) - focus_language = "scala" #"go" - focus_basename = "union.x" #"optional.x" + focus_language = "" #"go" + focus_basename = "" #"optional.x" generator_fixture_paths.each do |path| languages.each do |lang| From 93db0d519ff1f8a2a75c398899391c2d0bb0d51e Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 14:51:13 +1000 Subject: [PATCH 17/20] Union nested Union --- lib/xdrgen/generators/scala.rb | 16 ++-------------- spec/fixtures/generator/union.x | 16 +++++++++++++++- spec/lib/xdrgen/generator_spec.rb | 4 ++-- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index d9f8e7349..06acc5353 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -363,6 +363,8 @@ def encode(stream: XdrDataOutputStream): Unit EOS union.arms.map{|arm| class_arm(arm, union)}.each {|s| out.puts(s) } + + render_nested_definitions union, out end def render_top_matter(out) @@ -451,20 +453,6 @@ def encode(stream: XdrDataOutputStream) = Unit end end - # ---- - - - # def render_constants(node, out) - # node.namespaces.each{|n| render_constants(n, out) } - # node.definitions.select{|c| c.is_a? AST::Definitions::Const}.each { |c| render_const out, c } - # end - # - # def render_const(out, const) - # out.indent do - # out.puts "val #{const.name.downcase.camelize} = #{const.value.downcase.camelize}" - # end - # end - def render_file(element) path = "#{@namespace.downcase}/#{element.name.camelize}.scala" out = @output.open(path) diff --git a/spec/fixtures/generator/union.x b/spec/fixtures/generator/union.x index f71e4ac47..2b551c22e 100644 --- a/spec/fixtures/generator/union.x +++ b/spec/fixtures/generator/union.x @@ -43,10 +43,24 @@ union SomeDefaultUnion switch (int type) string str<64>; }; -union TypedIntDiscriminatedUnion switch (Error type) { +union TypedIntDiscriminatedUnion switch (Error type) +{ case 0: void; default: string str<64>; }; +union OuterUnion switch (int type) +{ + case 0: + union switch (int type) + { + case 0: + int anInt; + default: + void; + } inner; + default: + void; +}; diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index 0dc8969e1..1b91b7978 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -2,8 +2,8 @@ describe Xdrgen::Generators do languages = %w(ruby javascript go java scala) - focus_language = "" #"go" - focus_basename = "" #"optional.x" + focus_language = "scala" #"go" + focus_basename = "union.x" #"optional.x" generator_fixture_paths.each do |path| languages.each do |lang| From 7a15c74df719f8c5804f77f135d796b64e862aa3 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 15:06:42 +1000 Subject: [PATCH 18/20] support incomplete unions with no default --- lib/xdrgen/generators/scala.rb | 8 +++++++- spec/fixtures/generator/union.x | 16 ++++++++++++++++ spec/lib/xdrgen/generator_spec.rb | 4 ++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 06acc5353..7a14ac15d 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -347,10 +347,16 @@ def render_union(out, union) render_source_comment out, union + fallback_default = + if union.arms.any?{|a| a.is_a?(Xdrgen::AST::Definitions::UnionDefaultArm) } + "" + else + "\n case x => throw new IllegalArgumentException(s\"#{name union.discriminant} value $x cannot be decoded\")" + end out.puts <<-EOS.strip_heredoc object #{union_name} { def decode(stream: XdrDataInputStream): #{union_name} = #{decode_discriminant union.discriminant} match { - #{union.arms.map {|a| match_arm(a, union)}.join("\n ")} + #{union.arms.map {|a| match_arm(a, union)}.join("\n ")}#{fallback_default} } } diff --git a/spec/fixtures/generator/union.x b/spec/fixtures/generator/union.x index 2b551c22e..41704f5b0 100644 --- a/spec/fixtures/generator/union.x +++ b/spec/fixtures/generator/union.x @@ -64,3 +64,19 @@ union OuterUnion switch (int type) default: void; }; + +enum Dog { + AKITA=0, + CORGI=1, + DACHSHUND=2, + DALMATION=3 +}; + +union IncompleteUnion switch (Dog dog) +{ + case AKITA: + int anInt; + case DACHSHUND: + int anInt; + /* everything else is invalid */ +}; \ No newline at end of file diff --git a/spec/lib/xdrgen/generator_spec.rb b/spec/lib/xdrgen/generator_spec.rb index 1b91b7978..0dc8969e1 100644 --- a/spec/lib/xdrgen/generator_spec.rb +++ b/spec/lib/xdrgen/generator_spec.rb @@ -2,8 +2,8 @@ describe Xdrgen::Generators do languages = %w(ruby javascript go java scala) - focus_language = "scala" #"go" - focus_basename = "union.x" #"optional.x" + focus_language = "" #"go" + focus_basename = "" #"optional.x" generator_fixture_paths.each do |path| languages.each do |lang| From 9ac16e054dfe756d40308465c835724f2ba19ccb Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Sun, 16 Dec 2018 15:18:38 +1000 Subject: [PATCH 19/20] remove redundant methods --- lib/xdrgen/generators/scala.rb | 73 ---------------------------------- 1 file changed, 73 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index 7a14ac15d..f8afade39 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -191,15 +191,6 @@ def decode_discriminant(disc) end end - # def as_arguments(decl) - # if is_void?(decl) - # "" - # else - # decl_string(decl) - # end - # end - # - def decode_member_string(member, final) case member.declaration when AST::Declarations::Void; @@ -217,7 +208,6 @@ def encode_member_string(member) encode_decl_string(member.declaration, member.declaration.name, member.type.sub_type == :optional) end - def match_arm(arm, union) def constructor_params_construct(arm, default) if is_void?(arm.declaration) && default @@ -405,69 +395,6 @@ def render_lib @output.write "#{@namespace.downcase}/XdrDataOutputStream.scala", result end - def decl_wrapper(name, discriminator_name, super_name, decl) - case decl - when AST::Declarations::Opaque; - <<-EOS.strip_heredoc - case class #{name}(bs: Array[Byte]) extends #{super_name} { - def encode(stream: XdrDataOutputStream) = { - stream.writeInt(bs.length) - stream.write(bs, 0, bs.length) - } - } - EOS - when AST::Declarations::String; - <<-EOS.strip_heredoc - case class #{name}(s: String) extends #{super_name} { - def encode(stream: XdrDataOutputStream) = stream.writeString(s) - } - EOS - when AST::Declarations::Array; - <<-EOS.strip_heredoc - case class #{name}(xs: Array[#{type_string decl.type}]) extends #{super_name} { - def encode(stream: XdrDataOutputStream) = { - stream.writeInt(xs.length) - xs.foreach(_.encode(stream)) - } - } - EOS - when AST::Declarations::Optional; - <<-EOS.strip_heredoc - case class #{name}(m: Option[#{discriminator_name}]) extends #{super_name} { - def encode(stream: XdrDataOutputStream) = m match { - case None => stream.writeInt(0) - case Some(x) => - stream.writeInt(1) - x.encode(stream) - } - } - EOS - when AST::Declarations::Simple; - <<-EOS.strip_heredoc - case class #{name}(x: #{discriminator_name}) extends #{super_name} { - def encode(stream: XdrDataOutputStream) = x.encode(stream) - } - EOS - when AST::Declarations::Void; - <<-EOS.strip_heredoc - case object #{name} extends #{super_name} { - def encode(stream: XdrDataOutputStream) = Unit - } - EOS - else - raise "Unknown declaration type: #{decl.class.name}" - end - end - - def render_file(element) - path = "#{@namespace.downcase}/#{element.name.camelize}.scala" - out = @output.open(path) - render_top_matter out - render_source_comment out, element - - yield out - end - def render_nested_definitions(defn, out) return unless defn.respond_to? :nested_definitions unless defn.nested_definitions.empty? From 350aa06d811cfa85004c16dbde9f4658c6723493 Mon Sep 17 00:00:00 2001 From: Jem Mawson Date: Mon, 17 Dec 2018 17:34:54 +1000 Subject: [PATCH 20/20] meaningless change to force travis rebuild (rebuild button is not present for PRs!?) --- lib/xdrgen/generators/scala.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/xdrgen/generators/scala.rb b/lib/xdrgen/generators/scala.rb index f8afade39..6b9567b33 100644 --- a/lib/xdrgen/generators/scala.rb +++ b/lib/xdrgen/generators/scala.rb @@ -700,11 +700,7 @@ def name_string(name) end def sanitize(name) - if name == "type" - "`type`" - else - name - end + name == "type" ? "`type`" : name end end end