rpl 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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