From 1e4dafb1d4694d495005dc40b0fd0ca91e44dbc9 Mon Sep 17 00:00:00 2001 From: Gwenhael Le Moine Date: Thu, 18 Nov 2021 15:44:09 +0100 Subject: [PATCH] implements SQRT PREC DEFAULT TYPE TIME DATE TICKS --- lib/core.rb | 18 ++++++++++++++++++ lib/dictionary.rb | 14 +++++++------- lib/language/mode.rb | 33 +++++++++++++++++++++++++++++++++ lib/language/operations.rb | 8 ++++++++ lib/language/time-date.rb | 29 +++++++++++++++++++++++++++++ repl.rb | 2 ++ spec/language_time-date_spec.rb | 32 ++++++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 lib/language/mode.rb create mode 100644 lib/language/time-date.rb create mode 100644 spec/language_time-date_spec.rb diff --git a/lib/core.rb b/lib/core.rb index f03583d..c88e914 100644 --- a/lib/core.rb +++ b/lib/core.rb @@ -1,12 +1,30 @@ +require 'bigdecimal/math' + require_relative './language/general' +require_relative './language/mode' require_relative './language/operations' require_relative './language/program' require_relative './language/stack' +require_relative './language/time-date' module Rpn module Core module_function + include BigMath + + def precision + @precision + end + + def precision=( value ) + @precision = value + end + + def init + @precision = 12 # default precision for math operations + end + def stack_extract( stack, needs ) raise 'Not enough elements' if stack.size < needs.size diff --git a/lib/dictionary.rb b/lib/dictionary.rb index 1607fb7..01aa618 100644 --- a/lib/dictionary.rb +++ b/lib/dictionary.rb @@ -38,7 +38,7 @@ module Rpn add( '/', proc { |stack| Rpn::Core::Operations.divide( stack ) } ) add( 'inv', proc { |stack| Rpn::Core::Operations.inverse( stack ) } ) add( '^', proc { |stack| Rpn::Core::Operations.power( stack ) } ) - add( 'sqrt', proc { |stack| Rpn::Core.__todo( stack ) } ) # rpn_square root + add( 'sqrt', proc { |stack| Rpn::Core::Operations.sqrt( stack ) } ) add( 'sq', proc { |stack| Rpn::Core.__todo( stack ) } ) # rpn_square add( 'abs', proc { |stack| Rpn::Core.__todo( stack ) } ) # absolute value add( 'dec', proc { |stack| Rpn::Core.__todo( stack ) } ) # decimal representation @@ -75,10 +75,10 @@ module Rpn add( 'std', proc { |stack| Rpn::Core.__todo( stack ) } ) # standard floating numbers representation. ex: std add( 'fix', proc { |stack| Rpn::Core.__todo( stack ) } ) # fixed point representation. ex: 6 fix add( 'sci', proc { |stack| Rpn::Core.__todo( stack ) } ) # scientific floating point representation. ex: 20 sci - add( 'prec', proc { |stack| Rpn::Core.__todo( stack ) } ) # set float precision in bits. ex: 256 prec + add( 'prec', proc { |stack| Rpn::Core::Mode.prec( stack ) } ) add( 'round', proc { |stack| Rpn::Core.__todo( stack ) } ) # set float rounding mode. ex: ["nearest", "toward zero", "toward +inf", "toward -inf", "away from zero"] round - add( 'default', proc { |stack| Rpn::Core.__todo( stack ) } ) # set float representation and precision to default - add( 'type', proc { |stack| Rpn::Core.__todo( stack ) } ) # show type of stack first entry + add( 'default', proc { |stack| Rpn::Core::Mode.default( stack ) } ) + add( 'type', proc { |stack| Rpn::Core::Mode.type( stack ) } ) # TEST add( '>', proc { |stack| Rpn::Core.__todo( stack ) } ) # binary operator > @@ -165,9 +165,9 @@ module Rpn add( 'atanh', proc { |stack| Rpn::Core.__todo( stack ) } ) # inverse hyperbolic tangent # TIME AND DATE - add( 'time', proc { |stack| Rpn::Core.__todo( stack ) } ) # time in local format - add( 'date', proc { |stack| Rpn::Core.__todo( stack ) } ) # date in local format - add( 'ticks', proc { |stack| Rpn::Core.__todo( stack ) } ) # system tick in µs + add( 'time', proc { |stack| Rpn::Core::TimeDate.time( stack ) } ) + add( 'date', proc { |stack| Rpn::Core::TimeDate.date( stack ) } ) + add( 'ticks', proc { |stack| Rpn::Core::TimeDate.ticks( stack ) } ) end def add( name, implementation ) diff --git a/lib/language/mode.rb b/lib/language/mode.rb new file mode 100644 index 0000000..28dfe91 --- /dev/null +++ b/lib/language/mode.rb @@ -0,0 +1,33 @@ +module Rpn + module Core + module Mode + module_function + + # set float precision in bits. ex: 256 prec + def prec( stack ) + stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] ) + + Rpn::Core.precision = args[0][:value] + + stack + end + + # set float representation and precision to default + def default( stack ) + Rpn::Core.precision = 12 + + stack + end + + # show type of stack first entry + def type( stack ) + stack, args = Rpn::Core.stack_extract( stack, [:any] ) + + stack << args[0] + stack << { type: :string, + value: args[0][:type].to_s } + stack + end + end + end +end diff --git a/lib/language/operations.rb b/lib/language/operations.rb index 6421ab5..1ed5de1 100644 --- a/lib/language/operations.rb +++ b/lib/language/operations.rb @@ -88,6 +88,14 @@ module Rpn stack << { type: :numeric, value: args[0][:value]**args[1][:value] } end + + # rpn_square root + def sqrt( stack ) + stack, args = Rpn::Core.stack_extract( stack, [%i[numeric]] ) + + stack << { type: :numeric, + value: BigMath.sqrt( BigDecimal( args[0][:value] ), Rpn::Core.precision ) } + end end end end diff --git a/lib/language/time-date.rb b/lib/language/time-date.rb new file mode 100644 index 0000000..9abb73c --- /dev/null +++ b/lib/language/time-date.rb @@ -0,0 +1,29 @@ +require 'date' + +module Rpn + module Core + module TimeDate + module_function + + # time in local format + def time( stack ) + stack << { type: :string, + value: Time.now.to_s } + end + + # date in local format + def date( stack ) + stack << { type: :string, + value: Date.today.to_s } + end + + # system tick in µs + def ticks( stack ) + ticks_since_epoch = Time.utc( 1, 1, 1 ).to_i * 10_000_000 + now = Time.now + stack << { type: :numeric, + value: now.to_i * 10_000_000 + now.nsec / 100 - ticks_since_epoch } + end + end + end +end diff --git a/repl.rb b/repl.rb index 6cb0938..b2adb4c 100644 --- a/repl.rb +++ b/repl.rb @@ -15,6 +15,8 @@ module Rpn @dictionary = Dictionary.new @parser = Parser.new @runner = Runner.new + + Rpn::Core.init end def run diff --git a/spec/language_time-date_spec.rb b/spec/language_time-date_spec.rb new file mode 100644 index 0000000..e0401f5 --- /dev/null +++ b/spec/language_time-date_spec.rb @@ -0,0 +1,32 @@ +# coding: utf-8 +# frozen_string_literal: true + +require 'test/unit' + +require_relative '../lib/core' + +class TestParser < Test::Unit::TestCase + def test_time + now = Time.now.to_s + stack = Rpn::Core::TimeDate.time( [] ) + + assert_equal [{ value: now, type: :string }], + stack + end + + def test_date + now = Date.today.to_s + stack = Rpn::Core::TimeDate.date( [] ) + + assert_equal [{ value: now, type: :string }], + stack + end + + def test_ticks + stack = Rpn::Core::TimeDate.ticks( [] ) + + # TODO: better test, but how? + assert_equal :numeric, + stack[0][:type] + end +end