Compare commits

..

10 Commits

Author SHA1 Message Date
90381840af add trait checking 2025-08-19 21:54:06 -06:00
d370fb44a2 added ast parsing 2025-08-18 22:53:36 -06:00
03662d980f switching language to tswq 2025-08-17 16:10:13 -06:00
f554b09efc got redo working 2025-08-17 15:44:17 -06:00
491cf29e68 stared redo 2025-08-11 22:17:34 -06:00
1c08ce3a0c updated readme 2025-08-02 14:31:05 -06:00
33ce920c0b added line to readme 2025-08-02 14:28:24 -06:00
9b497b7958 added disclaimer 2025-08-02 12:54:13 -06:00
f2f7e04a37 updated readme for 2025 2025-08-02 12:52:47 -06:00
ce23415663 added strings 2022-10-12 11:06:58 -06:00
34 changed files with 1372 additions and 4581 deletions

130
.gitignore vendored
View File

@@ -1,112 +1,36 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
.direnv/
# C extensions
*.so
# dependencies (bun install)
node_modules
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# output
out
dist
*.tgz
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# code coverage
coverage
*.lcov
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
# dotenv environment variable files
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
.env.development.local
.env.test.local
.env.production.local
.env.local
# Spyder project settings
.spyderproject
.spyproject
# caches
.eslintcache
.cache
*.tsbuildinfo
# Rope project settings
.ropeproject
# IntelliJ based IDEs
.idea
# mkdocs documentation
/site
# mypy
.mypy_cache/
#Added by cargo
#
#already existing elements are commented out
/target
**/*.rs.bk
# Finder (MacOS) folder config
.DS_Store

View File

@@ -1 +0,0 @@
max_width=140 # Not ideal

501
Cargo.lock generated
View File

@@ -1,501 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "ascii-canvas"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6"
dependencies = [
"term",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bit-set"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "boring-lang"
version = "0.0.1"
dependencies = [
"clap",
"lalrpop",
"lalrpop-util",
"regex",
"thiserror",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "diff"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "ena"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
dependencies = [
"log",
]
[[package]]
name = "fixedbitset"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hermit-abi"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"either",
]
[[package]]
name = "lalrpop"
version = "0.19.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988"
dependencies = [
"ascii-canvas",
"atty",
"bit-set",
"diff",
"ena",
"itertools",
"lalrpop-util",
"petgraph",
"pico-args",
"regex",
"regex-syntax",
"string_cache",
"term",
"tiny-keccak",
"unicode-xid",
]
[[package]]
name = "lalrpop-util"
version = "0.19.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278"
dependencies = [
"regex",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "new_debug_unreachable"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "once_cell"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
[[package]]
name = "petgraph"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]]
name = "pico-args"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]]
name = "precomputed-hash"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom",
"redox_syscall",
]
[[package]]
name = "regex"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
[[package]]
name = "rustversion"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
[[package]]
name = "siphasher"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1"
[[package]]
name = "string_cache"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a"
dependencies = [
"lazy_static",
"new_debug_unreachable",
"phf_shared",
"precomputed-hash",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "term"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
dependencies = [
"dirs-next",
"rustversion",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
dependencies = [
"once_cell",
]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@@ -1,18 +0,0 @@
[package]
name = "boring-lang"
version = "0.0.1"
authors = ["asegavac"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies.lalrpop] # <-- We added this and everything after!
version = "0.19.6"
features = ["lexer"]
[dependencies]
lalrpop-util = "0.19.6"
regex = "1"
# inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm7-0" }
clap = "2.33.0"
thiserror = "1"

View File

@@ -1,5 +0,0 @@
FROM rust:1.54
RUN apt update && apt-get install -y llvm clang
RUN rustup component add rustfmt
WORKDIR /code

104
README.md
View File

@@ -1,17 +1,20 @@
Note: This is mostly a background project that I like to tinker with while I build real things. Consistant progress on this language is unlikely any time soon.
# Boring Lang
The Boring Programming Language (Boring-Lang) is an attempt to create an easy, productive, general purpose programming language that makes as few interesting choices as possible while still being in line with modern concepts in programming languages.
The language (goals):
* is compiled with a run-time (llvm for convenience + c/rust compatibility)
* has managed memory (via strong/weak pointers and automatic reference counting)
* uses async-await for all IO, with a built-in multi-core scheduler (tokio-based)
* supports algebraic data types (Result type for errors, Maybe/Optional type for nullables)
* supports parametric polymorphism (generics) with higher kinded types
* uses struct+traits, rather than classes or stuct+interfaces
* has a rich standard library (similar scale to python or go)
* is immutable by default
* is sandboxed by default
- is compiled with a run-time (llvm for convenience + c/rust compatibility)
- has managed memory (via strong/weak pointers and automatic reference counting)
- uses monadic IO, with a built-in multi-core scheduler (tokio-based)
- supports algebraic data types (Result type for errors, Maybe/Optional type for nullables)
- supports parametric polymorphism (generics) with higher kinded types
- uses struct+traits, rather than classes or stuct+interfaces
- has a rich standard library (similar scale to python or go)
- is immutable by default
- is sandboxed by default
It's a middle-ground of Rust, Golang, Swift, Typescript, and Python. The goal is not to break any new ground in PL theory, or even create a language anyone likes, but rather to create a language with as few deal-breakers as possible for maximum day-to-day industrial programming ergonomics.
@@ -20,6 +23,7 @@ This language is under active development, progress will be marked here as the l
- [x] Functions
- [x] Int Literals
- [x] Float Literals
- [x] String Literals
- [x] Block expression
- [x] Return keyword
- [x] Normal assignment
@@ -39,13 +43,13 @@ This language is under active development, progress will be marked here as the l
- [x] Basic
- [ ] Inferred
- [ ] Higher kinded types
- [ ] Variadic generic types
- [ ] Control Flow
- [x] If
- [ ] While
- [ ] For
- [ ] Async-Await / Futures
- [ ] IO
- [ ] Enums
- [ ] Errors
- [ ] Lambdas
- [ ] Imports
- [ ] Visibility
@@ -66,15 +70,19 @@ We accomplish this in a few ways:
### Sandboxing
Unlike many other programming languages, boringlang's `main` function takes in two arguments: a vector of command line arguments, and a reference to the OS which is the program's only link to the outside world. To open a file in boringlang, you cannot just call `open` anywhere, you *must* call `os.fs().open("path")`. All `os.whatever()` methods return an interface for interacting with that part of the OS, such as `fs`, `net`, `datetime`, and `syscall`. Because this is the only way to interact with the world outside of the program, this means that any IO the program does can be trivially mocked for testing, and that all operations the program can perform are sandboxed. If a function doesn't require a reference to the `FS` trait, you can be sure it doesn't interact with the file system.
Unlike many other programming languages, boringlang's `main` function takes in two arguments: a vector of command line arguments, and a reference to the OS which is the program's only link to the outside world. To open a file in boringlang, you cannot just call `open` anywhere, you _must_ call `os.fs().open("path")`. All `os.whatever()` methods return an interface for interacting with that part of the OS, such as `fs`, `net`, `datetime`, and `syscall`. Because this is the only way to interact with the world outside of the program, this means that any IO the program does can be trivially mocked for testing, and that all operations the program can perform are sandboxed. If a function doesn't require a reference to the `FS` trait, you can be sure it doesn't interact with the file system.
### "Effects" System
Boring-lang doesn't have a formal effects system, but rather the "effects" are simply traits that get tacked onto a functions type. For an example, let's use a GUI program where clicking on a button can have an effect, in this case writing to a file.
Boring-lang doesn't use an algebraic effects system, since those often work by just creating one super monad that everything uses so it has to compose with itself. Monads not composing is something we treat as a feature, rather than a bug, as usually you rarely ever want to go directly from an `IO[Result[Optional[int], Error]]` directly to an int, but rather you want to handle each stage of the stack individually (join the promise, handle the error, default the optional).
Not being able to "await" an async function in an iterator's `.map()` call is likewise intentional. This language despises the notion of being able to do anything, anywhere, and is rather built with the belief that the pain of virality in typing will force people to write better code. It is event impossible to implement a singleton in the language due to the lack of any global variables or globally mutable state.
Instead in Boring-lang the "effects" are simply traits that get tacked onto a functions type. For an example, let's use a GUI program where clicking on a button can have an effect, in this case writing to a file.
```rust
type ClickHandler trait {
async fn on_click(self): ClickError;
fn on_click(self): IO[Result[(), ClickError]];
}
type MyButton[T: FS] struct { // T is a generic type implementing fs
@@ -82,19 +90,21 @@ type MyButton[T: FS] struct { // T is a generic type implementing fs
}
impl MyButton[T] {
fn new(fs: T): MyButton {
pub fn new(fs: T): MyButton {
return MyButton{fs: fs};
}
}
impl ClickHandler for MyButton[T] {
async fn on_click(self): ClickError {
pub fn on_click(self): IO[Result[(), ClickError]] {
let file = await self.fs.open("my_file")?;
await file.write("foo")?;
}
}
```
Because you must get your `FS` handle from the program's `main` and all side effects are captured in the monad stack of the result type, all effects are explicit and encoded directly in to the type system.
## Http Server Example
```rust
@@ -102,29 +112,39 @@ import net.http as http;
import logging as logging;
import json as json;
type ExampleResponse struct {
id: i32,
name: Str,
email: Str,
pub id: i32;
pub name: str;
pub email: str;
}
async fn handle(req: http.Request, resp: mut http.Response): {
let response_data = ExampleResponse{
id: 4,
name: "Andrew",
email: "andrew@boringlang.com",
};
await resp.set_status(200);
await resp.write(json.encode[ExampleResponse](response_data));
type Router struct {
logger: logging::Logger;
pub fn new(logger: logging::Logger): Router {
return Self{logger: logger};
}
pub fn get_user_data(self: Self, req: http.Request): IO[Result[http::Response, http::Error]] {
let response_data = ExampleResponse{
id: 4,
name: "Andrew",
email: "andrew@boringlang.com",
};
self.logger.info("getting user data")?;
return ok(http::Response::ok(json::dumps(response_data)?));
}
}
async fn main(args: Vec[String], os: OS): i32 {
let log = logging.new_logger(os.console.stdout());
let router = http.Router("").add_route("/myroute", handle);
let http_server = http.Server(os.net(), "localhost", 8080, router);
let err = await http_server.serve_forever();
await log.info("error serving: ", err);
return 1;
pub fn main(args: List[String], os: OS): IO[i32] {
let logger = logging::ConsoleLogger::new(os.console.stdout());
let router = Router::new(logger);
let app = http::Router::new("").add_route("/myroute", router.get_user_data);
let http_server = http::Server::new(os.net(), "localhost", 8080, app);
let err = http_server.serve_forever()?;
logger.info("error serving: ", err)?;
return 1;
}
```
@@ -150,16 +170,20 @@ Methods on a struct must specify if they mutate the struct.
```rust
impl Dict[Key: Hashable, Value] {
fn insert(self: mut Self, key: Key, value: Value) {
pub fn insert(self: mut Self, key: Key, value: Value) {
// mutate self here
}
fn get(self: Self, key: Key) Optional[Value] {
pub fn get(self: Self, key: Key) Optional[Value] {
// no need for `mut`
}
}
```
## Error Handling
Built in support for error handling, via Result types and Error enums, with capabilities similar to Rust's thiserror library.
## Context
Context is an exceptionally useful feature in golang, but a common complaint is that:
@@ -205,7 +229,7 @@ for the above examples, you would pass a context type that implements all three
## Import System
Similar to python, folders/files represent the `.` seperated import path, but relative imports are *not* supported. Exported values must be marked with `pub`. All imports take the form:
Similar to python, folders/files represent the `.` seperated import path, but relative imports are _not_ supported. Exported values must be marked with `pub`. All imports take the form:
```rust
import package.path as local_name;
@@ -215,8 +239,8 @@ pub type MyStruct struct {
}
```
## Basic Statements
### `if`
`if` is an expression in boring-lang, with the last expression in a block being the return value.
@@ -232,7 +256,7 @@ let a = if (true) {
```
Conditions do not require parenthesis and *must* evaluate to the Boolean type.
Conditions do not require parenthesis and _must_ evaluate to the Boolean type.
### Loops
@@ -270,7 +294,7 @@ for i in range(100) {
### `with`
`with` and `async with` blocks are similar to the python statement with the same name. But unlike the python version, `with` blocks are expressions. `with` blocks take in an expression that implements the `With` or `AWith` trait, and execute a block that *may* return a result (non-result returns are assumed success).
`with` and `async with` blocks are similar to the python statement with the same name. But unlike the python version, `with` blocks are expressions. `with` blocks take in an expression that implements the `With` or `AWith` trait, and execute a block that _may_ return a result (non-result returns are assumed success).
```rust
// commits on success, aborts on error.

View File

@@ -1,5 +0,0 @@
extern crate lalrpop;
fn main() {
lalrpop::process_root().unwrap();
}

412
bun.lock Normal file
View File

@@ -0,0 +1,412 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "boringlang",
"dependencies": {
"ohm-js": "^17.2.1",
},
"devDependencies": {
"@types/bun": "latest",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"jiti": "^2.5.1",
"prettier": "^3.6.2",
"typescript": "^5.9.2",
"typescript-language-server": "^4.4.0",
"vscode-langservers-extracted": "^4.10.0",
},
},
"packages/boringlang": {
"name": "boringlang",
"version": "0.1.0",
"dependencies": {
"@bunli/core": "latest",
},
"devDependencies": {
"@bunli/test": "latest",
"@types/bun": "latest",
"bunli": "latest",
},
},
},
"packages": {
"@bunli/core": ["@bunli/core@0.1.0", "", { "dependencies": { "@bunli/utils": "0.1.0", "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0" } }, "sha512-+1hZ3cLgFLbpXndHhxZDkGNYj8zLvxmTSW4y+Kna6sJXI/L1lKaQGZBzu8+LmUckG+25BMydgocP59yaRURRag=="],
"@bunli/test": ["@bunli/test@0.1.0", "", { "dependencies": { "@bunli/core": "0.1.0" }, "peerDependencies": { "bun": ">=1.0.0" } }, "sha512-kSvZ/CxoJ6xZLTCEF53bJnOqAGsMI2qd5oq9PwW9n4wPvnJhHO624+cS+1LHUhmCc68LzI0QyxoWTZJ1SnUqAg=="],
"@bunli/utils": ["@bunli/utils@0.1.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "bun": ">=1.0.0" } }, "sha512-2KR4ZWhkFzRR4PYc9FB0Y738Xle98Heyr8se6YK+vetDXxhlBiM4W2WZ7IBHmZn12PcXedc5DBvDjrvOdXXv/Q=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
"@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="],
"@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
"@eslint/js": ["@eslint/js@9.33.0", "", {}, "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
"@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.2.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zygk+yeaww9kBw2JBWwA13KyOKySxbnetms/WyRFaUYhxiuJHkzv1c6/Ou7sIHa9Gbq4fYQEhx88Ywy1wu2oTQ=="],
"@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.2.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-k2akVmSvJHuzpwgwIU8ltary7EQbqlbvxgtYlVqYvnqUpRdRbkuJXAZhN5zuDNTftaG4l22Q/bX04tBB8Txmjg=="],
"@oven/bun-darwin-x64-baseline": ["@oven/bun-darwin-x64-baseline@1.2.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-bxXZlLD6DJ8rc/Ht0Cgm0BH1AJVO/axOElXJP42LUUKQ/U4t3OKkFDbFiTPGphcy5teMLkoYl+a2Cz8P9q2gVQ=="],
"@oven/bun-linux-aarch64": ["@oven/bun-linux-aarch64@1.2.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-g+CzF02RzKgSmuEHNLoDTtiiQR33cEZWcd/tWR+24h92xe5wXuqQsV7vQJLR6e44BWkDOACpTIrfW4UAaHw4Cw=="],
"@oven/bun-linux-aarch64-musl": ["@oven/bun-linux-aarch64-musl@1.2.20", "", { "os": "linux", "cpu": "none" }, "sha512-zB3aKckyUdKENLP+lm/PoXQPBTthJsY7dhYih+qVT95N29acLO2eWeSHgRkS7Pl2FV+mLJo9LvjRhC8oaSSoOw=="],
"@oven/bun-linux-x64": ["@oven/bun-linux-x64@1.2.20", "", { "os": "linux", "cpu": "x64" }, "sha512-KJZ0zJKadKCD6EI/mBv/0PUysMpd1r4o3WhQ73PjCZx2w95Ka2fSBAIsy9e/rxc07D4LHr26nGyMmC1K8IcS6Q=="],
"@oven/bun-linux-x64-baseline": ["@oven/bun-linux-x64-baseline@1.2.20", "", { "os": "linux", "cpu": "x64" }, "sha512-xtYPn84ur9U7YaS0+rwjs6YMgSv5Z4gMnqPQ1QTLw92nt1v9Cw17YypVab4zUk222o5Y6kS3DRkDdSHBh8uQfA=="],
"@oven/bun-linux-x64-musl": ["@oven/bun-linux-x64-musl@1.2.20", "", { "os": "linux", "cpu": "x64" }, "sha512-XPtQITGbJXgUrMXOJo3IShwQd3awB93ZIh5+4S3DF9Ek/lwXVSuueIIAfnveR/r9JRgEA5+g/1ZHVf1/3qaElg=="],
"@oven/bun-linux-x64-musl-baseline": ["@oven/bun-linux-x64-musl-baseline@1.2.20", "", { "os": "linux", "cpu": "x64" }, "sha512-rANapFZRrgOTeotaf556iIxguyjQbensL6gT3cXZDnXG+aVhv65hSnjqzM7vfHxlzoXbAmoUkJOpce0qEg/HlA=="],
"@oven/bun-windows-x64": ["@oven/bun-windows-x64@1.2.20", "", { "os": "win32", "cpu": "x64" }, "sha512-Jt4bAf30qG4SvnL6tO4QzZNbMjg5sLZHif22rZLwX7W6rWPAvgqyYdwDSGHN8Kkbe6KqV4DceyKQgRr83sU66Q=="],
"@oven/bun-windows-x64-baseline": ["@oven/bun-windows-x64-baseline@1.2.20", "", { "os": "win32", "cpu": "x64" }, "sha512-2291+pyVQ771zd8jgCNJ/jpPBaLJg/X7BWX06M9GpBNmC1tu3Rfr3LaWP8C/XTi80PZJnzNZGeMlcDhRY57y/A=="],
"@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
"@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
"@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="],
"@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
"@vscode/l10n": ["@vscode/l10n@0.0.18", "", {}, "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="],
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
"boringlang": ["boringlang@workspace:packages/boringlang"],
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"bun": ["bun@1.2.20", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.2.20", "@oven/bun-darwin-x64": "1.2.20", "@oven/bun-darwin-x64-baseline": "1.2.20", "@oven/bun-linux-aarch64": "1.2.20", "@oven/bun-linux-aarch64-musl": "1.2.20", "@oven/bun-linux-x64": "1.2.20", "@oven/bun-linux-x64-baseline": "1.2.20", "@oven/bun-linux-x64-musl": "1.2.20", "@oven/bun-linux-x64-musl-baseline": "1.2.20", "@oven/bun-windows-x64": "1.2.20", "@oven/bun-windows-x64-baseline": "1.2.20" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, "sha512-1ZGQynT+jPOHLY4IfzSubjbWcXsY2Z+irhW5D8RKC0wQ6KG4MvtgniAYQbSFYINGg8Wb2ydx+WgAG2BdhngAfw=="],
"bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="],
"bunli": ["bunli@0.1.1", "", { "dependencies": { "@bunli/core": "0.1.0", "@bunli/utils": "0.1.0", "glob": "^11.0.0", "zod": "^3.24.1" }, "bin": { "bunli": "dist/cli.js" } }, "sha512-hRNwQZGtXaKSnwGW1cCVPlBG14QMKAcQIg01Vf8eVmyJzIu2LE/wF5uFkdKXAXF/SDGVs9LZcXK9I8oQn5igRg=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"core-js": ["core-js@3.45.0", "", {}, "sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.33.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.33.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA=="],
"eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="],
"eslint-plugin-prettier": ["eslint-plugin-prettier@5.5.4", "", { "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.11.7" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "optionalPeers": ["@types/eslint", "eslint-config-prettier"] }, "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg=="],
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
"glob": ["glob@11.0.3", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="],
"jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="],
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"node-html-parser": ["node-html-parser@6.1.13", "", { "dependencies": { "css-select": "^5.1.0", "he": "1.2.0" } }, "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg=="],
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
"ohm-js": ["ohm-js@17.2.1", "", {}, "sha512-4cXF0G09fAYU9z61kTfkNbKK1Kz/sGEZ5NbVWHoe9Qi7VB7y+Spwk051CpUTfUENdlIr+vt8tMV4/LosTE2cDQ=="],
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="],
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
"prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"regenerator-runtime": ["regenerator-runtime@0.13.11", "", {}, "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="],
"request-light": ["request-light@0.7.0", "", {}, "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
"typescript-language-server": ["typescript-language-server@4.4.0", "", { "bin": { "typescript-language-server": "lib/cli.mjs" } }, "sha512-enWhplhHX7PA0q+IcKHBMpTQh9I2Bmb3L45rwnkATHMsZ7YLduyyCdOmVUWJSYZfkWaBMiKwi/e2FQo4xsKeWw=="],
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"vscode-css-languageservice": ["vscode-css-languageservice@6.3.7", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-5TmXHKllPzfkPhW4UE9sODV3E0bIOJPOk+EERKllf2SmAczjfTmYeq5txco+N3jpF8KIZ6loj/JptpHBQuVQRA=="],
"vscode-html-languageservice": ["vscode-html-languageservice@5.5.1", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-/ZdEtsZ3OiFSyL00kmmu7crFV9KwWR+MgpzjsxO60DQH7sIfHZM892C/E4iDd11EKocr+NYuvOA4Y7uc3QzLEA=="],
"vscode-json-languageservice": ["vscode-json-languageservice@5.6.1", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-IQIURBF2VMKBdWcMunbHSI3G2WmJ9H7613E1hRxIXX7YsAPSdBxnEiIUrTnsSW/3fk+QW1kfsvSigqgAFYIYtg=="],
"vscode-jsonrpc": ["vscode-jsonrpc@9.0.0-next.9", "", {}, "sha512-IM/RHL7ZklEUh1N2Rh4OjRL6D9MyIXq3v+zIkPLXq74hM1eW7WRLP0/cjzNu/baRFC00sFxJm95RBKsT8dXzRQ=="],
"vscode-langservers-extracted": ["vscode-langservers-extracted@4.10.0", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "core-js": "^3.20.1", "jsonc-parser": "^3.2.1", "regenerator-runtime": "^0.13.9", "request-light": "^0.7.0", "semver": "^7.6.1", "typescript": "^4.0.5", "vscode-css-languageservice": "^6.2.14", "vscode-html-languageservice": "^5.2.0", "vscode-json-languageservice": "^5.3.11", "vscode-languageserver": "^10.0.0-next.3", "vscode-languageserver-textdocument": "^1.0.11", "vscode-languageserver-types": "^3.17.5", "vscode-markdown-languageservice": "^0.5.0-alpha.6", "vscode-nls": "^5.2.0", "vscode-uri": "^3.0.8" }, "bin": { "vscode-css-language-server": "bin/vscode-css-language-server", "vscode-eslint-language-server": "bin/vscode-eslint-language-server", "vscode-html-language-server": "bin/vscode-html-language-server", "vscode-json-language-server": "bin/vscode-json-language-server", "vscode-markdown-language-server": "bin/vscode-markdown-language-server" } }, "sha512-EFf9uQI4dAKbzMQFjDvVm1xJq1DXAQvBEuEfPGrK/xzfsL5xWTfIuRr90NgfmqwO+IEt6vLZm9EOj6R66xIifg=="],
"vscode-languageserver": ["vscode-languageserver@10.0.0-next.14", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.6-next.14" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-1TqBDfRLlAIPs6MR5ISI8z7sWlvGL3oHGm9GAHLNOmBZ2+9pmw0yR9vB44/SYuU4bSizxU24tXDFW+rw9jek4A=="],
"vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.6-next.14", "", { "dependencies": { "vscode-jsonrpc": "9.0.0-next.9", "vscode-languageserver-types": "3.17.6-next.6" } }, "sha512-0VD83wxN5kI9vgeaIDQnAxgrbZfKiFNIxdFY5LKe3SZdZd+LAJLMrklSrwfefS7hEzaHw6Z++VFdVJJU+gh1Zg=="],
"vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="],
"vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
"vscode-markdown-languageservice": ["vscode-markdown-languageservice@0.5.0-alpha.11", "", { "dependencies": { "@vscode/l10n": "^0.0.10", "node-html-parser": "^6.1.5", "picomatch": "^2.3.1", "vscode-languageserver-protocol": "^3.17.1", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.7" } }, "sha512-P1uBMAD5iylgpcweWCU1kQwk8SZngktnljXsZk1vFPorXv1mrEI7BkBpOUU0fhVssKgvFlCNLkI7KmwZLC7pdA=="],
"vscode-nls": ["vscode-nls@5.2.0", "", {}, "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="],
"vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
"glob/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"vscode-langservers-extracted/typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="],
"vscode-languageserver-protocol/vscode-languageserver-types": ["vscode-languageserver-types@3.17.6-next.6", "", {}, "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ=="],
"vscode-markdown-languageservice/@vscode/l10n": ["@vscode/l10n@0.0.10", "", {}, "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ=="],
"vscode-markdown-languageservice/vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"vscode-markdown-languageservice/vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
}
}

View File

@@ -1,6 +0,0 @@
version: "3"
services:
boring:
build: .
volumes:
- .:/code/

18
eslint.config.ts Normal file
View File

@@ -0,0 +1,18 @@
import { defineConfig } from "eslint/config";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
export default defineConfig([
{
files: ["**/*.ts"],
rules: {
"prettier/prettier": [
"error",
{},
{
usePrettierrc: true,
},
],
},
},
eslintPluginPrettierRecommended,
]);

