waltz_rb 0.0.1
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 +7 -0
- data/lib/waltz.rb +211 -0
- metadata +48 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: da3fe36065329e7374616b0d3492f6ededc9f35b
|
4
|
+
data.tar.gz: 7a4efb5890246e5b2b494c1b5d07db43900e24b4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: df1b60bbea9fe24b09afa2106240917876234b3f7b9b300a1fe2b0b032e8648244da1c5fceb36da3a4ba2aa2ebf2a8945b16c2d9168b0b931d94e830c5512aee
|
7
|
+
data.tar.gz: 3fb5cf5b08ad6d08b7adbeb6fb504cc73c50a61a0db671c8e4a53c120bc5a12e6bbefa9392d538d7fc97285ec73dc765bd62b26a2c91ff57670a04660b32a624
|
data/lib/waltz.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'readline'
|
2
|
+
|
3
|
+
class String
|
4
|
+
def remove_backslash_comments
|
5
|
+
self.gsub(/\\.*/, '')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Waltz
|
10
|
+
attr_accessor :runtime_stack, :runtime_words,
|
11
|
+
:control_stack, :control_words,
|
12
|
+
:compiled
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@runtime_stack = []
|
16
|
+
@control_stack = []
|
17
|
+
@compiled = []
|
18
|
+
|
19
|
+
@runtime_words = {
|
20
|
+
'+' => -> {
|
21
|
+
a, b = @runtime_stack.pop(2)
|
22
|
+
@runtime_stack.push (a + b)
|
23
|
+
},
|
24
|
+
|
25
|
+
'*' => -> {
|
26
|
+
a, b = @runtime_stack.pop(2)
|
27
|
+
@runtime_stack.push (a * b)
|
28
|
+
},
|
29
|
+
|
30
|
+
'-' => -> {
|
31
|
+
a, b = @runtime_stack.pop(2)
|
32
|
+
@runtime_stack.push (a - b)
|
33
|
+
},
|
34
|
+
|
35
|
+
'/' => -> {
|
36
|
+
a, b = @runtime_stack.pop(2)
|
37
|
+
@runtime_stack.push (a / b)
|
38
|
+
},
|
39
|
+
|
40
|
+
'=' => -> {
|
41
|
+
a, b = @runtime_stack.pop(2)
|
42
|
+
@runtime_stack.push (a == b)
|
43
|
+
},
|
44
|
+
|
45
|
+
'>' => -> {
|
46
|
+
a, b = @runtime_stack.pop(2)
|
47
|
+
@runtime_stack.push (a > b)
|
48
|
+
},
|
49
|
+
|
50
|
+
'<' => -> {
|
51
|
+
a, b = @runtime_stack.pop(2)
|
52
|
+
@runtime_stack.push (a < b)
|
53
|
+
},
|
54
|
+
|
55
|
+
'swap' => -> {
|
56
|
+
a, b = @runtime_stack.pop(2)
|
57
|
+
@runtime_stack.push b, a
|
58
|
+
},
|
59
|
+
|
60
|
+
'dup' => -> {
|
61
|
+
@runtime_stack.push @runtime_stack.last
|
62
|
+
},
|
63
|
+
|
64
|
+
'drop' => -> {
|
65
|
+
@runtime_stack.pop
|
66
|
+
},
|
67
|
+
|
68
|
+
'over' => -> {
|
69
|
+
@runtime_stack.push @runtime_stack[-2]
|
70
|
+
},
|
71
|
+
|
72
|
+
'..' => -> {
|
73
|
+
puts "stack: #{@runtime_stack.inspect}"
|
74
|
+
},
|
75
|
+
|
76
|
+
'.' => -> {
|
77
|
+
p @runtime_stack.pop
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
@control_words = {
|
82
|
+
':' => -> {
|
83
|
+
if @control_stack.empty?
|
84
|
+
@control_stack.push ":"
|
85
|
+
else
|
86
|
+
raise ": inside control stack: #{@control_stack}"
|
87
|
+
end
|
88
|
+
},
|
89
|
+
|
90
|
+
';' => -> {
|
91
|
+
unless @control_stack.first == ":"
|
92
|
+
raise ": not balanced with ; in control stack: #{@control_stack}"
|
93
|
+
end
|
94
|
+
|
95
|
+
word, *body = @control_stack[1..-1]
|
96
|
+
|
97
|
+
unless word
|
98
|
+
raise "Unnamed word definition in control stack: #{@control_stack}"
|
99
|
+
end
|
100
|
+
|
101
|
+
@runtime_words[word] = body
|
102
|
+
@control_stack = []
|
103
|
+
}
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def state
|
108
|
+
[@runtime_stack.dup, @control_stack.dup, @compiled.dup]
|
109
|
+
end
|
110
|
+
|
111
|
+
def load_state state
|
112
|
+
runtime_stack, control_stack, compiled = state
|
113
|
+
@runtime_stack = runtime_stack
|
114
|
+
@control_stack = control_stack
|
115
|
+
@compiled = compiled
|
116
|
+
end
|
117
|
+
|
118
|
+
def compile line
|
119
|
+
words = line.remove_backslash_comments.split
|
120
|
+
|
121
|
+
words.each do |word|
|
122
|
+
control_action = @control_words[word]
|
123
|
+
runtime_action = @runtime_words[word]
|
124
|
+
|
125
|
+
if @control_stack.first == ":"
|
126
|
+
if @control_stack.count == 1
|
127
|
+
if control_action or runtime_action
|
128
|
+
raise "#{word} is already defined."
|
129
|
+
else
|
130
|
+
@control_stack.push word # name of new word
|
131
|
+
next
|
132
|
+
end
|
133
|
+
else
|
134
|
+
current_stack = @control_stack
|
135
|
+
end
|
136
|
+
else
|
137
|
+
current_stack = @compiled
|
138
|
+
end
|
139
|
+
|
140
|
+
if control_action
|
141
|
+
control_action.call
|
142
|
+
elsif runtime_action
|
143
|
+
if runtime_action.is_a? Array
|
144
|
+
# do dynamic lookup for now
|
145
|
+
current_stack.push -> { run_word word }
|
146
|
+
else
|
147
|
+
current_stack.push runtime_action
|
148
|
+
end
|
149
|
+
else
|
150
|
+
if /(0|[1-9]\d*)\.\d+/ =~ word
|
151
|
+
current_stack.push -> { @runtime_stack.push word.to_f }
|
152
|
+
elsif /0|[1-9]\d*/ =~ word
|
153
|
+
current_stack.push -> { @runtime_stack.push word.to_i }
|
154
|
+
else
|
155
|
+
# assume word will be defined by the time it's run
|
156
|
+
current_stack.push -> { run_word word }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def run_word word
|
163
|
+
runtime_action = @runtime_words[word]
|
164
|
+
if runtime_action.is_a? Proc
|
165
|
+
runtime_action.call
|
166
|
+
elsif runtime_action.is_a? Array
|
167
|
+
runtime_action.each { |action| action.call }
|
168
|
+
elsif runtime_action.is_a? NilClass
|
169
|
+
raise "Undefined word: #{word}"
|
170
|
+
else
|
171
|
+
raise "Unexpected runtime word type #{word}: #{word.class}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def run
|
176
|
+
@compiled.each do |action|
|
177
|
+
action.call
|
178
|
+
end
|
179
|
+
ensure
|
180
|
+
@compiled = []
|
181
|
+
end
|
182
|
+
|
183
|
+
def compile_and_run line
|
184
|
+
compile line
|
185
|
+
if @control_stack.empty?
|
186
|
+
run
|
187
|
+
else
|
188
|
+
raise "Control stack not empty: #{@control_stack.inspect}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def repl
|
193
|
+
prompt = 'waltz> '
|
194
|
+
while line = Readline.readline(prompt, true)
|
195
|
+
save_state = state
|
196
|
+
begin
|
197
|
+
compile line
|
198
|
+
if @control_stack.empty?
|
199
|
+
run
|
200
|
+
prompt = 'waltz> '
|
201
|
+
else
|
202
|
+
prompt = ' ... '
|
203
|
+
end
|
204
|
+
rescue => e
|
205
|
+
p e
|
206
|
+
load_state(save_state)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: waltz_rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dave Yarwood
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-11 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |2
|
14
|
+
Waltz is a portable music theory library implemented in Forth.
|
15
|
+
|
16
|
+
waltz_rb provides an implementation of a Forth interpreter that can
|
17
|
+
load the Waltz library and evaluate strings of Waltz code.
|
18
|
+
email: dave.yarwood@gmail.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/waltz.rb
|
24
|
+
homepage: http://rubygems.org/gems/waltz_rb
|
25
|
+
licenses:
|
26
|
+
- Eclipse Public License
|
27
|
+
metadata: {}
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
require_paths:
|
31
|
+
- lib
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
requirements: []
|
43
|
+
rubyforge_project:
|
44
|
+
rubygems_version: 2.4.6
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: Ruby runtime for the Waltz music theory DSL
|
48
|
+
test_files: []
|