rquark 0.1.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/bin/rquark +22 -0
- data/lib/prelude.qrk +118 -0
- data/lib/qeval.rb +178 -0
- data/lib/qparse.rb +42 -0
- data/lib/qrun.rb +54 -0
- data/lib/qtypes.rb +139 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b708c25549f1eec79d6d5b856110b96e0e2565fc
|
4
|
+
data.tar.gz: c57594b3c62fa984da21a8e52cf87d75356efed5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 89954af0306b8dbea34f9ea97634556d4304644d6c2db63f91bac81f94462b3894611609413b209124359e88f52c28587da7fe741da5679e517ced5725bf1456
|
7
|
+
data.tar.gz: c956ac76e8b1c68b35edb6411ad5dc58d45ae6276da334ffbf8343ba16ac47429c8cee8f52e5eea324bb474bab299e247fa5f76e261d13a3cd79959dc45ba272
|
data/bin/rquark
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/qrun'
|
4
|
+
|
5
|
+
|
6
|
+
# setup base vm
|
7
|
+
base_vm = QVM.new([], [], {})
|
8
|
+
unless ARGV.include? '--only-core'
|
9
|
+
base_vm = qrun(File.read File.expand_path('../../lib/prelude.qrk', __FILE__))
|
10
|
+
end
|
11
|
+
|
12
|
+
# get script filenames
|
13
|
+
script_names = ARGV.select { |a| a[0] != '-' }
|
14
|
+
|
15
|
+
# run either REPL or scripts
|
16
|
+
if script_names.empty?
|
17
|
+
qrepl base_vm
|
18
|
+
else
|
19
|
+
script_names.each do |s|
|
20
|
+
qrun(File.read(s), base_vm.stack.dup, base_vm.bindings.dup)
|
21
|
+
end
|
22
|
+
end
|
data/lib/prelude.qrk
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
|
2
|
+
[ x | ] :drop def
|
3
|
+
|
4
|
+
[ drop ] :comment def
|
5
|
+
|
6
|
+
|
7
|
+
"## Handy Macros ##" comment
|
8
|
+
|
9
|
+
[ q n | [ q match ] n def ] :mdef def
|
10
|
+
|
11
|
+
[ call ] :; def
|
12
|
+
|
13
|
+
|
14
|
+
"## Stack Manipulation Operators ##" comment
|
15
|
+
|
16
|
+
[ x | x x ] :dup def
|
17
|
+
|
18
|
+
[ x y | y x ] :swap def
|
19
|
+
|
20
|
+
[ x y | y ] :nip def
|
21
|
+
|
22
|
+
[ x y | x y x ] :dipdup def
|
23
|
+
|
24
|
+
[ x y z | z x y ] :rot def
|
25
|
+
|
26
|
+
[ x y z | y z x ] :-rot def
|
27
|
+
|
28
|
+
[[ x | clear ]] :clear mdef
|
29
|
+
|
30
|
+
|
31
|
+
"## Combinators ##" comment
|
32
|
+
|
33
|
+
[ x y f | x f call y ] :dip def
|
34
|
+
|
35
|
+
[ x y z f | x y f call z ] :double-dip def
|
36
|
+
|
37
|
+
[ w x y z f | w x y f call z ] :triple-dip def
|
38
|
+
|
39
|
+
[ x fa fb | x fa call x fb call ] :bi def
|
40
|
+
|
41
|
+
[ x fa fb fc | x fa call a fb call x fc call ] :tri def
|
42
|
+
|
43
|
+
[ q fa fb | q call fa call q call fb call ] :q-bi def
|
44
|
+
|
45
|
+
[ x y fx fy | x fx call y fy call ] :bi-call def
|
46
|
+
|
47
|
+
[ x y z fx fy fz | x fx call y fy call z fz call ] :tri-call def
|
48
|
+
|
49
|
+
|
50
|
+
"## Numeric Operators ##" comment
|
51
|
+
|
52
|
+
[ -1 * + ] :- def
|
53
|
+
|
54
|
+
[ 1 + ] :++ def
|
55
|
+
|
56
|
+
[ -1 + ] :-- def
|
57
|
+
|
58
|
+
[ 1 swap [ [ dup ] dip * ] times-do swap drop ] :^ def
|
59
|
+
|
60
|
+
[ swap < ] :> def
|
61
|
+
|
62
|
+
[ x y | [ x y ] [<] [=] q-bi or ] :<= def
|
63
|
+
|
64
|
+
[ x y | [ x y ] [>] [=] q-bi or ] :>= def
|
65
|
+
|
66
|
+
|
67
|
+
"## String Operators ##" comment
|
68
|
+
|
69
|
+
[ "" [ weld ] fold ] :multiWeld def
|
70
|
+
|
71
|
+
|
72
|
+
"## Boolean Operators ##" comment
|
73
|
+
|
74
|
+
[ x x | :true ] := def
|
75
|
+
|
76
|
+
[ :true :true | :true ] :and def
|
77
|
+
|
78
|
+
[[ :true x | :true ] [ x :true | :true ]] :or mdef
|
79
|
+
|
80
|
+
[[ :true | :nil ] [ x | :true ]] :not mdef
|
81
|
+
|
82
|
+
[ = not ] :!= def
|
83
|
+
|
84
|
+
[ x y | [ x y ] [!=] [or] q-bi and ] :xor mdef
|
85
|
+
|
86
|
+
|
87
|
+
"## Control Flow ##" comment
|
88
|
+
|
89
|
+
[[ :true x | x call ] [ z x | ]] :if mdef
|
90
|
+
|
91
|
+
[[ :true x y | x call ] [ z x y | y call ]] :if-else mdef
|
92
|
+
|
93
|
+
[[ 0 f | ] [ n f | f call n 1 - f times-do ]] :times-do mdef
|
94
|
+
|
95
|
+
[ p f | p call not [ f call p f while ] if ] :while def
|
96
|
+
|
97
|
+
|
98
|
+
"## Quote Functions ##" comment
|
99
|
+
|
100
|
+
[ @- drop ] :pattern def
|
101
|
+
|
102
|
+
[ @- nip ] :body def
|
103
|
+
|
104
|
+
[ @- swap >> [ swap @+ ] dip ] :@> def
|
105
|
+
|
106
|
+
[ q x | q @- swap x << swap @+ ] :<@ def
|
107
|
+
|
108
|
+
[[ [] x f | x ] [ xs x f | xs >> x f call f fold ]] :fold mdef
|
109
|
+
|
110
|
+
[[ [] f | [] ] [ xs f | xs >> [ f map ] dip f call << ]] :map mdef
|
111
|
+
|
112
|
+
[ [] [ swap << ] fold ] :reverse def
|
113
|
+
|
114
|
+
[ reverse [ swap << ] fold reverse ] :concat def
|
115
|
+
|
116
|
+
[ [] rot [ dup [ << ] dip ++ ] times-do drop ] :range def
|
117
|
+
|
118
|
+
[ 1 swap range ] :iota def
|
data/lib/qeval.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'qtypes'
|
2
|
+
|
3
|
+
## Boilerplate ##
|
4
|
+
|
5
|
+
QuarkError = Class.new(RuntimeError)
|
6
|
+
|
7
|
+
$core_func = {}
|
8
|
+
$core_func_type = {}
|
9
|
+
|
10
|
+
def def_cf(name, type_sig, &code)
|
11
|
+
$core_func_type[name] = type_sig
|
12
|
+
$core_func[name] = code
|
13
|
+
end
|
14
|
+
|
15
|
+
def apply_core_func(name, vm, eval_func)
|
16
|
+
eq, expected, got = type_check(vm.stack, $core_func_type[name])
|
17
|
+
raise(QuarkError, "Type error with function #{name}\n expected a stack of #{expected}\n but got #{got}") if not eq
|
18
|
+
$core_func[name].call(vm, eval_func)
|
19
|
+
end
|
20
|
+
|
21
|
+
def apply_runtime_func(name, vm)
|
22
|
+
quote = vm.bindings[name]
|
23
|
+
vm.program.unshift(*[quote, QAtom.new('call')])
|
24
|
+
end
|
25
|
+
|
26
|
+
def pattern_match(stack_args, args)
|
27
|
+
return false if stack_args.length < args.length
|
28
|
+
return {} if args.length == 0
|
29
|
+
bindings = {}
|
30
|
+
args.zip(stack_args).each do |a, s|
|
31
|
+
if a.is_a? QAtom
|
32
|
+
return false if (bindings.has_key? a.val) && (bindings[a.val] != s)
|
33
|
+
bindings[a.val] = s
|
34
|
+
else
|
35
|
+
return false if a != s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
return bindings
|
39
|
+
end
|
40
|
+
|
41
|
+
def deep_clone x
|
42
|
+
Marshal.load(Marshal.dump(x))
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
## Quark Core Functions ##
|
47
|
+
|
48
|
+
def_cf('+', [:Num, :Num]) do |vm|
|
49
|
+
vm.stack.push QNum.new(vm.stack.pop.val + vm.stack.pop.val)
|
50
|
+
end
|
51
|
+
|
52
|
+
def_cf('*', [:Num, :Num]) do |vm|
|
53
|
+
vm.stack.push QNum.new(vm.stack.pop.val * vm.stack.pop.val)
|
54
|
+
end
|
55
|
+
|
56
|
+
def_cf('/', [:Num, :Num]) do |vm|
|
57
|
+
vm.stack.push QNum.new(vm.stack.pop.val / vm.stack.pop.val)
|
58
|
+
end
|
59
|
+
|
60
|
+
def_cf('<', [:Num, :Num]) do |vm|
|
61
|
+
smaller_than = vm.stack.pop.val > vm.stack.pop.val
|
62
|
+
vm.stack.push QSym.new(smaller_than ? 'true' : 'nil')
|
63
|
+
end
|
64
|
+
|
65
|
+
def_cf('print', [:Str]) do |vm|
|
66
|
+
print vm.stack.pop.val
|
67
|
+
end
|
68
|
+
|
69
|
+
def_cf('.', []) do |vm|
|
70
|
+
puts vm.stack.join(' ')
|
71
|
+
end
|
72
|
+
|
73
|
+
def_cf('>>', [:Quote]) do |vm|
|
74
|
+
vm.stack.push vm.stack.last.pop
|
75
|
+
end
|
76
|
+
|
77
|
+
def_cf('<<', [:Quote, :Any]) do |vm|
|
78
|
+
vm.stack[-2].push vm.stack.pop
|
79
|
+
end
|
80
|
+
|
81
|
+
def_cf('@+', [:Quote, :Quote]) do |vm|
|
82
|
+
q1, q2 = vm.stack.pop(2)
|
83
|
+
vm.stack.push QQuote.new q1.body, q2.body
|
84
|
+
end
|
85
|
+
|
86
|
+
def_cf('@-', [:Quote]) do |vm|
|
87
|
+
q = vm.stack.pop
|
88
|
+
vm.stack.push QQuote.new [], q.pattern
|
89
|
+
vm.stack.push QQuote.new [], q.body
|
90
|
+
end
|
91
|
+
|
92
|
+
def_cf('show', [:Any]) do |vm|
|
93
|
+
vm.stack.push QStr.new(vm.stack.pop.to_s)
|
94
|
+
end
|
95
|
+
|
96
|
+
def_cf('call', [:Quote]) do |vm|
|
97
|
+
quote = vm.stack.pop
|
98
|
+
args = vm.stack.pop(quote.pattern.length)
|
99
|
+
if bindings=pattern_match(args, quote.pattern)
|
100
|
+
vm.program.unshift(*quote.bind(bindings).body)
|
101
|
+
else vm.stack.push QSym.new('nil') end
|
102
|
+
end
|
103
|
+
|
104
|
+
def_cf('match', [:Quote]) do |vm|
|
105
|
+
quotes = vm.stack.pop.body
|
106
|
+
quotes.each do |q|
|
107
|
+
if bindings=pattern_match(vm.stack.last(q.pattern.length), q.pattern)
|
108
|
+
vm.stack.pop(q.pattern.length)
|
109
|
+
vm.program.unshift(*q.bind(bindings).body)
|
110
|
+
break
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def_cf('chars', [:Str]) do |vm|
|
116
|
+
chars = vm.stack.pop.val.chars.map { |c| QStr.new c }
|
117
|
+
vm.stack.push QQuote.new([], chars)
|
118
|
+
end
|
119
|
+
|
120
|
+
def_cf('weld', [:Str, :Str]) do |vm|
|
121
|
+
string_a = vm.stack.pop.val
|
122
|
+
string_b = vm.stack.pop.val
|
123
|
+
vm.stack.push QStr.new(string_b + string_a)
|
124
|
+
end
|
125
|
+
|
126
|
+
def_cf('def', [:Quote, :Sym]) do |vm|
|
127
|
+
name = vm.stack.pop.val
|
128
|
+
quote = vm.stack.pop
|
129
|
+
vm.bindings[name] = quote
|
130
|
+
end
|
131
|
+
|
132
|
+
def_cf('eval', [:Str]) do |vm, eval_func|
|
133
|
+
begin
|
134
|
+
eval_str = vm.stack.pop.val
|
135
|
+
vm2 = eval_func.call(eval_str, vm.stack.dup, vm.bindings.dup)
|
136
|
+
vm.stack, vm.bindings = vm2.stack, vm2.bindings
|
137
|
+
vm.stack.push QSym.new('ok')
|
138
|
+
rescue
|
139
|
+
vm.stack.push QSym.new('not-ok')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def_cf('type', [:Any]) do |vm|
|
144
|
+
type = type_to_qitem vm.stack.pop.qtype
|
145
|
+
vm.stack.push type
|
146
|
+
end
|
147
|
+
|
148
|
+
def_cf('load', [:Str]) do |vm|
|
149
|
+
begin
|
150
|
+
vm.stack.push QStr.new(File.read(vm.stack.pop.val))
|
151
|
+
vm.stack.push QSym.new 'ok'
|
152
|
+
rescue
|
153
|
+
vm.stack.push QSym.new 'not-ok'
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def_cf('cmd', [:Str]) do |vm|
|
158
|
+
begin
|
159
|
+
vm.stack.push QStr.new(IO.popen(vm.stack.pop.val).read)
|
160
|
+
vm.stack.push QSym.new 'ok'
|
161
|
+
rescue
|
162
|
+
vm.stack.push QSym.new 'not-ok'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def_cf('write', [:Str, :Str]) do |vm|
|
167
|
+
begin
|
168
|
+
filename, content = vm.stack.pop.val, vm.stack.pop.val
|
169
|
+
File.open(filename, 'w+') { |f| f << content }
|
170
|
+
vm.stack.push QSym.new 'ok'
|
171
|
+
rescue
|
172
|
+
vm.stack.push QSym.new 'not-ok'
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def_cf('exit', []) do |vm|
|
177
|
+
exit
|
178
|
+
end
|
data/lib/qparse.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
require 'qtypes'
|
3
|
+
|
4
|
+
## Parsing ##
|
5
|
+
|
6
|
+
class QuarkParse < Parslet::Parser
|
7
|
+
rule(:integer) { match('[0-9]').repeat(1) }
|
8
|
+
rule(:num) { (str("-").maybe >> integer >> (str('.') >> integer).maybe).as(:num) >> sep? } # Number
|
9
|
+
rule(:atom) { match('[^0-9\[\]|:"\' \n\t]').repeat(1).as(:atom) >> sep? } # Function or Variable
|
10
|
+
rule(:sym) { str(':') >> match('[^0-9\[\]|:"\' \n\t]').repeat(1).as(:sym) >> sep? } # Symbol
|
11
|
+
rule(:stringA) { str("'") >> match("[^']").repeat(0).as(:string) >> str("'") }
|
12
|
+
rule(:stringB) { str('"') >> match('[^"]').repeat(0).as(:string) >> str('"') }
|
13
|
+
rule(:string) { (stringA | stringB) >> sep? } # String
|
14
|
+
rule(:quote) { # Quote
|
15
|
+
str("[") >> sep? >>
|
16
|
+
(qexpr >> sep? >> str("|") >> sep?).maybe.as(:pattern) >>
|
17
|
+
qexpr.as(:body) >> sep? >> str("]") >> sep?
|
18
|
+
}
|
19
|
+
rule(:qexpr) { sep? >> ((num | atom | sym | string | quote)).repeat(0) }
|
20
|
+
rule(:sep) { (match('\s') | match('\n') | match('\t')).repeat(1) }
|
21
|
+
rule(:sep?) { sep.maybe }
|
22
|
+
root :qexpr
|
23
|
+
end
|
24
|
+
|
25
|
+
class QuarkTransform < Parslet::Transform
|
26
|
+
rule(:num => simple(:x)) { QNum.new(x.to_f) }
|
27
|
+
rule(:atom => simple(:x)) { QAtom.new(x.to_s) }
|
28
|
+
rule(:sym => simple(:x)) { QSym.new(x.to_s) }
|
29
|
+
rule(:string => simple(:x)) { QStr.new(x.to_s) }
|
30
|
+
rule(:string => sequence(:x)) { QStr.new('') }
|
31
|
+
rule(:pattern => sequence(:a), :body => sequence(:b)) { QQuote.new(a, b) }
|
32
|
+
rule(:pattern => simple(:a), :body => sequence(:b)) { QQuote.new([], b) }
|
33
|
+
rule(:pattern => sequence(:a), :body => simple(:b)) { QQuote.new(a, []) }
|
34
|
+
rule(:pattern => simple(:a), :body => simple(:b)) { QQuote.new([], []) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def qparse str
|
38
|
+
parsed = QuarkParse.new.parse(str)
|
39
|
+
QuarkTransform.new.apply parsed
|
40
|
+
rescue Parslet::ParseFailed => e
|
41
|
+
e.cause.ascii_tree
|
42
|
+
end
|
data/lib/qrun.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'qtypes'
|
2
|
+
require 'qparse'
|
3
|
+
require 'qeval'
|
4
|
+
|
5
|
+
## Evaluation ##
|
6
|
+
|
7
|
+
QVM = Struct.new(:stack, :program, :bindings)
|
8
|
+
|
9
|
+
def qrun(str, stack=[], bindings={})
|
10
|
+
qreduce QVM.new(stack, qparse(str), bindings)
|
11
|
+
end
|
12
|
+
|
13
|
+
def qreduce vm
|
14
|
+
until vm.program.empty?
|
15
|
+
item = vm.program.shift
|
16
|
+
if item.is_a? QAtom
|
17
|
+
if $core_func.has_key? item.val
|
18
|
+
apply_core_func(item.val, vm, ->(*x){qrun(*x)})
|
19
|
+
elsif vm.bindings.has_key? item.val
|
20
|
+
quote = vm.bindings[item.val]
|
21
|
+
vm.program.unshift(*[quote.dup, QAtom.new('call')])
|
22
|
+
else
|
23
|
+
raise QuarkError, "No such function: #{item}"
|
24
|
+
end
|
25
|
+
else
|
26
|
+
vm.stack.push item
|
27
|
+
end
|
28
|
+
end
|
29
|
+
return vm
|
30
|
+
end
|
31
|
+
|
32
|
+
def qrepl(vm)
|
33
|
+
loop do
|
34
|
+
print ':> '
|
35
|
+
begin
|
36
|
+
input = $stdin.gets.chomp
|
37
|
+
case input.strip
|
38
|
+
when '*q'
|
39
|
+
exit!
|
40
|
+
when '*f'
|
41
|
+
vm.bindings.sort.each { |k, v| puts "#{k}\n #{v.to_s}\n\n"}
|
42
|
+
when /\*f\s+(.+)/
|
43
|
+
if vm.bindings.has_key? $1
|
44
|
+
puts vm.bindings[$1]
|
45
|
+
else puts "No such function: #{$1}" end
|
46
|
+
else
|
47
|
+
vm = qrun(input, vm.stack.dup, vm.bindings.dup)
|
48
|
+
end
|
49
|
+
puts vm.stack.map { |x| x.is_a?(QQuote) ? x.to_s(20) : x.to_s }.join(' ')
|
50
|
+
rescue Exception => e
|
51
|
+
puts e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/qtypes.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
## Quark Values ##
|
3
|
+
|
4
|
+
# parent class for all the quark primitive values
|
5
|
+
class QVal
|
6
|
+
|
7
|
+
attr_accessor :val
|
8
|
+
|
9
|
+
def initialize(v)
|
10
|
+
@val = v
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
@val.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(a)
|
18
|
+
return @val == a.val if a.is_a? self.class
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# quark numbers
|
25
|
+
class QNum < QVal
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
@val.to_i == @val ? @val.to_i.to_s : @val.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
def qtype; :Num end
|
32
|
+
end
|
33
|
+
|
34
|
+
# quark functions/variables
|
35
|
+
class QAtom < QVal
|
36
|
+
def qtype; :Atom end
|
37
|
+
end
|
38
|
+
|
39
|
+
# quark symbols
|
40
|
+
class QSym < QVal
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
":#{@val.to_s}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def qtype; :Sym end
|
47
|
+
end
|
48
|
+
|
49
|
+
# quark strings
|
50
|
+
class QStr < QVal
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"\"#{@val.to_s}\""
|
54
|
+
end
|
55
|
+
|
56
|
+
def qtype; :Str end
|
57
|
+
end
|
58
|
+
|
59
|
+
# class for quark quotes
|
60
|
+
class QQuote
|
61
|
+
|
62
|
+
attr_accessor :pattern, :body
|
63
|
+
|
64
|
+
def initialize(p, b)
|
65
|
+
@pattern = p
|
66
|
+
@body = b
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s(n=nil)
|
70
|
+
return "[]" if @body.empty? and @pattern.empty?
|
71
|
+
serialize = lambda do |arr|
|
72
|
+
xs = n ? arr.first(n) : arr
|
73
|
+
xs.map { |x| x.is_a?(QQuote) ? x.to_s(n) : x.to_s }.join(' ') + (n && arr.length > n ? ' ...' : '')
|
74
|
+
end
|
75
|
+
pattern_str = " #{serialize.call(@pattern)} |" if !@pattern.empty?
|
76
|
+
body_str = " #{serialize.call(@body)} " if !@body.empty?
|
77
|
+
"[#{pattern_str}#{body_str}]"
|
78
|
+
end
|
79
|
+
|
80
|
+
def dup
|
81
|
+
Marshal.load(Marshal.dump(self))
|
82
|
+
end
|
83
|
+
|
84
|
+
def ==(x)
|
85
|
+
if x.is_a? QQuote
|
86
|
+
(@pattern == x.pattern) && (@body == x.body)
|
87
|
+
else false end
|
88
|
+
end
|
89
|
+
|
90
|
+
def qtype; :Quote end
|
91
|
+
|
92
|
+
# pushes to quote body
|
93
|
+
def push x
|
94
|
+
@body.push x
|
95
|
+
end
|
96
|
+
|
97
|
+
# pops from quark body
|
98
|
+
def pop
|
99
|
+
@body.pop
|
100
|
+
end
|
101
|
+
|
102
|
+
# takes a hash of var strings to quark items and recursively subs its body
|
103
|
+
def bind bindings
|
104
|
+
@body.map! do |x|
|
105
|
+
if x.is_a? QQuote then x.bind bindings
|
106
|
+
elsif x.is_a? QAtom then bindings[x.val] || x
|
107
|
+
else x end
|
108
|
+
end
|
109
|
+
return self
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
## Type Checking ##
|
116
|
+
|
117
|
+
# compares two qtype to see if they are equivelent
|
118
|
+
# type comparison is not symmetric
|
119
|
+
# `a` is the signature type, so (:Any, :Empty) will match, but (:Empty, :Any) won't
|
120
|
+
def type_match(a, b)
|
121
|
+
return true if a == :Any
|
122
|
+
a == b
|
123
|
+
end
|
124
|
+
|
125
|
+
# matches a type signature against a data stack
|
126
|
+
def type_check(stack, type_sig)
|
127
|
+
return false if stack.length < type_sig.length
|
128
|
+
return true if type_sig.length == 0
|
129
|
+
stack_type = stack.last(type_sig.length).map(&:qtype)
|
130
|
+
types_eq = type_sig.zip(stack_type)
|
131
|
+
.map { |sig, type| type_match(sig, type) }
|
132
|
+
.inject(true) { |a, b| a && b}
|
133
|
+
return types_eq, type_sig, stack_type
|
134
|
+
end
|
135
|
+
|
136
|
+
# converts a qtype to a qitem representation (used in the `type` function)
|
137
|
+
def type_to_qitem t
|
138
|
+
return QSym.new(t.to_s)
|
139
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rquark
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "⊥"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: parslet
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
description: This is a ruby implementation of the Quark language
|
28
|
+
email:
|
29
|
+
executables:
|
30
|
+
- rquark
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- bin/rquark
|
35
|
+
- lib/prelude.qrk
|
36
|
+
- lib/qeval.rb
|
37
|
+
- lib/qparse.rb
|
38
|
+
- lib/qrun.rb
|
39
|
+
- lib/qtypes.rb
|
40
|
+
homepage: http://kdt.io/~/quark
|
41
|
+
licenses:
|
42
|
+
- CC0
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 2.4.5
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: Quark, A Functional, Purely Homoiconic, Concatenative Language
|
64
|
+
test_files: []
|