Skip to content

Commit

Permalink
Support field names that are Ruby keywords
Browse files Browse the repository at this point in the history
This commit adds support for field names that are also Ruby keywords.
For example, you can have a proto message with a field named `end` and
we need to support that
  • Loading branch information
tenderlove committed Apr 16, 2024
1 parent 377b17a commit 44e9fa5
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 10 deletions.
21 changes: 11 additions & 10 deletions lib/protoboeuf/codegen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -549,11 +549,11 @@ def initialize_oneof(oneof)
"@#{oneof.name} = nil # oneof field\n" +
oneof.fields.map { |field|
<<~RUBY
if #{field.name} == NONE
@#{field.name} = #{default_for(field)}
if #{field.lvar_read} == NONE
#{field.iv_name} = #{default_for(field)}
else
@#{oneof.name} = :#{field.name}
@#{field.name} = #{field.name}
#{field.iv_name} = #{field.lvar_read}
end
RUBY
}.join("\n")
Expand All @@ -571,11 +571,11 @@ def initialize_field(field)

def initialize_optional_field(field)
<<~RUBY
if #{field.name} == NONE
@#{field.name} = #{default_for(field)}
if #{field.lvar_read} == NONE
#{field.iv_name} = #{default_for(field)}
else
#{set_bitmask(field)}
@#{field.name} = #{field.name}
#{field.iv_name} = #{field.lvar_read}
end
RUBY
end
Expand Down Expand Up @@ -864,13 +864,13 @@ def initialize_signature
self.fields.flat_map { |f|
if f.field?
if f.optional?
"#{f.name}: NONE"
"#{f.lvar_name}: NONE"
else
"#{f.name}: #{default_for(f)}"
"#{f.lvar_name}: #{default_for(f)}"
end
elsif f.oneof?
f.fields.map { |child|
"#{child.name}: NONE"
"#{child.lvar_name}: NONE"
}
else
raise NotImplementedError
Expand Down Expand Up @@ -1139,7 +1139,8 @@ def to_ruby

tail = "\n" + packages.map { "end" }.join("\n")

SyntaxTree.format(head + body + tail)
#SyntaxTree.format(head + body + tail)
head + body + tail
end
end
end
24 changes: 24 additions & 0 deletions lib/protoboeuf/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,30 @@ def oneof?

alias :enum? :enum

# Return a local variable name for use in generated code
def lvar_name
name
end

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

# Return code for reading the local variable returned by `lvar_name`
def lvar_read
if RUBY_KEYWORDS.include?(name)
"binding.local_variable_get(:#{name})"
else
name
end
end

# Return an instance variable name for use in generated code
def iv_name
"@#{name}"
end

def map?
MapType === type
end
Expand Down
24 changes: 24 additions & 0 deletions test/codegen_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,29 @@ def test_fixture_file

assert_equal [], klass::TestRepeatedField.new.e
end

def test_fields_keyword_end
unit = ProtoBoeuf.parse_string('message Test1 { optional int32 end = 1; }')
gen = CodeGen.new unit
klass = Class.new { self.class_eval gen.to_ruby }
obj = klass::Test1.new(end: 1234)
assert_equal 1234, obj.end
end

def test_fields_keyword_class
unit = ProtoBoeuf.parse_string('message Test1 { optional int32 class = 1; }')
gen = CodeGen.new unit
klass = Class.new { self.class_eval gen.to_ruby }
obj = klass::Test1.new(class: 1234)
assert_equal 1234, obj.class
end

def test_fields_keyword_nil
unit = ProtoBoeuf.parse_string('message Test1 { optional int32 nil = 1; }')
gen = CodeGen.new unit
klass = Class.new { self.class_eval gen.to_ruby }
obj = klass::Test1.new(nil: 1234)
assert_equal 1234, obj.nil
end
end
end

0 comments on commit 44e9fa5

Please sign in to comment.