diff --git a/lib/rpl/types.rb b/lib/rpl/types.rb new file mode 100644 index 0000000..3e99c7e --- /dev/null +++ b/lib/rpl/types.rb @@ -0,0 +1,184 @@ +# frozen_string_literal: true + +# :boolean +class RplBoolean + attr_accessor :value + + def initialize( value ) + raise RplTypeError unless self.class.can_parse?( value ) + + @value = if value.is_a?( String ) + value.downcase == 'true' + else + value + end + end + + def to_s + @value.to_s + end + + def self.can_parse?( value ) + return %w[true false].include?( value.downcase ) if value.is_a?( String ) + + %w[TrueClass FalseClass].include?( value.class.to_s ) + end +end + +# :list +class RplList + attr_accessor :value + + def initialize( value ) + raise RplTypeError unless self.class.can_parse?( value ) + + # we systematicalyl trim enclosing { } + @value = value[2..-3] # TODO: parse each element + end + + def to_s + "{ #{@value.map(&:to_s).join(' ')} }" + end + + def self.can_parse?( value ) + value[0] == '{' && value[-1] == '}' + end +end + +# :name +class RplName + attr_accessor :value + + def initialize( value ) + raise RplTypeError unless self.class.can_parse?( value ) + + # we systematicalyl trim enclosing ' + @value = value[1..-2] + end + + def to_s + "'#{@value}'" + end + + def self.can_parse?( value ) + value[0] == "'" && value[-1] == "'" + end +end + +# :numeric +class RplNumeric + attr_accessor :value + + def initialize( value, base = 10 ) + raise RplTypeError unless self.class.can_parse?( value ) + + @base = base + @value = value + + underscore_position = @value.index('_') + + if @value[0] == '0' && ( %w[b o x].include?( @value[1] ) || !underscore_position.nil? ) + if @value[1] == 'x' + @base = 16 + elsif @value[1] == 'b' + @base = 2 + elsif @value[1] == 'o' + @base = 8 + @value = @value[2..-1] + elsif !underscore_position.nil? + @base = @value[1..(underscore_position - 1)].to_i + @value = @value[(underscore_position + 1)..-1] + end + end + + @value = @value.to_i( @base ) unless @base == 10 + @value = BigDecimal( @value, @precision ) # FIXME: how to get @precision? + end + + def to_s + prefix = case @base + when 2 + '0b' + when 8 + '0o' + when 10 + '' + when 16 + '0x' + else + "0#{@base}_" + end + + if @value.infinite? + suffix = @value.infinite?.positive? ? '∞' : '-∞' + elsif @value.nan? + suffix = '' + else + suffix = if @value.to_i == @value + @value.to_i + else + @value.to_s('F') + end + suffix = @value.to_s( @base ) unless @base == 10 + end + + "#{prefix}#{suffix}" + end + + def self.can_parse?( value ) + # FIXME + !Float( value ).nil? + rescue ArgumentError + begin + !Integer( value ).nil? + rescue ArgumentError + false + end + end + + def self.infer_resulting_base( numerics ) + 10 if numerics.length.zero? + + numerics.last.base + end +end + +# :string +class RplString + attr_accessor :value + + def initialize( value ) + raise RplTypeError unless self.class.can_parse?( value ) + + # we systematicalyl trim enclosing " + @value = value[1..-2] + end + + def to_s + "\"#{@value}\"" + end + + def self.can_parse?( value ) + value[0] == '"' && value[-1] == '"' + end +end + +# :program +class RplProgram + attr_accessor :value + + def initialize( value ) + raise RplTypeError unless self.class.can_parse?( value ) + + # we systematicalyl trim enclosing « » + @value = value[2..-3] + end + + def to_s + "« #{@value} »" + end + + def self.can_parse?( value ) + value[0] == '«' && value[-1] == '»' + end +end diff --git a/spec/types_spec.rb b/spec/types_spec.rb new file mode 100644 index 0000000..e403245 --- /dev/null +++ b/spec/types_spec.rb @@ -0,0 +1,48 @@ +# coding: utf-8 +# frozen_string_literal: true + +require 'minitest/autorun' + +require 'rpl/types' + +class TestTypes < MiniTest::Test + def test_boolean + assert_equal true, RplBoolean.can_parse?( true ) + assert_equal true, RplBoolean.can_parse?( false ) + assert_equal true, RplBoolean.can_parse?( 'true' ) + assert_equal true, RplBoolean.can_parse?( 'false' ) + assert_equal true, RplBoolean.can_parse?( 'TRUE' ) + assert_equal true, RplBoolean.can_parse?( 'FALSE' ) + assert_equal false, RplBoolean.can_parse?( 'prout' ) + assert_equal false, RplBoolean.can_parse?( 1 ) + + assert_equal RplBoolean, RplBoolean.new( true ).class + assert_equal RplBoolean, RplBoolean.new( false ).class + assert_equal RplBoolean, RplBoolean.new( 'true' ).class + assert_equal RplBoolean, RplBoolean.new( 'false' ).class + assert_equal RplBoolean, RplBoolean.new( 'TRUE' ).class + assert_equal RplBoolean, RplBoolean.new( 'FALSE' ).class + + assert_equal true, RplBoolean.new( true ).value + assert_equal false, RplBoolean.new( false ).value + assert_equal true, RplBoolean.new( 'true' ).value + assert_equal false, RplBoolean.new( 'false' ).value + assert_equal true, RplBoolean.new( 'TRUE' ).value + assert_equal false, RplBoolean.new( 'FALSE' ).value + end + + def test_name + end + + def test_string + end + + def test_program + end + + def test_list + end + + def test_numeric + end +end