rpl 0.12.0 → 0.13.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: 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