https://github.com/phenax/aether
Another one of those lisp interpreters
https://github.com/phenax/aether
haskell lisp-interpreter programming-language scheme-interpreter
Last synced: 3 months ago
JSON representation
Another one of those lisp interpreters
- Host: GitHub
- URL: https://github.com/phenax/aether
- Owner: phenax
- License: gpl-3.0
- Created: 2025-01-06T06:23:10.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2025-01-28T16:21:53.000Z (4 months ago)
- Last Synced: 2025-03-19T14:48:45.612Z (3 months ago)
- Topics: haskell, lisp-interpreter, programming-language, scheme-interpreter
- Language: Haskell
- Homepage:
- Size: 198 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Aether lang
It's an interpreter and a programming language. It's based on scheme. It's mine. Leave me alone.## Install
Clone and build it yourself
- Via nix: `nix build`
- Via cabal: `cabal build`Or just run directly it with nix flakes: `nix run github:phenax/aether -- repl`.
## Running
- Run the repl with `aether repl`. Use rlwrap for a better experience (`rlwrap aether repl`)
- Evaluate a script file with `aether run ./path/to/file.scm -- arg1 arg2`There is no help menu. I don't care about you.
## Examples
The [examples](./examples/) directory has some examples to look at. For a (sort of) usable CLI example, take a look at [crop-video-cli](./examples/crop-video-cli.scm).Heres some normal stuff you can do in aether so far.
### Functions
`define` is used to define functions `(define (symbol ... args) ... exprs)`.It can also be used to define values `(define symbol value)`.
```scheme
(define (factorial num)
(if (<= num 1) 1
(* num (factorial (- num 1)))))(displayNl (factorial 5))
(set results '[])
(for (range 10 20) { -> [n]
(let [ (result (factorial n)) ]
(set results (concat results result))
(displayNl n "! is " result))
})
; `()`, `[]`, `{}` are the same thing(displayNl results)
```### Macros
```scheme
(defmacro (when condition ... exprs)
'(if ,condition
(progn ,@exprs)
#nil))(set n 5)
(when (>= n 5)
(set yay "Yay!")
(displayNl "N is >= 5 and everyone you love will die some day! " yay)
(displayNl "Good bye"))
```### Error handling
`try` function handles errors in the expressions inside it and returns `'(error value)`.`error!` function is used to throw exceptions. `(error! 'error-identifier "Error message")`
```scheme
(define (divide! a b)
(if (zero? b)
(error! 'division-by-zero "Cant divide by zero bro")
(/ a b)))(expand [error value] (try (divide! 20))) ; expand macro destructures list into symbols
(cond
[ (nil? error)
{displayNl "Result: " value} ][ (= 'division-by-zero (error/label error))
{displayNl "DivByZero error: " (error/message error)} ][ else
{displayNl "Unexpected error: " (error/message error)} ])
```### Processes
`!` function spawns a process and waits for it to end. Returns `'(stdout stderrr)`.On non-zero exit code, it throws an error `(error! 'proc/non-zero-exit-code "... ")`
```scheme
; Example: Takes a screenshot of the screen of the focused window using imagemagick and saves it
(expand [window-id, _] (! xdotool getwindowfocus))
(! import -window ,window-id "/home/user/Pictures/screenshot.jpg") ; Imagemagick `import`; WIP: Process handling + pipes
```### File IO
Only supports reading and writing text files```scheme
(set contents (fs/read-file "path/to/file"))
(fs/write-file "path/to/file" (string contents "appending stuff")); WIP: file handle, stat, etc
```### Import scripts
`import` function imports script files, relative to cwd, into the current scope (symbol and strings both work).```scheme
(import 'path/to/script-file-1.scm "../foobar/script-file-2.scm" "./path/to/script-file-3.scm")
```### Infix syntax
Why not?```scheme
; Always evaluated left to right. No fixity because fuck you
($ 5 + 4 * 3 - 2) ; 25
; Equivalent to (- (* (+ 5 4) 3) 2)($ 5 + ($ 4 * 3) - 2) ; 15
; Equivalent to (- (+ 5 (* 4 3)) 2)
```### Pipe/currying and other functional nerd stuff
```scheme
(set nums (list 1 2 3 4 5))
(|> nums
(_^ map (-> [x] (* x 2))) ; curried map x2
reverse ; reverse list
(^_ for displayNl)) ; display each item; _^ is curry second (same as `curry`): ((a, b) -> c) -> a -> b -> c
; ^_ is curry first: ((a, b) -> c) -> b -> a -> c
```### Records
`record` macro creates record structures. It's just a list with accessors so nothing fancy.```scheme
(record Person
:person/name ; :person/ is just for namespacing. Can be any symbol
:person/age)(set john (Person "John" 8))
(displayNl (:person/name john) " is " (:person/age john) " years old")
; Create a copy with a property updated
(set updated-john (set@:person/name "Johnathy McJohnathan" john))(displayNl (:person/name updated-john) " is " (:person/age updated-john) " years old")
; Compose setters together to update multiple properties
(set updated-john
(|> john
(_^ set@:person/name "Johneshwar")
(_^ set@:person/age 9)))
```