Skip to content

Commit 904147f

Browse files
authored
Merge pull request #53 from JohanWiltink/main
add message to syntax error
2 parents 450fd6e + c46fbc7 commit 904147f

File tree

5 files changed

+127
-101
lines changed

5 files changed

+127
-101
lines changed

src/lambda-calculus.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Lambda Calculus evaluator supporting:
33
- unlimited recursion
4+
- call by need
45
- fast (ish?) evaluation
56
- shortform syntax
67
@@ -179,7 +180,10 @@ function parseWith(cfg={}) {
179180
return tm;
180181
} else {
181182
if ( verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`);
182-
throw new ReferenceError(`undefined free variable ${ nm }`);
183+
if ( nm === name )
184+
throw new ReferenceError(`undefined free variable ${ nm }: direct recursive calls are not supported in Let mode`);
185+
else
186+
throw new ReferenceError(`undefined free variable ${ nm }`);
183187
}
184188
} , new Tuple( term, new Env ) );
185189
else if ( purity==="LetRec" )
@@ -212,7 +216,7 @@ function parseWith(cfg={}) {
212216
console.error(code);
213217
console.error(' '.repeat(i) + '^');
214218
console.error(msg + " at position " + i);
215-
throw new SyntaxError;
219+
throw new SyntaxError(msg);
216220
}
217221
function sp(i) { while ( whitespace.test( code[i] || "" ) ) i++; return i; }
218222
const expect = c => function(i) { return code[i]===c ? sp(i+1) : 0 ; } ;

tests/multiply/initialSolution.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
multiply = \ m n . n (m s ) z
1+
multiply = \ m n . n (m s) z

tests/multiply/solution.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
multiply = \ m n s . n ( m s )
1+
multiply = \ m n s z . n (m s) z

tests/scott-lists/solution.txt

+82-79
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# scott-lists.lc
1+
# scott-list.lc
22

33
#import combinators.lc
44
B = \ f g x . f (g x)
@@ -18,7 +18,7 @@ Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) )
1818
#import scott-booleans.ls
1919
False = K
2020
True = KI
21-
not = \ p . p True False
21+
not = C
2222
and = M
2323
or = W C
2424
#import scott-ordering.lc
@@ -58,7 +58,7 @@ is-none = \ x . x True (K False) # = is-zero
5858
is-some = \ x . x False (K True)
5959
from-option = \ z x . x z I
6060
from-some = \ x . x () I
61-
# additional definitions depend on nil and cons
61+
# additional definitions depend on nil, cons, singleton
6262

6363
# data List a = Nil | Cons a (List a)
6464

@@ -71,32 +71,43 @@ cons = \ x xs . \ _nil cons . cons x xs
7171
# singleton :: a -> List a
7272
singleton = \ x . cons x nil
7373

74-
# these scott-options definitions depend on nil, cons, singleton
74+
# these scott-option definitions depend on nil, cons, singleton
7575
list-to-option = \ xs . xs None \ x _xs . Some x
7676
option-to-list = \ x . x nil singleton
7777
map-option = \ fn xs . xs nil \ x xs . fn x (map-option fn xs) (C cons (map-option fn xs))
7878
cat-options = map-option I
7979

80-
# continuing scott-lists.lc
80+
# continuing scott-list.lc
8181

8282
# foldr :: (a -> z -> z) -> z -> List a -> z
83-
foldr = \ fn z xs . xs z ( \ x xs . fn x (foldr fn z xs) )
83+
foldr = \ fn z xs . xs z \ x xs . fn x (foldr fn z xs)
8484

85-
# null :: List a -> Boolean
86-
null = \ xs . xs True (KK False)
85+
# foldl :: (z -> a -> z) -> z -> List a -> z
86+
foldl = \ fn z xs . xs z (B (foldl fn) (fn z))
87+
88+
# scanr :: (a -> z -> z) -> z -> List a -> List z
89+
scanr = \ fn z xs . xs (singleton z) \ x xs . ( \ zs . zs () \ z _zs . cons (fn x z) zs ) (scanr fn z xs)
90+
91+
# scanl :: (z -> a -> z) -> z -> List a -> List z
92+
scanl = \ fn z xs . cons z (xs nil (B (scanl fn) (fn z)))
8793

8894
# take :: Number -> List a -> List a
8995
take = \ n xs . is-zero n (xs nil \ x xs . cons x (take (pred n) xs)) nil
9096

97+
# drop :: Number -> List a -> List a
98+
drop = \ n xs . is-zero n (xs nil (K (drop (pred n)))) xs
99+
91100
# append :: List a -> List a -> List a
92101
append = C (foldr cons)
93102

94103
# concat :: List (List a) -> List a
95-
concat = \ xss . foldr xss append nil
104+
concat = foldr append nil
96105

97-
# sum,product :: List Number -> Number
98-
sum = foldr add zero
99-
product = foldr mul one
106+
# snoc :: List a -> a -> List a
107+
snoc = C (B (foldr cons) singleton)
108+
109+
# uncons :: List a -> Option (Pair a (List a))
110+
uncons = \ xs . xs None (BB Some Pair)
100111

101112
# iterate :: (a -> a) -> a -> List a
102113
iterate = \ fn x . cons x (iterate fn (fn x))
@@ -105,47 +116,57 @@ iterate = \ fn x . cons x (iterate fn (fn x))
105116
repeat = \ x . cons x (repeat x) # repeat = Y (S cons)
106117

107118
# cycle :: List a -> List a
108-
cycle = \ xs . null xs (concat (repeat xs)) ()
119+
cycle = \ xs . xs () (concat (repeat xs))
109120

110121
# replicate :: Number -> a -> List a
111122
replicate = \ n . B (take n) repeat
112123

124+
# unfold :: (a -> Option (Pair z a)) -> a -> List z
125+
unfold = \ fn x . fn x nil (T \ z x . cons z (unfold fn x))
126+
113127
# head :: List a -> a
114128
head = \ xs . xs () K
115129

116130
# tail :: List a -> List a
117131
tail = \ xs . xs () KI
118132

133+
# null :: List a -> Boolean
134+
null = \ xs . xs True (KK False)
135+
119136
# length :: List a -> Number
120137
length = foldr (K succ) zero
121138

122-
# snoc :: List a -> a -> List a
123-
snoc = C (B (foldr cons) singleton)
139+
# sum,product :: List Number -> Number
140+
sum = foldr add zero
141+
product = foldr mul one
124142

125143
# map :: (a -> b) -> List a -> List b
126144
map = \ fn . foldr (B cons fn) nil
127145

128146
# concat-map :: (a -> List b) -> List a -> List b
129147
concat-map = BB concat map
130148

131-
# filter :: () -> List a -> List a
149+
# filter :: (a -> Boolean) -> List a -> List a
132150
filter = \ p . foldr ( \ x z . p x z (cons x z) ) nil
133-
filter = \ p . foldr ( \ x . S (p x) (cons x) ) nil
134-
filter = \ p . foldr (S (B S p) cons) nil
135151

136-
# drop :: Number -> List a -> List a
137-
drop = \ n xs . is-zero n ( \ _x xs . drop (pred n) xs ) xs
138-
drop = \ n . is-zero n (K (drop (pred n)))
152+
# take-while :: (a -> Boolean) -> List a -> List a
153+
take-while = \ p xs . xs nil \ x xs . p x nil (cons x (take-while p xs))
154+
155+
# drop-while :: (a -> Boolean) -> List a -> List a
156+
drop-while = \ p xs . xs nil \ x xs . p x xs (drop-while p xs)
157+
158+
# drop-while-end :: (a -> Boolean) -> List a -> List a
159+
drop-while-end = \ p . foldr ( \ x z . and (null z) (p x) (cons x z) nil ) nil
139160

140161
# split-at :: Number -> List a -> Pair (List a) (List a)
141162
split-at = \ i xs . is-zero i (xs (Pair nil nil) \ x xs . first (cons x) (split-at (pred i) xs)) (Pair nil xs)
142163

143164
# get :: Number -> List a -> a
144-
get = \ i xs . is-zero i ( \ x xs . xs () (get (pred i) xs) ) (head xs)
165+
get = \ i xs . is-zero i (xs () (K (get (pred i)))) (head xs)
145166

146167
# set :: Number -> a -> List a -> List a
147168
set = \ i x xs . uncurry append (second (B (cons x) tail) (split-at i xs))
148-
set = \ i x xs . is-zero i (xs nil \ y ys . cons y (set (pred i) x ys)) (xs nil (K (cons x)))
169+
set = \ i x xs . is-zero i (xs nil \ y . cons y (set (pred i) x)) (xs nil (K (cons x)))
149170

150171
# any :: (a -> Boolean) -> List a -> Boolean
151172
any = \ p . foldr (B or p) False
@@ -154,96 +175,78 @@ any = \ p . foldr (B or p) False
154175
all = \ p . foldr (B and p) True
155176

156177
# find :: (a -> Boolean) -> List a -> Option a
157-
find = \ p . foldr ( \ x z . p x z (Some x) ) None
178+
find = BB list-to-option filter
158179

159180
# find-indices :: (a -> Boolean) -> List a -> List Number
160181
find-indices = \ p . foldr ( \ x k i . p x I (cons i) (k (succ i)) ) (K nil) zero
161182

162183
# find-index :: (a -> Boolean) -> List a -> Option Number
163-
find-index = \ p . B list-to-option (find-indices p)
184+
find-index = BB list-to-option find-indices
164185

165186
# partition :: (a -> Boolean) -> List a -> Pair (List a) (List a)
166187
partition = \ p . foldr ( \ x . p x second first (cons x) ) (Pair nil nil)
167188

168189
# span :: (a -> Boolean) -> List a -> Pair (List a) (List a)
169190
span = \ p xs . xs (Pair nil nil) \ y ys . p y (Pair nil xs) (first (cons y) (span p ys))
170191

171-
# minimum-by :: (a -> a -> Boolean) -> List a -> a # cmp ~ le
172-
minimum-by = \ cmp xs . xs () (foldr \ x z . cmp x z z x)
173-
174-
# maximum-by :: (a -> a -> Boolean) -> List a -> a # cmp ~ le
175-
maximum-by = \ cmp xs . xs () (foldr \ x z . cmp x z x z)
192+
# minimum-by :: (a -> a -> Boolean) -> List a -> a
193+
minimum-by = \ le xs . xs () (foldl \ z x . le z x x z)
176194

177-
# insert-by :: (a-> a -> Boolean) -> a -> List a -> List a # cmp ~ le
178-
insert-by = \ cmp x xs . uncurry append (second (cons x) (span (C cmp x) xs))
195+
# maximum-by :: (a -> a -> Boolean) -> List a -> a
196+
maximum-by = \ le xs . xs () (foldl \ z x . le z x z x)
179197

180-
# sort-by :: (a -> a -> Boolean) -> List a -> List a # cmp ~ le
181-
sort-by = \ cmp . foldr (insert-by cmp) nil
182-
183-
# foldl :: (z -> a -> z) -> z -> List a -> z
184-
foldl = \ fn z xs . xs z (B (foldl fn) (fn z))
185-
186-
# scanl :: (z -> a -> z) -> z -> List a -> List z
187-
scanl = \ fn z xs . cons z (xs nil (B (scanl fn) (fn z)))
198+
# insert-by :: (a -> a -> Boolean) -> a -> List a -> List a
199+
insert-by = \ le x xs . uncurry append (second (cons x) (span (C le x) xs))
188200

189-
# scanr :: (a -> z -> z) -> z -> List a -> List z
190-
scanr = \ fn z xs . xs (singleton z) \ x xs . ( \ zs . zs \ z _zs . cons (fn x z) zs ) (scanr fn z xs)
201+
# sort-by :: (a -> a -> Boolean) -> List a -> List a
202+
sort-by = \ le . foldr (insert-by le) nil
203+
# has all sorts of bad implementation details, but it's simple
191204

192205
# reverse :: List a -> List a
193206
reverse = foldl (C cons) nil
194207

195-
# unzip :: List (Pair a b) -> Pair (List a) (List b)
196-
unzip = foldr ( \ xy xys . xy \ x y . bimap (cons x) (cons y) xys ) (Pair nil nil)
197-
unzip = foldr (CB \ x y . bimap (cons x) (cons y)) (Pair nil nil)
198-
199208
# zip-with :: (a -> b -> z) -> List a -> List b -> List z
200209
zip-with = \ fn xs ys . xs nil \ x xs . ys nil \ y ys . cons (fn x y) (zip-with fn xs ys)
201210

202211
# zip :: List a -> List b -> List (Pair a b)
203212
zip = zip-with Pair
204213

205-
# init :: List a -> List a
206-
init = \ xs . xs () (S (zip-with K) tail xs)
207-
208-
# last :: List a -> a
209-
last = foldl KI ()
210-
211-
# slice :: Number -> Number -> List a -> List a
212-
slice = \ i j xs . gt j i nil (take (sub j i) (drop i xs))
213-
214-
# uncons :: List a -> Option (Pair (a) (List a))
215-
uncons = \ xs . xs None (B Some Pair)
214+
# unzip :: List (Pair a b) -> Pair (List a) (List b)
215+
unzip = foldr ( \ xy xys . xy \ x y . bimap (cons x) (cons y) xys ) (Pair nil nil)
216+
unzip = foldr (CB \ x y . bimap (cons x) (cons y)) (Pair nil nil)
216217

217-
# transpose :: List (List a) -> List (List a)
218-
transpose = \ xss . xss nil
219-
\ ys yss . ys (transpose yss)
220-
(unzip (map-option uncons xss) \ xs xxs . cons xs (transpose xss))
218+
# group-by :: (a -> a -> Bool) -> List a -> List (List a)
219+
group-by = \ eq xs . xs nil \ x xs . span (eq x) xs \ left right . cons (cons x left) (group-by eq right)
221220

222-
# unfold :: (a -> Option (Pair z a)) -> a -> List z
223-
unfold = \ fn x . fn x nil (T \ z x . cons z (unfold fn x))
221+
# lookup-by :: (a -> Boolean) -> List (Pair a b) -> Option b
222+
lookup-by = \ p xys . xys None \ xy xys . xy \ x y . p x (lookup-by p xys) (Some y)
224223

225-
# take-while :: (a -> Boolean) -> List a -> List a
226-
take-while = \ p xs . xs nil \ x xs . p x nil (cons x (take-while p xs))
224+
# nub-by :: (a -> a -> Boolean) -> List a -> List a
225+
go = \ z eq xs . xs z \ x xs . go (is-none (find (eq x) z) z (cons x z)) eq xs
226+
nub-by = go nil
227227

228-
# drop-while :: (a -> Boolean) -> List a -> List a
229-
drop-while = \ p xs . xs nil \ x xs . p x xs (drop-while p xs)
228+
# delete-by :: (a -> a -> Boolean) -> a -> List a -> List a
229+
delete-by = \ eq x xs . xs nil \ y ys . eq x y (cons y (delete-by eq x ys)) ys
230230

231-
# drop-while-end :: (a -> Boolean) -> List a -> List a
232-
drop-while-end = \ p . foldr ( \ x z . and (null z) (p x) (cons x z) nil ) nil
231+
# delete-firsts-by :: (a -> a -> Boolean) -> List a -> List a -> List a
232+
delete-firsts-by = \ eq . foldl (C (delete-by eq))
233233

234-
# group-by :: (a -> a -> Bool) -> List a -> List (List a)
235-
group-by = \ eq xs . xs nil \ x xs . span (eq x) xs \ left right . cons (cons x left) (group-by eq right)
236-
group-by = \ eq xs . xs nil \ x xs . uncurry cons (bimap (cons x) (group-by eq) (span (eq x) xs))
234+
# init :: List a -> List a
235+
init = \ xs . xs () (S (zip-with K) tail xs)
237236

238-
# inits
237+
# last :: List a -> a
238+
last = foldl KI ()
239239

240240
# tails :: List a -> List (List a)
241241
tails = \ xs . cons xs (xs nil (K tails))
242242

243-
# lookup-by :: (a -> Boolean) -> List (Pair a b) -> Option b
244-
lookup-by = \ eq xys . xys None \ xy xys . xy \ x y . eq x (lookup-by eq xys) (Some y)
243+
# inits :: List a -> List (List a)
244+
inits = \ xs . xs (singleton nil) \ x xs . cons nil (map (cons x) (inits xs))
245245

246-
# nub-by
247-
# delete-by
248-
# delete-firsts-by
249-
# sort-on
246+
# slice :: Number -> Number -> List a -> List a
247+
slice = \ i j xs . le i j nil (take (sub j i) (drop i xs))
248+
249+
# transpose :: List (List a) -> List (List a)
250+
transpose = \ xss . xss nil
251+
\ ys yss . ys (transpose yss)
252+
(unzip (map-option uncons xss) \ xs xss . cons xs (transpose xss))

tests/scott-lists/test.js

+37-18
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,51 @@ LC.config.verbosity = "Concise";
99

1010
const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"});
1111
const solution = LC.compile(solutionText);
12-
const fromInt = LC.fromIntWith(LC.config);
13-
const toInt = LC.toIntWith(LC.config);
1412

1513
const {nil,cons,singleton} = solution;
16-
const {foldr,head,tail,take} = solution;
17-
const {iterate,repeat,cycle,replicate} = solution;
18-
const {foldl,reverse} = solution;
14+
const {foldr,foldl,scanr,scanl} = solution;
15+
const {take,drop} = solution;
16+
const {append,concat,snoc,uncons} = solution;
17+
const {iterate,repeat,cycle,replicate,unfold} = solution;
18+
const {head,tail,"null":isNil,length,sum,product} = solution;
19+
const {map,"concat-map":concatMap,filter} = solution;
20+
const {"take-while":takeWhile,"drop-while":dropWhile,"drop-while-end":dropWhileEnd} = solution;
21+
const {"split-at":splitAt,get,set} = solution;
22+
const {any,all,find,"find-indices":findIndices,"find-index":findIndex} = solution;
23+
const {partition,span,"minimum-by":minimumBy,"maximum-by":maximumBy} = solution;
24+
const {"insret-by":insertBy,"sort-by":sortBy,reverse} = solution;
25+
const {"zip-with":zipWith,zip,unzip} = solution;
26+
const {"group-by":groupBy,"nub-by":nubBy,"delete-by":deleteBy,"delete-firsts-by":deleteFirstsBy} = solution;
27+
const {init,last,tails,inits,slice,transpose} = solution;
28+
const {add,zero} = solution;
29+
30+
const fromInt = LC.fromIntWith(LC.config);
31+
const toInt = LC.toIntWith(LC.config);
32+
const fromArray = xs => xs.reduceRight( (z,x) => cons(x)(z) , nil ) ;
33+
const toArray = foldl ( z => x => [...z,x] ) ([]) ;
1934

20-
const fromList = foldl ( z => x => [...z,x] ) ([]) ;
35+
const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ;
36+
const elements = xs => xs[ rnd(xs.length) ] ;
37+
const rndArray = size => Array.from( { length: rnd(size) }, () => rnd(size) ) ;
2138

2239
const refReplicate = length => x => Array.from( { length }, () => x ) ;
2340

2441
describe("Scott Lists",function(){
25-
it("example tests",()=>{
26-
assert.deepEqual( fromList( nil ), [] );
27-
assert.deepEqual( fromList( singleton ("0") ), ["0"] );
28-
assert.deepEqual( fromList( cons ("0") (singleton ("1")) ), ["0","1"] );
29-
assert.deepEqual( fromList( replicate (fromInt(0)) ("0") ), [] );
30-
assert.deepEqual( fromList( replicate (fromInt(1)) ("0") ), ["0"] );
31-
assert.deepEqual( fromList( replicate (fromInt(2)) ("0") ), ["0","0"] );
42+
it("nil,cons,singleton",()=>{
43+
assert.deepEqual( toArray( nil ), [] );
44+
for ( let i=1; i<=10; i++ ) {
45+
const x = rnd(i), xs = rndArray(i);
46+
assert.deepEqual( toArray( cons (fromInt(x)) (fromArray(xs.map(fromInt))) ).map(toInt), [x,...xs], `after ${ i } tests` );
47+
assert.deepEqual( toArray( singleton (fromInt(x)) ).map(toInt), [x], `after ${ i } tests` );
48+
}
3249
});
33-
it("random tests",()=>{
34-
const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ;
35-
for ( let i=1; i<=100; i++ ) {
36-
const m = rnd(i), n = rnd(i);
37-
assert.deepEqual( fromList( replicate (fromInt(m)) (String(n)) ), refReplicate(m)(String(n)), `after ${ i } tests` );
50+
it("foldr,foldl,scanr,scanl",()=>{
51+
for ( let i=1; i<=10; i++ ) {
52+
const xs = rndArray(i);
53+
assert.deepEqual( toInt( foldr (add) (zero) (fromArray(xs.map(fromInt))) ), xs.reduce((x,y)=>x+y,0), `after ${ i } tests` );
54+
assert.deepEqual( toInt( foldl (add) (zero) (fromArray(xs.map(fromInt))) ), xs.reduce((x,y)=>x+y,0), `after ${ i } tests` );
55+
assert.deepEqual( toArray( scanr (add) (zero) (fromArray(xs.map(fromInt))) ).map(toInt), xs.reduceRight( (z,x) => [ z[0]+x, ...z ], [0] ), `after ${ i } tests` );
56+
assert.deepEqual( toArray( scanl (add) (zero) (fromArray(xs.map(fromInt))) ).map(toInt), xs.reduce( (z,x) => [ ...z, z[z.length-1]+x ] , [0] ), `after ${ i } tests` );
3857
}
3958
});
4059
});

0 commit comments

Comments
 (0)