View File

@@ -81,16 +81,17 @@ fn main(): i64 {
type TestTrait trait {
fn class_method(id: i64): Self;
fn instance_method(self: Self): i64;
fn default_impl(self: Self): i64 {
return self.instance_method();
}
fn default_impl(self: Self): i64;
}
impl TestTrait for User {
fn class_method(id: i64): Self {
return User{id: id,};
return Self{id: id,};
}
fn instance_method(self: Self): i64 {
return self.get_id();
}
fn default_impl(self: Self): i64 {
return self.instance_method();
}
}

31
examples/strings.bl Normal file
View File

@@ -0,0 +1,31 @@
fn main(): String {
let a = 2;
a;
a = 3;
a = if(true) {"asdf"} else {"fdsa"};
a.b.c.d();
a = (b + c.d()); // comment
return a.b() ;
}
type User struct {
id: i64
}
type TestTrait trait {
fn class_method(id: i64): Self;
fn instance_method(self: Self): i64;
fn default_impl(self: Self): i64;
}
impl TestTrait for User {
fn class_method(id: i64): Self {
return Self{id: id};
}
fn instance_method(self: Self): i64 {
return self.get_id();
}
fn default_impl(self: Self): i64 {
return self.instance_method();
}
}

31
package.json Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "boringlang",
"description": "The Boring programming language",
"private": true,
"author": "Andrew Segavac",
"homepage": "https://code.buildbetter.boats/asegavac/boringlang",
"repository": {
"type": "git",
"url": "ssh://gitea@code.buildbetter.boats:2282/asegavac/boringlang.git"
},
"scripts": {
"build": "bun run --filter 'boringlang' build",
"format": "bun run prettier **/*.ts",
"type-check": "bun run tsc --noEmit",
"check": "bun run format -- --check && bun run type-check"
},
"devDependencies": {
"@types/bun": "latest",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"jiti": "^2.5.1",
"prettier": "^3.6.2",
"typescript": "^5.9.2",
"typescript-language-server": "^4.4.0",
"vscode-langservers-extracted": "^4.10.0"
},
"workspaces": ["packages/*"],
"dependencies": {
"ohm-js": "^17.2.1"
}
}

View File

@@ -0,0 +1,26 @@
{
"name": "boringlang",
"version": "0.1.0",
"type": "module",
"description": "The Boring programming language CLI",
"author": "Andrew Segavac",
"homepage": "https://code.buildbetter.boats/asegavac/boringlang",
"repository": {
"type": "git",
"url": "ssh://gitea@code.buildbetter.boats:2282/asegavac/boringlang.git"
},
"scripts": {
"dev": "bun run src/index.ts",
"build": "bun build ./src/index.ts --outfile dist/boringlang --compile",
"test": "bun test",
"type-check": "tsc --noEmit",
},
"dependencies": {
"@bunli/core": "latest",
},
"devDependencies": {
"@bunli/test": "latest",
"@types/bun": "latest",
"bunli": "latest"
}
}

View File

