rpl 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6394da05e2547dd85c3ab87e8ad841d72edc90b99e16bb1abbd43850e4c824d
4
- data.tar.gz: 87e6d2304b45909e04ccdae776cea33fb26048c98f6488ceb44539c348ad7063
3
+ metadata.gz: e546030597f71600c2156d2e4b92e8e6385fce7b551fe24e30cb386db734bf32
4
+ data.tar.gz: 1e441262cd8c01d1fcb56905ca97f8efa56b9f607f2786a76ea71c30c4948cc2
5
5
  SHA512:
6
- metadata.gz: 0fbe309e4c373ab91b0f50022c3571d28e387b9476d7deb96fb35fb2db25970a946f254a4819b088a0741b3271fa07d6710165f186365478726ad01e6d3ce788
7
- data.tar.gz: 5d928b2865843d21275a26e14dce2fc4be75ddb371a7d0dd0cf185037c2766af85186da83fd8acdd56f6500647d4610bd245ae7fe243ef53790871a396d5f4c3
6
+ metadata.gz: 14e5ab387f0d91e57b7b89a1383343beda02c4dafbffedb07721b722fdb760f646867fbc64bff0b1a9335d3329c63d1358ef21307a6b55afc1caa57a52d08a58
7
+ data.tar.gz: 90f8b97a3f57ba05576970c623fc1c709c58f02d0cefd239df9ea275e44409ceea97f83bff14e159cfc2df900467d5b309f46f5d6a72c5548c1b11684133462b
data/bin/rpl CHANGED
@@ -84,5 +84,5 @@ end
84
84
  # third launch REPL if (explicitely or implicitely) asked
85
85
  RplRepl.new( interpreter ).run if options[:run_REPL]
86
86
 
87
- # last print resulting stack on exit (formatted so that it can be fed back later to interpreter)
88
- pp interpreter.export_stack
87
+ # last print defined vars and resulting stack on exit (formatted so that it can be fed back later to interpreter)
88
+ pp "#{interpreter.export_vars} #{interpreter.export_stack}"
@@ -7,11 +7,30 @@ require 'bigdecimal/util'
7
7
  require 'rpl/dictionary'
8
8
  require 'rpl/types'
9
9
 
10
+ class BitArray
11
+ def initialize
12
+ @mask = 0
13
+ end
14
+
15
+ def []=(position, value)
16
+ if value.zero?
17
+ @mask ^= (1 << position)
18
+ else
19
+ @mask |= (1 << position)
20
+ end
21
+ end
22
+
23
+ def [](position)
24
+ @mask[position]
25
+ end
26
+ end
27
+
10
28
  class Interpreter
11
29
  include BigMath
12
30
  include Types
13
31
 
14
32
  attr_reader :stack,
33
+ :frame_buffer,
15
34
  :dictionary,
16
35
  :version
17
36
 
@@ -22,6 +41,12 @@ class Interpreter
22
41
 
23
42
  @dictionary = dictionary
24
43
  @stack = stack
44
+
45
+ initialize_frame_buffer
46
+ end
47
+
48
+ def initialize_frame_buffer
49
+ @frame_buffer = BitArray.new
25
50
  end
26
51
 
27
52
  def run( input )
@@ -35,19 +60,15 @@ class Interpreter
35
60
  @stack << elt
36
61
  else
37
62
  command = @dictionary.lookup( elt.value )
38
- if command.nil?
39
- # if there isn't a command by that name then it's a name
40
- # elt[:type] = :name
41
63
 
64
+ if command.nil?
42
65
  @stack << elt
43
66
  elsif command.is_a?( Proc )
44
67
  command.call
68
+ elsif command.instance_of?( RplProgram )
69
+ run( command.value )
45
70
  else
46
- if command.instance_of?( RplProgram )
47
- run( command.value )
48
- else
49
- @stack << command
50
- end
71
+ @stack << command
51
72
  end
52
73
  end
53
74
  else
@@ -78,6 +99,12 @@ class Interpreter
78
99
  args
79
100
  end
80
101
 
102
+ def export_vars
103
+ @dictionary.vars
104
+ .map { |name, value| "#{value} '#{name}' sto" }
105
+ .join(' ')
106
+ end
107
+
81
108
  def export_stack
82
109
  @stack.map(&:to_s).join(' ')
83
110
  end
