lasp 0.8.0 → 0.9.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.
@@ -0,0 +1,79 @@
1
+ require "lasp/fn"
2
+ require "lasp/macro"
3
+ require "lasp/errors"
4
+
5
+ module Lasp
6
+ class Interpreter
7
+ def self.eval(form, env)
8
+ new.eval(form, env)
9
+ end
10
+
11
+ def eval(form, env)
12
+ case form
13
+ when Symbol then resolve_symbol(form, env)
14
+ when Array then eval_form(form, env)
15
+ else form
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def eval_form(form, env)
22
+ head, *tail = *form
23
+
24
+ case head
25
+ when :def then def_special_form(tail, env)
26
+ when :fn then fn_special_form(tail, env)
27
+ when :do then do_special_form(tail, env)
28
+ when :if then if_special_form(tail, env)
29
+ when :quote then quote_special_form(tail, env)
30
+ when :macro then macro_special_form(tail, env)
31
+ else call_function(head, tail, env)
32
+ end
33
+ end
34
+
35
+ def resolve_symbol(symbol, env)
36
+ env.fetch(symbol)
37
+ rescue KeyError
38
+ raise NameError, "#{symbol} is not present in this context"
39
+ end
40
+
41
+ def call_function(symbol, args, env)
42
+ fn = eval(symbol, env)
43
+
44
+ case fn
45
+ when Macro then eval(fn.(*args), env)
46
+ else fn.(*args.map { |form| eval(form, env) })
47
+ end
48
+ end
49
+
50
+ def def_special_form(form, env)
51
+ key, value = form
52
+ fail ArgumentError, "you can only def symbols" unless Symbol === key
53
+ env[key] = eval(value, env)
54
+ end
55
+
56
+ def fn_special_form(form, env)
57
+ params, func = form
58
+ Fn.new(params, func, env)
59
+ end
60
+
61
+ def do_special_form(form, env)
62
+ form.map { |form| eval(form, env) }.last
63
+ end
64
+
65
+ def if_special_form(form, env)
66
+ conditional, true_form, false_form = form
67
+ eval(conditional, env) ? eval(true_form, env) : eval(false_form, env)
68
+ end
69
+
70
+ def quote_special_form(form, _)
71
+ form.first
72
+ end
73
+
74
+ def macro_special_form(form, env)
75
+ params, func = form
76
+ Macro.new(params, func, env)
77
+ end
78
+ end
79
+ end
@@ -1,3 +1,5 @@
1
+ require "lasp/errors"
2
+
1
3
  module Lasp
2
4
  class Params
3
5
  attr_reader :param_list
@@ -14,7 +16,7 @@ module Lasp
14
16
 
15
17
  def rest
16
18
  unless variadic?
17
- fail ArgumentError, "A non-variadic function does not have rest-arguments"
19
+ fail LaspError, "a non-variadic function does not have rest-arguments"
18
20
  end
19
21
  param_list.last
20
22
  end
@@ -46,27 +48,34 @@ module Lasp
46
48
  private
47
49
 
48
50
  def validate_params!
51
+ validate_list!
49
52
  validate_single_ampersand!
50
53
  validate_single_rest_parameter!
51
54
  validate_unique_parameter_names!
52
55
  end
53
56
 
57
+ def validate_list!
58
+ unless Array === param_list
59
+ fail SyntaxError, "parameters must be a list"
60
+ end
61
+ end
62
+
54
63
  def validate_unique_parameter_names!
55
64
  unless param_list.uniq.length == param_list.length
56
- fail ArgumentError, "Parameter names have to be unique."
65
+ fail SyntaxError, "parameter names have to be unique"
57
66
  end
58
67
  end
59
68
 
60
69
  def validate_single_ampersand!
61
- unless param_list.select { |p| p == :& }.length <= 1
62
- fail ArgumentError, "Rest-arguments may only be used once, at the end, with a single binding."
63
- end
70
+ invalid_rest_argument_usage! unless param_list.select { |p| p == :& }.length <= 1
64
71
  end
65
72
 
66
73
  def validate_single_rest_parameter!
67
- if variadic? && param_list[-2] != :&
68
- fail ArgumentError, "Rest-arguments may only be used once, at the end, with a single binding."
69
- end
74
+ invalid_rest_argument_usage! if variadic? && param_list[-2] != :&
75
+ end
76
+
77
+ def invalid_rest_argument_usage!
78
+ fail SyntaxError, "rest-arguments may only be used once, at the end, with a single binding"
70
79
  end
71
80
  end
72
81
  end
@@ -1,5 +1,9 @@
1
1
  module Lasp
2
2
  class Parser
3
+ def self.parse(program)
4
+ new.parse(program)
5
+ end
6
+
3
7
  def parse(program)
