Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added more specs for table objects. Reimplemented Table. #2

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 20 additions & 53 deletions lib/poison/bootstrap/compiler/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,64 +91,31 @@ def imaginary(node)
end

# Creates a Table object.
#
# A table is a container with index-access or key-access
#
# foo = ("bar", "baz") # a list
# foo at(0) # => "bar"
#
# foo = (bar = "baz") # a dict
# foo at("bar") # => "baz"
#
# From _why's Potion, it's unclear what the behaviour should
# be for a table created with a mix of assign and values
#
# foo = (bar = "baz", "bat")
# foo at(0) # => nil
# foo at('bar') # => "baz"
# foo at(1) # => nil
#
# So what we do for Poison is this: if we find any of the
# table expressions to be an assignment, then the table is
# backed by a ruby Hash, otherwise its backed by a ruby List.
#
def table(node)
if node.entries.any? { |e| e.kind_of?(Syntax::Assign) }
# A hash, all entries not being an assign, get a value of nil

g.push_cpath_top
g.find_const :Hash
g.push node.entries.size
g.send :new_from_literal, 1

node.entries.each do |entry|
g.dup
if entry.kind_of? Syntax::Assign
# if key is a single name, we use its string value as key
if entry.name.expressions.size == 1 &&
entry.name.expressions.first.kind_of?(Syntax::Message)
g.push_literal entry.name.expressions.first.name
else
entry.name.visit self
end
entry.value.visit self
g.push_cpath_top
g.find_const :Table
g.send :new, 0

node.entries.each_with_index do |entry, index|
g.dup
g.push_literal index

if entry.kind_of?(Syntax::Assign)
if entry.name.kind_of?(Syntax::Expression) &&
entry.name.expressions.first.kind_of?(Syntax::Message)
g.push_literal entry.name.expressions.first.name.to_sym
else
entry.visit self
g.push_nil
entry.name.visit self
end
g.send :[]=, 2
g.pop
entry.value.visit self

g.send :put_key, 3
else
entry.visit self
g.send :put_val, 2
end
else
# A list
node.entries.each { |entry| entry.visit self }
g.make_array node.entries.size
g.pop
end

g.push_cpath_top
g.find_const :Table
g.swap
g.send :new, 1
end
end
end
52 changes: 45 additions & 7 deletions lib/poison/bootstrap/library/table.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,58 @@
# A Table object is created using Poison table literals:
# Poison Table is inspired by Lua's but not necessarily having the
# same exact behaviour.
#
# A table can be used as a list of objects or as a map of key-values
#
# table objects are created using Poison table literals:
#
# Creates a "list" like object:
# (foo, bar)
# table = ("foo", "bar")
# table at(0) #=> "foo"
# table at(1) #=> "bar"
#
# Creates a "dictionary" like object:
# (foo, bar)
# table = (foo = "bar")
# table at(0) #=> "bar"
# table foo #=> "bar"
#
#
class Table

def initialize(collection)
@collection = collection
Slot = Struct.new(:index, :key, :value)

def initialize
@ary = Array.new
@map = Hash.new
end

def delete_at(index)
slot = @ary[index]
@map.delete @ary[index].key if slot && slot.key
@ary.delete_at index
end

def put_val(index, value)
delete_at index
@ary[index] = Slot.new(index, nil, value)
value
end

def key_accessor(key)
metaclass.send :define_method, "pn:#{key}", lambda { at(key) }
end

def put_key(index, key, value)
delete_at index
@map[key] = @ary[index] = Slot.new(index, key, value)
key_accessor key if key.kind_of?(Symbol)
value
end

def pn_at(key)
@collection[key]
def at(key)
return @map[key].value if @map.key?(key)
@ary[key].value if @ary[key]
end
alias_method :pn_at, :at

poison_methods

Expand Down
40 changes: 40 additions & 0 deletions spec/library/table_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,43 @@
table.should be_kind_of(Table)
end
end

describe "Table#at" do
it "can access values by index" do
table = Poison::CodeLoader.execute "(9, 8, 7)"
table.poison(:at, 0).should == 9
table.poison(:at, 1).should == 8
table.poison(:at, 2).should == 7
end

it "can access values by key" do
table = Poison::CodeLoader.execute "(a = 1, b = 2, c = 3)"
table.poison(:at, :b).should == 2
end

it "can access value by named key or index" do
table = Poison::CodeLoader.execute "(9, b = 2, 7)"
table.poison(:at, 0).should == 9
table.poison(:at, :b).should == 2
table.poison(:at, 1).should == 2
table.poison(:at, 2).should == 7
end

it "can access value by expression key or index" do
table = Poison::CodeLoader.execute "('bar' = 90, 'foo' = 2)"
table.poison(:at, 1).should == 2
table.poison(:at, "foo").should == 2
end
end

describe "Table accessor" do
it "is generated for an assign having a name as left hand side" do
table = Poison::CodeLoader.execute "(9, foo = 42, 'bar' = 7)"
table.poison(:foo).should == 42
end

it "is not generated for an assign having an expression as left hand side" do
table = Poison::CodeLoader.execute "(9, foo = 42, 'bar' = 7)"
table.should_not respond_to("pn:bar")
end
end