initial version externalized from salama

This commit is contained in:
Torsten Ruger
2015-05-03 20:16:06 +03:00
parent 612588243b
commit c73983b224
11 changed files with 534 additions and 2 deletions

39
lib/sof/all.rb Normal file
View File

@ -0,0 +1,39 @@
require_relative "util"
require_relative "node"
require_relative "members"
require_relative "volotile"
require_relative "writer"
require_relative "array"
require_relative "hash"
require_relative "occurence"
Symbol.class_eval do
def to_sof()
":#{to_s}"
end
end
TrueClass.class_eval do
def to_sof()
"true"
end
end
NilClass.class_eval do
def to_sof()
"nil"
end
end
FalseClass.class_eval do
def to_sof()
"false"
end
end
String.class_eval do
def to_sof()
"'" + self + "'"
end
end
Fixnum.class_eval do
def to_sof()
to_s
end
end

51
lib/sof/array.rb Normal file
View File

@ -0,0 +1,51 @@
module Sof
class ArrayNode < Node
def initialize ref
super(ref)
@children = []
end
attr_reader :children
def add c
@children << c
end
def out io , level = 0
super
short = true
children.each do |c|
short = false unless c.is_a?(SimpleNode)
end
if(short and children.length < 7 )
short_out(io,level)
else
long_out(io , level)
end
end
private
def short_out(io,level)
io.write("[")
@children.each_with_index do |child , i|
child.out(io , level + 1)
io.write ", " unless (i+1) == children.length
end
io.write("]")
end
def long_out io , level
indent = " " * level
@children.each_with_index do |child , i|
io.write "\n#{indent}" unless i == 0
io.write "-"
child.out(io , level + 1)
end
end
end
end
Array.class_eval do
def to_sof_node(writer , level , ref )
node = Sof::ArrayNode.new(ref)
each do |object|
node.add writer.to_sof_node( object , level + 1)
end
node
end
end

60
lib/sof/hash.rb Normal file
View File

@ -0,0 +1,60 @@
module Sof
class HashNode < Node
def initialize ref
super(ref)
@children = []
end
attr_reader :children
def add key , val
@children << [key,val]
end
def out io , level = 0
super
short = true
children.each do |k,v|
short = false unless k.is_a?(SimpleNode)
short = false unless v.is_a?(SimpleNode)
end
if(short and children.length < 7 )
short_out(io,level)
else
long_out(io , level)
end
end
def short_out(io,level)
io.write("{")
children.each_with_index do |child , i|
key , val = child
key.out(io , level + 1)
io.write " => "
val.out(io , level + 1)
io.write ", " unless (i+1) == children.length
end
io.write("}")
end
def long_out io , level
indent = " " * level
children.each_with_index do |child , i|
key , val = child
io.write "\n#{indent}" unless i == 0
io.write "-"
key.out(io , level + 1)
io.write " => "
val.out(io , level + 1)
end
end
end
end
Hash.class_eval do
def to_sof_node(writer , level , ref)
node = Sof::HashNode.new(ref)
each do |key , object|
k = writer.to_sof_node( key ,level + 1)
v = writer.to_sof_node( object ,level +1)
node.add(k , v)
end
node
end
end

49
lib/sof/members.rb Normal file
View File

@ -0,0 +1,49 @@
module Sof
class Members
include Util
def initialize root
@root = root
@counter = 1
@objects = {}
@referenced = false
add(root , 0)
end
attr_reader :objects , :root , :referenced
def add object , level
return if is_value?(object)
if( occurence = @objects[object.object_id] )
#puts "reset level #{level} at #{occurence.level}"
if occurence.level > level
occurence.level = level
end
unless occurence.referenced
#puts "referencing #{@counter} , at level #{level}/#{occurence.level} "
occurence.set_reference(@counter)
@counter = @counter + 1
end
return
end
o = Occurence.new( object , level )
@objects[object.object_id] = o
attributes = attributes_for(object)
attributes.each do |a|
val = get_value( object , a)
add(val , level + 1)
end
if( object.is_a? Array )
object.each do |a|
add(a , level + 1)
end
end
if( object.is_a? Hash )
object.each do |a,b|
add(a , level + 1)
add(b , level + 1)
end
end
end
end
end