4
8
  build_ast(tokenize(sanitize(program)))
5
9
  end
@@ -14,18 +18,24 @@ module Lasp
14
18
  return if tokens.empty?
15
19
  token = tokens.shift
16
20
 
17
- if token == "("
18
- form = []
19
- while tokens.first != ")"
20
- form << build_ast(tokens)
21
- end
22
- tokens.shift
23
- form
24
- elsif token == "'"
25
- [:quote] << build_ast(tokens)
26
- else
27
- atom(token)
21
+ case token
22
+ when "(" then form(tokens)
23
+ when "'" then quote(tokens)
24
+ else atom(token)
25
+ end
26
+ end
27
+
28
+ def form(tokens)
29
+ form = []
30
+ while tokens.first != ")"
31
+ form << build_ast(tokens)
28
32
  end
33
+ tokens.shift
34
+ form
35
+ end
36
+
37
+ def quote(tokens)
38
+ [:quote] << build_ast(tokens)
29
39
  end
30
40
 
31
41
  def atom(token)
@@ -36,7 +46,7 @@ module Lasp
36
46
  when /\A-?\d+\z/ then Integer(token)
37
47
  when /\A-?\d+.\d+\z/ then Float(token)
38
48
  when /"(.*)"/ then String($1)
39
- when /:(\w+)/ then String($1) # Symbol style strings are actually just strings
49
+ when /:([^\s]+)/ then String($1) # Symbol style strings are actually just strings
40
50
  else token.to_sym
41
51
  end
42
52
  end
@@ -1,43 +1,64 @@
1
1
  require "lasp"
2
2
  require "lasp/parser"
3
- require "lasp/representation"
4
3
  require "readline"
5
4
 
6
5
  module Lasp
7
- module_function
8
-
9
- def repl
10
- trap("SIGINT") { puts "\n\nBye!"; exit }
11
-
12
- puts "((( Läsp v#{Lasp::VERSION} REPL (ctrl+c to exit) )))\n\n"
13
- loop do
14
- begin
15
- history = true
16
- input = Readline.readline("lasp> ", history).to_s
17
- input = autoclose_parentheses(input)
18
- result = Lasp::execute(input)
19
- puts " => #{result.inspect}"
20
- rescue
21
- puts " !> #{$!}"
6
+ class Repl
7
+ def self.run
8
+ new.run
9
+ end
10
+
11
+ def run
12
+ trap("SIGINT") { puts "\n\nBye!"; exit }
13
+
14
+ puts "((( Läsp v#{Lasp::VERSION} REPL (ctrl+c to exit) )))\n\n"
15
+ loop do
16
+ begin
17
+ history = true
18
+ input = Readline.readline(prompt, history).to_s
19
+ input = autoclose_parentheses(input)
20
+ result = Lasp::execute(input)
21
+ print_result(result)
22
+ rescue => error
23
+ print_error(error)
24
+ end
22
25
  end
23
26
  end
24
- end
25
27
 
26
- def autoclose_parentheses(input)
27
- tokens = Parser.new.tokenize(input)
28
- num_opens = tokens.select { |t| t == "(" }.count
29
- num_closes = tokens.select { |t| t == ")" }.count
28
+ private
29
+
30
+ def autoclose_parentheses(input)
31
+ tokens = Parser.new.tokenize(input)
32
+ num_opens = tokens.select { |t| t == "(" }.count
33
+ num_closes = tokens.select { |t| t == ")" }.count
34
+
35
+ if num_opens > num_closes
36
+ missing_closes = num_opens - num_closes
37
+
38
+ print_info "Appending #{missing_closes} missing closing parentheses:"
39
+ corrected_input = input + (")" * missing_closes)
40
+ print_info "#{corrected_input}"
30
41
 
31
- if num_opens > num_closes
32
- missing_closes = num_opens - num_closes
42
+ corrected_input
43
+ else
44
+ input
45
+ end
46
+ end
47
+
48
+ def prompt
49
+ "lasp> "
50
+ end
33
51
 
34
- puts " ?> Appending #{missing_closes} missing closing parentheses:"
35
- corrected_input = input + (")" * missing_closes)
36
- puts " ?> #{corrected_input}"
52
+ def print_error(error)
53
+ puts " !> #{error.class}: #{error.message}"
54
+ end
55
+
56
+ def print_info(message)
57
+ puts " ?> #{message}"
58
+ end
37
59
 
38
- corrected_input
39
- else
40
- input
60
+ def print_result(result)
61
+ puts " => #{result.inspect}"
41
62
  end
42
63
  end
43
64
  end
@@ -1,168 +1,172 @@
1
- (require "./lib/lasp/stdmacros.lasp")
1
+ (require "stdmacros.lasp")
2
2
 
