Skip to content

Commit 225b118

Browse files
author
Ian Duncan
committed
Merge remote-tracking branch 'upstream/master'
2 parents e4f96b7 + adcd860 commit 225b118

File tree

73 files changed

+15584
-209
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+15584
-209
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
3+
*~
4+
5+
**/output
6+

LICENSE

Lines changed: 674 additions & 201 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ All programs in this repository are runnable in the [Source Academy playground](
66

77
## Evaluators
88

9-
The evaluators in this section all follow the general style of SICP JS Section 4.1.
9+
The evaluators in this section all follow the general style of SICP JS Chapter 4.
1010

1111
* [`src/evaluators/source-0.js`](https://github.com/source-academy/source-programs/blob/master/src/evaluators/source-0.js): evaluator for Source §0 (calculator language)
1212
* [`src/evaluators/source-0pp.js`](https://github.com/source-academy/source-programs/blob/master/src/evaluators/source-0pp.js): evaluator for Source §0++ (calculator language plus conditionals, blocks and sequences)
13-
* [`src/evaluators/source-1.js`](https://github.com/source-academy/source-programs/blob/master/src/evaluators/source-1.js): evaluator for Source §1
13+
* [`src/evaluators/source-4-1.js`](https://github.com/source-academy/source-programs/blob/master/src/evaluators/source-4-1.js): evaluator for Source §1, described in SICP JS 4.1
1414
* [`src/evaluators/typed-source.js`](https://github.com/source-academy/source-programs/blob/master/src/evaluators/typed-source.js): evaluator for Typed Source (typed version of a Source §1 sublanguage)
15+
* [`src/evaluators/source-4-3.js`](https://github.com/source-academy/source-programs/blob/master/src/evaluators/source-4-3.js): meta-circular evaluator for Source §4.3 (non-deterministic programming)
1516

1617
## Steppers
1718

@@ -36,14 +37,21 @@ The virtual machines in this section are SECD-style and follow a description in
3637
* [`src/virtual-machines/source-1.js`](https://github.com/source-academy/source-programs/blob/master/src/virtual-machines/source-1.js): virtual machine for a Source §1 sublanguage (without memory management)
3738
* [`src/virtual-machines/source-1-with-copying-gc.js`](https://github.com/source-academy/source-programs/blob/master/src/virtual-machines/source-1-with-copying-gc.js): virtual machine for a Source §1 sublanguage with a Cheney-style stop-and-copy garbage collector
3839
* [`src/virtual-machines/register-machine-gcd.js`](https://github.com/source-academy/source-programs/blob/master/src/virtual-machines/register-machine-gcd.js): register machine following SICP JS Section 5.2, using GCD example
40+
* [`src/virtual-machines/source-2.js`](https://github.com/source-academy/source-programs/blob/master/src/virtual-machines/source-2.js): virtual machine for a Source §2 sublanguage (without memory management)
41+
* [`src/virtual-machines/source-2-with-copying-gc.js`](https://github.com/source-academy/source-programs/blob/master/src/virtual-machines/source-2-with-copying-gc.js): virtual machine for a Source §2 sublanguage with a Cheney-style stop-and-copy garbage collector
42+
* [`src/virtual-machines/source-2-with-ms-gc.js`](https://github.com/source-academy/source-programs/blob/master/src/virtual-machines/source-2-with-ms-gc.js): virtual machine for a Source §2 sublanguage with a Mark-and-Sweep-style garbage collector
3943

4044
## Tool Demos
4145

46+
(click to run; for actual sources, go to [`src/tool-demos/`](https://github.com/source-academy/source-programs/blob/master/src/tool-demos/))
47+
4248
* [`src/tool-demos/stepper.js`](https://tinyurl.com/SICPJS-stepper): stepper tool (small-step semantics, based on substitution)
4349
* [`src/tool-demos/box-and-pointer-diagrams.js`](https://tinyurl.com/SICPJS-box-and-pointer): box-and-pointer diagram visualizer for pairs and lists (following SICP JS chapter 2)
4450
* [`src/tool-demos/environment-model.js`](https://tinyurl.com/SICPJS-env-diagram): environment model visualizer (following SICP JS chapter 3)
4551

46-
## Library Demos
52+
## Module Demos
53+
54+
(click to run; for actual sources, go to [`src/module-demos/`](https://github.com/source-academy/source-programs/blob/master/src/module-demos/))
4755

4856
* [`src/module-demos/runes.js`](https://tinyurl.com/SICPJS-hearts): the "picture language" of SICP JS 2.2.4
4957
* [`src/module-demos/twist.js`](https://tinyurl.com/SICPJS-twist): some fun with the "picture language"
@@ -53,4 +61,68 @@ The virtual machines in this section are SECD-style and follow a description in
5361
* [`src/module-demos/bohemian.js`](https://tinyurl.com/SICPJS-rhapsody): Bohemian Rhapsody cover using the "sounds" library
5462
* [`src/module-demos/pix-n-flix.js`](https://tinyurl.com/SICP-distortion): a library for image and video processing, based on the constituent pixels
5563

64+
## Test framework
65+
* [`src/test/framework/main.js`](https://github.com/source-academy/source-programs/blob/master/src/test/framework/): test framework for Source programs, written in Source §4
66+
67+
# Testing
68+
69+
[requires bash (any version) and awk (BSD awk 20070501); does not work with gawk]
70+
71+
For testing your Source programs, you need `node` and `yarn`.
72+
73+
Write your test cases in a folder `__tests__` in each `src` subfolder. The name of the file specifies the targeted Source of your test case. For example, if `src/steppers/source-0.js` is the Source, a test case might be `src/steppers/__tests__/source-0.test1.js`.
74+
75+
Only the tests written will be run.
76+
77+
Each test case is appended to your Source, and then run with `js-slang` (using Source §4). The last line of the test case is a `//` comment that must contain the expected result. For example, a stepper test case may be:
78+
79+
``` js
80+
parse_and_evaluate("! (1 === 1 && 2 > 3);");
81+
// true
82+
```
83+
84+
Before you can run the tests, you need to install `js-slang` by typing:
85+
86+
``` sh
87+
% yarn
88+
% yarn install
89+
```
90+
91+
Run all test cases by typing:
92+
93+
``` sh
94+
% yarn test
95+
```
96+
97+
For failure cases (where you program is to throw error, e.g. memory exhausted error for virtual machines), you can include the error message as per normal. The lastest JS-Slang already throws the error message explicitly, without letting the underlying TypeScript handling it. Hence, an error message
98+
99+
``` sh
100+
Line 2073: Error: memory exhausted despite garbage collection undefined
101+
```
102+
103+
can be written in the test file:
104+
105+
``` js
106+
// Line 2073: Error: memory exhausted despite garbage collection undefined
107+
```
108+
109+
or
110+
111+
``` js
112+
// Error: memory exhausted despite garbage collection undefined
113+
```
114+
115+
where only the part that starts from `Error:` will be compared. Line number is be ignored as it varies. If line number is needed for a particular reason, it can be appended to the back.
116+
117+
>**Note**: for virtual machine tests, you will have to make a function that outputs a single line output without the help of any `displays()`, as the test currently only supports 1 line comparison. Help to make this generic is appreciated.
118+
119+
>Integration of the `test` script with `src/test/framework/` is pending a fix to the `--variant` parameter; any help appreciated.
120+
121+
# License
122+
123+
[![GPL 3][gpl3-image]][gpl3]
124+
All JavaScript programs in this repository are licensed under the
125+
[GNU General Public License Version 3][gpl3].
56126

127+
[gpl3]: https://www.gnu.org/licenses/gpl-3.0.en.html
128+
[gpl3-image]: https://upload.wikimedia.org/wikipedia/commons/thumb/7/79/License_icon-gpl.svg/50px-License_icon-gpl.svg.png

package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "source-programs",
3+
"version": "0.4.21",
4+
"description": "Javascript-based implementations of Source, written in Typescript",
5+
"author": {
6+
"name": "Source Academy",
7+
"url": "https://github.com/source-academy/"
8+
},
9+
"scripts": {
10+
"build": "tsc",
11+
"test": "./scripts/test.sh"
12+
},
13+
"dependencies": {
14+
"js-slang": "^0.4.33",
15+
"lodash": "^4.17.13"
16+
},
17+
"repository": {
18+
"type": "git",
19+
"url": "git+https://github.com/source-academy/source-programs.git"
20+
},
21+
"bugs": {
22+
"url": "https://github.com/source-academy/source-programs/issues"
23+
},
24+
"homepage": "https://github.com/source-academy/source-programs"
25+
}

scripts/test.sh

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#! /usr/bin/env bash
2+
3+
JS_SLANG="node node_modules/js-slang/dist/repl/repl.js"
4+
5+
# must use BSD awk
6+
AWK="awk"
7+
8+
SOURCEFILES=src/*/*.js
9+
SOURCE_TEST="src/test/framework/main.js"
10+
11+
DEFAULT_CHAPTER=4
12+
DEFAULT_VARIANT="default"
13+
14+
red=`tput setaf 1`
15+
green=`tput setaf 2`
16+
normal=`tput setaf 7`
17+
18+
passed=0
19+
failed=0
20+
21+
# $1 is the source file to be tested
22+
# $2 is the test file
23+
# $3 is the chapter
24+
# $4 is the variant
25+
26+
test_source() {
27+
28+
# if program throws error, ignore the output and compare the error message only
29+
# NOTE: error output line is striped of line number as it is machine dependent
30+
ERROR=$( { $JS_SLANG -e --chapter=$3 "$(cat $1 $2)">$DIR/__tests__/output; } 2>&1 )
31+
if [ ! -z "$ERROR" ]
32+
then
33+
DIFF=$(diff <(echo $ERROR | grep -o 'Error:.*') <(cat $2 | tail -1 | grep -o 'Error:.*'))
34+
else
35+
DIFF=$(diff <(cat $DIR/__tests__/output) \
36+
<(cat $2 | tail -1 | cut -c4-))
37+
fi
38+
39+
if [ "$DIFF" = "" ]
40+
then passed=$(($passed+1)); echo "${green}PASS $2"
41+
else failed=$(($failed+1)); echo "${red}FAIL $2:
42+
$DIFF"
43+
fi
44+
# clean up temp file
45+
rm $DIR/__tests__/output
46+
47+
}
48+
49+
test_source_framework() {
50+
51+
# run concatenation of source-test framework, source and test files
52+
RESULTS=$($JS_SLANG -e --chapter=$3 --variant=$4 "$(cat $SOURCE_TEST $1 $2)")
53+
54+
# retrieve names for tests that passed
55+
while read test_name
56+
do
57+
passed=$(($passed+1))
58+
echo "${green}PASS $2 $test_name"
59+
done < <(echo ${RESULTS} | grep -o '\w* PASSED' | $AWK -F 'PASSED' '{ print $1 }')
60+
61+
# retrieve names and error messages for tests that failed
62+
while read test_info
63+
do
64+
failed=$(($failed+1))
65+
echo $test_info | $AWK -F 'FAILED:' '{ print $1 ":" $2 }' | $AWK -F '"' '{ print $1 $2 }' |
66+
while read test_name test_error
67+
do echo "${red}FAIL $2 $test_name $test_error";
68+
done
69+
done < <(echo ${RESULTS} | grep -o '\w* FAILED:[^"]*')
70+
}
71+
72+
main() {
73+
for s in ${SOURCEFILES}
74+
do
75+
DIR=$(dirname ${s})
76+
# if __tests__ directory exists
77+
if [ -d "$DIR/__tests__" ]
78+
then
79+
# call test_source on each test case in __tests__
80+
for i in "$DIR/__tests__/$(basename ${s} .js)".*
81+
do
82+
if [ -f "$i" ]; then
83+
# check if first line of test file contains 'chapter=' and retrieve its value. Set to the default chapter if it does not
84+
chapter=$($AWK -F 'chapter=' 'FNR==1{ if ($0~"chapter=") { print $2 } else { print '$DEFAULT_CHAPTER' } }' $i | $AWK -F ' ' '{ print $1 }')
85+
86+
# check if first line of test file contains 'variant=' and retrieve its value. Set to the default variant if it does not
87+
variant=$($AWK -F 'variant=' 'FNR==1{ if ($0~"variant=") { print $2 } else { print '$DEFAULT_VARIANT' } }' $i | $AWK -F ' ' '{ print $1 }')
88+
89+
# check if first line of test file contains 'framework'
90+
use_source_test=$($AWK 'FNR==1{ if ($0~"framework") print "yes" }' $i)
91+
if [[ $use_source_test == "yes" ]]
92+
then chapter=4 ; test_source_framework $s $i $chapter $variant
93+
else test_source $s $i $chapter $variant
94+
fi
95+
fi
96+
done
97+
fi
98+
done
99+
}
100+
101+
main
102+
echo "${normal}test cases completed; $passed passed, $failed failed"
103+
exit 0

src/classics/permutations.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// permutations
2+

src/classics/sorting_lists.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// The following sorting algorithms work on numbers and
2+
// strings: they use the comparison operator <=.
3+
// For other data types, replace this operator
4+
// appropriately.
5+
6+
// Merge sort
7+
8+
function merge_sort(xs) {
9+
if ( is_null(xs) || is_null(tail(xs))) {
10+
return xs;
11+
} else {
12+
const mid = middle(length(xs)) ;
13+
return merge(merge_sort(take(xs, mid)),
14+
merge_sort(drop(xs, mid)));
15+
}
16+
}
17+
18+
function middle(n) {
19+
return math_floor(n / 2) ;
20+
}
21+
22+
function merge(xs , ys) {
23+
if ( is_null(xs)) {
24+
return ys;
25+
} else if (is_null(ys)) {
26+
return xs;
27+
} else {
28+
const x = head(xs) ;
29+
const y = head(ys) ;
30+
if (x <= y) {
31+
return pair(x, merge(tail(xs), ys));
32+
} else {
33+
return pair(y, merge(xs, tail(ys)));
34+
}
35+
}
36+
}
37+
38+
function take(xs , n) {
39+
return (n === 0)
40+
? null
41+
: pair(head(xs), take(tail(xs), n - 1));
42+
}
43+
44+
function drop(xs, n) {
45+
return n === 0
46+
? xs
47+
: drop(tail(xs), n - 1);
48+
}
49+
50+
// selection sort
51+
52+
function selection_sort(xs) {
53+
if (is_null(xs)) {
54+
return xs;
55+
} else {
56+
const s = smallest(xs) ;
57+
return pair(s, selection_sort(remove(s, xs)));
58+
}
59+
}
60+
61+
// iterative smallest
62+
function smallest (xs) {
63+
function sm(x, ys) {
64+
return is_null(ys)
65+
? x
66+
: x <= head(ys)
67+
? sm(x, tail(ys))
68+
: sm(head(ys), tail(ys));
69+
}
70+
return sm(head(xs), tail(xs));
71+
}
72+
73+
// insertion sort
74+
75+
function insertion_sort (xs) {
76+
return is_null(xs)
77+
? xs
78+
: insert(head(xs), insertion_sort(tail(xs)));
79+
}
80+
81+
function insert(x, xs) {
82+
return is_null(xs)
83+
? list(x)
84+
: x <= head(xs)
85+
? pair(x, xs)
86+
: pair(head(xs), insert(x, tail(xs)));
87+
}
88+
89+
// quicksort (not given here, because it's a homework exercise
90+
// in CS1101S at NUS; no spoilers please)
91+
92+
function quicksort(xs) {
93+
// uses partition
94+
}
95+
96+
function partition (xs, p) {
97+
// ...
98+
}

src/classics/subsets.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// subsets
2+

0 commit comments

Comments
 (0)