@@ -0,0 +1,32 @@
import { defineCommand } from "@bunli/core";
import { boringGrammar } from "../parse/grammar";
import { semantics } from "../parse/semantics";
import TraitChecker from "../types/trait_checker";
export const run = defineCommand({
name: "run",
description: "Run a boringlang file",
handler: async ({ positional }) => {
const [path] = positional;
if (!path) {
throw new Error("Usage: run <path>");
}
const file = Bun.file(path);
const text = await file.text();
const match = boringGrammar.match(text, "Module");
if (match.succeeded()) {
const adapter = semantics(match);
const ast = adapter.toAST();
new TraitChecker().withModule(ast);
console.log(JSON.stringify(ast, null, 2));
} else {
console.log(match.message);
// console.log(boringGrammar.trace(text, "Module").toString());
}
},
});

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bun
import { createCLI } from "@bunli/core";
import { run } from "./commands/run.js";
const cli = createCLI({
name: "boringlang",
version: "0.1.0",
description: "Boring programming language CLI",
});
cli.command(run);
await cli.run();

View File

@@ -0,0 +1,194 @@
export interface Spanned {
spanStart: number;
spanEnd: number;
}
export interface Identifier extends Spanned {
name: string;
}
export interface LiteralInt {
expressionType: "LiteralInt";
value: string;
type: TypeUsage;
}
export interface LiteralFloat {
expressionType: "LiteralFloat";
value: string;
type: TypeUsage;
}
export interface LiteralBool {
expressionType: "LiteralBool";
value: string;
type: TypeUsage;
}
export interface LiteralString {
expressionType: "LiteralString";
value: string;
type: TypeUsage;
}
export interface StructField {
name: Identifier;
expression: Expression;
}
export interface LiteralStruct {
expressionType: "LiteralStruct";
name: Identifier;
fields: StructField[];
type: TypeUsage;
}
export interface FunctionCall {
expressionType: "FunctionCall";
source: Expression;
arguments: Expression[];
type: TypeUsage;
}
export interface StructGetter {
expressionType: "StructGetter";
source: Expression;
attribute: Identifier;
type: TypeUsage;
}
export interface Operation {
expressionType: "Operation";
left: Expression;
op: "+" | "-" | "*" | "/";
right: Expression;
}
export interface VariableUsage {
expressionType: "VariableUsage";
name: Identifier;
type: TypeUsage;
}
export interface IfExpression {
expressionType: "IfExpression";
condition: Expression;
block: Block;
else: Block | null;
type: TypeUsage;
}
export interface Expression {
statementType: "Expression";
subExpression:
| LiteralInt
| LiteralFloat
| LiteralBool
| LiteralString
| LiteralStruct
| FunctionCall
| VariableUsage
| IfExpression
| StructGetter
| Block
| Operation;
type: TypeUsage;
}
export interface ReturnStatement {
statementType: "ReturnStatement";
source: Expression;
}
export interface LetStatement {
statementType: "LetStatement";
variableName: Identifier;
expression: Expression;
type: TypeUsage;
}
export interface AssignmentStatement {
statementType: "AssignmentStatement";
source: VariableUsage | StructGetter;
expression: Expression;
}
export type Statement =
| ReturnStatement
| LetStatement
| AssignmentStatement
| Expression;
export interface Block {
statements: Statement[];
type: TypeUsage;
}
export interface FunctionArgument {
name: Identifier;
type: TypeUsage;
}
export interface FunctionDeclaration {
name: Identifier;
arguments: FunctionArgument[];
returnType: TypeUsage;
}
export interface Function {
moduleItem: "Function";
declaration: FunctionDeclaration;
block: Block;
}
export interface StructTypeField {
name: Identifier;
type: TypeUsage;
}
export interface StructTypeDeclaration {
moduleItem: "StructTypeDeclaration";
typeDeclaration: "StructTypeDeclaration";
name: Identifier;
fields: StructTypeField[];
}
export interface TraitTypeDeclaration {
moduleItem: "TraitTypeDeclaration";
typeDeclaration: "TraitTypeDeclaration";
name: Identifier;
functions: FunctionDeclaration[];
}
export type TypeDeclaration = StructTypeDeclaration | TraitTypeDeclaration;
export interface Impl {
moduleItem: "Impl";
struct: NamedTypeUsage;
trait: NamedTypeUsage | null;
functions: Function[];
}
export type ModuleItem = Function | TypeDeclaration | Impl;
export interface Module {
items: ModuleItem[];
}
export interface NamedTypeUsage {
typeUsage: "NamedTypeUsage";
name: Identifier;
}
export interface FunctionTypeUsage {
typeUsage: "FunctionTypeUsage";
arguments: TypeUsage[];
returnType: TypeUsage;
}
export interface UnknownTypeUsage {
typeUsage: "UnknownTypeUsage";
name: string;
}
export type TypeUsage = NamedTypeUsage | FunctionTypeUsage | UnknownTypeUsage;

View File

@@ -0,0 +1,66 @@
import * as ohm from "ohm-js";
export const boringGrammar = ohm.grammar(String.raw`
Boringlang {
ReturnStatement = "return" Expression ";"
LetStatement = "let" identifier (":" TypeUsage)? "=" Expression ";"
AssignmentStatement = VariableUsage "=" Expression ";"
| StructGetter "=" Expression ";"
ExpressionStatement = Expression ";"
Statement = ExpressionStatement
| LetStatement
| ReturnStatement
| AssignmentStatement
LiteralInt = digit+
LiteralFloat = digit* "." digit+
LiteralBool = "true" | "false"
LiteralString = "\"" (~"\"" any)* "\""
| "'" (~"'" any)* "'"
LiteralStructField = identifier ":" Expression
LiteralStruct = identifier "{" ListOf<LiteralStructField, ","> "}"
identifier = (letter | "_")+(letter | digit | "_")*
FunctionCall = Expression "(" ListOf<Expression, ","> ")"
StructGetter = Expression "." identifier
VariableUsage = identifier
IfExpression = "if" "(" Expression ")" Block ("else" Block)?
Term = LiteralInt
| LiteralFloat
| LiteralBool
| LiteralString
| LiteralStruct
| IfExpression
| Block
| "(" Expression ")" -- parens
| VariableUsage
Factor = Factor "*" Term -- mult
| Factor "/" Term -- div
| Term
Expression = Expression "+" Factor -- plus
| Expression "-" Factor -- minus
| StructGetter
| FunctionCall
| Factor
Block = "{" Statement* Expression? "}"
NamedTypeUsage = identifier
TypeUsage = NamedTypeUsage
| "fn" "(" ListOf<TypeUsage, ","> ")" ":" TypeUsage -- function_tu
FunctionArgument = identifier ":" TypeUsage
FunctionDeclaration = "fn" identifier "(" ListOf<FunctionArgument, ","> ")" ":" TypeUsage
Function = FunctionDeclaration Block
StructTypeField = identifier ":" TypeUsage
StructTypeDeclaration = "type" identifier "struct" "{" ListOf<StructTypeField, ","> "}"
TraitMethod = FunctionDeclaration ";"
TraitTypeDeclaration = "type" identifier "trait" "{" TraitMethod* "}"
TypeDeclaration = StructTypeDeclaration
| TraitTypeDeclaration
Impl = "impl" (NamedTypeUsage "for")? NamedTypeUsage "{" Function* "}"
ModuleItem = Function
| TypeDeclaration
| Impl
Module = ModuleItem*
lineTerminator = "\n" | "\r" | "\u2028" | "\u2029"
comment = "//" (~lineTerminator any)* lineTerminator
space += comment
}
`);

View File