3
3
  ; Aliases
4
4
  (def first head)
5
5
  (def rest tail)
6
6
 
7
7
  ; Increment a number by one
8
- (def inc (fn (x) (+ x 1)))
8
+ (defn inc (x) (+ x 1))
9
9
 
10
10
  ; Decrement a number by one
11
- (def dec (fn (x) (- x 1)))
11
+ (defn dec (x) (- x 1))
12
12
 
13
13
  ; Is it nil?
14
- (def nil? (fn (arg) (= nil arg)))
14
+ (defn nil? (arg) (= nil arg))
15
15
 
16
16
  ; If a list is empty
17
- (def empty?
18
- (fn (coll)
19
- (nil? (head coll))))
17
+ (defn empty?
18
+ (coll)
19
+ (nil? (head coll)))
20
20
 
21
21
  ; If all arguments are not equal
22
- (def not=
23
- (fn (& args)
24
- (not (apply = args))))
22
+ (defn not= (& args)
23
+ (not (apply = args)))
25
24
 
26
25
  ; The second item in a list
27
- (def second
28
- (fn (coll)
29
- (head (tail coll))))
26
+ (defn second (coll)
27
+ (head (tail coll)))
30
28
 
31
29
  ; Modulus
32
- (def mod
33
- (fn (x y) (- x (* (/ x y) y))))
30
+ (defn mod (x y)
31
+ (- x (* (/ x y) y)))
34
32
 
35
33
  ; Returns a function that does the opposite of the given function
36
- (def complement
37
- (fn (f) (fn (x) (not (f x)))))
34
+ (defn complement (f)
35
+ (fn (x) (not (f x))))
38
36
 
39
37
  ; If a number is even
40
- (def even?
41
- (fn (x) (zero? (mod x 2))))
38
+ (defn even? (x)
39
+ (zero? (mod x 2)))
42
40
 
43
41
  ; If a number is odd
44
42
  (def odd? (complement even?))
45
43
 
46
44
  ; If a number is zero
47
- (def zero?
48
- (fn (x) (= 0 x)))
45
+ (defn zero? (x)
46
+ (= 0 x))
49
47
 
50
48
  ; Length of a list
51
- (def len
52
- (fn (coll)
53
- (if (empty? coll)
54
- 0
55
- (inc (len (tail coll))))))
49
+ (defn len (coll)
50
+ (if (empty? coll)
51
+ 0
52
+ (inc (len (tail coll)))))
56
53
 
57
54
  ; Gets an item in a list by index
58
- (def nth
59
- (fn (index coll)
60
- (if (zero? index)
61
- (head coll)
62
- (nth (dec index) (tail coll)))))
55
+ (defn nth (index coll)
56
+ (if (zero? index)
57
+ (head coll)
58
+ (nth (dec index) (tail coll))))
63
59
 
64
60
  ; Last item in list
65
- (def last
66
- (fn (coll)
67
- (nth (dec (len coll)) coll)))
61
+ (defn last (coll)
62
+ (nth (dec (len coll)) coll))
68
63
 
69
64
  ; Reverses a list
70
- (def reverse
71
- (fn (coll)
72
- (reduce (fn (acc item) (cons item acc)) (list) coll)))
65
+ (defn reverse (coll)
66
+ (reduce
67
+ (fn (acc item) (cons item acc))
68
+ (list)
69
+ coll))
73
70
 
74
71
  ; Apply f to all items in list
75
- (def map
76
- (fn (f coll)
77
- (if (nil? (head coll))
78
- coll
79
- (cons
80
- (f (head coll))
81
- (map f (tail coll))))))
72
+ (defn map (f coll)
73
+ (if (nil? (head coll))
74
+ coll
75
+ (cons
76
+ (f (head coll))
77
+ (map f (tail coll)))))
82
78
 
83
79
  ; Go through a list passing an accumulator and each item of the list through f
84
- ; f(acc item)
85
- (def reduce
86
- (fn (f acc coll)
87
- (if (empty? coll)
88
- acc
89
- (reduce f (f acc (head coll)) (tail coll)))))
80
+ ; f has the signature (acc item)
81
+ (defn reduce (f acc coll)
82
+ (if (empty? coll)
83
+ acc
84
+ (reduce f (f acc (head coll)) (tail coll))))
90
85
 
91
86
  ; Filter a list of items based on a function
92
- (def filter
93
- (fn (f coll)
94
- (reduce
95
- (fn (acc item) (if (f item) (cons item acc) acc))
96
- (list)
97
- (reverse coll))))
87
+ (defn filter (f coll)
88
+ (reduce
89
+ (fn (acc item) (if (f item) (cons item acc) acc))
90
+ (list)
91
+ (reverse coll)))
98
92
 