58
lib/sof/node.rb Normal file
View File

@ -0,0 +1,58 @@
# We transform objects into a tree of nodes
module Sof
#abstract base class for nodes in the tree
# may be referenced (should be a simple name or number)
class Node
include Util
def initialize ref
@referenced = ref
end
# must be able to output to a stream
def out io ,level
io.write "&#{@referenced} " if @referenced
end
def as_string(level)
io = StringIO.new
out(io,level)
io.string
end
attr_reader :referenced
end
class SimpleNode < Node
def initialize data , ref = nil
super(ref)
@data = data
end
attr_reader :data
def out io , level
super(io,level)
io.write(data)
end
end
class ObjectNode < Node
def initialize data , ref
super(ref)
@data = data
@children = []
end
attr_reader :children , :data
def add k , v
@children << [k,v]
end
def out io , level = 0
super
io.write(@data)
indent = " " * (level + 1)
@children.each_with_index do |child , i|
k , v = child
io.write "\n#{indent}"
k.out(io , level + 2)
io.write " "
v.out(io , level + 2)
end
end
end
end

16
lib/sof/occurence.rb Normal file
View File

@ -0,0 +1,16 @@
module Sof
class Occurence
def initialize object , level
@object = object
@level = level
@referenced = nil
end
def set_reference r
@referenced = r
end
attr_reader :object , :referenced
attr_accessor :level
end
end

25
lib/sof/util.rb Normal file
View File

@ -0,0 +1,25 @@
module Sof
module Util
def is_value? o
return true if o == true
return true if o == false
return true if o == nil
return true if o.class == Fixnum
return true if o.class == Symbol
return true if o.class == String
return false
end
def get_value(object,name)
object.instance_variable_get "@#{name}".to_sym
end
def attributes_for object
Sof::Util.attributes(object)
end
def self.attributes( object )
atts = object.instance_variables.collect{|i| i.to_s[1..-1].to_sym } # chop of @
atts - Volotile.attributes(object.class)
end
end
end

11
lib/sof/volotile.rb Normal file
View File

@ -0,0 +1,11 @@
module Sof
class Volotile
@@mapping = { }
def self.attributes clazz
@@mapping[clazz] || []
end
def self.add clazz , attributes
@@mapping[clazz] = attributes
end
end
end

60
lib/sof/writer.rb Normal file
View File

@ -0,0 +1,60 @@
module Sof
class Writer
include Util
def initialize members
@members = members
end
def write
node = to_sof_node(@members.root , 0)
io = StringIO.new
node.out( io , 0 )
io.string
end
def to_sof_node(object , level)
if is_value?(object)
return SimpleNode.new(object.to_sof())
end
occurence = @members.objects[object.object_id]
raise "no object #{object}" unless occurence
if(level > occurence.level )
#puts "ref #{occurence.referenced} level #{level} at #{occurence.level}"
return SimpleNode.new("*#{occurence.referenced}")
end
ref = occurence.referenced
if(object.respond_to? :to_sof_node) #mainly meant for arrays and hashes
object.to_sof_node(self , level , ref )
else
object_sof_node(object , level , ref )
end
end
def object_sof_node( object , level , ref)
if( object.is_a? Class )
return SimpleNode.new( object.name , ref )
end
head = object.class.name + "("
atts = {}
attributes_for(object).each() do |a|
val = get_value(object , a)
next if val.nil?
atts[a] = to_sof_node(val , level + 1)
end
immediate , extended = atts.partition {|a,val| val.is_a?(SimpleNode) }
head += immediate.collect {|a,val| "#{a.to_sof()} => #{val.as_string(level)}"}.join(", ") + ")"
return SimpleNode.new(head) if( ref.nil? and extended.empty? and head.length < 30 )
node = ObjectNode.new(head , ref)
extended.each do |a , val|
node.add( to_sof_node(a,level + 1) , val )
end
node
end
def self.write object
writer = Writer.new(Members.new(object) )
writer.write
end
end
end