@@ -0,0 +1,335 @@
import {
AssignmentStatement,
Block,
Expression,
Function,
FunctionArgument,
FunctionCall,
FunctionDeclaration,
FunctionTypeUsage,
Identifier,
IfExpression,
Impl,
LetStatement,
LiteralBool,
LiteralFloat,
LiteralInt,
LiteralString,
LiteralStruct,
Module,
ModuleItem,
NamedTypeUsage,
Operation,
ReturnStatement,
Statement,
StructField,
StructGetter,
StructTypeDeclaration,
StructTypeField,
TraitTypeDeclaration,
TypeDeclaration,
TypeUsage,
VariableUsage,
} from "./ast";
import { boringGrammar } from "./grammar";
let unknownTypeCounter = 0;
function nextUnknown() {
let name = "S" + unknownTypeCounter.toString();
unknownTypeCounter += 1;
return name;
}
export const semantics = boringGrammar.createSemantics();
semantics.addOperation<any>("toAST", {
LiteralInt(a): LiteralInt {
console.log(this);
console.log(a.source.startIdx);
return {
expressionType: "LiteralInt",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
name: { name: "i64", spanStart: 0, spanEnd: 0 },
},
};
},
LiteralFloat(_1, _2, _3): LiteralFloat {
return {
expressionType: "LiteralFloat",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
name: { name: "f64", spanStart: 0, spanEnd: 0 },
},
};
},
LiteralBool(_): LiteralBool {
return {
expressionType: "LiteralBool",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
name: { name: "bool", spanStart: 0, spanEnd: 0 },
},
};
},
LiteralString(_1, text, _3): LiteralString {
return {
expressionType: "LiteralString",
value: text.sourceString,
type: {
typeUsage: "NamedTypeUsage",
name: { name: "String", spanStart: 0, spanEnd: 0 },
},
};
},
LiteralStructField(identifier, _2, expression): StructField {
return {
name: identifier.toAST(),
expression: expression.toAST(),
};
},
LiteralStruct(identifier, _2, fields, _4): LiteralStruct {
return {
expressionType: "LiteralStruct",
name: identifier.toAST(),
fields: fields.asIteration().children.map((c) => c.toAST()),
type: { typeUsage: "NamedTypeUsage", name: identifier.toAST() },
};
},
identifier(_1, _2): Identifier {
return {
name: this.sourceString,
spanStart: this.source.startIdx,
spanEnd: this.source.endIdx,
};
},
FunctionCall(expression, _2, args, _4): FunctionCall {
return {
expressionType: "FunctionCall",
source: expression.toAST(),
arguments: args.asIteration().children.map((c) => c.toAST()),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
StructGetter(expression, _2, identifier): StructGetter {
return {
expressionType: "StructGetter",
source: expression.toAST(),
attribute: identifier.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
VariableUsage(identifier): VariableUsage {
return {
expressionType: "VariableUsage",
name: identifier.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
IfExpression(_1, _2, expression, _4, block, _6, elseBlock): IfExpression {
const eb = elseBlock.toAST();
return {
expressionType: "IfExpression",
condition: expression.toAST(),
block: block.toAST(),
else: eb.length > 0 ? eb[0] : null,
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Term(term): Expression {
return term.toAST();
},
Term_parens(_1, term, _3): Expression {
return term.toAST();
},
Factor(factor): Expression {
return factor.toAST();
},
Expression(expression): Expression {
return expression.toAST();
},
Expression_plus(expression, _2, factor): Operation {
return {
expressionType: "Operation",
left: expression.toAST(),
op: "+",
right: factor.toAST(),
};
},
Expression_minus(expression, _2, factor): Operation {
return {
expressionType: "Operation",
left: expression.toAST(),
op: "-",
right: factor.toAST(),
};
},
Factor_mult(factor, _2, term): Operation {
return {
expressionType: "Operation",
left: factor.toAST(),
op: "*",
right: term.toAST(),
};
},
Factor_div(factor, _2, term): Operation {
return {
expressionType: "Operation",
left: factor.toAST(),
op: "/",
right: term.toAST(),
};
},
Statement(statement): Statement {
return statement.toAST();
},
ReturnStatement(_1, expression, _3): ReturnStatement {
return {
statementType: "ReturnStatement",
source: expression.toAST(),
};
},
LetStatement(_1, ident, _3, typeUsage, _5, expression, _7): LetStatement {
const tu = typeUsage.toAST();
return {
statementType: "LetStatement",
variableName: ident.toAST(),
expression: expression.toAST(),
type:
tu.length > 0
? tu[0]
: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
AssignmentStatement(variable, _2, expression, _4): AssignmentStatement {
return {
statementType: "AssignmentStatement",
source: variable.toAST(),
expression: expression.toAST(),
};
},
ExpressionStatement(expression, _2): Expression {
return {
statementType: "Expression",
subExpression: expression.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Block(_1, statements, expression, _4): Block {
const lines = statements.asIteration().children.map((c) => c.toAST());
const finalExpression = expression.toAST();
lines.push(finalExpression.length > 0 ? finalExpression[0] : null);
return {
statements: lines,
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
NamedTypeUsage(name): NamedTypeUsage {
return {
typeUsage: "NamedTypeUsage",
name: name.toAST(),
};
},
TypeUsage_function_tu(_1, _2, args, _4, _5, returnType): FunctionTypeUsage {
return {
typeUsage: "FunctionTypeUsage",
arguments: args.asIteration().children.map((c) => c.toAST()),
returnType: returnType.toAST(),
};
},
TypeUsage(typeUsage): TypeUsage {
return typeUsage.toAST();
},
FunctionArgument(identifier, _2, typeUsage): FunctionArgument {
return {
name: identifier.toAST(),
type: typeUsage.toAST(),
};
},
FunctionDeclaration(
_1,
identifier,
_3,
args,
_4,
_5,
returnType,
): FunctionDeclaration {
return {
name: identifier.toAST(),
arguments: args.asIteration().children.map((c) => c.toAST()),
returnType: returnType.toAST(),
};
},
Function(declaration, block): Function {
return {
moduleItem: "Function",
declaration: declaration.toAST(),
block: block.toAST(),
};
},
StructTypeField(identifier, _2, typeUsage): StructTypeField {
return {
name: identifier.toAST(),
type: typeUsage.toAST(),
};
},
StructTypeDeclaration(
_1,
identifier,
_3,
_4,
fields,
_6,
): StructTypeDeclaration {
return {
moduleItem: "StructTypeDeclaration",
typeDeclaration: "StructTypeDeclaration",
name: identifier.toAST(),
fields: fields.asIteration().children.map((c) => c.toAST()),
};
},
TraitMethod(declaration, _2): FunctionDeclaration {
return declaration.toAST();
},
TraitTypeDeclaration(
_1,
identifier,
_3,
_4,
methods,
_5,
): TraitTypeDeclaration {
return {
moduleItem: "TraitTypeDeclaration",
typeDeclaration: "TraitTypeDeclaration",
name: identifier.toAST(),
functions: methods.asIteration().children.map((c) => c.toAST()),
};
},
TypeDeclaration(declaration): TypeDeclaration {
return declaration.toAST();
},
Impl(_1, trait, _3, struct, _4, methods, _5): Impl {
const tr = trait.toAST();
return {
moduleItem: "Impl",
struct: struct.toAST(),
trait: tr.length > 0 ? tr[0] : null,
functions: methods.asIteration().children.map((c) => c.toAST()),
};
},
ModuleItem(item): ModuleItem {
return item.toAST();
},
Module(items): Module {
return {
items: items.asIteration().children.map((c) => c.toAST()),
};
},
_iter(...children) {
return children.map((c) => c.toAST());
},
});

View File

@@ -0,0 +1,70 @@
import { Impl, Module, TraitTypeDeclaration, TypeUsage } from "../parse/ast";
interface Context {
environment: Record<string, TraitTypeDeclaration>;
}
const compareTypes = (typeA: TypeUsage, typeB: TypeUsage) => {
if (typeA.typeUsage !== typeB.typeUsage) {
throw Error(`Mismatched types: ${typeA.typeUsage} ${typeB.typeUsage}`);
}
if (typeA.typeUsage == "NamedTypeUsage" && typeB.typeUsage == "NamedTypeUsage") {
if (typeA.name.name !== typeB.name.name) {
throw Error(`Mismatched types: ${typeA.name.name} ${typeB.name.name}`);
}
}
if (typeA.typeUsage == "FunctionTypeUsage" && typeB.typeUsage == "FunctionTypeUsage") {
if (typeA.arguments.length !== typeB.arguments.length) {
throw Error(`Mismatched arg lengths: ${typeA.arguments.length} ${typeB.arguments.length}`);
}
for (let i = 0; i < typeA.arguments.length; i++) {
compareTypes(typeA.arguments[i], typeB.arguments[i]);
}
compareTypes(typeA.returnType, typeB.returnType);
}
};
export default class TraitChecker {
withModule = (module: Module) => {
let ctx: Context = { environment: {} };
for (const item of module.items) {
if (item.moduleItem == "TraitTypeDeclaration") {
ctx.environment[item.name.name] = item;
}
}
for (const item of module.items) {
if (item.moduleItem == "Impl") {
this.withImpl(ctx, item);
}
}
};
withImpl = (ctx: Context, impl: Impl) => {
if (new Set(impl.functions.map((fn) => fn.declaration.name)).size !== impl.functions.length) {
throw Error(`Duplicate functions in ${impl.struct.name.name}`);
}
if (impl.trait == null) {
return;
}
const trait = ctx.environment[impl.trait.name.name];
if (!trait) {
throw Error(`No such trait: ${impl.trait.name}`);
}
if (impl.functions.length !== trait.functions.length) {
throw Error(`Mismatched impl/trait len ${impl.trait.name.name} for ${impl.struct.name.name}`);
}
for (let i = 0; i < impl.functions.length; i++) {
if (impl.functions[i].declaration.name.name !== trait.functions[i].name.name) {
throw Error(
`Mismatched impl/trait names ${impl.functions[i].declaration.name} for ${trait.functions[i].name}`,
);
}
for (let j = 0; j < impl.functions[i].declaration.arguments.length; j++) {
compareTypes(
impl.functions[i].declaration.arguments[j].type,
trait.functions[i].arguments[j].type,
);
}
compareTypes(impl.functions[i].declaration.returnType, trait.functions[i].returnType);
}
};
}

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src",
"types": ["bun-types"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "test/**/*"]
}

9
prettier.config.js Normal file
View File

@@ -0,0 +1,9 @@
const config = {
trailingComma: "all",
singleQuote: false,
printWidth: 100,
semi: true,
};
export default config;

View File

@@ -1,385 +0,0 @@
use std::cell::RefCell;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IdGenerator {
id_key: String,
counter: RefCell<i64>,
}
impl IdGenerator {
pub fn new(key: &str) -> Self {
IdGenerator { id_key: key.to_string(), counter: RefCell::new(0) }
}
pub fn next(&self) -> String {
*self.counter.borrow_mut() += 1;
(self.id_key.to_owned() + &self.counter.borrow().to_string()).to_string()
}
}
pub fn new_unit() -> TypeUsage {
TypeUsage::Named(NamedTypeUsage {
type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }),
name: Identifier {
name: Spanned {
span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these
value: "unit".to_string(),
},
},
})
}
pub fn new_never() -> TypeUsage {
TypeUsage::Named(NamedTypeUsage {
type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }),
name: Identifier {
name: Spanned {
span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these
value: "!".to_string(),
},
},
})
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Span {
pub left: usize,
pub right: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Spanned<T> {
pub span: Span,
pub value: T,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FunctionTypeUsage {
pub arguments: Vec<TypeUsage>,
pub return_type: Box<TypeUsage>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NamedTypeUsage {
pub type_parameters: GenericUsage,
pub name: Identifier,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UnknownTypeUsage {
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum NamespaceTypeUsage {
Type(NamedTypeUsage)
// Module
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TypeUsage {
Function(FunctionTypeUsage),
Named(NamedTypeUsage),
Unknown(UnknownTypeUsage),
Namespace(NamespaceTypeUsage)
}
impl TypeUsage {
pub fn new_unknown(id_gen: &IdGenerator) -> TypeUsage {
return TypeUsage::Unknown(UnknownTypeUsage { name: id_gen.next() });
}
pub fn new_named(identifier: &Identifier, generic_usage: &GenericUsage) -> TypeUsage {
return TypeUsage::Named(NamedTypeUsage {
type_parameters: generic_usage.clone(),
name: identifier.clone(),
});
}
pub fn new_builtin(name: String) -> TypeUsage {
TypeUsage::Named(NamedTypeUsage {
type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }),
name: Identifier {
name: Spanned {
span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these
value: name,
},
},
})
}
pub fn new_function(arg_count: usize, id_gen: &IdGenerator) -> TypeUsage {
return TypeUsage::Function(FunctionTypeUsage {
arguments: (0..arg_count).map(|_| TypeUsage::new_unknown(&id_gen)).collect(),
return_type: Box::new(TypeUsage::new_unknown(&id_gen)),
});
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericParameter {
pub name: Identifier,
pub bounds: Vec<Identifier>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Generic {
pub parameters: Vec<GenericParameter>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericInstantiation {
pub parameters: Vec<TypeUsage>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum GenericUsage {
Known(GenericInstantiation),
Unknown,
}
impl GenericUsage {
pub fn new(type_parameters: &[TypeUsage]) -> Self {
GenericUsage::Known(GenericInstantiation {
parameters: type_parameters.iter().map(|tp| tp.clone()).collect(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Operator {
Mul,
Div,
Plus,
Minus,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LiteralInt {
pub value: Spanned<String>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LiteralFloat {
pub value: Spanned<String>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LiteralBool {
pub value: Spanned<String>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LiteralStruct {
pub type_parameters: GenericUsage,
pub name: Identifier,
pub fields: Vec<(Identifier, Expression)>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Identifier {
pub name: Spanned<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FunctionCall {
pub source: Expression,
pub arguments: Vec<Expression>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StructGetter {
pub type_parameters: GenericUsage,
pub source: Expression,
pub attribute: Identifier,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Operation {
pub left: Expression,
pub op: Operator,
pub right: Expression,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VariableUsage {
pub name: Identifier,
pub type_parameters: GenericUsage,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IfExpression {
pub condition: Expression,
pub block: Block,
pub else_: Option<Block>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Subexpression {
LiteralInt(LiteralInt),
LiteralFloat(LiteralFloat),
LiteralBool(LiteralBool),
LiteralStruct(LiteralStruct),
FunctionCall(FunctionCall),
VariableUsage(VariableUsage),
If(IfExpression),
StructGetter(StructGetter),
Block(Block),
Op(Operation),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Expression {
pub subexpression: Box<Subexpression>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ReturnStatement {
pub source: Expression,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LetStatement {
pub variable_name: Identifier,
pub expression: Expression,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AssignmentTarget {
Variable(VariableUsage),
StructAttr(StructGetter),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AssignmentStatement {
pub source: AssignmentTarget,
pub expression: Expression,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Statement {
Return(ReturnStatement),
Let(LetStatement),
Assignment(AssignmentStatement),
Expression(Expression),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Block {
pub statements: Vec<Statement>,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VariableDeclaration {
pub name: Identifier,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FunctionDeclaration {
pub generic: Generic,
pub name: Identifier,
pub arguments: Vec<VariableDeclaration>,
pub return_type: TypeUsage,
}
impl FunctionDeclaration {
pub fn to_type(&self) -> TypeUsage {
TypeUsage::Function(FunctionTypeUsage {
arguments: self.arguments.iter().map(|arg| arg.type_.clone()).collect(),
return_type: Box::new(self.return_type.clone()),
})
}
pub fn to_method_type(&self) -> TypeUsage {
TypeUsage::Function(FunctionTypeUsage {
arguments: self.arguments[1..self.arguments.len()]
.iter()
.map(|arg| arg.type_.clone())
.collect(),
return_type: Box::new(self.return_type.clone()),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Function {
pub declaration: FunctionDeclaration,
pub block: Block,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PrimitiveTypeDeclaration {
pub name: String, // cannot be identifier as it's not declared anywhere specific, it's builtins
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StructField {
pub name: Identifier,
pub type_: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StructTypeDeclaration {
pub generic: Generic,
pub name: Identifier,
pub fields: Vec<StructField>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TraitItem {
FunctionDeclaration(FunctionDeclaration),
Function(Function),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TraitTypeDeclaration {
pub generic: Generic,
pub name: Identifier,
pub functions: Vec<TraitItem>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AliasTypeDeclaration {
pub name: Identifier,
pub replaces: TypeUsage,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TypeDeclaration {
Struct(StructTypeDeclaration),
Primitive(PrimitiveTypeDeclaration),
Alias(AliasTypeDeclaration),
Trait(TraitTypeDeclaration),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Impl {
pub generic: Generic,
pub struct_: NamedTypeUsage,
pub trait_: Option<NamedTypeUsage>,
pub functions: Vec<Function>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ModuleItem {
Function(Function),
TypeDeclaration(TypeDeclaration),
Impl(Impl),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Module {
pub items: Vec<ModuleItem>,
}

View File

@@ -1 +0,0 @@
use std::collections::HashMap;

View File

@@ -1,124 +0,0 @@
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue};
use std::collections::HashMap;
use std::convert::TryInto;
use std::mem;
use crate::ast;
type Scope<'ctx> = HashMap<String, BasicValueEnum<'ctx>>;
pub struct ModuleCodeGen<'ctx> {
context: &'ctx Context,
module: Module<'ctx>,
builder: Builder<'ctx>,
scope: Scope<'ctx>,
}
impl<'ctx> ModuleCodeGen<'ctx> {
pub fn new(context: &'ctx Context, name: String) -> Self {
return ModuleCodeGen {
context: context,
module: context.create_module(&name),
builder: context.create_builder(),
scope: Scope::new(),
};
}
pub fn gen_literal_int(&mut self, literal_int: &ast::LiteralInt) -> IntValue<'ctx> {
self.context
.i64_type()
.const_int(unsafe { mem::transmute::<i64, u64>(literal_int.value) }, true)
}
pub fn gen_op_expression(&mut self, scope: &Scope<'ctx>, operation: &ast::Operation) -> IntValue<'ctx> {
let lhs_result = self.gen_expression(scope, &operation.left);
let rhs_result = self.gen_expression(scope, &operation.right);
self.gen_op_int(&lhs_result, &rhs_result, &operation.op)
}
pub fn gen_op_int(&mut self, lhs: &IntValue<'ctx>, rhs: &IntValue<'ctx>, op: &ast::Operator) -> IntValue<'ctx> {
match *op {
ast::Operator::Plus => self.builder.build_int_add(*lhs, *rhs, "add"),
ast::Operator::Minus => self.builder.build_int_sub(*lhs, *rhs, "sub"),
ast::Operator::Mul => self.builder.build_int_mul(*lhs, *rhs, "mul"),
ast::Operator::Div => self.builder.build_int_signed_div(*lhs, *rhs, "div"),
}
}
pub fn gen_expression(&mut self, scope: &Scope<'ctx>, expression: &Box<ast::Expression>) -> IntValue<'ctx> {
match &**expression {
ast::Expression::LiteralInt(literal_int) => self.gen_literal_int(&literal_int),
ast::Expression::Identifier(identifier) => match scope[&identifier.name] {
BasicValueEnum::IntValue(value) => value,
_ => panic!("function returned type other than int, no types yet"),
},
ast::Expression::FunctionCall(function_call) => self.gen_function_call(scope, &function_call),
ast::Expression::Op(operation) => self.gen_op_expression(scope, &operation),
}
}
pub fn gen_function_call(&mut self, scope: &Scope<'ctx>, function_call: &ast::FunctionCall) -> IntValue<'ctx> {
let fn_value = self.module.get_function(&function_call.name.name).unwrap();
let mut arguments = Vec::new();
for expression in (&function_call.arguments).into_iter() {
arguments.push(BasicValueEnum::IntValue(self.gen_expression(scope, &expression)));
}
let result = self
.builder
.build_call(fn_value, &arguments, &function_call.name.name)
.try_as_basic_value()
.left()
.unwrap();
match result {
BasicValueEnum::IntValue(value) => value,
_ => panic!("function returned type other than int, no types yet"),
}
}
// Generates a FunctionValue for an `ast::Function`. This does not genereate a body,
// that task is left to the `gen_function` function. The reason this is split
// between two functions is that first all signatures are generated and then all bodies. This
// allows bodies to reference `FunctionValue` wherever they are declared in the file.
pub fn gen_signature(&mut self, function: &ast::Function) -> FunctionValue {
let mut args = Vec::new();
for _ in &function.arguments {
args.push(self.context.i64_type().into());
}
let fn_type = self.context.i64_type().fn_type(&args, false);
let fn_value = self.module.add_function(&function.name.name, fn_type, None);
fn_value
}
pub fn gen_function(&mut self, function: &ast::Function) {
let fn_value = self.module.get_function(&function.name.name).unwrap();
let basic_block = self.context.append_basic_block(fn_value, "entry");
self.builder.position_at_end(basic_block);
let mut scope = self.scope.clone();
for (i, param) in (&function.arguments).into_iter().enumerate() {
scope.insert(param.name.name.to_string(), fn_value.get_nth_param(i.try_into().unwrap()).unwrap());
}
let body = &function.block;
let return_value = self.gen_expression(&scope, &body.expression);
self.builder.build_return(Some(&return_value));
}
pub fn gen_module(&mut self, module: ast::Module) {
// generate all signatures before the fuction bodies
for function in &module.functions {
self.gen_signature(&function);
}
for function in module.functions {
self.gen_function(&function);
}
}
pub fn dump(&self) -> String {
self.module.print_to_string().to_string()
}
}

View File

@@ -1,64 +0,0 @@
use crate::ast;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum TypingError {
#[error("unknown named type")]
TypeDoesNotExist { identifier: ast::Identifier },
#[error("identifier is not type")]
IdentifierIsNotType { identifier: ast::Identifier },
#[error("argument length mismatch")]
ArgumentLengthMismatch {
// TODO: add position
},
#[error("type mismatch")]
TypeMismatch {
type_one: ast::TypeUsage,
type_two: ast::TypeUsage,
},
#[error("unknown field name")]
UnknownFieldName { identifier: ast::Identifier },
#[error("cannot assign to method")]
CannotAssignToMethod { identifier: ast::Identifier },
#[error("multiple field name matches")]
MultipleFieldName { identifier: ast::Identifier },
#[error("attribute gotten of non-struct")]
AttributeOfNonstruct { identifier: ast::Identifier },
#[error("name is not a struct, cannot instaniate")]
NotAStructLiteral { identifier: ast::Identifier },
#[error("struct literal fields mismatch")]
StructLiteralFieldsMismatch { struct_name: ast::Identifier },
#[error("missing trait function")]
MissingTraitFunction {
struct_name: ast::Identifier,
function_name: ast::Identifier,
},
#[error("function not in trait")]
FunctionNotInTrait { function_name: ast::Identifier },
#[error("impl trait must be trait")]
ImplTraitMustBeTrait { trait_name: ast::Identifier },
#[error("function call used with non-function")]
FunctionCallNotAFunction {
// TODO: add position
},
#[error("`if` condition must be bool")]
IfConditionMustBeBool {
// TODO: add position
},
#[error("cannot use type as an expression")]
TypeIsNotAnExpression { type_name: ast::Identifier },
#[error("wrong number of type parameters")]
WrongNumberOfTypeParameters {
// TODO: add position
},
#[error("invalid use of alias")]
InvalidUseofAlias,
#[error("alias cannot have type parameters")]
InvalidTypeParameterOnAlias {
alias: ast::Identifier,
},
#[error("type cannot be used for generic")]
InvalidTypeForGeneric,
#[error("multiple errors")]
MultipleErrors { errors: Vec<TypingError> },
}

View File

@@ -1,345 +0,0 @@
use crate::ast;
grammar(id_generator: &ast::IdGenerator);
match {
r"[0-9]+",
r"[0-9]+\.[0-9]+",
r"[A-Za-z_][A-Za-z0-9_]*",
":",
";",
"{",
"}",
"(",
")",
"[",
"]",
"",
".",
"+",
"-",
"*",
"/",
"fn",
"return",
"let",
"true",
"false",
"if",
"else",
"=",
"for",
"type",
"trait",
"struct",
"impl",
",",
r"\s*" => { },
r"//[^\n\r]*[\n\r]*" => { }, // `// comment`
}
pub LiteralInt: String = {
<literal:r"[0-9]+"> => literal.to_string()
};
pub SpannedLiteralInt: ast::LiteralInt = {
<literal_int:Spanned<LiteralInt>> => ast::LiteralInt{value: literal_int, type_: ast::TypeUsage::new_builtin("i64".to_string())}
};
pub LiteralFloat: String = {
<literal:r"[0-9]+\.[0-9]+"> => literal.to_string()
};
pub SpannedLiteralFloat: ast::LiteralFloat = {
<literal_float:Spanned<LiteralFloat>> => ast::LiteralFloat{value: literal_float, type_: ast::TypeUsage::new_builtin("f64".to_string())}
};
pub LiteralBool: String = {
"true" => "true".to_string(),
"false" => "false".to_string(),
};
pub SpannedLiteralBool: ast::LiteralBool = {
<literal_bool:Spanned<LiteralBool>> => ast::LiteralBool{value: literal_bool, type_: ast::TypeUsage::new_builtin("bool".to_string())}
};
pub Identifier: String = {
<i:r"[A-Za-z_][A-Za-z0-9_]*"> => i.to_string()
};
pub GenericUsage: ast::GenericUsage = {
"[" <tp:Comma<TypeUsage>> "]" => ast::GenericUsage::new(&tp),
};
pub LiteralStructField: (ast::Identifier, ast::Expression) = {
<field:SpannedIdentifier> ":" <expr:Expression> => (field, expr)
};
pub LiteralStruct: ast::LiteralStruct = {
<i:SpannedIdentifier> <gu:GenericUsage?> "{" <field_list:Comma<LiteralStructField>> "}" => {
match gu {
Some(tp) => {
ast::LiteralStruct{
type_parameters: tp.clone(),
name: i.clone(),
fields: field_list,
type_: ast::TypeUsage::new_named(&i, &tp),
}
},
None => {
ast::LiteralStruct{
type_parameters: ast::GenericUsage::new(&[]),
name: i.clone(),
fields: field_list,
type_: ast::TypeUsage::new_named(&i, &ast::GenericUsage::new(&[])),
}
}
}
}
};
pub SpannedIdentifier: ast::Identifier = {
<i:Spanned<Identifier>> => ast::Identifier{name: i}
};
pub FunctionCall: ast::FunctionCall = {
<source:Term> "(" <args:Comma<Expression>> ")" => ast::FunctionCall{source: source, arguments: args, type_: ast::TypeUsage::new_unknown(&id_generator)}
};
pub StructGetter: ast::StructGetter = {
<source:Term> "." <field:SpannedIdentifier> <gu:GenericUsage?> => match gu {
Some(tp) => ast::StructGetter{type_parameters: tp, source: source, attribute: field, type_: ast::TypeUsage::new_unknown(&id_generator)},
None => ast::StructGetter{type_parameters: ast::GenericUsage::Unknown, source: source, attribute: field, type_: ast::TypeUsage::new_unknown(&id_generator)},
}
};
pub VariableUsage: ast::VariableUsage = {
<identifier:SpannedIdentifier> <gu:GenericUsage?> => match gu {
Some(tp) => ast::VariableUsage{name: identifier, type_parameters: tp.clone(), type_: ast::TypeUsage::new_unknown(&id_generator)},
None => ast::VariableUsage{name: identifier, type_parameters: ast::GenericUsage::Unknown, type_: ast::TypeUsage::new_unknown(&id_generator)},
}
};
pub IfExpression: ast::IfExpression = {
"if" "("<c:Expression>")" <b:Block> => ast::IfExpression{condition: c, block: b, else_: None, type_: ast::TypeUsage::new_unknown(&id_generator)},
"if" "("<c:Expression>")" <b:Block> "else" <e:Block> => ast::IfExpression{condition: c, block: b, else_: Some(e), type_: ast::TypeUsage::new_unknown(&id_generator)},
};
pub Expression: ast::Expression = {
<l:Expression> "+" <r:Factor> => {
ast::Expression{
subexpression: Box::new(ast::Subexpression::Op(ast::Operation{left: l, op: ast::Operator::Plus, right: r})),
type_: ast::TypeUsage::new_unknown(&id_generator),
}
},
<l:Expression> "-" <r:Factor> => {
ast::Expression{
subexpression: Box::new(ast::Subexpression::Op(ast::Operation{left: l, op: ast::Operator::Minus, right: r})),
type_: ast::TypeUsage::new_unknown(&id_generator),
}
},
Factor,
};
pub Factor: ast::Expression = {
<l:Factor> "*" <r:Term> => {
ast::Expression{
subexpression: Box::new(ast::Subexpression::Op(ast::Operation{left: l, op: ast::Operator::Mul, right: r})),
type_: ast::TypeUsage::new_unknown(&id_generator),
}
},
<l:Factor> "/" <r:Term> => {
ast::Expression{
subexpression: Box::new(ast::Subexpression::Op(ast::Operation{left: l, op: ast::Operator::Div, right: r})),
type_: ast::TypeUsage::new_unknown(&id_generator),
}
},
Term,
};
pub Term: ast::Expression = {
SpannedLiteralInt => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralInt(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
SpannedLiteralFloat => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralFloat(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
SpannedLiteralBool => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralBool(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
LiteralStruct => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralStruct(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
FunctionCall => ast::Expression{subexpression: Box::new(ast::Subexpression::FunctionCall(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
StructGetter => ast::Expression{subexpression: Box::new(ast::Subexpression::StructGetter(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
VariableUsage => ast::Expression{subexpression: Box::new(ast::Subexpression::VariableUsage(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
IfExpression => ast::Expression{subexpression: Box::new(ast::Subexpression::If(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
Block => ast::Expression{subexpression: Box::new(ast::Subexpression::Block(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)},
"(" <e:Expression> ")" => e,
};
pub ReturnStatement: ast::ReturnStatement = {
"return" <e:Expression> => ast::ReturnStatement{source: e}
};
pub LetStatement: ast::LetStatement = {
//TODO: support destructuring with tuples, when they exist.
//TODO: add mut, weak
"let" <n:SpannedIdentifier> "=" <e:Expression> => ast::LetStatement{variable_name: n, type_: ast::TypeUsage::new_unknown(&id_generator), expression: e},
"let" <n:SpannedIdentifier> ":" <t:TypeUsage> "=" <e:Expression> => ast::LetStatement{variable_name: n, type_: t, expression: e},
};
pub AssignmentStatement: ast::AssignmentStatement = {
<v:VariableUsage> "=" <e:Expression> => ast::AssignmentStatement{source: ast::AssignmentTarget::Variable(v), expression: e},
<sg:StructGetter> "=" <e:Expression> => ast::AssignmentStatement{source: ast::AssignmentTarget::StructAttr(sg), expression: e},
};
pub Statement: ast::Statement = {
<r:ReturnStatement> ";" => ast::Statement::Return(r),
<l:LetStatement> ";" => ast::Statement::Let(l),
<a:AssignmentStatement> ";" => ast::Statement::Assignment(a),
<e:Expression> ";" => ast::Statement::Expression(e),
};
pub Block: ast::Block = {
"{" <v:(<Statement>)*> <e:Expression?> "}" => match e {
None => ast::Block{statements: v, type_: ast::TypeUsage::new_unknown(&id_generator)},
Some(e) => {
let mut v = v;
v.push(ast::Statement::Expression(e));
ast::Block{statements: v, type_: ast::TypeUsage::new_unknown(&id_generator)}
}
}
};
pub PartialNamedTypeUsage: ast::NamedTypeUsage = {
<n:SpannedIdentifier> <gu:GenericUsage?> => match gu {
Some(tp) => ast::NamedTypeUsage{type_parameters: tp, name: n},
None => ast::NamedTypeUsage{type_parameters: ast::GenericUsage::Unknown, name: n},
},
};
pub NamedTypeUsage: ast::NamedTypeUsage = {
<n:SpannedIdentifier> <gu:GenericUsage?> => match gu {
Some(tp) => ast::NamedTypeUsage{type_parameters: tp, name: n},
None => ast::NamedTypeUsage{type_parameters: ast::GenericUsage::new(&[]), name: n},
},
};
pub PartialTypeUsage: ast::TypeUsage = {
<n:PartialNamedTypeUsage> => ast::TypeUsage::Named(n),
"fn" "(" <args:Comma<PartialTypeUsage>> ")" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(ast::new_unit())}),
"fn" "(" <args:Comma<PartialTypeUsage>> ")" ":" <rt:PartialTypeUsage> => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(rt)}),
};
pub TypeUsage: ast::TypeUsage = {
<n:NamedTypeUsage> => ast::TypeUsage::Named(n),
"fn" "(" <args:Comma<TypeUsage>> ")" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(ast::new_unit())}),
"fn" "(" <args:Comma<TypeUsage>> ")" ":" <rt:TypeUsage> => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(rt)}),
};
pub VariableDeclaration: ast::VariableDeclaration = {
<i:SpannedIdentifier> ":" <t:TypeUsage> => ast::VariableDeclaration{name: i, type_: t},
};
pub GenericParameter: ast::GenericParameter = {
<i:SpannedIdentifier> => ast::GenericParameter{name: i, bounds: vec!()},
<i:SpannedIdentifier> ":" <bounds:PlusSeparated<SpannedIdentifier>> => ast::GenericParameter{name: i, bounds: bounds},
};
pub Generic: ast::Generic = {
"[" <p:Comma<GenericParameter>> "]" => ast::Generic{parameters: p},
};
pub FunctionDeclaration: ast::FunctionDeclaration = {
"fn" <n:SpannedIdentifier> <g:Generic> "(" <args:Comma<VariableDeclaration>> ")" => ast::FunctionDeclaration{name: n, generic: g, arguments: args, return_type: ast::new_unit()},
"fn" <n:SpannedIdentifier> <g:Generic> "(" <args:Comma<VariableDeclaration>> ")" ":" <rt:TypeUsage> => ast::FunctionDeclaration{name: n, generic: g, arguments: args, return_type: rt},
"fn" <n:SpannedIdentifier> "(" <args:Comma<VariableDeclaration>> ")" => ast::FunctionDeclaration{name: n, generic: ast::Generic{parameters: vec!()}, arguments: args, return_type: ast::new_unit()},
"fn" <n:SpannedIdentifier> "(" <args:Comma<VariableDeclaration>> ")" ":" <rt:TypeUsage> => ast::FunctionDeclaration{name: n, generic: ast::Generic{parameters: vec!()}, arguments: args, return_type: rt},
};
pub Function: ast::Function = {
<d:FunctionDeclaration> <b:Block> => ast::Function{declaration: d, block: b}
};
pub StructField: ast::StructField = {
<i:SpannedIdentifier> ":" <t:TypeUsage> => ast::StructField{name: i, type_: t},
};
pub StructTypeDeclaration: ast::StructTypeDeclaration = {
"type" <i:SpannedIdentifier> <g:Generic?> "struct" "{" <f:Comma<StructField>> "}" => match g {
Some(generic) => ast::StructTypeDeclaration{name: i, generic: generic, fields: f},
None => ast::StructTypeDeclaration{name: i, generic: ast::Generic{parameters: vec!()}, fields: f},
}
};
pub AliasTypeDeclaration: ast::AliasTypeDeclaration = {
"type" <i:SpannedIdentifier> "=" <t:TypeUsage> ";" => ast::AliasTypeDeclaration{name: i, replaces: t}
};
pub TraitItem: ast::TraitItem = {
<fd:FunctionDeclaration> ";" => ast::TraitItem::FunctionDeclaration(fd),
<f:Function> => ast::TraitItem::Function(f),
};
pub TraitTypeDeclaration: ast::TraitTypeDeclaration = {
"type" <i:SpannedIdentifier> <g:Generic> "trait" "{" <ti:TraitItem*> "}" => ast::TraitTypeDeclaration{name: i, generic: g, functions: ti},
"type" <i:SpannedIdentifier> "trait" "{" <ti:TraitItem*> "}" => ast::TraitTypeDeclaration{name: i, generic: ast::Generic{parameters: vec!()}, functions: ti},
};
pub TypeDeclaration: ast::TypeDeclaration = {
<s:StructTypeDeclaration> => ast::TypeDeclaration::Struct(s),
<a:AliasTypeDeclaration> => ast::TypeDeclaration::Alias(a),
<t:TraitTypeDeclaration> => ast::TypeDeclaration::Trait(t),
};
pub Impl: ast::Impl = {
"impl" <g:Generic?> <s:NamedTypeUsage> "{" <f:Function*> "}" => {
let generic = match g {
Some(g) => g,
None => ast::Generic{parameters: vec!()},
};
ast::Impl{generic: generic, trait_: None, struct_: s, functions: f}
},
"impl" <g:Generic?> <t:NamedTypeUsage> "for" <s:NamedTypeUsage> "{" <f:Function*> "}" => {
let generic = match g {
Some(g) => g,
None => ast::Generic{parameters: vec!()},
};
ast::Impl{generic: generic, trait_: Some(t), struct_: s, functions: f}
}
};
pub ModuleItem: ast::ModuleItem = {
<f:Function> => ast::ModuleItem::Function(f),
<td:TypeDeclaration> => ast::ModuleItem::TypeDeclaration(td),
<i:Impl> => ast::ModuleItem::Impl(i),
};
pub Module: ast::Module = {
<i:ModuleItem*> => ast::Module{items: i}
};
// From https://lalrpop.github.io/lalrpop/tutorial/006_macros.html
// Comma separated list of T with optional trailing comma
Comma<T>: Vec<T> = {
<v:(<T> ",")*> <e:T?> => match e {
None => v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
// PlusSeparated separated list of T with optional trailing comma
PlusSeparated<T>: Vec<T> = {
<v:(<T> "+")*> <e:T?> => match e {
None => v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
Spanned<Rule>: ast::Spanned<Rule> = {
<l: @L> <rule: Rule> <r: @R> => ast::Spanned{span: ast::Span{left: l, right: r}, value: rule}
};

View File

@@ -1,646 +0,0 @@
use crate::ast;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub enum NumericValue {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
ISize(isize),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
USize(usize),
F32(f32),
F64(f64),
}
#[derive(Debug, Clone)]
pub struct StructValue {
source: ast::StructTypeDeclaration,
fields: HashMap<String, Value>,
}
type BuiltinFunction = fn(Vec<Value>) -> Value;
#[derive(Debug, Clone)]
pub enum FunctionRef {
User(ast::Function),
Builtin(BuiltinFunction),
}
#[derive(Debug, Clone)]
pub struct Function {
pub partial: Vec<Value>,
pub ref_: FunctionRef,
}
#[derive(Debug, Clone)]
pub enum Value {
Numeric(NumericValue),
Bool(bool),
Function(Function),
Struct(Arc<Mutex<StructValue>>),
Unit,
}
#[derive(Debug, Clone)]
pub enum NamedEntity {
TypeDeclaration(ast::TypeDeclaration),
Variable(Value),
}
#[derive(Debug, Clone)]
struct Context {
pub environment: HashMap<String, NamedEntity>,
pub impls: HashMap<String, ast::Impl>,
pub current_module: ast::Module,
}
impl Context {
fn set_variable(&mut self, name: String, value: &Value) {
self.environment.insert(name.to_string(), NamedEntity::Variable(value.clone()));
}
fn new_env(&self) -> Context {
return Context::from_module(&self.current_module);
}
fn from_module(module: &ast::Module) -> Context {
let mut ctx = Context {
environment: create_builtins(),
impls: HashMap::new(),
current_module: module.clone(),
};
for item in ctx.current_module.items.iter() {
match item {
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Struct(struct_)) => {
ctx.environment.insert(
struct_.name.name.value.to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(struct_.clone())),
);
}
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => {
ctx.environment.insert(
alias.name.name.value.to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Alias(alias.clone())),
);
}
ast::ModuleItem::Function(function) => {
ctx.environment.insert(
function.declaration.name.name.value.to_string(),
NamedEntity::Variable(Value::Function(Function {
partial: vec![],
ref_: FunctionRef::User(function.clone()),
})),
);
}
ast::ModuleItem::Impl(impl_) => {
ctx.impls.insert(impl_.struct_.name.name.value.to_string(), impl_.clone());
}
_ => {}
}
}
return ctx;
}
}
fn create_builtins() -> HashMap<String, NamedEntity> {
let mut result = HashMap::new();
result.insert(
"i8".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "i8".to_string(),
})),
);
result.insert(
"i16".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "i16".to_string(),
})),
);
result.insert(
"i32".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "i32".to_string(),
})),
);
result.insert(
"i64".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "i64".to_string(),
})),
);
result.insert(
"isize".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "isize".to_string(),
})),
);
result.insert(
"u8".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "u8".to_string(),
})),
);
result.insert(
"u16".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "u16".to_string(),
})),
);
result.insert(
"u32".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "u32".to_string(),
})),
);
result.insert(
"u64".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "u64".to_string(),
})),
);
result.insert(
"usize".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "usize".to_string(),
})),
);
result.insert(
"f32".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "f32".to_string(),
})),
);
result.insert(
"f64".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "f64".to_string(),
})),
);
result.insert(
"bool".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "bool".to_string(),
})),
);
result.insert(
"!".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "!".to_string(),
})),
);
result.insert(
"unit".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "!".to_string(),
})),
);
return result;
}
pub enum ExpressionResult {
Value(Value),
Return(Value),
}
pub struct TreeWalkInterpreter {}
impl TreeWalkInterpreter {
pub fn with_module(self: &Self, module: &ast::Module) -> Value {
let mut ctx = Context::from_module(module);
let main = match &ctx.environment["main"] {
NamedEntity::Variable(Value::Function(func)) => match &func.ref_ {
FunctionRef::User(ref_) => ref_.clone(),
_ => panic!("main should be a user defined function"),
},
_ => panic!("main should be a user defined function"),
};
return self.with_function(&mut ctx, &main);
}
fn with_function(self: &Self, ctx: &mut Context, function: &ast::Function) -> Value {
let result = self.with_block(ctx, &function.block);
return match result {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => r,
};
}
fn with_block(self: &Self, ctx: &mut Context, block: &ast::Block) -> ExpressionResult {
let mut last = ExpressionResult::Value(Value::Unit);
for statement in block.statements.iter() {
let result = self.with_statement(ctx, statement);
match result {
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
ExpressionResult::Value(r) => {
last = ExpressionResult::Value(r);
}
}
}
return last;
}
fn with_statement(self: &Self, ctx: &mut Context, statement: &ast::Statement) -> ExpressionResult {
match statement {
ast::Statement::Return(return_statement) => {
let result = match self.with_expression(ctx, &return_statement.source) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
return ExpressionResult::Return(result);
}
ast::Statement::Let(let_statement) => {
let result = match self.with_expression(ctx, &let_statement.expression) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
ctx.set_variable(let_statement.variable_name.name.value.to_string(), &result);
return ExpressionResult::Value(Value::Unit);
}
ast::Statement::Assignment(assignment_statement) => {
return self.with_assignment_statement(ctx, assignment_statement);
}
ast::Statement::Expression(expression) => {
return self.with_expression(ctx, expression);
}
}
}
fn with_assignment_statement(self: &Self, ctx: &mut Context, statement: &ast::AssignmentStatement) -> ExpressionResult {
let result = match self.with_expression(ctx, &statement.expression) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
match &statement.source {
ast::AssignmentTarget::Variable(variable) => {
ctx.set_variable(variable.name.name.value.to_string(), &result);
}
ast::AssignmentTarget::StructAttr(struct_attr) => {
let mut source = match self.with_expression(ctx, &struct_attr.source) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
match &mut source {
Value::Struct(s) => {
let mut struct_ = s.lock().unwrap();
struct_.fields.insert(struct_attr.attribute.name.value.clone(), result);
}
_ => panic!("set attr on nonstruct, should never happen due to type system"),
}
}
}
return ExpressionResult::Value(Value::Unit);
}
fn with_expression(self: &Self, ctx: &mut Context, expression: &ast::Expression) -> ExpressionResult {
match &*expression.subexpression {
ast::Subexpression::LiteralInt(literal_int) => {
let value: i64 = literal_int.value.value.parse().unwrap();
return ExpressionResult::Value(Value::Numeric(NumericValue::I64(value)));
}
ast::Subexpression::LiteralFloat(literal_float) => {
let value: f64 = literal_float.value.value.parse().unwrap();
return ExpressionResult::Value(Value::Numeric(NumericValue::F64(value)));
}
ast::Subexpression::LiteralBool(literal_bool) => {
let value: bool = if &literal_bool.value.value == "true" { true } else { false };
return ExpressionResult::Value(Value::Bool(value));
}
ast::Subexpression::LiteralStruct(literal_struct) => {
let declaration = match &ctx.environment[&literal_struct.name.name.value] {
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(declaration)) => declaration.clone(),
_ => panic!("not a struct"),
};
let mut fields = HashMap::new();
for field in declaration.fields.iter() {
for (field_name, field_expression) in literal_struct.fields.iter() {
if field.name.name.value == field_name.name.value {
let field_result = match self.with_expression(ctx, field_expression) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
fields.insert(field.name.name.value.to_string(), field_result);
}
}
}
return ExpressionResult::Value(Value::Struct(Arc::new(Mutex::new(StructValue {
source: declaration.clone(),
fields: fields,
}))));
}
ast::Subexpression::FunctionCall(function_call) => {
let source = match self.with_expression(ctx, &function_call.source) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
let mut argument_values = vec![];
for arg in function_call.arguments.iter() {
let argument_value = match self.with_expression(ctx, arg) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
argument_values.push(argument_value);
}
match &source {
Value::Function(function) => match &function.ref_ {
FunctionRef::User(user_function) => {
let mut fn_ctx = ctx.new_env();
let mut i = 0;
for partial_arg in &function.partial {
fn_ctx.set_variable(
user_function.declaration.arguments[i].name.name.value.to_string(),
&partial_arg.clone(),
);
i = i + 1;
}
for argument_value in &argument_values {
fn_ctx.set_variable(
user_function.declaration.arguments[i].name.name.value.to_string(),
&argument_value.clone(),
);
}
return ExpressionResult::Value(self.with_function(&mut fn_ctx, user_function));
}
FunctionRef::Builtin(builtin_function) => {
let all_values = function
.partial
.iter()
.map(|val| val.clone())
.chain(argument_values.into_iter())
.collect();
return ExpressionResult::Value(builtin_function(all_values));
}
},
_ => panic!("type error: function call source must be a function"),
}
}
ast::Subexpression::VariableUsage(variable_usage) => {
let variable_value = match &ctx.environment[&variable_usage.name.name.value] {
NamedEntity::Variable(v) => v.clone(),
_ => panic!("variable lookup of type"),
};
return ExpressionResult::Value(variable_value);
}
ast::Subexpression::If(if_expression) => {
let condition = match self.with_expression(ctx, &if_expression.condition) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
match &condition {
Value::Bool(cond) => {
if cond.clone() {
return self.with_block(ctx, &if_expression.block);
} else {
return match &if_expression.else_ {
Some(else_) => self.with_block(ctx, else_),
None => ExpressionResult::Value(Value::Unit),
};
}
}
_ => panic!("TypeError: condition must be bool"),
}
}
ast::Subexpression::StructGetter(struct_getter) => {
let source = match self.with_expression(ctx, &struct_getter.source) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
match &source {
Value::Struct(struct_) => {
let s = struct_.lock().unwrap();
if s.fields.contains_key(&struct_getter.attribute.name.value) {
return ExpressionResult::Value(s.fields[&struct_getter.attribute.name.value].clone());
}
for module_item in &ctx.current_module.items {
match module_item {
ast::ModuleItem::Impl(impl_) => {
if impl_.struct_.name.name.value == s.source.name.name.value {
for method in &impl_.functions {
if method.declaration.name.name.value == struct_getter.attribute.name.value {
// if first type matches, partial apply self
if method.declaration.arguments.len() > 0 {
match &method.declaration.arguments[0].type_ {
ast::TypeUsage::Named(arg_named) => {
if arg_named.name.name.value == s.source.name.name.value {
return ExpressionResult::Value(Value::Function(Function {
partial: vec![source.clone()],
ref_: FunctionRef::User(method.clone()),
}));
}
}
_ => {}
}
}
return ExpressionResult::Value(Value::Function(Function {
partial: vec![],
ref_: FunctionRef::User(method.clone()),
}));
}
}
}
}
_ => {}
}
}
panic!("TypeError: Method not found");
}
_ => {
panic!("TypeError: struct getter used with non-struct");
}
}
}
ast::Subexpression::Block(block) => {
return self.with_block(ctx, block);
}
ast::Subexpression::Op(op) => {
let left = match self.with_expression(ctx, &op.left) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
let right = match self.with_expression(ctx, &op.right) {
ExpressionResult::Value(r) => r,
ExpressionResult::Return(r) => {
return ExpressionResult::Return(r);
}
};
let result = match (&left, &op.op, &right) {
//I
(Value::Numeric(NumericValue::I8(l)), ast::Operator::Plus, Value::Numeric(NumericValue::I8(r))) => {
Value::Numeric(NumericValue::I8(l + r))
}
(Value::Numeric(NumericValue::I8(l)), ast::Operator::Minus, Value::Numeric(NumericValue::I8(r))) => {
Value::Numeric(NumericValue::I8(l - r))
}
(Value::Numeric(NumericValue::I8(l)), ast::Operator::Mul, Value::Numeric(NumericValue::I8(r))) => {
Value::Numeric(NumericValue::I8(l * r))
}
(Value::Numeric(NumericValue::I8(l)), ast::Operator::Div, Value::Numeric(NumericValue::I8(r))) => {
Value::Numeric(NumericValue::I8(l / r))
}
(Value::Numeric(NumericValue::I16(l)), ast::Operator::Plus, Value::Numeric(NumericValue::I16(r))) => {
Value::Numeric(NumericValue::I16(l + r))
}
(Value::Numeric(NumericValue::I16(l)), ast::Operator::Minus, Value::Numeric(NumericValue::I16(r))) => {
Value::Numeric(NumericValue::I16(l - r))
}
(Value::Numeric(NumericValue::I16(l)), ast::Operator::Mul, Value::Numeric(NumericValue::I16(r))) => {
Value::Numeric(NumericValue::I16(l * r))
}
(Value::Numeric(NumericValue::I16(l)), ast::Operator::Div, Value::Numeric(NumericValue::I16(r))) => {
Value::Numeric(NumericValue::I16(l / r))
}
(Value::Numeric(NumericValue::I32(l)), ast::Operator::Plus, Value::Numeric(NumericValue::I32(r))) => {
Value::Numeric(NumericValue::I32(l + r))
}
(Value::Numeric(NumericValue::I32(l)), ast::Operator::Minus, Value::Numeric(NumericValue::I32(r))) => {
Value::Numeric(NumericValue::I32(l - r))
}
(Value::Numeric(NumericValue::I32(l)), ast::Operator::Mul, Value::Numeric(NumericValue::I32(r))) => {
Value::Numeric(NumericValue::I32(l * r))
}
(Value::Numeric(NumericValue::I32(l)), ast::Operator::Div, Value::Numeric(NumericValue::I32(r))) => {
Value::Numeric(NumericValue::I32(l / r))
}
(Value::Numeric(NumericValue::I64(l)), ast::Operator::Plus, Value::Numeric(NumericValue::I64(r))) => {
Value::Numeric(NumericValue::I64(l + r))
}
(Value::Numeric(NumericValue::I64(l)), ast::Operator::Minus, Value::Numeric(NumericValue::I64(r))) => {
Value::Numeric(NumericValue::I64(l - r))
}
(Value::Numeric(NumericValue::I64(l)), ast::Operator::Mul, Value::Numeric(NumericValue::I64(r))) => {
Value::Numeric(NumericValue::I64(l * r))
}
(Value::Numeric(NumericValue::I64(l)), ast::Operator::Div, Value::Numeric(NumericValue::I64(r))) => {
Value::Numeric(NumericValue::I64(l / r))
}
//U
(Value::Numeric(NumericValue::U8(l)), ast::Operator::Plus, Value::Numeric(NumericValue::U8(r))) => {
Value::Numeric(NumericValue::U8(l + r))
}
(Value::Numeric(NumericValue::U8(l)), ast::Operator::Minus, Value::Numeric(NumericValue::U8(r))) => {
Value::Numeric(NumericValue::U8(l - r))
}
(Value::Numeric(NumericValue::U8(l)), ast::Operator::Mul, Value::Numeric(NumericValue::U8(r))) => {
Value::Numeric(NumericValue::U8(l * r))
}
(Value::Numeric(NumericValue::U8(l)), ast::Operator::Div, Value::Numeric(NumericValue::U8(r))) => {
Value::Numeric(NumericValue::U8(l / r))
}
(Value::Numeric(NumericValue::U16(l)), ast::Operator::Plus, Value::Numeric(NumericValue::U16(r))) => {
Value::Numeric(NumericValue::U16(l + r))
}
(Value::Numeric(NumericValue::U16(l)), ast::Operator::Minus, Value::Numeric(NumericValue::U16(r))) => {
Value::Numeric(NumericValue::U16(l - r))
}
(Value::Numeric(NumericValue::U16(l)), ast::Operator::Mul, Value::Numeric(NumericValue::U16(r))) => {
Value::Numeric(NumericValue::U16(l * r))
}
(Value::Numeric(NumericValue::U16(l)), ast::Operator::Div, Value::Numeric(NumericValue::U16(r))) => {
Value::Numeric(NumericValue::U16(l / r))
}
(Value::Numeric(NumericValue::U32(l)), ast::Operator::Plus, Value::Numeric(NumericValue::U32(r))) => {
Value::Numeric(NumericValue::U32(l + r))
}
(Value::Numeric(NumericValue::U32(l)), ast::Operator::Minus, Value::Numeric(NumericValue::U32(r))) => {
Value::Numeric(NumericValue::U32(l - r))
}
(Value::Numeric(NumericValue::U32(l)), ast::Operator::Mul, Value::Numeric(NumericValue::U32(r))) => {
Value::Numeric(NumericValue::U32(l * r))
}
(Value::Numeric(NumericValue::U32(l)), ast::Operator::Div, Value::Numeric(NumericValue::U32(r))) => {
Value::Numeric(NumericValue::U32(l / r))
}
(Value::Numeric(NumericValue::U64(l)), ast::Operator::Plus, Value::Numeric(NumericValue::U64(r))) => {
Value::Numeric(NumericValue::U64(l + r))
}
(Value::Numeric(NumericValue::U64(l)), ast::Operator::Minus, Value::Numeric(NumericValue::U64(r))) => {
Value::Numeric(NumericValue::U64(l - r))
}
(Value::Numeric(NumericValue::U64(l)), ast::Operator::Mul, Value::Numeric(NumericValue::U64(r))) => {
Value::Numeric(NumericValue::U64(l * r))
}
(Value::Numeric(NumericValue::U64(l)), ast::Operator::Div, Value::Numeric(NumericValue::U64(r))) => {
Value::Numeric(NumericValue::U64(l / r))
}
//F
(Value::Numeric(NumericValue::F32(l)), ast::Operator::Plus, Value::Numeric(NumericValue::F32(r))) => {
Value::Numeric(NumericValue::F32(l + r))
}
(Value::Numeric(NumericValue::F32(l)), ast::Operator::Minus, Value::Numeric(NumericValue::F32(r))) => {
Value::Numeric(NumericValue::F32(l - r))
}
(Value::Numeric(NumericValue::F32(l)), ast::Operator::Mul, Value::Numeric(NumericValue::F32(r))) => {
Value::Numeric(NumericValue::F32(l * r))
}
(Value::Numeric(NumericValue::F32(l)), ast::Operator::Div, Value::Numeric(NumericValue::F32(r))) => {
Value::Numeric(NumericValue::F32(l / r))
}
(Value::Numeric(NumericValue::F64(l)), ast::Operator::Plus, Value::Numeric(NumericValue::F64(r))) => {
Value::Numeric(NumericValue::F64(l + r))
}
(Value::Numeric(NumericValue::F64(l)), ast::Operator::Minus, Value::Numeric(NumericValue::F64(r))) => {
Value::Numeric(NumericValue::F64(l - r))
}
(Value::Numeric(NumericValue::F64(l)), ast::Operator::Mul, Value::Numeric(NumericValue::F64(r))) => {
Value::Numeric(NumericValue::F64(l * r))
}
(Value::Numeric(NumericValue::F64(l)), ast::Operator::Div, Value::Numeric(NumericValue::F64(r))) => {
Value::Numeric(NumericValue::F64(l / r))
}
//fail
_ => panic!(""),
};
return ExpressionResult::Value(result);
}
}
}
}

View File

@@ -1,131 +0,0 @@
// mod types;
mod ast;
mod errors;
mod interpreter;
mod trait_checking;
mod type_alias_resolution;
mod type_checking;
#[macro_use]
extern crate lalrpop_util;
lalrpop_mod!(pub grammar); // synthesized by LALRPOP
use std::fs;
extern crate clap;
use clap::{App, Arg};
fn main() {
let matches = App::new("Boring Language Compiler")
.version("0.0.1")
.author("Andrew Segavac")
.about("Compiles boring language files to LLVM IR.")
.arg(
Arg::with_name("OUTPUT")
.short("o")
.long("out")
.value_name("OUTOUT")
.help("Sets an output file")
.takes_value(true),
)
.arg(Arg::with_name("INPUT").help("Sets the input file").required(true).index(1))
.arg(Arg::with_name("v").short("v").multiple(true).help("Sets the level of verbosity"))
.get_matches();
let input = matches.value_of("INPUT").unwrap();
let default_output = input.rsplitn(2, ".").collect::<Vec<&str>>().last().unwrap().clone();
let _output = matches.value_of("OUTPUT").unwrap_or(default_output);
let contents = fs::read_to_string(input).expect("input file not found");
let unknown_id_gen = ast::IdGenerator::new("S");
let module_ast = grammar::ModuleParser::new().parse(&unknown_id_gen, &contents).unwrap(); //TODO: convert to error
// println!("ast: {:#?}", &module_ast);
let alias_resolver = type_alias_resolution::TypeAliasResolver {};
let resolved_ast = match alias_resolver.with_module(&module_ast) {
Ok(r) => r,
Err(err) => {
println!("type checking error: {:#?}", &err);
panic!("bad alias");
}
};
// println!("resolved ast: {:#?}", &resolved_ast);
let trait_checker = trait_checking::TraitChecker {};
match trait_checker.with_module(&resolved_ast) {
Ok(_) => {}
Err(err) => {
println!("type checking error: {:#?}", &err);
return;
}
}
let type_checker = type_checking::TypeChecker {};
let type_checking_result = type_checker.with_module(&resolved_ast);
match &type_checking_result {
Ok((checked_ast, subst)) => {
println!("checked ast: {:#?}", &checked_ast);
println!("substitutions: {:#?}", &subst);
let interpreter = interpreter::TreeWalkInterpreter {};
let result = interpreter.with_module(&checked_ast);
println!("final result: {:#?}", &result);
}
Err(err) => {
println!("type checking error: {:#?}", &err);
}
}
// let context = Context::create();
// let mut code_gen = compiler::ModuleCodeGen::new(&context, "main".to_string());
// code_gen.gen_module(module_ast);
//
// let mut f = fs::File::create(output).expect("Unable to create out file");
// f.write_all(code_gen.dump().as_bytes()).expect("Unable to write data");
}
#[test]
fn grammar() {
let id_gen = ast::IdGenerator::new("S");
assert!(grammar::LiteralIntParser::new().parse(&id_gen, "22").is_ok());
assert!(grammar::IdentifierParser::new().parse(&id_gen, "foo").is_ok());
assert!(grammar::LiteralIntParser::new().parse(&id_gen, "2a").is_err());
assert!(grammar::TermParser::new().parse(&id_gen, "22").is_ok());
assert!(grammar::TermParser::new().parse(&id_gen, "foo").is_ok());
assert!(grammar::ExpressionParser::new().parse(&id_gen, "22 * foo").is_ok());
assert!(grammar::ExpressionParser::new().parse(&id_gen, "22 * 33").is_ok());
assert!(grammar::ExpressionParser::new().parse(&id_gen, "(22 * 33) + 24").is_ok());
assert!(grammar::BlockParser::new().parse(&id_gen, "{ (22 * 33) + 24 }").is_ok());
assert!(grammar::BlockParser::new().parse(&id_gen, "{ (22 * 33) + 24; 25 }").is_ok());
// assert!(grammar::BlockParser::new().parse("{ (22 * 33) + 24\n 24 }").is_ok());
assert!(grammar::BlockParser::new().parse(&id_gen, "{ }").is_ok());
assert!(grammar::VariableDeclarationParser::new().parse(&id_gen, "foo: i32").is_ok());
assert!(grammar::VariableDeclarationParser::new().parse(&id_gen, "foo").is_err());
assert!(grammar::VariableDeclarationParser::new().parse(&id_gen, "1234").is_err());
assert!(grammar::FunctionParser::new()
.parse(&id_gen, "fn add(a: i32, b: i32): i32 { a + b }")
.is_ok());
assert!(grammar::FunctionParser::new()
.parse(&id_gen, "fn random_dice_roll(): i32 { 4 }")
.is_ok());
assert!(grammar::FunctionParser::new()
.parse(&id_gen, "fn add(a: i32, b: i32): i32 { a + }")
.is_err());
assert!(grammar::FunctionParser::new()
.parse(&id_gen, "fn add(a: i32, b: i32): i32")
.is_err());
assert!(grammar::FunctionCallParser::new().parse(&id_gen, "foo(1, 2)").is_ok());
assert!(grammar::ModuleParser::new()
.parse(&id_gen, "fn add(a: i32, b: i32): i32 { a + b }")
.is_ok());
assert!(grammar::ModuleParser::new()
.parse(
&id_gen,
"fn add(a: i32, b: i32): i32 { a + b } fn subtract(a: i32, b: i32): i32 { a - b }"
)
.is_ok());
}

View File

@@ -1,171 +0,0 @@
use crate::ast;
use crate::errors;
use std::collections::HashMap;
pub type Result<T, E = errors::TypingError> = std::result::Result<T, E>;
#[derive(Debug, Clone, PartialEq, Eq)]
struct Context {
pub environment_traits: HashMap<String, ast::TraitTypeDeclaration>,
}
fn create_builtins() -> HashMap<String, ast::TraitTypeDeclaration> {
let result = HashMap::<String, ast::TraitTypeDeclaration>::new();
return result;
}
fn compare_struct_trait(
struct_: &ast::TypeUsage,
trait_: &ast::TypeUsage,
struct_name: &ast::Identifier,
trait_name: &ast::Identifier,
) -> Result<()> {
match struct_ {
ast::TypeUsage::Named(named) => match trait_ {
ast::TypeUsage::Named(trait_named) => {
if named.name.name.value == trait_named.name.name.value
|| (named.name.name.value == struct_name.name.value && trait_named.name.name.value == trait_name.name.value)
{
return Ok(());
}
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
ast::TypeUsage::Function(_) => {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
_ => panic!("Unknown in function definition"),
},
ast::TypeUsage::Function(function) => match trait_ {
ast::TypeUsage::Named(_) => {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
ast::TypeUsage::Function(trait_function) => {
if function.arguments.len() != trait_function.arguments.len() {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
for (i, _) in function.arguments.iter().enumerate() {
compare_struct_trait(&function.arguments[i], &trait_function.arguments[i], struct_name, trait_name)?;
}
compare_struct_trait(&function.return_type, &trait_function.return_type, struct_name, trait_name)?;
return Ok(());
}
_ => panic!("Unknown in function definition"),
},
_ => panic!("Unknown in function definition"),
}
}
pub struct TraitChecker {}
impl TraitChecker {
pub fn with_module(self: &Self, module: &ast::Module) -> Result<()> {
let mut ctx = Context {
environment_traits: create_builtins(),
};
for item in module.items.iter() {
match item {
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Trait(trait_)) => {
ctx.environment_traits.insert(trait_.name.name.value.to_string(), trait_.clone());
}
_ => {}
}
}
for item in module.items.iter() {
match item {
ast::ModuleItem::Impl(impl_) => {
self.with_impl(&ctx, impl_)?;
}
_ => {}
}
}
return Ok(());
}
fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> Result<()> {
// See if trait actually matches
match &impl_.trait_ {
Some(trait_) => {
// assert trait functions satisfied
if !ctx.environment_traits.contains_key(&trait_.name.name.value) {
return Err(errors::TypingError::TypeDoesNotExist {
identifier: trait_.name.clone(),
});
}
let trait_declaration = &ctx.environment_traits[&trait_.name.name.value];
for trait_item in trait_declaration.functions.iter() {
match trait_item {
ast::TraitItem::FunctionDeclaration(declaration) => {
let mut found = false;
for impl_function in impl_.functions.iter() {
if impl_function.declaration.name.name.value == declaration.name.name.value {
found = true;
compare_struct_trait(
&impl_function.declaration.to_type(),
&declaration.to_type(),
&impl_.struct_.name,
&trait_.name,
)?;
}
}
if found == false {
return Err(errors::TypingError::MissingTraitFunction {
struct_name: impl_.struct_.name.clone(),
function_name: declaration.name.clone(),
});
}
}
ast::TraitItem::Function(function) => {
// skip found check because it has a default
for impl_function in impl_.functions.iter() {
if impl_function.declaration.name.name.value == function.declaration.name.name.value {
compare_struct_trait(
&impl_function.declaration.to_type(),
&function.declaration.to_type(),
&impl_.struct_.name,
&trait_.name,
)?;
}
}
}
}
}
// assert all functions are in trait
for impl_function in impl_.functions.iter() {
let mut found = false;
for trait_item in trait_declaration.functions.iter() {
let declaration = match trait_item {
ast::TraitItem::Function(function) => &function.declaration,
ast::TraitItem::FunctionDeclaration(declaration) => &declaration,
};
if impl_function.declaration.name.name.value == declaration.name.name.value {
found = true;
break;
}
}
if found == false {
return Err(errors::TypingError::FunctionNotInTrait {
function_name: impl_function.declaration.name.clone(),
});
}
}
}
None => {}
}
// TODO: check for duplicate functions
return Ok(());
}
}

View File

@@ -1,375 +0,0 @@
use crate::ast;
use crate::errors;
pub type Result<T, E = errors::TypingError> = std::result::Result<T, E>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Context {
pub type_aliases: Vec<ast::AliasTypeDeclaration>,
}
fn resolve_type(ctx: &Context, type_: &ast::NamedTypeUsage) -> Result<ast::TypeUsage> {
let mut changed = true;
let mut result = ast::TypeUsage::Named(type_.clone());
while changed {
changed = false;
let current = &result.clone();
match current {
ast::TypeUsage::Named(named) => {
for alias in ctx.type_aliases.iter() {
if named.name.name.value == alias.name.name.value { // is alias, replace
changed = true;
result = alias.replaces.clone();
}
}
}
_ => break,
}
}
match &result {
ast::TypeUsage::Named(named) => {
match &named.type_parameters {
ast::GenericUsage::Known(known) => {
let mut result_params = vec!();
for param in known.parameters.iter() {
result_params.push(process_type(ctx, param)?);
}
let mut new_named = named.clone();
new_named.type_parameters = ast::GenericUsage::new(&result_params);
result = ast::TypeUsage::Named(new_named);
},
_ => {}
}
},
ast::TypeUsage::Function(func) => {
match &type_.type_parameters {
ast::GenericUsage::Known(known) => {
if known.parameters.len() > 0 {
return Err(errors::TypingError::InvalidTypeParameterOnAlias{alias: type_.name.clone()});
}
},
_ => {} //skip
}
},
_ => {
panic!("alias of a non-type, not possible");
}
}
return Ok(result);
}
fn process_type(ctx: &Context, type_: &ast::TypeUsage) -> Result<ast::TypeUsage> {
match type_ {
ast::TypeUsage::Named(named) => {
return Ok(resolve_type(ctx, named)?);
}
ast::TypeUsage::Function(function) => {
let mut arguments = vec!();
for a in function.arguments.iter() {
arguments.push(process_type(ctx, &a.clone())?);
}
return Ok(ast::TypeUsage::Function(ast::FunctionTypeUsage {
arguments: arguments,
return_type: Box::new(process_type(ctx, &function.return_type.clone())?),
}));
}
ast::TypeUsage::Unknown(unknown) => {
return Ok(ast::TypeUsage::Unknown(unknown.clone()));
},
ast::TypeUsage::Namespace(namespace) => {
match namespace {
ast::NamespaceTypeUsage::Type(named_type)=> {
let result = resolve_type(ctx, named_type)?;
match result {
ast::TypeUsage::Named(named) => {
return Ok(ast::TypeUsage::Namespace(ast::NamespaceTypeUsage::Type(named)));
},
_ => {
return Err(errors::TypingError::InvalidUseofAlias);
}
}
}
}
}
}
}
pub struct TypeAliasResolver {}
impl TypeAliasResolver {
pub fn with_module(self: &Self, module: &ast::Module) -> Result<ast::Module> {
let mut ctx = Context { type_aliases: vec![] };
for item in module.items.iter() {
match item {
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => {
ctx.type_aliases.push(alias.clone());
}
_ => {}
}
}
let mut items = vec!();
for item in module.items.iter() {
items.push(match item {
ast::ModuleItem::Function(function) => ast::ModuleItem::Function(self.with_function(&ctx, function)?),
ast::ModuleItem::TypeDeclaration(type_declaration) => {
ast::ModuleItem::TypeDeclaration(self.with_type_declaration(&ctx, type_declaration)?)
}
ast::ModuleItem::Impl(impl_) => ast::ModuleItem::Impl(self.with_impl(&ctx, impl_)?),
});
}
return Ok(ast::Module {
items: items,
});
}
fn with_function(self: &Self, ctx: &Context, function: &ast::Function) -> Result<ast::Function> {
return Ok(ast::Function {
declaration: self.with_function_declaration(ctx, &function.declaration)?,
block: self.with_block(ctx, &function.block)?,
});
}
fn with_function_declaration(self: &Self, ctx: &Context, declaration: &ast::FunctionDeclaration) -> Result<ast::FunctionDeclaration> {
let mut arguments = vec!();
for arg in declaration.arguments.iter() {
arguments.push(ast::VariableDeclaration {
name: arg.name.clone(),
type_: process_type(ctx, &arg.type_)?,
});
}
return Ok(ast::FunctionDeclaration {
name: declaration.name.clone(),
generic: declaration.generic.clone(),
arguments: arguments,
return_type: process_type(ctx, &declaration.return_type)?,
});
}
fn with_type_declaration(self: &Self, ctx: &Context, type_declaration: &ast::TypeDeclaration) -> Result<ast::TypeDeclaration> {
match type_declaration {
ast::TypeDeclaration::Struct(struct_) => {
return Ok(ast::TypeDeclaration::Struct(self.with_struct_declaration(ctx, struct_)?));
}
ast::TypeDeclaration::Primitive(primitive) => {
return Ok(ast::TypeDeclaration::Primitive(primitive.clone()));
}
ast::TypeDeclaration::Alias(alias) => {
return Ok(ast::TypeDeclaration::Alias(alias.clone()));
}
ast::TypeDeclaration::Trait(trait_) => {
return Ok(ast::TypeDeclaration::Trait(self.with_trait(ctx, trait_)?));
}
}
}
fn with_struct_declaration(self: &Self, ctx: &Context, struct_: &ast::StructTypeDeclaration) -> Result<ast::StructTypeDeclaration> {
let mut fields = vec!();
for field in struct_.fields.iter() {
fields.push(ast::StructField {
name: field.name.clone(),
type_: process_type(ctx, &field.type_)?,
});
}
return Ok(ast::StructTypeDeclaration {
generic: struct_.generic.clone(),
name: struct_.name.clone(),
fields: fields,
});
}
fn with_trait(self: &Self, ctx: &Context, trait_: &ast::TraitTypeDeclaration) -> Result<ast::TraitTypeDeclaration> {
let mut trait_ctx = ctx.clone();
trait_ctx.type_aliases.push(ast::AliasTypeDeclaration {
name: ast::Identifier {
name: ast::Spanned {
span: ast::Span { left: 0, right: 0 }, //todo: figure out a sane value for these
value: "Self".to_string(),
},
},
replaces: ast::TypeUsage::Named(ast::NamedTypeUsage {
type_parameters: ast::GenericUsage::Unknown,
name: trait_.name.clone(),
}),
});
let mut functions = vec!();
for f in trait_.functions.iter() {
functions.push(match f {
ast::TraitItem::Function(function) => ast::TraitItem::Function(self.with_function(&trait_ctx, function)?),
ast::TraitItem::FunctionDeclaration(function_declaration) => {
ast::TraitItem::FunctionDeclaration(self.with_function_declaration(&trait_ctx, function_declaration)?)
}
});
}
return Ok(ast::TraitTypeDeclaration {
generic: trait_.generic.clone(),
name: trait_.name.clone(),
functions: functions,
});
}
fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> Result<ast::Impl> {
let mut impl_ctx = ctx.clone();
impl_ctx.type_aliases.push(ast::AliasTypeDeclaration {
name: ast::Identifier {
name: ast::Spanned {
span: ast::Span { left: 0, right: 0 }, //todo: figure out a sane value for these
value: "Self".to_string(),
},
},
replaces: ast::TypeUsage::Named(impl_.struct_.clone()),
});
let mut functions = vec!();
for f in impl_.functions.iter() {
functions.push(self.with_function(&impl_ctx, f)?);
}
return Ok(ast::Impl {
generic: impl_.generic.clone(),
trait_: impl_.trait_.clone(),
struct_: impl_.struct_.clone(),
functions: functions,
});
}
fn with_block(self: &Self, ctx: &Context, block: &ast::Block) -> Result<ast::Block> {
let mut statements = vec!();
for s in block.statements.iter() {
statements.push(self.with_statement(ctx, s)?);
}
return Ok(ast::Block {
statements: statements,
type_: process_type(ctx, &block.type_)?,
});
}
fn with_statement(self: &Self, ctx: &Context, statement: &ast::Statement) -> Result<ast::Statement> {
match statement {
ast::Statement::Return(return_statement) => {
return Ok(ast::Statement::Return(self.with_return_statement(ctx, return_statement)?));
}
ast::Statement::Let(let_statement) => {
return Ok(ast::Statement::Let(self.with_let_statement(ctx, let_statement)?));
}
ast::Statement::Assignment(assignment_statement) => {
return Ok(ast::Statement::Assignment(self.with_assignment_statement(ctx, assignment_statement)?));
}
ast::Statement::Expression(expression) => {
return Ok(ast::Statement::Expression(self.with_expression(ctx, expression)?));
}
}
}
fn with_return_statement(self: &Self, ctx: &Context, statement: &ast::ReturnStatement) -> Result<ast::ReturnStatement> {
return Ok(ast::ReturnStatement {
source: self.with_expression(ctx, &statement.source)?,
});
}
fn with_let_statement(self: &Self, ctx: &Context, statement: &ast::LetStatement) -> Result<ast::LetStatement> {
return Ok(ast::LetStatement {
variable_name: statement.variable_name.clone(),
expression: self.with_expression(ctx, &statement.expression)?,
type_: process_type(ctx, &statement.type_)?,
});
}
fn with_assignment_statement(self: &Self, ctx: &Context, statement: &ast::AssignmentStatement) -> Result<ast::AssignmentStatement> {
return Ok(ast::AssignmentStatement {
source: match &statement.source {
ast::AssignmentTarget::Variable(variable) => ast::AssignmentTarget::Variable(ast::VariableUsage {
type_parameters: variable.type_parameters.clone(),
name: variable.name.clone(),
type_: process_type(ctx, &variable.type_)?,
}),
ast::AssignmentTarget::StructAttr(struct_attr) => ast::AssignmentTarget::StructAttr(ast::StructGetter {
type_parameters: struct_attr.type_parameters.clone(),
source: self.with_expression(ctx, &struct_attr.source)?,
attribute: struct_attr.attribute.clone(),
type_: process_type(ctx, &struct_attr.type_)?,
}),
},
expression: self.with_expression(ctx, &statement.expression)?,
});
}
fn with_expression(self: &Self, ctx: &Context, expression: &ast::Expression) -> Result<ast::Expression> {
return Ok(ast::Expression {
subexpression: Box::new(match &*expression.subexpression {
ast::Subexpression::LiteralInt(literal_int) => ast::Subexpression::LiteralInt(ast::LiteralInt {
value: literal_int.value.clone(),
type_: process_type(ctx, &literal_int.type_)?,
}),
ast::Subexpression::LiteralFloat(literal_float) => ast::Subexpression::LiteralFloat(ast::LiteralFloat {
value: literal_float.value.clone(),
type_: process_type(ctx, &literal_float.type_)?,
}),
ast::Subexpression::LiteralBool(literal_bool) => ast::Subexpression::LiteralBool(ast::LiteralBool {
value: literal_bool.value.clone(),
type_: process_type(ctx, &literal_bool.type_)?,
}),
ast::Subexpression::LiteralStruct(literal_struct) => {
let result = resolve_type(
ctx,
&ast::NamedTypeUsage {
type_parameters: literal_struct.type_parameters.clone(),
name: literal_struct.name.clone(),
},
)?;
let new_name = match &result {
ast::TypeUsage::Named(named) => named.name.clone(),
_ => panic!("LiteralStruct resolved to non-named-type"),
};
let mut fields = vec!();
for field in literal_struct.fields.iter() {
fields.push((field.0.clone(), self.with_expression(ctx, &field.1)?));
}
ast::Subexpression::LiteralStruct(ast::LiteralStruct {
type_parameters: literal_struct.type_parameters.clone(),
name: new_name.clone(),
fields: fields,
type_: process_type(ctx, &literal_struct.type_)?,
})
}
ast::Subexpression::FunctionCall(function_call) => {
let mut arguments = vec!();
for arg in function_call.arguments.iter() {
arguments.push(self.with_expression(ctx, arg)?);
}
ast::Subexpression::FunctionCall(ast::FunctionCall {
source: self.with_expression(ctx, &function_call.source)?,
arguments: arguments,
type_: process_type(ctx, &function_call.type_)?,
})
},
ast::Subexpression::VariableUsage(variable_usage) => ast::Subexpression::VariableUsage(ast::VariableUsage {
name: variable_usage.name.clone(),
type_parameters: variable_usage.type_parameters.clone(),
type_: process_type(ctx, &variable_usage.type_)?,
}),
ast::Subexpression::If(if_expression) => ast::Subexpression::If(ast::IfExpression {
condition: self.with_expression(ctx, &if_expression.condition)?,
block: self.with_block(ctx, &if_expression.block)?,
else_: match &if_expression.else_ {
Some(else_) => Some(self.with_block(ctx, else_)?),
None => None,
},
type_: process_type(ctx, &if_expression.type_)?,
}),
ast::Subexpression::StructGetter(struct_getter) => ast::Subexpression::StructGetter(ast::StructGetter {
type_parameters: struct_getter.type_parameters.clone(),
source: self.with_expression(ctx, &struct_getter.source)?,
attribute: struct_getter.attribute.clone(),
type_: process_type(ctx, &struct_getter.type_)?,
}),
ast::Subexpression::Block(block) => ast::Subexpression::Block(self.with_block(ctx, &block)?),
ast::Subexpression::Op(op) => ast::Subexpression::Op(ast::Operation {
left: self.with_expression(ctx, &op.left)?,
op: op.op.clone(),
right: self.with_expression(ctx, &op.right)?,
}),
}),
type_: process_type(ctx, &expression.type_)?,
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,58 +0,0 @@
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Signedness {
Signed,
Unsigned,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum IntBitness {
X8,
X16,
X32,
X64,
X128,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum FloatBitness {
X32,
X64,
X128,
}
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct IntTypeDef {
pub signedness: Signedness,
pub bitness: IntBitness,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FloatTypeDef {
pub bitness: FloatBitness,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FunctionTypeDef {
pub arguments: Vec<Type>,
pub return_type: Box<Type>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Type {
Bool,
Int(IntTypeDef),
Float(FloatTypeDef),
Function(FunctionTypeDef),
// String(StringTypeDef),
// Struct(StructTypeDef),
// Trait(TraitTypeDef),
// Void,
// Never,
}
/// Used for places where type info may or may not be solved.
#[derive(Clone, Eq, PartialEq, Hash)]
pub enum SpecifiedType {
Unknown,
Type(Type),
}

19
tsconfig.json Normal file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": ".",
"types": ["bun-types"]
},
"include": ["./**/*"],
"exclude": ["node_modules", "dist", "test/**/*"]
}