99
93
  ; Sum of all items in a list
100
- (def sum
101
- (fn (coll)
102
- (reduce + 0 coll)))
94
+ (defn sum (coll)
95
+ (reduce + 0 coll))
103
96
 
104
- ; Take x items from list
105
- (def take
106
- (fn (num coll)
107
- (if (zero? num)
108
- (list)
109
- (cons (head coll) (take (dec num) (tail coll))))))
97
+ ; Take n items from list
98
+ (defn take (n coll)
99
+ (if (zero? n)
100
+ (list)
101
+ (cons (head coll) (take (dec n) (tail coll)))))
110
102
 
111
103
  ; Drop x items from list
112
- (def drop
113
- (fn (num coll)
114
- (if (zero? num)
115
- coll
116
- (drop (dec num) (tail coll)))))
104
+ (defn drop (num coll)
105
+ (if (zero? num)
106
+ coll
107
+ (drop (dec num) (tail coll))))
117
108
 
118
109
  ; Exclusive range
119
- (def range
120
- (fn (from to)
121
- (if (>= from to)
122
- (list)
123
- (cons from (range (inc from) to)))))
110
+ (defn range (from to)
111
+ (if (>= from to)
112
+ (list)
113
+ (cons from (range (inc from) to))))
124
114
 
125
115
  ; Highest value in list
126
- (def max
127
- (fn (coll)
128
- (reduce
129
- (fn (acc item) (if (< acc item) item acc))
130
- (head coll)
131
- (tail coll))))
116
+ (defn max (coll)
117
+ (reduce
118
+ (fn (acc item) (if (< acc item) item acc))
119
+ (head coll)
120
+ (tail coll)))
132
121
 
133
122
  ; Lowest value in list
134
- (def min
135
- (fn (coll)
136
- (reduce
137
- (fn (acc item) (if (> acc item) item acc))
138
- (head coll)
139
- (tail coll))))
123
+ (defn min (coll)
124
+ (reduce
125
+ (fn (acc item) (if (> acc item) item acc))
126
+ (head coll)
127
+ (tail coll)))
128
+
129
+ ; The naming suggests that it is an implementation detail of `every`, that
130
+ ; needs to be named to allow recursion. If it has value in itself, it should be
131
+ ; renamed and documented.
132
+ (defn every* (n coll acc)
133
+ (if (empty? coll)
134
+ acc
135
+ (every*
136
+ n
137
+ (drop n coll)
138
+ (cons (first coll) acc))))
139
+
140
+ ; Every Nth item in list
141
+ (defn every (n coll)
142
+ (reverse (every* n coll (list))))
140
143
 
141
144
  ; Takes a method from Ruby-land and returns a Lasp function
142
- (def ruby-method
143
- (fn (meth)
144
- (fn (arg)
145
- (. arg meth))))
145
+ (defn ruby-method (meth)
146
+ (fn (arg) (. arg meth)))
146
147
 
147
- ; Convert string to list
148
- (def str->list (ruby-method :chars))
149
148
 
150
- ; Convert list to string
151
- (def list->str (ruby-method :join))
149
+ ; Conversion functions
150
+ (def text->list (ruby-method :chars)) ; Convert text to list
151
+ (def list->text (ruby-method :join)) ; Convert list to text
152
+ (def ->text (ruby-method :to_s)) ; Convert to text
153
+ (def ->integer (ruby-method :to_i)) ; Convert to int
154
+ (def ->decimal (ruby-method :to_f)) ; Convert to decimal
152
155
 
153
- ; Convert most things to a string
154
- (def ->str (ruby-method :to_s))
155
156
 
156
157
  ; Pass a value in order through a list of functions
157
- (def pipe
158
- (fn (item & fns)
159
- (if (empty? fns)
160
- item
161
- ; Note that you need to take special care when recursing with rest
162
- ; arguments, hence the use of apply.
163
- (apply pipe (cons ((head fns) item) (tail fns))))))
164
-
165
- ; Reverses a string
166
- (def reverse-str
167
- (fn (str)
168
- (pipe str str->list reverse list->str)))
158
+ (defn pipe (item & fns)
159
+ (if (empty? fns)
160
+ item
161
+ ; Note that you need to take special care when recursing with rest
162
+ ; arguments, hence the use of apply.
163
+ (apply pipe (cons ((head fns) item) (tail fns)))))
164
+
165
+ ; Reverses text
166
+ (defn reverse-text (text)
167
+ (pipe text text->list reverse list->text))
168
+
169
+ ; Reads an answer to a question
170
+ (defn prompt (question)
171
+ (print question)
172
+ (readln))