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