rpl 0.12.0 → 0.13.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: 745f492092d7e5dd03c90ca25ee5de900832730342075295a8f9d190734ed4d4
4
- data.tar.gz: b7a56161fcf77d5b90b015e4bc67ba3a770605aba261c46f40adfbac1232daa6
3
+ metadata.gz: 81bb665b199a735ae446f0c00d77ca0d7f0e7d0ee2a6ca676f7c3dc159501fbb
4
+ data.tar.gz: 9b4bbb9d917e49d34d14a2457cf665fe4e701ed9bc89334c076c8c7fca0f1937
5
5
  SHA512:
6
- metadata.gz: 4df6860f009ab1ae4c957000e98fd7f39be60c2d9370ab0626a60e71a84625b14e9d5bb9142c53d178d602b2654ec95ade37d4bf03825245a318d7da9f5a6296
7
- data.tar.gz: 19f365a0e10430051ba6ca75cbe55d4504fb3ef2e6d778e4114d335f20c558f7921b9921b949caf11eb3fccb1538f014f3c2a396421648fee0035fa8eac08f67
6
+ metadata.gz: ddd530974f32189a9b76ea9bb932e8452eb6ccdeb88f06754bae7c085289bb2989e2fd165c8d234056657b91d8575ab388e6d19f32163e589033f4949eeb20bb
7
+ data.tar.gz: 6e64d40c9a72f72dce91254b450d53ab1a204a503599a7b92bf8c6ee1148c0ca6049bdcda9b611289d06aa34b9364f6a86832e20641d8acd44522276b715ad10
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # rpl.rb
2
+
3
+ Reverse-Polish-Lisp inspired language in ruby
4
+
5
+ # Install
6
+ `gem install rpl`
7
+
8
+ # Usage
9
+ * `rpl --help`
10
+ * run RPL: `rpl`
11
+ * by default rpl persists its state in ~/.local/state/rpl.rb/machine which is created if needed
12
+
13
+ # Development
14
+ To run REPL locally: `rake run`
15
+
16
+ To run the test suite: `rake test`
17
+
18
+ To build gem: `rake gem`
19
+
20
+ # known bugs
21
+ -
22
+
23
+ # TODO-list
24
+ * Language:
25
+ * pseudo filesystem: subdir/namespace for variables
26
+ . 'a dir' crdir 'a dir' cd vars
27
+ * SDL-based graphic environment/API
28
+
29
+ # Not yet implemented
30
+ ```sh
31
+ $ grep "# @dictionary.add_word" ./lib/rpl/words/*.rb
32
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['ln'],
33
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['lnp1'],
34
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['exp'],
35
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['expm'],
36
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['log10'],
37
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['alog10'],
38
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['log2'],
39
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['alog2'],
40
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['sinh'],
41
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['asinh'],
42
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['cosh'],
43
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['acosh'],
44
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['tanh'],
45
+ ./lib/rpl/words/logarithm.rb: # @dictionary.add_word!( ['atanh'],
46
+ ./lib/rpl/words/mode.rb: # @dictionary.add_word!( ['std'],
47
+ ./lib/rpl/words/mode.rb: # @dictionary.add_word!( ['fix'],
48
+ ./lib/rpl/words/mode.rb: # @dictionary.add_word!( ['sci'],
49
+ ./lib/rpl/words/mode.rb: # @dictionary.add_word!( ['round'],
50
+ ./lib/rpl/words/operations-complexes.rb: # @dictionary.add_word!( ['p→r', 'p->r'],
51
+ ./lib/rpl/words/operations-complexes.rb: # @dictionary.add_word!( ['r→p', 'r->p'],
52
+ ```
53
+
54
+ # No implementation planned
55
+ * use IFT, IFTE instead of
56
+ . if
57
+ . then
58
+ . else
59
+ . end
60
+ * use LOOP, TIMES instead of
61
+ . start
62
+ . for
63
+ . next
64
+ . step
65
+ . do
66
+ . until
67
+ . while
68
+ . repeat
69
+ * use LSTO instead of
70
+ . ->, →
71
+
72
+ # inspirations and references
73
+ * https://en.wikipedia.org/wiki/RPL_(programming_language)
74
+ * https://github.com/louisrubet/rpn/
data/bin/rpl CHANGED
@@ -19,7 +19,7 @@ class RplRepl
19
19
  @interpreter = interpreter
20
20
  end
21
21
 
22
- def run
22
+ def run!
23
23
  print_stack unless @interpreter.stack.empty?
24
24
 
25
25
  Reline.completion_proc = proc do |s|
