iode 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +78 -14
- data/lib/iode.rb +4 -1
- data/lib/iode/core.rb +53 -0
- data/lib/iode/{built_ins.rb → core/comparisons.rb} +17 -39
- data/lib/iode/core/lists.rb +35 -0
- data/lib/iode/core/math.rb +41 -0
- data/lib/iode/core/output.rb +33 -0
- data/lib/iode/interpreter.rb +32 -8
- data/lib/iode/scope.rb +28 -9
- data/lib/iode/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef8311e9162a113a3aff4d5741524ac2bd6eafcb
|
4
|
+
data.tar.gz: 56634c23acfedee8d05d6bc38a78392dc9df0865
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 295055612ba622c3f84503f957e996a9996f32201bf934c571c63a17c93144dad7fe7fa206abfe469c5f4dc0e34262c12f1b19a9e69c24dabe9a9c401a25f837
|
7
|
+
data.tar.gz: fc8b1824b95dbf0583917390f64b4df9614f320e4d0c972d6c8e8d81f84518a9be58d5b2e570feaaf13d5178efddbbb6edee38f0368e03a9cda27be6a6455db9
|
data/README.md
CHANGED
@@ -53,11 +53,13 @@ with Ruby.
|
|
53
53
|
``` ruby
|
54
54
|
require "iode"
|
55
55
|
|
56
|
-
|
56
|
+
result = Iode.run <<-PROG
|
57
57
|
(if ((lambda (x) x) false)
|
58
58
|
"x = true"
|
59
59
|
"x = false")
|
60
60
|
PROG
|
61
|
+
|
62
|
+
puts result
|
61
63
|
```
|
62
64
|
|
63
65
|
The above code creates a lambda function that simply acts like the identity
|
@@ -65,7 +67,7 @@ function (i.e. it returns its input). That lambda is then immediately applied
|
|
65
67
|
with the input `false`, thereby returning `false`.
|
66
68
|
|
67
69
|
The `if` form evaluates false and therefore evaluates the else part of the
|
68
|
-
`if`, returning the string
|
70
|
+
`if`, returning the string "x = false".
|
69
71
|
|
70
72
|
Here's another example showing how you can pass values from Ruby into Iode.
|
71
73
|
|
@@ -75,12 +77,12 @@ require "iode"
|
|
75
77
|
prog = Iode.run <<-PROG
|
76
78
|
(lambda (x)
|
77
79
|
(if x
|
78
|
-
42
|
79
|
-
7))
|
80
|
+
(puts 42)
|
81
|
+
(puts 7)))
|
80
82
|
PROG
|
81
83
|
|
82
|
-
prog
|
83
|
-
prog
|
84
|
+
prog.call(false) #=> 7
|
85
|
+
prog.call(true) #=> 42
|
84
86
|
```
|
85
87
|
|
86
88
|
This works because internally, Iode lambdas as represented as Ruby Procs.
|
@@ -93,18 +95,80 @@ require "iode"
|
|
93
95
|
|
94
96
|
prog = Iode.run <<-PROG
|
95
97
|
(lambda (f)
|
96
|
-
(
|
97
|
-
42
|
98
|
-
7))
|
98
|
+
(f 42))
|
99
99
|
PROG
|
100
100
|
|
101
|
-
prog.call(->(){
|
102
|
-
prog.call(->(){
|
101
|
+
prog.call(->(x){ x * 2 }) #=> 84
|
102
|
+
prog.call(->(x){ x + 4 }) #=> 46
|
103
|
+
```
|
104
|
+
|
105
|
+
Of course, functions can be defined recursively too.
|
106
|
+
|
107
|
+
``` lisp
|
108
|
+
;; Recursive function example.
|
109
|
+
(def loop
|
110
|
+
(lambda (n)
|
111
|
+
(if (= n 0)
|
112
|
+
(quote done)
|
113
|
+
(progn
|
114
|
+
(puts n)
|
115
|
+
(loop (- n 1))))))
|
116
|
+
|
117
|
+
|
118
|
+
(loop 20)
|
119
|
+
```
|
120
|
+
|
121
|
+
The above code will print 20 through 1 to the screen and finally return the
|
122
|
+
Symbol `:done` to Ruby (quoted Iode Symbols are also Ruby Symbols). Note that
|
123
|
+
I haven't yet done tail call elimination.
|
124
|
+
|
125
|
+
Similarly, closures can be returned from functions.
|
126
|
+
|
127
|
+
``` lisp
|
128
|
+
;; Provides partial application of a function
|
129
|
+
(def curry
|
130
|
+
(lambda (fn a)
|
131
|
+
(lambda (b) (fn a b))))
|
132
|
+
|
133
|
+
((curry + 2) 3) ; 5
|
134
|
+
```
|
135
|
+
|
136
|
+
## Development
|
137
|
+
|
138
|
+
Iode (in this Ruby incarnation) is literally a few hours old at the time I
|
139
|
+
write this. Much is still not yet developed. However, you may poke around in
|
140
|
+
the internals and find some interesting this. A string of source code takes
|
141
|
+
this path to being executed as code.
|
142
|
+
|
143
|
+
String(source) -> Reader(data) -> Interpreter(data) -> Core(data) -> Result
|
144
|
+
|
145
|
+
The source string is parsed by the Reader into native lisp data (using Ruby
|
146
|
+
data types, like Array and Symbol). The data representation is then given to
|
147
|
+
the Interpreter's eval method, which is a simple recursive algorithm mapping
|
148
|
+
the elements in the data to executable types (e.g. `[:lambda, [], 42]` becomes
|
149
|
+
a Proc). Variables are held in the Scope class, which is able to chain Scopes
|
150
|
+
together to create lexical closures. Core functions are registered as mixins
|
151
|
+
in the Core module.
|
152
|
+
|
153
|
+
If you want to add a native Ruby function to be applied like an Iode function,
|
154
|
+
put it in a Module and register it into `Iode::Core`:
|
155
|
+
|
156
|
+
``` ruby
|
157
|
+
require "iode"
|
158
|
+
|
159
|
+
module MyFunctions
|
160
|
+
def example(a, b)
|
161
|
+
a + b
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
Iode::Core.register MyFunctions
|
166
|
+
|
167
|
+
Iode.run('(example 7 5)') #=> 12
|
103
168
|
```
|
104
169
|
|
105
|
-
I
|
106
|
-
|
107
|
-
though all the wiring is there, I just need to define a core library.
|
170
|
+
Once I have namespacing done, you'll be able to write actual Iode code in
|
171
|
+
separate files and have them loaded under a namespace.
|
108
172
|
|
109
173
|
## Copyright & Licensing
|
110
174
|
|
data/lib/iode.rb
CHANGED
@@ -15,8 +15,11 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
17
|
require "iode/version"
|
18
|
+
require "iode/core/comparisons"
|
19
|
+
require "iode/core/lists"
|
20
|
+
require "iode/core/math"
|
21
|
+
require "iode/core/output"
|
18
22
|
require "iode/scope"
|
19
|
-
require "iode/built_ins"
|
20
23
|
require "iode/interpreter"
|
21
24
|
require "iode/reader"
|
22
25
|
|
data/lib/iode/core.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# iode: core.rb
|
2
|
+
#
|
3
|
+
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
module Iode
|
18
|
+
# Iode Core language definitions.
|
19
|
+
#
|
20
|
+
# This is simply a module that exposes all its instance methods as functions.
|
21
|
+
#
|
22
|
+
# The actual functions are mixed in by other modules.
|
23
|
+
module Core
|
24
|
+
class << self
|
25
|
+
# Register a new library of functions into the global definitions.
|
26
|
+
#
|
27
|
+
# @param [Module] base
|
28
|
+
# the module to register
|
29
|
+
def register(base)
|
30
|
+
include(base).tap{@definitions = nil}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get a singleton instance of all the core definitions.
|
34
|
+
#
|
35
|
+
# @return [Hash]
|
36
|
+
# core functions and variables
|
37
|
+
def definitions
|
38
|
+
@definitions ||=
|
39
|
+
Hash[
|
40
|
+
names.zip(
|
41
|
+
names.map(&method(:instance_method)).map{|m| m.bind(self)}
|
42
|
+
)
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def names
|
49
|
+
instance_methods
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# iode:
|
1
|
+
# iode: core/comparisons.rb
|
2
2
|
#
|
3
3
|
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
|
4
4
|
#
|
@@ -14,48 +14,26 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
-
|
18
|
-
module BuiltIns
|
19
|
-
def car(list)
|
20
|
-
v, *_ = list
|
21
|
-
v
|
22
|
-
end
|
17
|
+
require "iode/core"
|
23
18
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
car(cdr(list))
|
31
|
-
end
|
32
|
-
|
33
|
-
def cddr(list)
|
34
|
-
cdr(cdr(list))
|
35
|
-
end
|
36
|
-
|
37
|
-
def caddr(list)
|
38
|
-
car(cddr(list))
|
39
|
-
end
|
40
|
-
|
41
|
-
def cdddr(list)
|
42
|
-
cdr(cddr(list))
|
43
|
-
end
|
44
|
-
|
45
|
-
def cadddr(list)
|
46
|
-
car(cdddr(list))
|
47
|
-
end
|
19
|
+
module Iode
|
20
|
+
module Core
|
21
|
+
module Comparisons
|
22
|
+
def >(a, b)
|
23
|
+
a > b
|
24
|
+
end
|
48
25
|
|
49
|
-
|
50
|
-
|
51
|
-
|
26
|
+
def <(a, b)
|
27
|
+
a < b
|
28
|
+
end
|
52
29
|
|
53
|
-
|
54
|
-
|
55
|
-
|
30
|
+
def ==(a, b)
|
31
|
+
a == b
|
32
|
+
end
|
56
33
|
|
57
|
-
|
58
|
-
cdr(cddddr(list))
|
34
|
+
alias_method :"=", :==
|
59
35
|
end
|
60
36
|
end
|
61
37
|
end
|
38
|
+
|
39
|
+
Iode::Core.register Iode::Core::Comparisons
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# iode: core/lists.rb
|
2
|
+
#
|
3
|
+
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require "iode/core"
|
18
|
+
|
19
|
+
module Iode
|
20
|
+
module Core
|
21
|
+
module Lists
|
22
|
+
def car(list)
|
23
|
+
v, *_ = list
|
24
|
+
v
|
25
|
+
end
|
26
|
+
|
27
|
+
def cdr(list)
|
28
|
+
_, *v = list
|
29
|
+
v
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Iode::Core.register Iode::Core::Lists
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# iode: core/math.rb
|
2
|
+
#
|
3
|
+
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require "iode/core"
|
18
|
+
|
19
|
+
module Iode
|
20
|
+
module Core
|
21
|
+
module Math
|
22
|
+
def *(*args)
|
23
|
+
args.reduce(:*)
|
24
|
+
end
|
25
|
+
|
26
|
+
def /(*args)
|
27
|
+
args.reduce(:/)
|
28
|
+
end
|
29
|
+
|
30
|
+
def +(*args)
|
31
|
+
args.reduce(:+)
|
32
|
+
end
|
33
|
+
|
34
|
+
def -(*args)
|
35
|
+
args.reduce(:-)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Iode::Core.register Iode::Core::Math
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# iode: core/output.rb
|
2
|
+
#
|
3
|
+
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require "iode/core"
|
18
|
+
|
19
|
+
module Iode
|
20
|
+
module Core
|
21
|
+
module Output
|
22
|
+
def puts(*args, &block)
|
23
|
+
Kernel.puts(*args, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def p(*args, &block)
|
27
|
+
Kernel.p(*args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Iode::Core.register Iode::Core::Output
|
data/lib/iode/interpreter.rb
CHANGED
@@ -17,8 +17,6 @@
|
|
17
17
|
module Iode
|
18
18
|
# Iode interpreter, providing the central #eval function.
|
19
19
|
class Interpreter
|
20
|
-
include BuiltIns # FIXME: Remove this coupling!
|
21
|
-
|
22
20
|
# Create a new Interpreter with a given Scope.
|
23
21
|
#
|
24
22
|
# @param [Scope] scope
|
@@ -27,6 +25,30 @@ module Iode
|
|
27
25
|
@env = scope
|
28
26
|
end
|
29
27
|
|
28
|
+
# Get the head (car) of a list.
|
29
|
+
#
|
30
|
+
# @param [Array] list
|
31
|
+
# the list to return the car from
|
32
|
+
#
|
33
|
+
# @return [Object]
|
34
|
+
# the first element in list
|
35
|
+
def car(list)
|
36
|
+
v, *_ = list
|
37
|
+
v
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get the tail (cdr) of a list.
|
41
|
+
#
|
42
|
+
# @param [Array] list
|
43
|
+
# the list to return the cdr from
|
44
|
+
#
|
45
|
+
# @return [Array]
|
46
|
+
# all but the head of the list
|
47
|
+
def cdr(list)
|
48
|
+
_, *v = list
|
49
|
+
v
|
50
|
+
end
|
51
|
+
|
30
52
|
# Create an explicit progn block.
|
31
53
|
#
|
32
54
|
# A progn encapsulates a list of S-Expressions to be evaluated in sequence.
|
@@ -93,19 +115,21 @@ module Iode
|
|
93
115
|
when nil
|
94
116
|
nil
|
95
117
|
when :quote
|
96
|
-
|
118
|
+
car(cdr(sexp))
|
97
119
|
when :if
|
98
|
-
if eval(
|
99
|
-
eval(
|
120
|
+
if eval(car(cdr(sexp)))
|
121
|
+
eval(car(cdr(cdr(sexp))))
|
100
122
|
else
|
101
|
-
eval(
|
123
|
+
eval(car(cdr(cdr(cdr(sexp)))))
|
102
124
|
end
|
103
125
|
when :progn
|
104
126
|
progn(*cdr(sexp))
|
105
127
|
when :set!
|
106
|
-
@env[
|
128
|
+
@env[car(cdr(sexp))] = eval(car(cdr(cdr(sexp))))
|
129
|
+
when :def
|
130
|
+
@env.define(car(cdr(sexp)), eval(car(cdr(cdr(sexp)))))
|
107
131
|
when :lambda
|
108
|
-
lambda(
|
132
|
+
lambda(car(cdr(sexp)), *cdr(cdr(sexp)))
|
109
133
|
else
|
110
134
|
apply(eval(car(sexp)), cdr(sexp).map(&method(:eval)))
|
111
135
|
end
|
data/lib/iode/scope.rb
CHANGED
@@ -26,12 +26,26 @@ module Iode
|
|
26
26
|
#
|
27
27
|
# @param [Scope] parent
|
28
28
|
# the parent scope, if any
|
29
|
-
def initialize(values =
|
30
|
-
@values = values
|
29
|
+
def initialize(values = nil, parent = nil)
|
30
|
+
@values = values || Core.definitions
|
31
31
|
@parent = parent
|
32
32
|
end
|
33
33
|
|
34
|
-
#
|
34
|
+
# Define a new variable.
|
35
|
+
#
|
36
|
+
# @param [Symbol] k
|
37
|
+
# the name of the variable to define
|
38
|
+
#
|
39
|
+
# @param [Object] v
|
40
|
+
# the value to set
|
41
|
+
#
|
42
|
+
# @return [Object]
|
43
|
+
# the newly defined value
|
44
|
+
def define(k, v)
|
45
|
+
@values[k] = v
|
46
|
+
end
|
47
|
+
|
48
|
+
# Reference a variable in this Scope or any parent Scopes.
|
35
49
|
#
|
36
50
|
# Raises a RuntimeError if the variable does not exist.
|
37
51
|
#
|
@@ -46,13 +60,13 @@ module Iode
|
|
46
60
|
elsif @parent
|
47
61
|
@parent[k]
|
48
62
|
else
|
49
|
-
raise "
|
63
|
+
raise "Reference to undefined variable `#{k}`"
|
50
64
|
end
|
51
65
|
end
|
52
66
|
|
53
|
-
# Re-assign a variable in this
|
67
|
+
# Re-assign a variable in this Scope, or any parent Scope.
|
54
68
|
#
|
55
|
-
#
|
69
|
+
# Raises a RuntimeError if the variable does not exist.
|
56
70
|
#
|
57
71
|
# @param [Symbol] k
|
58
72
|
# the variable name to assign
|
@@ -63,13 +77,18 @@ module Iode
|
|
63
77
|
# @return [Object]
|
64
78
|
# the assigned object
|
65
79
|
def []=(k, v)
|
66
|
-
@values
|
80
|
+
if @values.key?(k)
|
81
|
+
@values[k] = v
|
82
|
+
elsif @parent
|
83
|
+
@parent[k] = v
|
84
|
+
else
|
85
|
+
raise "Reference to undefined variable `#{k}`"
|
86
|
+
end
|
67
87
|
end
|
68
88
|
|
69
89
|
# Create a new Scope with this Scope as its parent.
|
70
90
|
#
|
71
|
-
# The new Scope will have access to all variables in this Scope
|
72
|
-
# but assignments will only mask variables, not replace them.
|
91
|
+
# The new Scope will have access to all variables in this Scope.
|
73
92
|
#
|
74
93
|
# @param [Hash] values
|
75
94
|
# variables to exist in the new Scope
|
data/lib/iode/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Corbyn
|
@@ -72,7 +72,11 @@ files:
|
|
72
72
|
- bin/iode-rb
|
73
73
|
- iode.gemspec
|
74
74
|
- lib/iode.rb
|
75
|
-
- lib/iode/
|
75
|
+
- lib/iode/core.rb
|
76
|
+
- lib/iode/core/comparisons.rb
|
77
|
+
- lib/iode/core/lists.rb
|
78
|
+
- lib/iode/core/math.rb
|
79
|
+
- lib/iode/core/output.rb
|
76
80
|
- lib/iode/interpreter.rb
|
77
81
|
- lib/iode/reader.rb
|
78
82
|
- lib/iode/scope.rb
|