MalO
A small Hy-like Lisp interpreter that runs on Python 3.13+. Programs are indented forms with nested parentheses for arguments, plus Python interop, macros, and keyword arguments.
Requirements
- Python 3.13+
- uv
Install and run
uv sync
uv run malo path/to/file.malo
python -m malo path/to/file.malo does the same thing. Passing no file prints usage and exits with status 2.
Try the bundled example:
uv run malo examples/main.malo
Syntax
Each non-empty line is a form (a call or special form). Nested arguments on the same line use (...), [...], or {key val ...}. Comments start with ; and run to the end of the line.
Block structure uses indentation: one tab or four spaces per level. Dedenting closes the block. Do not wrap a block in parentheses.
while (< i 10)
print i
update i + 1
Reader macros:
| Sugar | Expands to |
|---|---|
`form |
(quasiquote form) |
~form |
(unquote form) |
~@form |
(unquote-splice form) |
Language
Values and builtins
True, False, and None are bound. Arithmetic and comparison are Python-backed: +, -, *, /, =, <, <=, >, >=. Also available: print, list, range, first, rest, len.
Bindings and functions
setv x 100
def y 1
defn greet [name &optional [suffix "!"]]
print "Hello," name suffix
greet "MalO"
greet :name "MalO"
let [a 1 b 2]
print (+ a b)
setv and def are aliases. fn is an anonymous function. update applies a function to the current value of a symbol and writes it back: update x + 100 sets x to (+ x 100).
Tail calls to defn / fn reuse the current stack frame. A recursive call that is the whole result of if, do, let, return, or the last body form will not grow the Python stack. Nested calls such as (+ "1" (additor (- x 1))) are not tail calls.
Parameter lists are [required... &optional opt... &rest name? &keys name?]:
&optional nameor&optional [name default]- bare
&restbinds extra positionals toargs - bare
&keysbinds leftover keywords tokeys
Calls take positional args and :key value pairs. Keywords may fill named parameters (:name "MalO") or go into &keys.
Control flow
if test then else?
do body...
while test body...
for [pat iterable] body...
break
continue
return value?
assert test msg?
for can destructure:
for [[k v] (.items d)]
print k v
break and continue only work inside while / for. return exits a defn / fn immediately; a bare return yields None. It is an error outside a function.
Python interop
import math
import math :as mt
from math :import sin cos
from math :import sqrt :as square_root
from math :import *
from builtins :import str type
print (math.sqrt 2)
print (.format "Hello, {}!" "world")
Dotted names resolve as attribute chains (math.sqrt). (.method obj args...) calls a method. (. obj attr) and (. obj (method args...)) do the same with an explicit object first.
Macros and quoting
quote form
quasiquote form
defmacro setv-str [var val]
setv var (str val)
Macros substitute parameter forms into a template (they do not evaluate the body as Python). Use ` / ~ / ~@ for quasiquote, unquote, and splice.
Project layout
src/malo/ interpreter (tokenizer, parser, AST, eval, CLI)
examples/main.malo sample program
Metadata
Release files for run-malo 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| run_malo-0.1.0.tar.gz | 13.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| run_malo-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 29.5 kB
Release files / run_malo-0.1.0.tar.gz
| Download URL | run_malo-0.1.0.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2043a63930c11a96e009c991d9bbdd9f67b019075f51098750100d9e5b3800ea
|
|
BLAKE2b-256 checksum How to use checksums |
6472c69f09725e5213283c9dea3bc8de05f791ebbec0a2d0b62d5a5a93baee56
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / run_malo-0.1.0-py3-none-any.whl
| Download URL | run_malo-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3e6686b0b1803cb0fa9439263edaedcd94141fca3e38895531c5a5194af576d0
|
|
BLAKE2b-256 checksum How to use checksums |
2b5418b0b7b6c9206927136a3c8775867e7845841f4a84fc4488c56cf71a6cfb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for run-malo 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.