lasp 0.10.1 → 0.11.0
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 +4 -4
- data/CHANGELOG.md +24 -0
- data/DOCUMENTATION.md +87 -17
- data/README.md +7 -4
- data/bin/lasp +1 -2
- data/lasp.gemspec +3 -2
- data/lib/lasp/corelib.rb +1 -4
- data/lib/lasp/env.rb +12 -4
- data/lib/lasp/fn.rb +3 -22
- data/lib/lasp/interpreter.rb +17 -6
- data/lib/lasp/lexer.rb +1 -0
- data/lib/lasp/params.rb +16 -51
- data/lib/lasp/params_builder.rb +69 -0
- data/lib/lasp/parser.rb +7 -0
- data/lib/lasp/repl.rb +3 -1
- data/lib/lasp/stdlib.lasp +3 -3
- data/lib/lasp/stdmacros.lasp +18 -0
- data/lib/lasp/variadic_params.rb +27 -0
- data/lib/lasp/version.rb +1 -1
- data/lib/lasp.rb +16 -6
- metadata +21 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 239d33a36efb77e46301fa1f4f5eab3355d95371
|
4
|
+
data.tar.gz: 2b63d5918420dd74b0e4e73e9b799eed3ab33113
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74d78665c4fa130cb7a583c4cce512bdce06db31086f4ad889a0ef0015d60b3a84892e530236f7f21937f4663e5e6e1c5f6383f5d9af6baa6871974524074d97
|
7
|
+
data.tar.gz: 1a7daa286ff20db4c8b3008525c1b10edff6c61a4848f5bb2c04b97ad58cef832bfb72961765c422a761cdae26371c297e1a47d6fc1d307e2e2c943add69e1ea
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# Läsp changelog
|
2
2
|
|
3
|
+
## v0.11.0 - 2016-03-05
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- Allow `require` to accept a second argument, when it is truthy it uses a path
|
8
|
+
relative to the file that called `require`.
|
9
|
+
- Allow `_` to be used several times in parameter lists to ignore parameters
|
10
|
+
- Additions to standard library
|
11
|
+
- `and`
|
12
|
+
- `or`
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
|
16
|
+
- `require` now uses a static paths by default.
|
17
|
+
- The `.` function has been renamed to `send`, the `.` can now instead be used
|
18
|
+
as a prefix to call Ruby methods, what previously looked like this: `(. obj method)`
|
19
|
+
now looks like this `(.method obj)`. This is equivalent to `(send :method obj)`.
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
|
23
|
+
- Relative paths in `require` was completely broken before this.
|
24
|
+
- `empty?` no longer returns `true` for lists with a `nil` value at the first position.
|
25
|
+
|
26
|
+
|
3
27
|
## v0.10.1 - 2016-02-22
|
4
28
|
|
5
29
|
### Fixed
|
data/DOCUMENTATION.md
CHANGED
@@ -40,7 +40,7 @@ create a local binding, use [let](#let) instead.
|
|
40
40
|
|
41
41
|
Creates a function.
|
42
42
|
|
43
|
-
Parameters `(parameters body)
|
43
|
+
Parameters `(parameters body)`:
|
44
44
|
|
45
45
|
1. A list of parameters that the function shall accept, e.g. `(arg1 arg2)`, `(one two & others)`, `(& args)`
|
46
46
|
2. The body of the function, the declared parameters will be available in here.
|
@@ -49,11 +49,12 @@ Parameters `(parameters body)`
|
|
49
49
|
; This creates a function object, it can be read as "a function of x".
|
50
50
|
(fn (x) (+ x 2)) ; => #<Fn (x)>
|
51
51
|
|
52
|
-
; Functions are called when placed as the first
|
52
|
+
; Functions are called when placed as the first element in a form:
|
53
53
|
((fn (x) (+ x 2)) 40) ; => 42
|
54
54
|
```
|
55
55
|
|
56
|
-
Most of the time, you'll want to define a function before using it (see
|
56
|
+
Most of the time, you'll want to define a function before using it (see
|
57
|
+
[defn](#defn) for a nicer syntax for doing this):
|
57
58
|
|
58
59
|
```clojure
|
59
60
|
(def plus-two (fn (x) (+ x 2)))
|
@@ -67,6 +68,8 @@ Functions will enforce that the correct number of arguments is passed to them:
|
|
67
68
|
(plus-two 40 41) ; !> Lasp::ArgumentError: wrong number of arguments (2 for 1)
|
68
69
|
```
|
69
70
|
|
71
|
+
#### Variadic parameter lists
|
72
|
+
|
70
73
|
Functions can also accept any number of extra arguments as a list:
|
71
74
|
|
72
75
|
```clojure
|
@@ -84,6 +87,16 @@ Functions can also accept any number of extra arguments as a list:
|
|
84
87
|
(show-args) ; !> Lasp::ArgumentError: wrong number of arguments (0 for 1+)
|
85
88
|
```
|
86
89
|
|
90
|
+
#### Ignoring parameters
|
91
|
+
|
92
|
+
Normally you get an error from naming 2 parameters the same, but if you want to
|
93
|
+
ignore parameters, the `_` is allowed to be used several times.
|
94
|
+
|
95
|
+
```clojure
|
96
|
+
(def ignore-2 (fn (nope nope) (println "lalalala"))) ; !> Lasp::SyntaxError
|
97
|
+
(def ignore-2 (fn (_ _) (println "lalalala"))) ; => #<Fn (_ _)>
|
98
|
+
```
|
99
|
+
|
87
100
|
|
88
101
|
### do
|
89
102
|
|
@@ -181,7 +194,8 @@ Except for the order of evaluation, macros behave just like functions and can
|
|
181
194
|
accept rest-arguments etc. the same way. Just like functions, you mostly want
|
182
195
|
to define them before you use them, see [defm](#defm).
|
183
196
|
|
184
|
-
**It is important to quote things that you do not want evaluated until after
|
197
|
+
**It is important to quote things that you do not want evaluated until after
|
198
|
+
the macro has returned.**
|
185
199
|
|
186
200
|
```clojure
|
187
201
|
; Note that we want `if` to be evaluated after the macro has returned, so we have to quote it.
|
@@ -193,7 +207,25 @@ to define them before you use them, see [defm](#defm).
|
|
193
207
|
(reverse-if true "yes" "no") ; => "no"
|
194
208
|
```
|
195
209
|
|
196
|
-
To debug macros and see what they expand to without trying to evaluate the
|
210
|
+
To debug macros and see what they expand to without trying to evaluate the
|
211
|
+
result, see [macroexpand](#macroexpand).
|
212
|
+
|
213
|
+
|
214
|
+
### require
|
215
|
+
|
216
|
+
Loads and runs a Läsp file. Paths are relative to the folder where the `lasp`
|
217
|
+
command was invoked, or, if a second truthy parameter is passed, to the file
|
218
|
+
where `require` is called.
|
219
|
+
|
220
|
+
```clojure
|
221
|
+
(require "lasp_file.lasp")
|
222
|
+
(require "dir/lasp_file.lasp")
|
223
|
+
(require "../lasp_file.lasp")
|
224
|
+
|
225
|
+
; Pass a truthy second parameter to use relative paths:
|
226
|
+
(require "lasp_file.lasp" true)
|
227
|
+
(require "lasp_file.lasp" :relative)
|
228
|
+
```
|
197
229
|
|
198
230
|
|
199
231
|
## Core library
|
@@ -469,18 +501,18 @@ Applies a function to a list of arguments as if they were passed in directly.
|
|
469
501
|
```
|
470
502
|
|
471
503
|
|
472
|
-
###
|
504
|
+
### send
|
473
505
|
|
474
|
-
|
506
|
+
Calls a Ruby method on an object.
|
475
507
|
|
476
|
-
Parameters `(object
|
508
|
+
Parameters `(message object & args)`:
|
477
509
|
|
478
510
|
1. The object to send the message to
|
479
511
|
2. A text with the message name
|
480
512
|
3. Any number of arguments to be passed along with the message
|
481
513
|
|
482
514
|
```clojure
|
483
|
-
(
|
515
|
+
(send :to_i "01011101" 2) ; => 93
|
484
516
|
```
|
485
517
|
|
486
518
|
```ruby
|
@@ -488,16 +520,11 @@ Parameters `(object message & args)`:
|
|
488
520
|
"01011101".to_i(2)
|
489
521
|
```
|
490
522
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
Loads and runs a Läsp file. Paths are relative to the file they are being
|
495
|
-
required in.
|
523
|
+
You can accomplish the same by prepending a dot to the function name, this
|
524
|
+
makes it look very similar to a normal Läsp function call:
|
496
525
|
|
497
526
|
```clojure
|
498
|
-
(
|
499
|
-
(require "dir/lasp_file.lasp") ; dir/ is a folder with a lasp file in this directory
|
500
|
-
(require "../lasp_file.lasp") ; lasp_file.lasp is in the parent folder
|
527
|
+
(.upcase "hello") ; => "HELLO"
|
501
528
|
```
|
502
529
|
|
503
530
|
|
@@ -947,6 +974,49 @@ If given an uneven number of bindings, the last one will be assigned to `nil`.
|
|
947
974
|
```
|
948
975
|
|
949
976
|
|
977
|
+
### or
|
978
|
+
|
979
|
+
Evaluates its arguments in turn, returning:
|
980
|
+
|
981
|
+
- the first **truthy** value it encounters
|
982
|
+
- the last value, if all forms evaluate to logical **false**
|
983
|
+
- `nil` if no arguments are given
|
984
|
+
|
985
|
+
```clojure
|
986
|
+
; Typical usage in if-statements
|
987
|
+
(or (= 1 2) (= 3 3)) ; => true
|
988
|
+
(or (= 1 2) (= 3 4)) ; => false
|
989
|
+
|
990
|
+
(or) ; => nil
|
991
|
+
(or nil nil false) ; => false
|
992
|
+
|
993
|
+
; will return without printing anything
|
994
|
+
(or (not true) 42 (println "nope")) ; => 42
|
995
|
+
```
|
996
|
+
|
997
|
+
|
998
|
+
### and
|
999
|
+
|
1000
|
+
Evaluates its arguments in turn, returning:
|
1001
|
+
|
1002
|
+
- the first **falsy** value it encounters
|
1003
|
+
- the last value, if all forms evaluate to logical **true**
|
1004
|
+
- `true` if no arguments are given
|
1005
|
+
|
1006
|
+
```clojure
|
1007
|
+
; Typical usage in if-statements
|
1008
|
+
(and (= 1 1) (= 2 2)) ; => true
|
1009
|
+
(and (= 1 1) (= 2 3)) ; => false
|
1010
|
+
|
1011
|
+
(and) ; => nil
|
1012
|
+
(and true true 42) ; => 42
|
1013
|
+
|
1014
|
+
; will return without printing anything
|
1015
|
+
(and 42 false (println "nope")) ; => false
|
1016
|
+
```
|
1017
|
+
|
1018
|
+
|
1019
|
+
|
950
1020
|
### macroexpand
|
951
1021
|
|
952
1022
|
Returns the unevaluated result of a macro, indispensable for debugging.
|
data/README.md
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
-
# Läsp
|
1
|
+
# Läsp [](https://travis-ci.org/alcesleo/lasp)
|
2
2
|
|
3
3
|
A Lisp implementation in Ruby.
|
4
4
|
|
5
5
|
## Features
|
6
6
|
|
7
|
-
-
|
8
|
-
- [Standard library](lib/lasp/stdlib.lisp) written in Läsp itself
|
7
|
+
- Comprehensive [documentation](DOCUMENTATION.md)
|
9
8
|
- Interactive REPL with auto-closing of missing trailing parentheses
|
9
|
+
- Closures
|
10
|
+
- let-bindings
|
10
11
|
- Fully functional macro system
|
11
12
|
- Interoperability with Ruby
|
13
|
+
- Very concise [core library](lib/lasp/corelib.rb) written in Ruby
|
14
|
+
- [Standard library](lib/lasp/stdlib.lisp) written in Läsp itself
|
12
15
|
|
13
16
|
## Installation
|
14
17
|
|
@@ -75,7 +78,7 @@ Comments start with a `;` and end at the end of a line
|
|
75
78
|
### Run the specs
|
76
79
|
|
77
80
|
```bash
|
78
|
-
rake
|
81
|
+
bundle exec rake
|
79
82
|
```
|
80
83
|
|
81
84
|
### Dev REPL
|
data/bin/lasp
CHANGED
data/lasp.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Jimmy Börjesson"]
|
10
10
|
spec.email = ["lagginglion@gmail.com"]
|
11
11
|
|
12
|
-
spec.summary = %q{A simple programming language
|
12
|
+
spec.summary = %q{A simple Lisp-dialect programming language inspired by Clojure.}
|
13
13
|
spec.homepage = "https://github.com/alcesleo/lasp"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.required_ruby_version = '~> 2.0'
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler", "~> 1.
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.4"
|
25
26
|
end
|
data/lib/lasp/corelib.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "lasp"
|
2
|
-
|
3
1
|
module Lasp
|
4
2
|
CORELIB = {
|
5
3
|
:+ => -> (*args) { args.reduce(:+) },
|
@@ -23,7 +21,6 @@ module Lasp
|
|
23
21
|
:print => -> (*output) { STDOUT.print(*output) },
|
24
22
|
:readln => -> () { STDIN.gets.chomp },
|
25
23
|
:apply => -> (f, list) { f.call(*list) },
|
26
|
-
:
|
27
|
-
:require => -> (p) { execute_file(File.expand_path(p, __dir__)) },
|
24
|
+
:send => -> (m, o, *args) { o.public_send(m, *args) },
|
28
25
|
}
|
29
26
|
end
|
data/lib/lasp/env.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
|
-
require "
|
1
|
+
require "forwardable"
|
2
2
|
|
3
3
|
module Lasp
|
4
|
-
|
4
|
+
class Env
|
5
|
+
extend Forwardable
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
def_delegators :@env, :fetch, :[]=
|
8
|
+
|
9
|
+
def initialize(env = {})
|
10
|
+
@env = env
|
11
|
+
end
|
12
|
+
|
13
|
+
def merge(hash)
|
14
|
+
Env.new(@env.merge(hash))
|
15
|
+
end
|
8
16
|
end
|
9
17
|
end
|
data/lib/lasp/fn.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require "lasp/interpreter"
|
2
|
-
require "lasp/
|
2
|
+
require "lasp/params_builder"
|
3
3
|
require "lasp/errors"
|
4
4
|
|
5
5
|
module Lasp
|
@@ -7,7 +7,7 @@ module Lasp
|
|
7
7
|
attr_reader :params, :body, :env
|
8
8
|
|
9
9
|
def initialize(params, body, env)
|
10
|
-
@params =
|
10
|
+
@params = ParamsBuilder.build(params)
|
11
11
|
@body = body
|
12
12
|
@env = env
|
13
13
|
end
|
@@ -24,26 +24,7 @@ module Lasp
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def env_with_args(args)
|
27
|
-
|
28
|
-
|
29
|
-
params_with_args = params
|
30
|
-
.ordered
|
31
|
-
.zip(args.take(params.length))
|
32
|
-
.to_h
|
33
|
-
|
34
|
-
if params.variadic?
|
35
|
-
params_with_args[params.rest] = args.drop(params.length)
|
36
|
-
end
|
37
|
-
|
38
|
-
env.merge(params_with_args)
|
39
|
-
end
|
40
|
-
|
41
|
-
def enforce_arity!(args)
|
42
|
-
wrong_number_of_args!(args) unless params.matches_arity?(args.length)
|
43
|
-
end
|
44
|
-
|
45
|
-
def wrong_number_of_args!(args)
|
46
|
-
fail ArgumentError, "wrong number of arguments (#{args.length} for #{params.arity})"
|
27
|
+
env.merge(params.with_args(args))
|
47
28
|
end
|
48
29
|
end
|
49
30
|
end
|
data/lib/lasp/interpreter.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "lasp"
|
1
2
|
require "lasp/fn"
|
2
3
|
require "lasp/macro"
|
3
4
|
require "lasp/errors"
|
@@ -22,12 +23,13 @@ module Lasp
|
|
22
23
|
head, *tail = *form
|
23
24
|
|
24
25
|
case head
|
25
|
-
when :def
|
26
|
-
when :fn
|
27
|
-
when :do
|
28
|
-
when :if
|
29
|
-
when :quote
|
30
|
-
when :macro
|
26
|
+
when :def then def_special_form(tail, env)
|
27
|
+
when :fn then fn_special_form(tail, env)
|
28
|
+
when :do then do_special_form(tail, env)
|
29
|
+
when :if then if_special_form(tail, env)
|
30
|
+
when :quote then quote_special_form(tail, env)
|
31
|
+
when :macro then macro_special_form(tail, env)
|
32
|
+
when :require then require_special_form(tail, env)
|
31
33
|
else call_function(head, tail, env)
|
32
34
|
end
|
33
35
|
end
|
@@ -75,5 +77,14 @@ module Lasp
|
|
75
77
|
params, func = form
|
76
78
|
Macro.new(params, func, env)
|
77
79
|
end
|
80
|
+
|
81
|
+
def require_special_form(form, env)
|
82
|
+
require_path, is_relative = form
|
83
|
+
|
84
|
+
require_root = is_relative ? File.dirname(env.fetch(:__FILE__)) : Dir.pwd
|
85
|
+
absolute_path = File.expand_path(require_path, require_root)
|
86
|
+
|
87
|
+
Lasp::execute_file(absolute_path, env)
|
88
|
+
end
|
78
89
|
end
|
79
90
|
end
|
data/lib/lasp/lexer.rb
CHANGED
data/lib/lasp/params.rb
CHANGED
@@ -6,76 +6,41 @@ module Lasp
|
|
6
6
|
|
7
7
|
def initialize(param_list)
|
8
8
|
@param_list = param_list
|
9
|
-
|
10
|
-
validate_params!
|
11
9
|
end
|
12
10
|
|
13
|
-
def
|
14
|
-
param_list.
|
11
|
+
def to_s
|
12
|
+
"(" + param_list.join(" ") + ")"
|
15
13
|
end
|
16
14
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
param_list.last
|
15
|
+
def with_args(args)
|
16
|
+
enforce_arity!(args)
|
17
|
+
fixed_params.zip(args.take(length)).to_h
|
22
18
|
end
|
23
19
|
|
24
|
-
|
25
|
-
|
20
|
+
private
|
21
|
+
|
22
|
+
def fixed_params
|
23
|
+
param_list
|
26
24
|
end
|
27
25
|
|
28
26
|
def arity
|
29
|
-
|
27
|
+
length.to_s
|
30
28
|
end
|
31
29
|
|
32
30
|
def matches_arity?(num_args)
|
33
|
-
|
34
|
-
num_args >= length
|
35
|
-
else
|
36
|
-
num_args == length
|
37
|
-
end
|
31
|
+
num_args == length
|
38
32
|
end
|
39
33
|
|
40
34
|
def length
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
def to_s
|
45
|
-
"(" + param_list.join(" ") + ")"
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def validate_params!
|
51
|
-
validate_list!
|
52
|
-
validate_single_ampersand!
|
53
|
-
validate_single_rest_parameter!
|
54
|
-
validate_unique_parameter_names!
|
55
|
-
end
|
56
|
-
|
57
|
-
def validate_list!
|
58
|
-
unless Array === param_list
|
59
|
-
fail SyntaxError, "parameters must be a list"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def validate_unique_parameter_names!
|
64
|
-
unless param_list.uniq.length == param_list.length
|
65
|
-
fail SyntaxError, "parameter names have to be unique"
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def validate_single_ampersand!
|
70
|
-
invalid_rest_argument_usage! unless param_list.select { |p| p == :& }.length <= 1
|
35
|
+
fixed_params.length
|
71
36
|
end
|
72
37
|
|
73
|
-
def
|
74
|
-
|
38
|
+
def enforce_arity!(args)
|
39
|
+
wrong_number_of_args!(args) unless matches_arity?(args.length)
|
75
40
|
end
|
76
41
|
|
77
|
-
def
|
78
|
-
fail
|
42
|
+
def wrong_number_of_args!(args)
|
43
|
+
fail ArgumentError, "wrong number of arguments (#{args.length} for #{arity})"
|
79
44
|
end
|
80
45
|
end
|
81
46
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "lasp/errors"
|
2
|
+
require "lasp/params"
|
3
|
+
require "lasp/variadic_params"
|
4
|
+
|
5
|
+
module Lasp
|
6
|
+
class ParamsBuilder
|
7
|
+
def self.build(param_list)
|
8
|
+
new(param_list).build
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(param_list)
|
12
|
+
@param_list = param_list
|
13
|
+
end
|
14
|
+
|
15
|
+
def build
|
16
|
+
validate_params!
|
17
|
+
params_object
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :param_list
|
23
|
+
|
24
|
+
def params_object
|
25
|
+
if variadic?
|
26
|
+
VariadicParams.new(param_list)
|
27
|
+
else
|
28
|
+
Params.new(param_list)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_params!
|
33
|
+
validate_list!
|
34
|
+
validate_single_ampersand!
|
35
|
+
validate_single_rest_parameter!
|
36
|
+
validate_unique_parameter_names!
|
37
|
+
end
|
38
|
+
|
39
|
+
def variadic?
|
40
|
+
param_list.include?(:&)
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_list!
|
44
|
+
unless Array === param_list
|
45
|
+
fail SyntaxError, "parameters must be a list"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_unique_parameter_names!
|
50
|
+
accepted_params = param_list.reject { |p| p == :_ }
|
51
|
+
|
52
|
+
unless accepted_params.uniq.length == accepted_params.length
|
53
|
+
fail SyntaxError, "parameter names must be unique (except for _)"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate_single_ampersand!
|
58
|
+
invalid_rest_argument_usage! unless param_list.select { |p| p == :& }.length <= 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate_single_rest_parameter!
|
62
|
+
invalid_rest_argument_usage! if variadic? && param_list[-2] != :&
|
63
|
+
end
|
64
|
+
|
65
|
+
def invalid_rest_argument_usage!
|
66
|
+
fail SyntaxError, "rest-arguments may only be used once, at the end, with a single binding"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/lasp/parser.rb
CHANGED
@@ -30,6 +30,7 @@ module Lasp
|
|
30
30
|
case token
|
31
31
|
when "(" then form(tokens)
|
32
32
|
when "'" then quote(tokens)
|
33
|
+
when "." then dot(tokens)
|
33
34
|
else atom(token)
|
34
35
|
end
|
35
36
|
end
|
@@ -47,6 +48,12 @@ module Lasp
|
|
47
48
|
[:quote] << build_ast(tokens)
|
48
49
|
end
|
49
50
|
|
51
|
+
def dot(tokens)
|
52
|
+
method = tokens.shift
|
53
|
+
tokens.unshift(":" + method)
|
54
|
+
:send
|
55
|
+
end
|
56
|
+
|
50
57
|
def atom(token)
|
51
58
|
case token
|
52
59
|
when "true" then true
|
data/lib/lasp/repl.rb
CHANGED
@@ -11,13 +11,15 @@ module Lasp
|
|
11
11
|
def run
|
12
12
|
trap("SIGINT") { puts "\n\nBye!"; exit }
|
13
13
|
|
14
|
+
env = Lasp::env_with_stdlib
|
15
|
+
|
14
16
|
puts "((( Läsp v#{Lasp::VERSION} REPL (ctrl+c to exit) )))\n\n"
|
15
17
|
loop do
|
16
18
|
begin
|
17
19
|
history = true
|
18
20
|
input = Readline.readline(prompt, history).to_s
|
19
21
|
input = autoclose_parentheses(input)
|
20
|
-
result = Lasp::execute(input)
|
22
|
+
result = Lasp::execute(input, env)
|
21
23
|
print_result(result)
|
22
24
|
rescue => error
|
23
25
|
print_error(error)
|
data/lib/lasp/stdlib.lasp
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
(require "stdmacros.lasp")
|
1
|
+
(require "stdmacros.lasp" :relative)
|
2
2
|
|
3
3
|
; Aliases
|
4
4
|
(def first head)
|
@@ -16,7 +16,7 @@
|
|
16
16
|
; If a list is empty
|
17
17
|
(defn empty?
|
18
18
|
(coll)
|
19
|
-
(
|
19
|
+
(= coll (rest coll)))
|
20
20
|
|
21
21
|
; If all arguments are not equal
|
22
22
|
(defn not= (& args)
|
@@ -143,7 +143,7 @@
|
|
143
143
|
|
144
144
|
; Takes a method from Ruby-land and returns a Lasp function
|
145
145
|
(defn ruby-method (meth)
|
146
|
-
(fn (arg) (
|
146
|
+
(fn (arg) (send meth arg)))
|
147
147
|
|
148
148
|
|
149
149
|
; Conversion functions
|
data/lib/lasp/stdmacros.lasp
CHANGED
@@ -67,3 +67,21 @@
|
|
67
67
|
(form)
|
68
68
|
(list 'apply (first form)
|
69
69
|
(list 'quote (rest form))))
|
70
|
+
|
71
|
+
(defm or
|
72
|
+
(& args)
|
73
|
+
(if (empty? args) nil
|
74
|
+
(if (empty? (tail args)) (head args)
|
75
|
+
|
76
|
+
(list 'let
|
77
|
+
(list 'or-value (head args))
|
78
|
+
(list 'if 'or-value 'or-value (cons 'or (tail args)))))))
|
79
|
+
|
80
|
+
(defm and
|
81
|
+
(& args)
|
82
|
+
(if (empty? args) true
|
83
|
+
(if (empty? (tail args)) (head args)
|
84
|
+
|
85
|
+
(list 'let
|
86
|
+
(list 'and-value (head args))
|
87
|
+
(list 'if 'and-value (cons 'and (tail args)) 'and-value)))))
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "lasp/params"
|
2
|
+
|
3
|
+
module Lasp
|
4
|
+
class VariadicParams < Params
|
5
|
+
def with_args(args)
|
6
|
+
super.merge(rest_args(args))
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def fixed_params
|
12
|
+
param_list.take_while { |p| p != :& }
|
13
|
+
end
|
14
|
+
|
15
|
+
def arity
|
16
|
+
super + "+"
|
17
|
+
end
|
18
|
+
|
19
|
+
def matches_arity?(num_args)
|
20
|
+
num_args >= length
|
21
|
+
end
|
22
|
+
|
23
|
+
def rest_args(args)
|
24
|
+
{ param_list.last => args.drop(length) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/lasp/version.rb
CHANGED
data/lib/lasp.rb
CHANGED
@@ -2,6 +2,7 @@ require "lasp/version"
|
|
2
2
|
require "lasp/env"
|
3
3
|
require "lasp/parser"
|
4
4
|
require "lasp/interpreter"
|
5
|
+
require "lasp/corelib"
|
5
6
|
require "lasp/ext"
|
6
7
|
|
7
8
|
module Lasp
|
@@ -9,15 +10,24 @@ module Lasp
|
|
9
10
|
|
10
11
|
module_function
|
11
12
|
|
12
|
-
def
|
13
|
-
|
13
|
+
def execute(program, env = env_with_corelib)
|
14
|
+
Interpreter.eval(Parser.parse(program), env)
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
|
17
|
+
def execute_file(path, env = env_with_corelib)
|
18
|
+
env[:__FILE__] = path
|
19
|
+
result = execute("(do #{File.read(path)})", env)
|
20
|
+
env[:__FILE__] = nil
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def env_with_corelib
|
25
|
+
Env.new(CORELIB.dup)
|
18
26
|
end
|
19
27
|
|
20
|
-
def
|
21
|
-
|
28
|
+
def env_with_stdlib
|
29
|
+
env_with_corelib.tap do |env|
|
30
|
+
Lasp::execute_file(STDLIB_PATH, env)
|
31
|
+
end
|
22
32
|
end
|
23
33
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lasp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jimmy Börjesson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.7'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.4'
|
41
55
|
description:
|
42
56
|
email:
|
43
57
|
- lagginglion@gmail.com
|
@@ -67,10 +81,12 @@ files:
|
|
67
81
|
- lib/lasp/lexer.rb
|
68
82
|
- lib/lasp/macro.rb
|
69
83
|
- lib/lasp/params.rb
|
84
|
+
- lib/lasp/params_builder.rb
|
70
85
|
- lib/lasp/parser.rb
|
71
86
|
- lib/lasp/repl.rb
|
72
87
|
- lib/lasp/stdlib.lasp
|
73
88
|
- lib/lasp/stdmacros.lasp
|
89
|
+
- lib/lasp/variadic_params.rb
|
74
90
|
- lib/lasp/version.rb
|
75
91
|
homepage: https://github.com/alcesleo/lasp
|
76
92
|
licenses:
|
@@ -95,5 +111,5 @@ rubyforge_project:
|
|
95
111
|
rubygems_version: 2.4.5
|
96
112
|
signing_key:
|
97
113
|
specification_version: 4
|
98
|
-
summary: A simple programming language
|
114
|
+
summary: A simple Lisp-dialect programming language inspired by Clojure.
|
99
115
|
test_files: []
|