@@ -48,13 +48,13 @@ class RplRepl
48
48
  prefill = @interpreter.stack.pop.to_s
49
49
  elsif input.strip == 'history'
50
50
  history = Reline::HISTORY.map { |line| "\"#{line}\"" }.join(' ')
51
- @interpreter.run( "{ #{history} }" )
51
+ @interpreter.run!( "{ #{history} }" )
52
52
  elsif input.empty?
53
53
  # Remove blank lines from history
54
54
  Reline::HISTORY.pop
55
55
  else
56
56
  begin
57
- @interpreter.run( input )
57
+ @interpreter.run!( input )
58
58
  rescue ArgumentError => e
59
59
  pp e
60
60
  end
@@ -117,17 +117,15 @@ interpreter = Rpl.new( persistence_filename: options[:persistence_filename],
117
117
 
118
118
  # first run provided files if any
119
119
  options[:files].each do |filename|
120
- interpreter.run "\"#{File.expand_path( filename )}\" feval"
120
+ interpreter.run!( "\"#{File.expand_path( filename )}\" feval" )
121
121
  end
122
122
 
123
123
  # second run provided code if any
124
124
  options[:programs].each do |program|
125
- interpreter.run program
125
+ interpreter.run!( program )
126
126
  end
127
127
 
128
128
  # third launch REPL if (explicitely or implicitely) asked
129
- if options[:run_REPL]
130
- RplRepl.new( interpreter: interpreter ).run
131
- else
132
- interpreter.persist_state
133
- end
129
+ RplRepl.new( interpreter: interpreter ).run! if options[:run_REPL]
130
+
131
+ interpreter.persist_state
@@ -11,7 +11,7 @@ class Dictionary
11
11
  @local_vars_layers = []
12
12
  end
13
13
 
14
- def add_word( names, category, help, implementation )
14
+ def add_word!( names, category, help, implementation )
15
15
  names.each do |name|
16
16
  @words[ name ] = { category: category,
17
17
  help: help,
@@ -19,43 +19,43 @@ class Dictionary
19
19
  end
20
20
  end
21
21
 
22
- def add_var( name, implementation )
22
+ def add_var!( name, implementation )
23
23
  @vars[ name ] = implementation
24
24
  end
25
25
 
26
- def remove_vars( names )
26
+ def remove_vars!( names )
27
27
  names.each do |name|
28
28
  @vars.delete( name )
29
29
  end
30
30
  end
31
31
 
32
- def remove_var( name )
33
- remove_vars( [name] )
32
+ def remove_var!( name )
33
+ remove_vars!( [name] )
34
34
  end
35
35
 
36
- def remove_all_vars
36
+ def remove_all_vars!
37
37
  @vars = {}
38
38
  end
39
39
 
40
- def add_local_vars_layer
40
+ def add_local_vars_layer!
41
41
  @local_vars_layers << {}
42
42
  end
43
43
 
44
- def add_local_var( name, implementation )
44
+ def add_local_var!( name, implementation )
45
45
  @local_vars_layers.last[ name ] = implementation
46
46
  end
47
47
 
48
- def remove_local_vars( names )
48
+ def remove_local_vars!( names )
49
49
  names.each do |name|
50
50
  @local_vars_layers.last.delete( name )
51
51
  end
52
52
  end
53
53
 
54
- def remove_local_var( name )
54
+ def remove_local_var!( name )
55
55
  remove_local_vars( [name] )
56
56
  end
57
57
 
58
- def remove_local_vars_layer
58
+ def remove_local_vars_layer!
59
59
  @local_vars_layers.pop
60
60
  end
61
61
 
@@ -68,7 +68,7 @@ class Dictionary
68
68
  word ||= @vars[ name ]
69
69
 
70
70
  # or is it a core word
71
- word ||= @words[ name ].nil? ? nil : @words[ name ][:implementation]
71
+ word ||= @words[ name.downcase ].nil? ? nil : @words[ name.downcase ][:implementation]
72
72
 
73
73
  word
74
74
  end
@@ -47,8 +47,8 @@ class Interpreter
47
47
  @frame_buffer = BitArray.new
48
48
  end
49
49
 
50
- def run( input )
51
- @dictionary.add_local_vars_layer
50
+ def run!( input )
51
+ @dictionary.add_local_vars_layer!
52
52
 
53
53
  Parser.parse( input.to_s ).each do |elt|
54
54
  if elt.instance_of?( RplName )
@@ -64,7 +64,7 @@ class Interpreter
64
64
  elsif command.is_a?( Proc )
65
65
  command.call
66
66
  elsif command.instance_of?( RplProgram )
67
- run( command.value )
67
+ run!( command.value )
68
68
  else
69
69
  @stack << command
70
70
  end
@@ -74,7 +74,7 @@ class Interpreter
74
74
  end
75
75
  end
76
76
 
77
- @dictionary.remove_local_vars_layer
77
+ @dictionary.remove_local_vars_layer!
78
78
 
79
79
  # superfluous but feels nice
80
80
  @stack
@@ -10,45 +10,45 @@ module RplLang
10
10
 
11
11
  category = 'Branch'
12
12
 
13
- @dictionary.add_word( ['ift'],
14
- category,
15
- '( t pt -- … ) eval pt or not based on the value of boolean t',
16
- Types.new_object( RplProgram, '« « nop » ifte »' ) )
17
-
18
- @dictionary.add_word( ['ifte'],
19
- category,
20
- '( t pt pf -- … ) eval pt or pf based on the value of boolean t',
21
- proc do
22
- args = stack_extract( [:any, :any, [RplBoolean]] )
23
-
24
- run( args[ args[2].value ? 1 : 0 ].value )
25
- end )
26
-
27
- @dictionary.add_word( ['times'],
28
- category,
29
- '( p n -- … ) eval p n times while pushing counter on stack before',
30
- proc do
31
- args = stack_extract( [[RplNumeric], :any] )
32
-
33
- args[0].value.to_i.times do |counter|
34
- @stack << Types.new_object( RplNumeric, counter )
35
-
36
- run( args[1].value )
37
- end
38
- end )
39
-
40
- @dictionary.add_word( ['loop'],
41
- category,
42
- '( p n1 n2 -- … ) eval p looping from n1 to n2 while pushing counter on stack before',
43
- proc do
44
- args = stack_extract( [[RplNumeric], [RplNumeric], :any] )
45
-
46
- ((args[1].value.to_i)..(args[0].value.to_i)).each do |counter|
47
- @stack << Types.new_object( RplNumeric, counter )
48
-
49
- run( args[2].value )
50
- end
51
- end )
13
+ @dictionary.add_word!( ['ift'],
14
+ category,
15
+ '( t pt -- … ) eval pt or not based on the value of boolean t',
16
+ Types.new_object( RplProgram, '« « nop » ifte »' ) )
17
+
18
+ @dictionary.add_word!( ['ifte'],
19
+ category,
20
+ '( t pt pf -- … ) eval pt or pf based on the value of boolean t',
21
+ proc do
22
+ args = stack_extract( [:any, :any, [RplBoolean]] )
23
+
24
+ run!( args[ args[2].value ? 1 : 0 ].value )
25
+ end )
26
+
27
+ @dictionary.add_word!( ['times'],
28
+ category,
29
+ '( p n -- … ) eval p n times while pushing counter on stack before',
30
+ proc do
31
+ args = stack_extract( [[RplNumeric], :any] )
32
+
33
+ args[0].value.to_i.times do |counter|
34
+ @stack << Types.new_object( RplNumeric, counter )
35
+
36
+ run!( args[1].value )
37
+ end
38
+ end )
39
+
40
+ @dictionary.add_word!( ['loop'],
41
+ category,
42
+ '( p n1 n2 -- … ) eval p looping from n1 to n2 while pushing counter on stack before',
43
+ proc do
44
+ args = stack_extract( [[RplNumeric], [RplNumeric], :any] )
45
+
46
+ ((args[1].value.to_i)..(args[0].value.to_i)).each do |counter|
47
+ @stack << Types.new_object( RplNumeric, counter )
48
+
49
+ run!( args[2].value )
50
+ end
51
+ end )
52
52
  end
53
53
  end
54
54
  end
@@ -10,19 +10,19 @@ module RplLang
10
10
 
11
11
  category = 'Display'
12
12
 
13
- @dictionary.add_word( ['erase'],
14
- category,
15
- '( -- ) erase display',
16
- proc do
17
- initialize_frame_buffer
18
- end )
13
+ @dictionary.add_word!( ['erase'],
14
+ category,
15
+ '( -- ) erase display',
16
+ proc do
17
+ initialize_frame_buffer
18
+ end )
19
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 )
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
26
  end
27
27
  end
28
28
  end
@@ -10,31 +10,31 @@ module RplLang
10
10
 
11
11
  category = 'Filesystem'
12
12
 
13
- @dictionary.add_word( ['fread'],
14
- category,
15
- '( filename -- content ) read file and put content on stack as string',
16
- proc do
17
- args = stack_extract( [[RplString]] )
18
-
19
- path = File.expand_path( args[0].value )
20
-
21
- @stack << Types.new_object( RplString, "\"#{File.read( path )}\"" )
22
- end )
23
-
24
- @dictionary.add_word( ['feval'],
25
- category,
26
- '( filename -- … ) read and run file',
27
- Types.new_object( RplProgram, '« fread eval »' ) )
28
-
29
- @dictionary.add_word( ['fwrite'],
30
- category,
31
- '( content filename -- ) write content into filename',
32
- proc do
33
- args = stack_extract( [[RplString], :any] )
34
-
35
- File.write( File.expand_path( args[0].value ),
36
- args[1].value )
37
- end )
13
+ @dictionary.add_word!( ['fread'],
14
+ category,
15
+ '( filename -- content ) read file and put content on stack as string',
16
+ proc do
17
+ args = stack_extract( [[RplString]] )
18
+
19
+ path = File.expand_path( args[0].value )
20
+
21
+ @stack << Types.new_object( RplString, "\"#{File.read( path )}\"" )
22
+ end )
23
+
24
+ @dictionary.add_word!( ['feval'],
25
+ category,
26
+ '( filename -- … ) read and run file',
27
+ Types.new_object( RplProgram, '« fread eval »' ) )
28
+
29
+ @dictionary.add_word!( ['fwrite'],
30
+ category,
31
+ '( content filename -- ) write content into filename',
32
+ proc do
33
+ args = stack_extract( [[RplString], :any] )
34
+
35
+ File.write( File.expand_path( args[0].value ),
36
+ args[1].value )
37
+ end )
38
38
  end
39
39
  end
40
40
  end
@@ -10,38 +10,38 @@ module RplLang
10
10
 
11
11
  category = 'General'
12
12
 
13
- @dictionary.add_word( ['nop'],
14
- category,
15
- '( -- ) no operation',
16
- proc {} )
17
-
18
- @dictionary.add_word( ['help'],
19
- category,
20
- '( w -- s ) pop help string of the given word',
21
- proc do
22
- args = stack_extract( [[RplName]] )
23
-
24
- word = @dictionary.words[ args[0].value ]
25
-
26
- @stack << Types.new_object( RplString, "\"#{args[0].value}: #{word.nil? ? 'not a core word' : word[:help]}\"" )
27
- end )
28
-
29
- @dictionary.add_word( ['quit'],
30
- category,
31
- '( -- ) Stop and quit interpreter',
32
- proc {} )
33
-
34
- @dictionary.add_word( ['version'],
35
- category,
36
- '( -- n ) Pop the interpreter\'s version number',
37
- proc do
38
- @stack << Types.new_object( RplString, "\"#{Rpl::VERSION}\"" )
39
- end )
40
-
41
- @dictionary.add_word( ['uname'],
42
- category,
43
- '( -- s ) Pop the interpreter\'s complete indentification string',
44
- Types.new_object( RplProgram, '« "Rpl Interpreter version " version + »' ) )
13
+ @dictionary.add_word!( ['nop'],
14
+ category,
15
+ '( -- ) no operation',
16
+ proc {} )
17
+
18
+ @dictionary.add_word!( ['help'],
19
+ category,
20
+ '( w -- s ) pop help string of the given word',
21
+ proc do
22
+ args = stack_extract( [[RplName]] )
23
+
24
+ word = @dictionary.words[ args[0].value ]
25
+
26
+ @stack << Types.new_object( RplString, "\"#{args[0].value}: #{word.nil? ? 'not a core word' : word[:help]}\"" )
27
+ end )
28
+
29
+ @dictionary.add_word!( ['quit'],
30
+ category,
31
+ '( -- ) Stop and quit interpreter',
32
+ proc {} )
33
+
34
+ @dictionary.add_word!( ['version'],
35
+ category,
36
+ '( -- n ) Pop the interpreter\'s version number',
37
+ proc do
38
+ @stack << Types.new_object( RplString, "\"#{Rpl::VERSION}\"" )
39
+ end )
40
+
41
+ @dictionary.add_word!( ['uname'],
42
+ category,
43
+ '( -- s ) Pop the interpreter\'s complete indentification string',
44
+ Types.new_object( RplProgram, '« "Rpl Interpreter version " version + »' ) )
45
45
  end
46
46
  end
47
47
  end
@@ -10,40 +10,40 @@ module RplLang
10
10
 
11
11
  category = 'Lists'
12
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 )
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
47
  end
48
48
  end
49
49
  end