data/lib/rpl/parser.rb CHANGED
@@ -1,5 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ class RplTypeError < StandardError
4
+ attr_reader :reason
5
+
6
+ def initialize( reason = '-undefined-' )
7
+ super
8
+ @reason = reason
9
+ end
10
+ end
11
+
3
12
  class Parser
4
13
  include Types
5
14
 
@@ -24,13 +33,20 @@ class Parser
24
33
  splitted_input = input.split(' ')
25
34
 
26
35
  # 2-passes:
27
- # 1. regroup strings and programs
36
+ # 1. regroup strings, lists, complexes, names and programs
28
37
  opened_programs = 0
29
38
  closed_programs = 0
39
+
30
40
  opened_lists = 0
31
41
  closed_lists = 0
42
+
43
+ opened_complexes = 0
44
+ closed_complexes = 0
45
+
32
46
  string_delimiters = 0
47
+
33
48
  name_delimiters = 0
49
+
34
50
  regrouping = false
35
51
 
36
52
  regrouped_input = []
@@ -41,13 +57,16 @@ class Parser
41
57
  elsif elt[0] == '{'
42
58
  opened_lists += 1
43
59
  elt.gsub!( '{', '{ ') if elt.length > 1 && elt[1] != ' '
60
+ elsif elt[0] == '('
61
+ opened_complexes += 1
62
+ elt.gsub!( '( ', '(') if elt.length > 1 && elt[1] == ' '
44
63
  elsif elt[0] == '"' && elt.length > 1
45
64
  string_delimiters += 1
46
65
  elsif elt[0] == "'" && elt.length > 1
47
66
  name_delimiters += 1
48
67
  end
49
68
 
50
- elt = "#{regrouped_input.pop} #{elt}".strip if regrouping
69
+ elt = "#{regrouped_input.pop}#{opened_complexes > closed_complexes ? '' : ' '}#{elt}".strip if regrouping
51
70
 
52
71
  regrouped_input << elt
53
72
 
@@ -58,21 +77,26 @@ class Parser
58
77
  when '}'
59
78
  closed_lists += 1
60
79
  elt.gsub!( '}', ' }') if elt.length > 1 && elt[-2] != ' '
80
+ when ')'
81
+ closed_complexes += 1
82
+ elt.gsub!( ' )', ')') if elt.length > 1 && elt[-2] == ' '
61
83
  when '"'
62
84
  string_delimiters += 1
63
85
  when "'"
64
86
  name_delimiters += 1
65
87
  end
66
88
 
67
- regrouping = string_delimiters.odd? || name_delimiters.odd? || (opened_programs > closed_programs ) || (opened_lists > closed_lists )
89
+ regrouping = string_delimiters.odd? || name_delimiters.odd? || (opened_programs > closed_programs) || (opened_lists > closed_lists) || (opened_complexes > closed_complexes)
68
90
  end
69
91
 
70
92
  # 2. parse
71
- regrouped_input.map do |element|
93
+ parsed_input = regrouped_input.map do |element|
72
94
  if RplBoolean.can_parse?( element )
73
95
  Types.new_object( RplBoolean, element )
74
96
  elsif RplNumeric.can_parse?( element )
75
97
  Types.new_object( RplNumeric, element )
98
+ elsif RplComplex.can_parse?( element )
99
+ Types.new_object( RplComplex, element )
76
100
  elsif RplList.can_parse?( element )
77
101
  Types.new_object( RplList, element )
78
102
  elsif RplString.can_parse?( element )
@@ -83,5 +107,7 @@ class Parser
83
107
  Types.new_object( RplName, element )
84
108
  end
85
109
  end
110
+
111
+ parsed_input
86
112
  end
87
113
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rpl/parser'
4
+
5
+ module Types
6
+ class RplComplex
7
+ attr_accessor :value
8
+
9
+ def initialize( value )
10
+ raise RplTypeError unless self.class.can_parse?( value )
11
+
12
+ # we systematicalyl trim enclosing ()
13
+ value = value[1..-2] if value.is_a?( String ) && value[0] == '(' && value[-1] == ')'
14
+
15
+ @value = Complex( value )
16
+ end
17
+
18
+ def to_s
19
+ "(#{@value})"
20
+ end
21
+
22
+ def self.can_parse?( value )
23
+ # we systematicalyl trim enclosing ()
24
+ value = value[1..-2] if value.is_a?( String ) && value[0] == '(' && value[-1] == ')'
25
+
26
+ begin
27
+ Complex( value )
28
+ rescue ArgumentError
29
+ return false
30
+ end
31
+
32
+ true
33
+ end
34
+
35
+ def ==( other )
36
+ other.class == RplComplex and
37
+ other.value == value
38
+ end
39
+ end
40
+ end
@@ -23,7 +23,7 @@ module Types
23
23
 
