├── .clang-format
├── .gitattributes
├── .github
└── workflows
│ └── c.yml
├── .gitignore
├── LICENSE
├── README.md
├── docs
├── LICENSE
├── css3-github-buttons
│ ├── LICENSE
│ ├── gh-buttons.css
│ └── gh-icons.png
├── documentation.html
├── download.html
├── examples.html
├── fonts
│ ├── junction
│ │ ├── junction-bold.eot
│ │ ├── junction-bold.svg
│ │ ├── junction-bold.ttf
│ │ ├── junction-bold.woff
│ │ ├── junction-light.eot
│ │ ├── junction-light.svg
│ │ ├── junction-light.ttf
│ │ ├── junction-light.woff
│ │ ├── junction-regular.eot
│ │ ├── junction-regular.svg
│ │ ├── junction-regular.ttf
│ │ ├── junction-regular.woff
│ │ └── junction.css
│ └── league
│ │ ├── .gitignore
│ │ ├── leaguespartan-bold.eot
│ │ ├── leaguespartan-bold.svg
│ │ ├── leaguespartan-bold.ttf
│ │ ├── leaguespartan-bold.woff
│ │ ├── leaguespartan-bold.woff2
│ │ └── stylesheet.css
├── hljs-ms.js
├── hljs-railcasts.css
├── img
│ ├── GitHub-Mark-32px.png
│ ├── GitHub-Mark-Light-32px.png
│ ├── heart.png
│ ├── hello.png
│ ├── logo.png
│ ├── logosmall.png
│ ├── mini.png
│ ├── sub.png
│ └── types.png
├── index.html
├── jquery-1.11.2.min.js
├── literature.html
├── local.css
├── old
│ ├── binaries
│ │ ├── microscheme_0.7_BUILD400_linux_32.tar.gz
│ │ ├── microscheme_0.7_BUILD400_linux_64.tar.gz
│ │ ├── microscheme_0.7_BUILD400_win_32.zip
│ │ ├── microscheme_0.7_BUILD400_win_64.zip
│ │ ├── microscheme_0.8_BUILD511_linux_32.tar.gz
│ │ ├── microscheme_0.8_BUILD511_linux_64.tar.gz
│ │ ├── microscheme_0.8_BUILD511_win_32.zip
│ │ └── microscheme_0.8_BUILD511_win_64.zip
│ ├── caveats.html
│ ├── css
│ │ ├── foundation.css
│ │ ├── foundation.min.css
│ │ └── normalize.css
│ ├── download.html
│ ├── examples.html
│ ├── guide.html
│ ├── guide2.html
│ ├── guide3.html
│ ├── guide4.html
│ ├── guide5.html
│ ├── guide6.html
│ ├── guide7.html
│ ├── help.html
│ ├── img
│ │ ├── .gitkeep
│ │ └── hello.png
│ ├── index.html
│ ├── js
│ │ ├── foundation.min.js
│ │ ├── foundation
│ │ │ ├── foundation.abide.js
│ │ │ ├── foundation.accordion.js
│ │ │ ├── foundation.alert.js
│ │ │ ├── foundation.clearing.js
│ │ │ ├── foundation.dropdown.js
│ │ │ ├── foundation.interchange.js
│ │ │ ├── foundation.joyride.js
│ │ │ ├── foundation.js
│ │ │ ├── foundation.magellan.js
│ │ │ ├── foundation.offcanvas.js
│ │ │ ├── foundation.orbit.js
│ │ │ ├── foundation.reveal.js
│ │ │ ├── foundation.tab.js
│ │ │ ├── foundation.tooltip.js
│ │ │ └── foundation.topbar.js
│ │ ├── jquery.js
│ │ ├── modernizr.js
│ │ └── vendor
│ │ │ ├── fastclick.js
│ │ │ ├── jquery.cookie.js
│ │ │ ├── jquery.js
│ │ │ ├── modernizr.js
│ │ │ └── placeholder.js
│ ├── local.css
│ ├── logo.png
│ ├── logo.xcf
│ ├── logosmall.png
│ ├── logosquare.xcf
│ ├── mini.png
│ ├── sam.php
│ └── workflow.html
└── static
│ └── dissertation.pdf
├── emulator.scm
├── examples
├── BLINK.ms
├── GERALD.ms
├── blink-led-morse.ms
├── clock.ms
├── fade.ms
├── helloworld.ms
├── marquee.ms
├── music.ms
└── serial.ms
├── ffi_stuff
├── dump.py
├── ffi-test.ms
├── ffitest.c
└── microscheme_types.c
├── libraries
├── ascii.ms
├── lcd.ms
└── max7.ms
├── makefile
└── src
├── assembly_hex.h
├── avr_core.ms
├── codegen.c
├── codegen.h
├── common.c
├── common.h
├── lexer.c
├── lexer.h
├── main.c
├── main.h
├── microscheme_hex.h
├── models.c
├── models.h
├── parser.c
├── parser.h
├── preamble.s
├── primitives.ms
├── scoper.c
├── scoper.h
├── stdlib.ms
├── treeshaker.c
└── treeshaker.h
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | BasedOnStyle: LLVM
3 | AllowShortCaseLabelsOnASingleLine: 'true'
4 | AllowShortFunctionsOnASingleLine: All
5 | AllowShortIfStatementsOnASingleLine: Always
6 | AllowShortLoopsOnASingleLine: 'true'
7 | AlwaysBreakBeforeMultilineStrings: 'true'
8 | BinPackArguments: 'false'
9 | BinPackParameters: 'false'
10 | BreakBeforeBraces: Allman
11 | BreakStringLiterals: 'false'
12 | ColumnLimit: '0'
13 | IncludeBlocks: Regroup
14 | IndentCaseLabels: 'true'
15 | IndentWidth: '4'
16 |
17 | ...
18 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.ms linguist-language=Scheme
2 |
--------------------------------------------------------------------------------
/.github/workflows/c.yml:
--------------------------------------------------------------------------------
1 | name: Build and Test
2 |
3 | on:
4 | push:
5 | branches: [ '*' ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-20.04
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: get build dependencies
15 | run: |
16 | sudo apt update
17 | sudo apt -y install make avr-libc xxd cppcheck
18 | - name: static analysis
19 | run: make check
20 | - name: generate source
21 | run: make hexify
22 | - name: build
23 | run: make build
24 | - name: test
25 | run: ./microscheme examples/BLINK.ms
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Object files
2 | *.o
3 | *.ko
4 |
5 | # Libraries
6 | *.lib
7 | *.a
8 |
9 | # Shared objects (inc. Windows DLLs)
10 | *.dll
11 | *.so
12 | *.so.*
13 | *.dylib
14 |
15 | # Executables
16 | *.exe
17 | *.out
18 | *.app
19 |
20 | src/*_hex.c
21 | bin/
22 | release/
23 | examples/*.s
24 | examples/*.elf
25 | examples/*.hex
26 | *.s
27 | *.elf
28 | *.hex
29 |
30 | working
31 | working/*
32 |
33 | production
34 | production/*
35 |
36 | microscheme
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Ryan Suchocki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Microscheme
2 | ===========
3 |
4 | [](https://gitlab.com/ryansuchocki/microscheme/-/commits/master)
5 | 
6 |
7 | Microscheme is a Scheme subset designed for Atmel microcontrollers, especially as found on Arduino boards.
8 |
9 | Recent Changes
10 | --------------
11 |
12 | 1. Microscheme now has apply!
13 | 2. Microscheme now has an FFI!
14 |
15 | Compiling
16 | ---------
17 |
18 | ### Quick-Start
19 |
20 | `$ make hexify`
21 |
22 | `$ make build`
23 |
24 | `$ ./microscheme examples/BLINK.ms`
25 |
26 | If you have an arduino *on hand*:
27 |
28 | `$ ./microscheme -m [MODEL] -d [/dev/WHATEVER] -auc examples/BLINK.ms`
29 |
30 | ### Detail
31 |
32 | The microscheme source code is located in src/, and includes files written in C (.c, .h), assembly (.s)
33 | and in microscheme (.ms).
34 |
35 | In order to compile microscheme, those source files written in assembly and microscheme are 'hexified', i.e.
36 | converted into C byte arrays by invoking `$ make hexify`. Next, the compiler is compiled by invoking `$ make build`.
37 |
38 | The result is a standalone binary called 'microscheme' which is entirely self-contained, and can be separated
39 | from other files in this repository. On linux systems, invoke `$ sudo make install` to copy the binary to
40 | /usr/local/bin/, thus making it available system-wide.
41 |
42 | Usage
43 | -----
44 |
45 | As of the latest commit, the usage is:
46 |
47 | ```
48 | Usage: microscheme [-auscvrio] [-m model] [-d device] [-p programmer] [-w filename] [-t rounds] program[.ms]
49 |
50 | Option flags:
51 | -a Assemble (implied by -u or -s) (requires -m)
52 | -u Upload (requires -d)
53 | -s Disassemble (to inspect final binary)
54 | -c Cleanup (removes intermediate files)
55 | -v Verbose
56 | -r Verify (Uploading takes longer)
57 | -i Allow the same file to be included more than once
58 | -o Disable optimisations
59 | -h Show this help message
60 |
61 | Configuration flags:
62 | -m model Specify a model (UNO/MEGA/LEO...)
63 | -d device Specify a physical device
64 | -p programmer Tell avrdude to use a particular programmer
65 | -w files 'Link' with external C or assembly files
66 | -t rounds Specify the maximum number of tree-shaker rounds
67 |
68 | ```
69 |
70 | Prerequisites
71 | -------------
72 |
73 | In order to compile microscheme, you will need some implementation of GCC and the unix utility XXD. (Readily
74 | available on Linux/OSX.)
75 |
76 | In order to assemble programs and upload them to real Arduinos, you will need some implementation of *avr-gcc*, *avr-libc* and *avrdude*. Microscheme will try to invoke these tools directly, if the -a or -u options are used.
77 | Packages are available on all good linux distro's:
78 |
79 | For example, on Arch linux:
80 |
81 | `$ sudo pacman -S avr-gcc avr-libc avrdude`
82 |
83 | Or, on Ubuntu or Debian:
84 |
85 | `$ sudo apt-get install gcc-avr avr-libc avrdude`
86 |
87 | These tools are available via the [Homebrew](http://brew.sh/) and [MacPorts](https://www.macports.org/) package
88 | managers on Mac OS X and the [winavr](http://winavr.sourceforge.net/) project on Windows.
89 |
90 | Targets
91 | -------
92 |
93 | Microscheme currently supports the ATMega168/328 (used on the Arduino UNO), the ATMega2560 (used on most Arduino MEGA boards), and the ATMega32u4. The target controller is set using the command line argument `-m` follwed by `MEGA`, `UNO`, or `LEO` (not required if you're just compiling).
94 |
95 | Note: An Arduino Pro Mini with a 168/328 chip (programmed via an UNO board with its chip removed) can be treated as an UNO, because it uses the same chip.
96 |
97 | Other chips can be supported by writing model definitions in [models.c](src/models.c), containing values derived from the relevant Atmel datasheet.
98 |
99 | Compiler pipeline
100 | -----------------
101 |
102 | The entire compiler pipeline, as orchestrated by main.c, is:
103 |
104 | > [source code] → lexer.c → [token tree] → parser.c → [abstract syntax tree] → scoper.c → [(scoped) AST] → treeshaker.c → [(reduced) AST] → codegen.c → [assembly code] ...
105 |
106 | If the -a (assemble) option is given:
107 |
108 | > [assembly code] → avr-gcc → [ELF binary] → avr-objcopy → [IHEX binary] ...
109 |
110 | If the -u (upload) option is given:
111 |
112 | > [IHEX binary] → avrdude → Arduino Device
113 |
114 |
115 | License
116 | -------
117 |
118 | The MIT License (MIT)
119 |
120 | Copyright (c) 2014 Ryan Suchocki
121 |
122 | Permission is hereby granted, free of charge, to any person obtaining a copy
123 | of this software and associated documentation files (the "Software"), to deal
124 | in the Software without restriction, including without limitation the rights
125 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
126 | copies of the Software, and to permit persons to whom the Software is
127 | furnished to do so, subject to the following conditions:
128 |
129 | The above copyright notice and this permission notice shall be included in all
130 | copies or substantial portions of the Software.
131 |
132 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
133 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
134 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
135 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
136 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
137 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
138 | SOFTWARE.
139 |
--------------------------------------------------------------------------------
/docs/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Ryan Suchocki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/css3-github-buttons/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Ryan Suchocki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/css3-github-buttons/gh-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ryansuchocki/microscheme/aeee3db837b6eefd78b73696e9cbf4066da4f6a6/docs/css3-github-buttons/gh-icons.png
--------------------------------------------------------------------------------
/docs/download.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
In order to assemble programs and upload them to real Arduinos, you will need some implementation of avr-gcc and avrdude. For most Linux distributions, these tools are available via the standard package managers. They are available via the Homebrew and MacPorts package managers for Mac OS X, and the winavr project on Windows.
53 |
54 |
Get the latest code
55 |
56 |
57 | The latest (not necessarily stable) code is always available via GitHub.
58 |
The latest stable release is available as source code, and as compiled binaries for Linux and Windows. This is the version of microscheme to which the online documentation applies.
53 | GERALD—the fractal drawing robot—is a great example of why the functional programming paradigm is relevant to microcontroller platforms. The algorithm for tracing fractals (such as the Koch snowflake shown here) is intuitively expressed as a recursive procedure. GERALD is driven by stepper motors, which can be controlled accurately in software by sending sequences of high and low voltage signals to the microcontroller's General Purpose Input/Output pins. The low-level code to generate such sequences (which is essentially imperative) is also easily expressed in Scheme code, as sequences of side-effects are easily expressed as lists of (impure) procedures. For the full code, see 'examples/GERALD.ms' in the source code repository.
54 |
71 | Microscheme, or (ms) for short, is a functional programming language for the Arduino, and for Atmel 8-bit AVR microcontrollers in general. Microscheme is a subset of Scheme, in the sense that every valid (ms) program is also a valid Scheme program (with the exception of Arduino hardware-specific primitives). The (ms) compiler performs function inlining, and features an aggressive tree-shaker, eliminating unused top-level definitions. Microscheme has a robust FFI (Foreign Function Interface) meaning that C code may be invoked directly from (ms) programs. Therefore, the power of the existing wealth of Arduino libraries is available within Microscheme.
72 |
51 | Microscheme was originally a final-year undergraduate project, and had to be closed-source for examination purposes. The project has changed since then, and there have been rapid improvements since it was made open-source. However, much of the information in the original dissertation is still relevant.
52 |
53 |
54 |
55 | More recently, a short paper was prepared for the Scheme and Functional Programming workshop, containing a concise exposition of the Microscheme runtime system, and some of the considerations necessary for very resource-constrained platforms such as the ATMega controllers. This paper was very kindly presented by Will Byrd.
56 |
57 |
58 |
59 |
60 |
61 | Original Dissertation
62 | An extensive account of the project motivations and development.
63 | Download (PDF)From June 2014
64 |
65 |
66 |
67 |
68 |
69 | SFP ‘14 Paper
70 | A condensed technical exposition of the project.
71 | SFP 2014
72 | Download (PDF)From October 2014
73 |
The compiler is being released now because it is at a stage where substantial, useful programs can be compiled. It is still, however, a work-in-progress: and that's why the current version is 0.6. The key deficiencies, that are stopping it from being version 1.0, are explained here.
26 |
27 |
Garbage Collection: is not currently implemented. Garbage collection is an important feature because several primitive procedures silently use memory without warning, which is never given back. For example, calling ‘(reverse …)’ on a list creates a new, reversed list in memory. If you call such procedures enough, the Arduino will eventually run out of RAM and start behaving unpredictably.
28 |
29 |
30 |
31 |
The numeric stack is fairly limited. There are no signed numbers, and no floating-point support. On the other hand, the long.ms and fixedpoint libraries have shown how richer numeric types can easily be built using pairs and numbers.
32 |
33 |
34 |
35 |
Arduino Input/Output functionalities are not complete. There is full support for digital I/O and sending of serial data. However, receiving of serial data is not complete. Input and output via the analog pins is not supported yet.
36 |
37 |
38 |
39 |
The compiled programs are large and slow. The number of instructions generated for a given microscheme program is ~10 times more than an equivalent C program. (This is not surprising because the GCC compiler has been around for a long time, and has had millions of man-hours spent on its optimisations.) However, the increased number of instructions is the price paid for the massively richer runtime semmantics of a dynamically typed, functional language like Scheme. Also, the arduino has plenty (~128K) of ROM, and I have not yet seen a microscheme program that even half fills it.
Microscheme will probably never beat GCC on performance, but the main motivation for it is to give people the choice of using the functional style on the Arduino.
The microscheme compiler is written in pure C (99), and relies only on standard C libraries. Linux and MS Windows binaries are provided. The current release is v0.8, build 511
28 |
Update Alert Version 0.8 now available, featuring arbitrary lambda nesting and multiple-include protection.
29 |
30 |
31 |
32 |
33 |
Linux Binaries
34 |
A .tar.gz archive containing the compiler, libraries, example program, and a makefile for automating the build process
Version 0.7, Build 400: Full any-dimensional vector support. Minor bug-fixes.
65 |
Version 0.7, Build 363: Improved performance, full arduino UNO support
66 |
Version 0.6: Initial release. Full arduino MEGA support.
67 |
68 |
69 |
70 |
71 |
Source
72 |
Since this compiler is my final-year (dissertation) project, the code will not be available until it is submitted and marked
73 |
74 |
75 |
76 |
Required tools
77 |
Microscheme compiles your programs, but it does not assemble, link, or upload them. These functions are provided by avr-gcc and avrdude, which are available in mainstream Linux repositories. For example:
Firstly, the canonical ‘Hello World’ example: (If you would like to see the output, it might be a good idea to have a 16×2 LCD shield such as this is connected.)
Using the general purpose digital input/output pins on the arduino is simple. Assuming you are using an Arduino MEGA, and have an LED connected to digital pin 20, you can make it blink like so:
37 |
38 |
(include "libraries/io.ms")
39 |
40 | (set-ddr 20 #t) ; Set direction register for pin 20 to 'output'
41 |
42 | (define (blink_forever)
43 | (set-pin 20 #t) ; Set pin 20 high
44 | (pause 500) ; Wait half a second
45 | (set-pin 20 #f) ; Set pin 20 low
46 | (pause 500) ; Wait half a second
47 | (blink_forever)) ; Repeat...
48 |
49 | (blink_forever)
50 |
51 |
Fibonacci Sequence
52 |
In the next example, we build a list of numbers from the Fibnacci sequence. This time, rather than producing output on an LCD screen, we send each of the values to some external device via the serial interface.
Here we see two approaches to writing a factorial function; the second of which is tail-recursive. In each function, the value of (stacksize), which returns the number of bytes occupied at the moment of evaluation, is reported at the deepest part of the recursion. This demonstrates the importance of the tail-recursive style in environments such as microcontrollers.
A crash course in the particular workings of microscheme
24 |
25 |
26 |
27 |
Fundemental Forms (continued)
28 |
29 |
30 |
31 |
Assignment: (set! <name> <expr>)
32 |
Assignment looks just like definition, but with the ‘set!’ keyword instead of ‘define’. This is used to change the value to which some variable name is bound. That could be a global variable, which is introduced by (define …), a procedure argument, or a local variable introduced by (let …) The set keyword includes an exclamation mark to remind you that it is changing the state of the system; and this is why Scheme is considered not to be purely functional.
The conditional form takes at least a predicate and a consequent, and optinally an alternative. Each of these are expressions of any kind. If the predicate evaluates to true (In Scheme, anything other than false, denoted #f, counts as true) then the consequent will be evaluated. If the predicate evaluates to false, and an alternative is given, then it will be evaluated. This is subtly different from the conditional branches of imperative programming. As well as making a decision about which expression to evaluate, the conditional itself inherits the value of whichever branch is chosen. This means you can use the whole expression as a subexpression, whose value depends on the predicate. e.g. (+ 1 (if (= 2 3) 7 13)) evaluates to 14.
37 |
38 |
39 |
Conjunction: (and A B C …)
40 |
The conjunction form takes any number of arguments, each of which is a subexpression. It will evaluate those expressions in order. If it reaches one that evaluates to false (denoted #f), then it will stop and return #f. If none of them evaluates to #f, then the value of the final expression will be returned (remember, anything other than #f is considered true). Using this form with zero arguments is equivalent to the true constant #t.
41 |
42 |
43 |
44 |
Disjunction: (or A B C …)
45 |
Like conjunction, this disjunction form evaluates each of its arguments in order. If any one of them evaluates to anything other than #f, it stops and returns that value. If it reaches the end of the list, and every expression evaluated to #f, then it returns #f. Using this form with zer oarguments is equivalent to the false constant #f.
46 |
47 |
When used with Boolean type values, the conjunctive and disjunctive forms work just like boolean operators in imperative languages. (or #f #t #f) evaluates to #t and so forth. In Scheme, however, these forms perform a much more powerful function. Since they are variadic, and will keep evaluating until a false or true subexpression is reached respectively, they can be used as control-flow mechanisms in place of nested (if …) forms.
48 |
49 |
50 |
51 |
52 |
53 | Local Binding: (let ((a X) (b Y) (c Z) …) B …)
54 |
55 |
The let form is used to bind names to values only for a specific part of the program. The first argument to let is a list of binding pairs. Each binding pair is a pair of brackets containing a variable name and an expression. The expressions that are given as the body ‘B …’ are evaluated with those names bound to their corrseponding values, and the value of the final expression is returned. Those bindings do not persist outside of the let form. For any code outside of the let's parentheses, the variables a b c … are unchanged, and may not be defined at all.
56 |
57 |
Important nuances:
Even if you only give one binding pair, the parentheses around the list of binding pairs is still needed. Hence, you end up with double brackets: (let ((x 5)) (+ x 1)). Missing those is a common mistake.
The variable bindings apply in the body, but not within other binding pairs in the list. i.e., the expression Y should not rely on X being bound to a.
58 |
59 |
60 |
61 | Sequence: (begin B1 B2 …)
62 |
Finally, you can group together expressions with the sequencial form, using the begin keyword. The whole thing is treated as one expression, whose subexpressions are executed in sequence. As usual, the value of the final subexpression is returned for the overall expression.
63 |
64 |
You can use this in cases where you want to guarantee a group of expressions will be evaluated, or where you want to give multiple expressions in a context where only one is expected.
65 |
66 |
(+ 1 (begin 2 4 6)) evaluates to 7. The subexpressions 2 and 4 are evaluated, but they have no effect. 6 is evaluated and returned to the outer + procedure.
A crash course in the particular workings of microscheme
24 |
25 |
26 |
27 |
Primitive Procedures
28 |
29 |
Primitive procedures are procedures that are built-in to the language. This means that the compiler produces efficient low-level routines for them.
30 |
31 |
Unlike full-blown Scheme, microscheme primitives are not first-class. i.e., they can only appear in the function application form. This is a problem when you want to pass a primitive function as the argument to a higher-order function such as map. For example, you may want to invert a list of Booleans: (map not list_of_booleans).
32 |
33 |
The solution is to make a simple wrapper-function which is first-class but performs the same function as the primitive you want to work with: (define (not* x) (not x)).
34 |
35 |
Then, you are free to use it as a value: (map not* list_of_booleans). This might seem annoying, but it is not without good reason. Making all primitive functions first-class would tie up around .5 KB of RAM. On the arduino, RAM is precious. This compromise ammounts to you, the programmer, telling the compiler exactly which primitives need to be loaded into RAM. For the vast majority of programs, this ammounts to a massive memory saving.
36 |
37 |
Available Primitives
38 |
The primitive procedures built-in to compiler version 0.6 are:
A crash course in the particular workings of microscheme
24 |
25 |
26 |
27 |
Type System
28 |
29 |
Microscheme has a strong dynamic type system. It is strong in the sense that:
30 |
All values have a specific, definite type
31 |
No type coersion occurs
32 |
Procedures are generally valid for a specific set of types
33 |
Type exceptions are raised when procedures are applied to values of the wrong type
34 |
It is dynamic in the sense that a variable is not restricted to hold values of a certain type. The type of value to which a variable name will be bound is not known until runtime, and can change as the program progresses.
35 |
36 |
The built-in types are: Number, Char, Boolean, Pair, Vector, ‘The Empty List’ aka null, which is said to have a type of its own, and Procedure. From these basic types we can infer compound types. A List is defined to be something of the type null or pair where the value of the cdr field has type List. This definition is effectively implemented by the (list? …) function in the ‘list.ms’ library.
37 |
38 |
Even though the built-in numeric type is fairly restricted (15-bit unsigned integer), a richer numeric stack can be built using combinations of pairs, vectors and numbers. For example, the 'xtypes.ms' library provides types long and fp, which represent 8-digit unsigned integers, and 4+4 digit fixed-point real numbers respectively.
39 |
40 |
For every type, there is a predicate function which answers the question 'is this value of type X'. These predicates are consistently formed by appending a question mark to the type name. For example, (number? 4) evaluates to #t. (boolean? 4) evaluates to #f. (boolean? (number? 4)) evaluates to #t.
41 |
42 |
Procedures for converting between types are formed with an arrow between the type names, e.g. (vector->list a b). These conversions are not provided for many types, but they can be written manually.
A crash course in the particular workings of microscheme
24 |
25 |
26 |
Microscheme Libraries
27 |
Microscheme supports the (include …) primitive, which effectively loads the whole contents of another file into the program. This allows commonly used program segments to be saved in 'libraries' that can be included in any other program. Typically, libraries contain definitions, but do not perform any input or output, so including them simply makes a set of procedures and data structures available to the program. Some useful libraries are included with microscheme, and more will become available as the project matures:
28 |
29 |
libraries/io.ms provides digital Input/Output functionality. This allows you to work with the Arduino's digital I/O pins, using the indices given to them on the arduino board. It provides the procedures:
30 |
(set-ddr N X) to set the DDR (data-direction-register) for a pin. N is the pin number. X is #f for ‘input’ and #t for ‘output’.
31 |
(get-ddr N) returns a boolean representing the DDR value for pin N. #t means ‘output’.
32 |
(set-pin N Y) sets the value (high or low) for pin N.
33 |
(set-pin N Y) gets the value (high or low) of pin N.
34 |
35 |
36 |
libraries/list.ms provides various functions for working with lists, which are linear data structures built using pairs and null. Procedures provided include:
37 |
38 |
(list? X) returns true if and only if X is a list.
39 |
(reverse X) if X is a list, returns a new list which is the reverse of it.
40 |
(map P X) returns a list formed by performing procedure P on every element of list X.
41 |
foldr, foldl, for-each, all various common higher-order list procedures.
42 |
(vector->list V) returns a list whose elements are identical to those of vector V.
43 |
44 |
45 | NB: the primitive (list …) for building lists is built-in, and implemented efficiently by the compiler.
46 |
47 |
libraries/long.ms provides an implementation for 8-digit unsigned integers:
48 |
(long hi lo) forms a long where hi represents the high four digits and lo represents the low four digits of the number. The number 994020 is produced by (long 99 4020).
49 |
(hi X) and (lo X) extract the high and low parts of a long.
50 |
(long? X) returns true if X is a valid long. Warning: any pair of numbers will satisfy this.
51 |
l+ l- l* l/ standard arithmetic operators. (NB: l* and l/ are slow, software-based implementations.
52 |
l++ l-- l** are in-place versions of l+ l- and l*. i.e. (l++ lX lY) is equivalent to (set! lX (l+ lX lY)), but allocates no new memory. You should use these operators wherever possible
53 |
l= l< l> l<= l>= standard numeric comparators.
54 |
55 |
56 |
57 |
libraries/fixedpoint.ms provides an implementation for 5+5 digit unsigned fixed-point reals:
58 |
59 |
60 |
NB: including the xtypes library has the same effect as including long and fixedpoint individually, but saves memory by taking advantage of the overlap between their functions.
A crash course in the particular workings of microscheme
24 |
25 |
Compiler Errors
26 |
27 |
As of version 0.6, build 230, the possible compile-time errors are:
28 |
29 |
30 | 0 Out of memory
31 | 1 Char buffer full
32 | 2 while lexing the file '%s'. File could not be opened
33 | 3 Comment before end of token
34 | 4 Extraneous )
35 | 5 Missing )
36 | 6 Procedure '%s' is primitive, and cannot be used as a value
37 | 7 Non-identifier in formal argument list
38 | 8 Malford lambda. No formals given
39 | 9 Wrong number of operands to IF form
40 | 10 First operand to SET should be IDENTIFIER
41 | 11 Wrong number of operands to SET form
42 | 12 Wrong number of operands to DEFINE form
43 | 13 Non-identifier in formal argument list
44 | 14 First operand to DEFINE should be IDENTIFIER or PARENS
45 | 15 Definition not allowed here
46 | 16 Malformed Binding
47 | 17 Malformed LET?
48 | 18 First operand to INCLUDE should be STRING
49 | 19 Wrong number of operands to INCLUDE form
50 | 20 Unknown parenthesized form
51 | 21 Unknown form\n
52 | 22 Unexpected list of expressions
53 | 23 NOT IN SCOPE %s
54 | 24 Integer constant too large
55 | 25 Freevar refs of degree > 1 not supported yet
56 | 26 No primitive P taking N arguments
57 | 27 Internal Error
58 |
A crash course in the particular workings of microscheme
24 |
25 |
26 |
Runtime Exceptions
27 |
28 |
29 |
Like Scheme, microscheme is strongly, dynamically typed. Exceptions are semmantic errors that arise at runtime. Microscheme makes use of the Arduino's built-in LED on digital pin 13 to give on-device indications of these situations. Generally, exceptions are not recoverable, and the device will need to be reset if an exception is raised. While it is possible to use digital pin 13 for general input and output, it is highly recommended to leave it free for exception indication.
30 |
31 |
32 |
Status
Meaning
Indication
33 |
RUN
Program Running
No Light
34 |
NVP
Not a Valued Procedure
Single Flashes
35 |
NAR
Number of ARguments
2 Flashes
36 |
NAN
Not A Number
3 Flashes
37 |
NAP
Not A Pair
4 Flashes
38 |
NAV
Not A Vector
5 Flashes
39 |
OOB
Out Of Bounds
6 Flashes
40 |
DBZ
Divide By Zero
7 Flashes
41 |
ERR
Custom Exception
Continuous Flashes
42 |
HALT
Program Completed
Continuous Light
43 |
44 |
Exception Details
45 |
NVP: A procedure application takes the form (proc X1 X2 ... Xn) where proc is an expression. At the time of application, if proc does not evaluate to a (valued) procedure, such as the result of a (lambda …) form, or a variable bound to a procedure, then NVP will be raised.
46 |
NAR: A procedure application takes the form (proc X1 X2 ... Xn) where X1 X2 ... Xn are arguments. At the time of application, if proc evaluates to a procedure taking m arguments, but m ≠ n, then NAR will be raised.
47 |
NAN: Indicates that an arithmetic operator (+, -, *, /, div, mod) received an argument that did not evaluate to a number.
48 |
NAP: Indicates that a pair operator (car, cdr, set-car!, set-cdr!) received an argument that did not evaluate to a pair.
49 |
NAV: Indicates that a vector operator (vector-ref, vector-set!) received an argument that did not evaluate to a vector.
50 |
OOB: Indicates that a vector operator (vector-ref, vector-set!) received an index that was outside the dimensions of the vector given.
51 |
DBZ: Indicates an attempt to divide by zero.
52 |
ERR: This exception is raised manually by the programmer. See (error) and (assert expr) in the language guide.
Microscheme is a Scheme-like functional programming language, specifically targeting the Atmel 8-bit AVR microcontrollers, and designed to be used with the Arduino UNO and MEGA development boards. Microscheme is a ‘subset’ of Scheme in the sense that every valid microscheme program is also a valid Scheme program (with minor syntax substitutions). It can be characterised as compiled Scheme without the following features: first-class continuations, a macro system, variadic functions and full closures. These are deliberate shortcomings, and arise from the fact that the AVR-based Arduinos only have up to 8KB —Yes, Kilobytes!— of RAM. The overhead spared by leaving out those features means that the programmer is left with a useful amount of RAM to work with. The microscheme compiler is a hand-written recursive descent compiler, written in C, relying only on the C standard library. It is (slightly) novel among microcontroller-targeting Scheme implementations in that it compiles directly to assembly, rather than generating equivalent C code. Microscheme supports more than 80 fundemental forms, primitive and library functions out-of-the-box.
Compile it using microscheme, resulting in myprogram.s
28 |
Assemble it using avr-gcc, resulting in myprogram.elf
29 |
Convert it to HEX format using avr-objcopy, resulting in myprogram.hex
30 |
Upload it to the arduino using avrdude
31 |
32 |
However, this process is quite laborious, and the combination of arguments given to each tool can be complicated. Therefore, microscheme comes with a Makefile designed to automate this process. The simplified workflow is:
33 |
Write your program, and save it as myprogram.ms
34 |
Determine the path of the special device file allocated to the Arduino. This will be something like /dev/ttyACM0 or /dev/ttyUSB0.
35 |
Execute $ make upload MODEL=… DEV=… Filling in either MEGA or UNO for model, and the DEV path from step 2