24
24
  def self.can_parse?( value )
25
25
  value.instance_of?( Array ) or
26
- value[0..1] == '{ ' && value[-2..-1] == ' }'
26
+ value[0..1] == '{ ' && value[-2..] == ' }'
27
27
  end
28
28
 
29
29
  def ==( other )
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'rpl/parser'
4
+
3
5
  module Types
4
6
  class RplString
5
7
  attr_accessor :value
data/lib/rpl/types.rb CHANGED
@@ -6,9 +6,11 @@ require 'rpl/types/list'
6
6
  require 'rpl/types/string'
7
7
  require 'rpl/types/program'
8
8
  require 'rpl/types/numeric'
9
+ require 'rpl/types/complex'
9
10
 
10
11
  module Types
11
12
  module_function
13
+
12
14
  def new_object( type_class, value )
13
15
  if type_class.can_parse?( value )
14
16
  type_class.new( value )
@@ -8,13 +8,15 @@ module RplLang
8
8
  def populate_dictionary
9
9
  super
10
10
 
11
+ category = 'Branch'
12
+
11
13
  @dictionary.add_word( ['ift'],
12
- 'Branch',
14
+ category,
13
15
  '( t pt -- … ) eval pt or not based on the value of boolean t',
14
16
  Types.new_object( RplProgram, '« « nop » ifte »' ) )
15
17
 
16
18
  @dictionary.add_word( ['ifte'],
17
- 'Branch',
19
+ category,
18
20
  '( t pt pf -- … ) eval pt or pf based on the value of boolean t',
19
21
  proc do
20
22
  args = stack_extract( [:any, :any, [RplBoolean]] )
@@ -23,7 +25,7 @@ module RplLang
23
25
  end )
24
26
 
25
27
  @dictionary.add_word( ['times'],
26
- 'Branch',
28
+ category,
27
29
  '( p n -- … ) eval p n times while pushing counter on stack before',
28
30
  proc do
29
31
  args = stack_extract( [[RplNumeric], :any] )
@@ -36,7 +38,7 @@ module RplLang
36
38
  end )
37
39
 
38
40
  @dictionary.add_word( ['loop'],
39
- 'Branch',
41
+ category,
40
42
  '( p n1 n2 -- … ) eval p looping from n1 to n2 while pushing counter on stack before',
41
43
  proc do
42
44
  args = stack_extract( [[RplNumeric], [RplNumeric], :any] )
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RplLang
4
+ module Words
5
+ module Display
6
+ include Types
7
+
8
+ def populate_dictionary
9
+ super
10
+
11
+ category = 'Display'
12
+
13
+ @dictionary.add_word( ['erase'],
14
+ category,
15
+ '( -- ) erase display',
16
+ proc do
17
+ initialize_frame_buffer
18
+ end )
19
+
20
+ @dictionary.add_word( ['display→', 'display->'],
21
+ category,
22
+ '( -- pict ) put current display state on stack',
23
+ proc do
24
+ @stack << @frame_buffer # FIXME: RplPict type
25
+ end )
26
+ end
27
+ end
28
+ end
29
+ end
@@ -8,8 +8,10 @@ module RplLang
8
8
  def populate_dictionary
9
9
  super
10
10
 
11
+ category = 'Filesystem'
12
+
11
13
  @dictionary.add_word( ['fread'],
12
- 'Filesystem',
14
+ category,
13
15
  '( filename -- content ) read file and put content on stack as string',
14
16
  proc do
15
17
  args = stack_extract( [[RplString]] )
@@ -20,12 +22,12 @@ module RplLang
20
22
  end )
21
23
 
22
24
  @dictionary.add_word( ['feval'],
23
- 'Filesystem',
25
+ category,
24
26
  '( filename -- … ) read and run file',
25
27
  Types.new_object( RplProgram, '« fread eval »' ) )
26
28
 
27
29
  @dictionary.add_word( ['fwrite'],
28
- 'Filesystem',
30
+ category,
29
31
  '( content filename -- ) write content into filename',
30
32
  proc do
31
33
  args = stack_extract( [[RplString], :any] )
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tempfile'
4
-
5
3
  module RplLang
6
4
  module Words
7
5
  module General
@@ -10,13 +8,15 @@ module RplLang
10
8
  def populate_dictionary
11
9
  super
12
10
 
11
+ category = 'General'
12
+
13
13
  @dictionary.add_word( ['nop'],
14
- 'General',
14
+ category,
15
15
  '( -- ) no operation',
16
16
  proc {} )
17
17
 
18
18
  @dictionary.add_word( ['help'],
19
- 'General',
19
+ category,
20
20
  '( w -- s ) pop help string of the given word',
21
21
  proc do
22
22
  args = stack_extract( [[RplName]] )
@@ -26,86 +26,24 @@ module RplLang
26
26
  @stack << Types.new_object( RplString, "\"#{args[0].value}: #{word.nil? ? 'not a core word' : word[:help]}\"" )
27
27
  end )
28
28
 
29
- @dictionary.add_word( ['words'],
30
- 'REPL',
31
- 'DEBUG',
32
- proc do
33
- @dictionary.words
34
- .to_a
35
- .group_by { |word| word.last[:category] }
36
- .each do |cat, words|
37
- puts cat
38
- puts " #{words.map(&:first).join(', ')}"
39
- end
40
- end )
41
-
42
29
  @dictionary.add_word( ['quit'],
43
- 'General',
30
+ category,
44
31
  '( -- ) Stop and quit interpreter',
45
32
  proc {} )
46
33
 
47
34
  @dictionary.add_word( ['version'],
48
- 'General',
35
+ category,
49
36
  '( -- n ) Pop the interpreter\'s version number',
50
37
  proc do
51
38
  @stack << Types.new_object( RplString, "\"#{@version}\"" )
52
39
  end )
53
40
 
54
41
  @dictionary.add_word( ['uname'],
55
- 'General',
42
+ category,
56
43
  '( -- s ) Pop the interpreter\'s complete indentification string',
57
44
  proc do
58
45
  @stack << Types.new_object( RplString, "\"Rpl Interpreter version #{@version}\"" )
59
46
  end )
60
-
61
- @dictionary.add_word( ['history'],
62
- 'REPL',
63
- '',
64
- proc {} )
65
-
66
- @dictionary.add_word( ['edit'],
67
- 'General',
68
- '( -- s ) Pop the interpreter\'s complete indentification string',
69
- proc do
70
- args = stack_extract( [:any] )
71
-
72
- value = args[0].to_s
73
- tempfile = Tempfile.new('rpl')
74
-
75
- begin
76
- tempfile.write( value )
77
- tempfile.rewind
78
-
79
- `$EDITOR #{tempfile.path}`
80
-
81
- edited_value = tempfile.read
82
- ensure
83
- tempfile.close
84
- tempfile.unlink
85
- end
86
-
87
- @stack << Types.new_object( args[0].class, edited_value )
88
- end )
89
-
90
- @dictionary.add_word( ['.s'],
91
- 'REPL',
92
- 'DEBUG',
93
- proc { pp @stack } )
94
-
95
- @dictionary.add_word( ['.d'],
96
- 'REPL',
97
- 'DEBUG',
98
- proc { pp @dictionary } )
99
-
100
- @dictionary.add_word( ['.v'],
101
- 'REPL',
102
- 'DEBUG',
103
- proc { pp @dictionary.vars } )
104
-
105
- @dictionary.add_word( ['.lv'],
106
- 'REPL',
107
- 'DEBUG',
108
- proc { pp @dictionary.local_vars_layers } )
109
47
  end
110
48
  end
111
49
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RplLang
4
+ module Words
5
+ module List
6
+ include Types
7
+
8
+ def populate_dictionary
9
+ super
10
+
11
+ category = 'Lists'
12
+
13
+ @dictionary.add_word( ['→list', '->list'],
14
+ category,
15
+ '( … x -- […] ) pack x stacks levels into a list',
16
+ proc do
17
+ args = stack_extract( [[RplNumeric]] )
18
+ args = stack_extract( %i[any] * args[0].value )
19
+
20
+ @stack << Types.new_object( RplList, args.reverse )
21
+ end )
22
+
23
+ @dictionary.add_word( ['list→', 'list->'],
24
+ category,
25
+ '( […] -- … ) unpack list on stack',
26
+ proc do
27
+ args = stack_extract( [[RplList]] )
28
+
29
+ args[0].value.each do |elt|
30
+ @stack << elt
31
+ end
32
+ end )
33
+
34
+ @dictionary.add_word( ['dolist'],
35
+ category,
36
+ '( […] prg -- … ) run prg on each element of a list',
37
+ proc do
38
+ args = stack_extract( [[RplProgram], [RplList]] )
39
+
40
+ args[1].value.each do |elt|
41
+ @stack << elt
42
+ run( args[0].value )
43
+ end
44
+
45
+ run( "#{args[1].value.length} →list" )
46
+ end )
47
+ end
48
+ end
49
+ end
50
+ end
@@ -8,106 +8,108 @@ module RplLang
8
8
  def populate_dictionary
9
9
  super
10
10
 
11
+ category = 'Logs on reals and complexes'
12
+
11
13
  @dictionary.add_word( ['ℇ', 'e'],
12
- 'Logs on reals and complexes',
14
+ category,
13
15
  '( … -- ℇ ) push ℇ',
14
16
  proc do
15
17
  @stack << Types.new_object( RplNumeric, BigMath.E( RplNumeric.precision ) )
16
18
  end )
17
19
 
18
20
  # @dictionary.add_word( ['ln'],
19
- # 'Logs on reals and complexes',
21
+ # category,
20
22
  # 'logarithm base e',
21
23
  # proc do
22
24
 
23
25
  # end )
24
26
 
25
27
  # @dictionary.add_word( ['lnp1'],
26
- # 'Logs on reals and complexes',
28
+ # category,
27
29
  # 'ln(1+x) which is useful when x is close to 0',
28
30
  # proc do
29
31
 
30
32
  # end )
31
33
 
32
34
  # @dictionary.add_word( ['exp'],
33
- # 'Logs on reals and complexes',
35
+ # category,
34
36
  # 'exponential',
35
37
  # proc do
36
38
 
37
39
  # end )
38
40
 
39
41
  # @dictionary.add_word( ['expm'],
40
- # 'Logs on reals and complexes',
42
+ # category,
41
43
  # 'exp(x)-1 which is useful when x is close to 0',
42
44
  # proc do
43
45
 
44
46
  # end )
45
47
 
46
48
  # @dictionary.add_word( ['log10'],
47
- # 'Logs on reals and complexes',
49
+ # category,
48
50
  # 'logarithm base 10',
49
51
  # proc do
50
52
 
51
53
  # end )
52
54
 
53
55
  # @dictionary.add_word( ['alog10'],
54
- # 'Logs on reals and complexes',
56
+ # category,
55
57
  # 'exponential base 10',
56
58
  # proc do
57
59
 
58
60
  # end )
59
61
 
60
62
  # @dictionary.add_word( ['log2'],
61
- # 'Logs on reals and complexes',
63
+ # category,
62
64
  # 'logarithm base 2',
63
65
  # proc do
64
66
 
65
67
  # end )
66
68
 
67
69
  # @dictionary.add_word( ['alog2'],
68
- # 'Logs on reals and complexes',
70
+ # category,
69
71
  # 'exponential base 2',
70
72
  # proc do
71
73
 
72
74
  # end )
73
75
 
74
76
  # @dictionary.add_word( ['sinh'],
75
- # 'Logs on reals and complexes',
77
+ # category,
76
78
  # 'hyperbolic sine',
77
79
  # proc do
78
80
 
79
81
  # end )
80
82
 
81
83
  # @dictionary.add_word( ['asinh'],
82
- # 'Logs on reals and complexes',
84
+ # category,
83
85
  # 'inverse hyperbolic sine',
84
86
  # proc do
85
87
 
86
88
  # end )
87
89
 
88
90
  # @dictionary.add_word( ['cosh'],
89
- # 'Logs on reals and complexes',
91
+ # category,
90
92
  # 'hyperbolic cosine',
91
93
  # proc do
92
94
 
93
95
  # end )
94
96
 
95
97
  # @dictionary.add_word( ['acosh'],
96
- # 'Logs on reals and complexes',
98
+ # category,
97
99
  # 'inverse hyperbolic cosine',
98
100
  # proc do
99
101
 
100
102
  # end )
101
103
 
102
104
  # @dictionary.add_word( ['tanh'],
103
- # 'Logs on reals and complexes',
105
+ # category,
104
106
  # 'hyperbolic tangent',
105
107
  # proc do
106
108
 
107
109
  # end )
108
110
 
109
111
  # @dictionary.add_word( ['atanh'],
110
- # 'Logs on reals and complexes',
112
+ # category,
111
113
  # 'inverse hyperbolic tangent',
112
114
  # proc do
113
115