Compare commits

..

21 Commits

Author SHA1 Message Date
7cad512010 fix parsing 2025-10-09 17:40:54 -06:00
4e981a69a8 rename strings 2025-08-31 23:07:37 -06:00
126524a9e9 added interpreter 2025-08-31 23:06:26 -06:00
dd4f5b9ee6 added type resolver 2025-08-30 22:11:19 -06:00
66c7864df0 removed return/arg type comparisons 2025-08-29 22:36:17 -06:00
b2709ffc82 got type system working 2025-08-25 21:51:50 -06:00
0a315c5615 added withFunction 2025-08-20 22:47:26 -06:00
68e51cf8aa started on type checker 2025-08-20 21:42:42 -06:00
982603aa54 fix type system not resolving func args 2025-08-19 23:00:32 -06:00
05856f5d07 add check step to all type comparisons 2025-08-19 22:44:05 -06:00
df1083df3b added type system 2025-08-19 22:40:45 -06:00
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
46 changed files with 3417 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();
}
}

27
examples/structs.bl Normal file
View File

@@ -0,0 +1,27 @@
fn main(): i64 {
let user = User{id: 4};
let result = user.instance_method() + User::instance_method(user);
return result;
}
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.id;
}
fn default_impl(self: Self): i64 {
return self.instance_method();
}
}

View File

@@ -0,0 +1,38 @@
import net.http as http;
import logging as logging;
import json as json;
type ExampleResponse struct {
pub id: i32;
pub name: str;
pub email: str;
}
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)?));
}
}
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;
}

View File

@@ -0,0 +1,33 @@
import uuid as uuid;
import orm as orm;
#[derive(DeepCopy, Debug, PartialEq, DeriveEntityModel)]
#[orm::model(table_name = "user")]
pub type User struct {
#[orm::model(primary_key)]
pub id: uuid::UUID;
#[orm::model(unique, column_type = "VARCHAR(256)")]
pub email: String;
#[orm::model(nullable)]
pub password_hash: Option[String];
}
#[derive(DeepCopy, Debug, PartialEq, DeriveEntityModel)]
#[orm::model(table_name = "todo")]
pub type Todo struct {
#[orm::model(primary_key)]
pub id: uuid::UUID;
#[orm::model(foreign_key = "user", on_delete="CASCADE")]
pub user_id: uuid::UUID;
#[orm::model(column_type = "VARCHAR(256)")]
pub title: String;
#[orm::model(column_type = "TEXT")]
pub description: String;
pub created_at: DateTime;
pub updated_at: DateTime;
}

View File

@@ -0,0 +1,45 @@
import logging as logging;
import orm as orm;
import uuid as uuid;
import webapp.models as models;
#[derive(DeepCopy, Debug, PartialEq)]
type TodoGetParams struct {
#[validate(format="short")]
pub id: uuid.UUID;
}
#[derive(DeepCopy, Debug, PartialEq, Serialize)]
type TodoResponse struct {
#[validate(format="short")]
pub id: uuid.UUID;
#[validate(format="short")]
pub user_id: uuid.UUID;
#[validate(min_length=1, max_length=256)]
pub title: String;
#[validate(min_length=1, max_length=1000000)]
pub description: String;
pub created_at: DateTime;
pub updated_at: DateTime;
}
impl Into<TodoResponse> for models.Todo {
fn into(self: Self): TodoResponse {
return TodoResponse{..self};
}
}
type Router struct {
logger: logging::Logger;
db: orm::DB;
pub fn new(logger: logging::Logger, db: orm::DB): Router {
return Self{logger: logger, db: db};
}
pub fn get_todo(self: Self, req: http.Request[TodoGetParams, http::NoQuery, http::NoBody]): IO[Result[http::JsonResponse[TodoResponse], http::Error]] {
let id = req.params.id;
let instance = models::Todo::select().filter(models::Todo::Id::equals(id)).first(self.db)??;
return http::JsonResponse<TodoResponse>::ok(instance.into());
}
}

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,57 @@
import { defineCommand } from "@bunli/core";
import { boringGrammar } from "../parse/grammar";
import { semantics } from "../parse/semantics";
import TraitChecker from "../types/trait_checker";
import { TypeAliasResolver } from "../types/type_alias_resolution";
import { TypeSystem } from "../types/type_system";
import { TypeChecker } from "../types/type_checker";
import { TypeResolver } from "../types/type_resolver";
import { TreeWalkInterpreter } from "../interpreter";
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();
// console.log(JSON.stringify(ast, null, 2));
new TraitChecker().withModule(ast);
const aliasResolvedAst = new TypeAliasResolver().withModule(ast);
const typeSystem = new TypeSystem();
const typeChecker = new TypeChecker();
const typeResolver = new TypeResolver();
typeChecker.withModule(aliasResolvedAst, typeSystem);
try {
typeSystem.solve();
} catch (e) {
console.log(e);
console.log(JSON.stringify(typeSystem.result, null, 2));
return;
}
const typeResolvedAst = typeResolver.withModule(aliasResolvedAst, typeSystem);
// console.log(JSON.stringify(typeResolvedAst, null, 2));
const interpreter = new TreeWalkInterpreter();
const result = interpreter.withModule(typeResolvedAst);
console.log(JSON.stringify(result, null, 2));
// console.log(JSON.stringify(aliasResolvedAst, 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,92 @@
import { Module } from "../parse/ast";
import { Context, FunctionRef } from "./context";
export function contextFromModule(module: Module): Context {
const ctx: Context = {
environment: {},
currentModule: module,
};
ctx.environment["i8"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["i16"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["i32"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["i64"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["f8"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["f16"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["f32"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["f64"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["String"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["Void"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["Never"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
// add functions, structs, and traits to the context
for (const item of module.items) {
if (item.moduleItem === "StructTypeDeclaration") {
ctx.environment[item.name.text] = {
namedEntity: "NamedType",
isA: "Struct",
fields: Object.fromEntries(item.fields.map((field) => [field.name.text, field.type])),
impls: [],
};
}
if (item.moduleItem === "TraitTypeDeclaration") {
ctx.environment[item.name.text] = {
namedEntity: "NamedType",
isA: "Trait",
fields: {},
impls: [
{
trait: item.name.text,
functions: Object.fromEntries(
item.functions.map((fn) => {
return [
fn.name,
{
functionType: "UserFunction",
function: fn,
},
];
}),
),
},
],
};
}
if (item.moduleItem === "Function") {
ctx.environment[item.declaration.name.text] = {
namedEntity: "Variable",
value: {
value: "FunctionValue",
partial: [],
ref: {
functionType: "UserFunction",
function: item,
},
},
};
}
}
// now that structs and traits are added, add impls
for (const item of module.items) {
if (item.moduleItem === "Impl") {
const struct = ctx.environment[item.struct.name.text];
if (!struct || struct.namedEntity !== "NamedType" || struct.isA !== "Struct") {
throw Error("Impl for non-struct");
}
const functions: Record<string, FunctionRef> = {};
for (const fn of item.functions) {
if (
fn.declaration.arguments.length &&
fn.declaration.arguments[0].type.typeUsage == "NamedTypeUsage" &&
fn.declaration.arguments[0].type.name.text === item.struct.name.text
) {
functions[fn.declaration.name.text] = { functionType: "UserFunction", function: fn };
}
}
struct.impls.push({
trait: item.trait?.name.text ?? null,
functions: functions,
});
}
}
return ctx;
}

View File

@@ -0,0 +1,74 @@
import { TypeUsage, Function, Module } from "../parse/ast";
export interface NumericValue {
value: "NumericValue";
number: number;
}
export interface BoolValue {
value: "BoolValue";
bool: boolean;
}
export interface StringValue {
value: "StringValue";
string: string;
}
export interface UserFunction {
functionType: "UserFunction";
function: Function;
}
export interface BuiltinFunction {
functionType: "BuiltinFunction";
function: (value: Value[]) => Value;
}
export type FunctionRef = UserFunction | BuiltinFunction;
export interface FunctionValue {
value: "FunctionValue";
partial: Value[];
ref: FunctionRef;
}
export interface UnitValue {
value: "UnitValue";
}
export interface StructValue {
value: "StructValue";
source: NamedType;
fields: Record<string, Value>;
}
export type Value =
| NumericValue
| BoolValue
| StringValue
| FunctionValue
| StructValue
| UnitValue;
export interface Variable {
namedEntity: "Variable";
value: Value;
}
export interface EnvImpl {
trait: string | null;
functions: Record<string, FunctionRef>;
}
export interface NamedType {
namedEntity: "NamedType";
isA: "Scalar" | "Trait" | "Struct";
fields: Record<string, TypeUsage>;
impls: EnvImpl[];
}
export interface Context {
environment: Record<string, NamedType | Variable>;
currentModule: Module;
}

View File

@@ -0,0 +1,345 @@
import {
AssignmentStatement,
Block,
Expression,
Function,
FunctionCall,
LetStatement,
Module,
Operation,
Path,
ReturnStatement,
StructGetter,
StructTypeDeclaration,
TypeUsage,
} from "../parse/ast";
import { contextFromModule } from "./builtins";
import { Context, NamedType, Value } from "./context";
interface ExpressionResultValue {
resultType: "Value";
value: Value;
}
interface ExpressionResultReturn {
resultType: "Return";
value: Value;
}
type ExpressionResult = ExpressionResultValue | ExpressionResultReturn;
export class TreeWalkInterpreter {
withModule = (module: Module) => {
const ctx = contextFromModule(module);
const main = ctx.environment["main"];
if (
!main ||
!(main.namedEntity === "Variable") ||
!(main.value.value === "FunctionValue") ||
!(main.value.ref.functionType === "UserFunction")
) {
throw Error("No main function");
}
return this.withFunction(ctx, main.value.ref.function);
};
withFunction = (ctx: Context, fn: Function): Value => {
let result = this.withBlock(ctx, fn.block);
return result.value;
};
withBlock = (ctx: Context, block: Block): ExpressionResult => {
let last: ExpressionResult = { resultType: "Value", value: { value: "UnitValue" } };
for (const statement of block.statements) {
if (statement.statementType === "AssignmentStatement") {
last = this.withAssignmentStatement(ctx, statement);
}
if (statement.statementType === "LetStatement") {
last = this.withLetStatement(ctx, statement);
}
if (statement.statementType === "Expression") {
last = this.withExpression(ctx, statement);
}
if (statement.statementType === "ReturnStatement") {
last = this.withReturnStatement(ctx, statement);
}
if (last.resultType === "Return") {
return last;
}
}
return last;
};
withAssignmentStatement = (ctx: Context, statement: AssignmentStatement): ExpressionResult => {
let result = this.withExpression(ctx, statement.expression);
if (result.resultType === "Return") {
return result;
}
if (statement.source.type == "Identifier") {
ctx.environment[statement.source.name.text] = {
namedEntity: "Variable",
value: result.value,
};
}
if (statement.source.type == "StructGetter") {
let source = this.withStructGetter(ctx, statement.source.source);
if (source.resultType === "Return") {
return source;
}
if (source.value.value !== "StructValue") {
throw Error("set attr on nonstruct, should never happen due to type system");
}
source.value.fields[statement.source.source.attribute.text] = result.value;
}
return { resultType: "Value", value: { value: "UnitValue" } };
};
withLetStatement = (ctx: Context, statement: LetStatement): ExpressionResult => {
let result = this.withExpression(ctx, statement.expression);
if (result.resultType === "Return") {
return result;
}
ctx.environment[statement.variableName.text] = {
namedEntity: "Variable",
value: result.value,
};
return { resultType: "Value", value: { value: "UnitValue" } };
};
withReturnStatement = (ctx: Context, statement: ReturnStatement): ExpressionResult => {
let result = this.withExpression(ctx, statement.source);
if (result.resultType === "Return") {
return result;
}
return { resultType: "Return", value: result.value };
};
withExpression = (ctx: Context, expression: Expression): ExpressionResult => {
if (expression.subExpression.expressionType === "LiteralInt") {
return {
resultType: "Value",
value: { value: "NumericValue", number: parseInt(expression.subExpression.value) },
};
}
if (expression.subExpression.expressionType === "LiteralFloat") {
return {
resultType: "Value",
value: { value: "NumericValue", number: parseFloat(expression.subExpression.value) },
};
}
if (expression.subExpression.expressionType === "LiteralString") {
return {
resultType: "Value",
value: { value: "StringValue", string: expression.subExpression.value },
};
}
if (expression.subExpression.expressionType === "LiteralBool") {
return {
resultType: "Value",
value: { value: "BoolValue", bool: expression.subExpression.value === "true" },
};
}
if (expression.subExpression.expressionType === "LiteralStruct") {
const def = ctx.environment[expression.subExpression.name.text];
if (def.namedEntity !== "NamedType") {
throw Error("Not a struct");
}
const fields: Record<string, Value> = {};
for (const field of expression.subExpression.fields) {
const fieldResult = this.withExpression(ctx, field.expression);
if (fieldResult.resultType === "Return") {
return fieldResult;
}
fields[field.name.text] = fieldResult.value;
}
return {
resultType: "Value",
value: {
value: "StructValue",
source: def,
fields: fields,
},
};
}
if (expression.subExpression.expressionType === "FunctionCall") {
return this.withFunctionCall(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "Path") {
// const variableValue = ctx.environment[expression.subExpression.name.text];
// if (!variableValue || variableValue.namedEntity !== "Variable") {
// throw Error(`not found: ${expression.subExpression.name.text}`);
// }
const value = this.withPath(ctx, expression.subExpression);
return { resultType: "Value", value: value };
}
if (expression.subExpression.expressionType === "IfExpression") {
const condition = this.withExpression(ctx, expression.subExpression.condition);
if (condition.resultType === "Return") {
return condition;
}
if (condition.value.value === "BoolValue" && condition.value.bool === true) {
return this.withBlock(ctx, expression.subExpression.block);
} else {
if (expression.subExpression.else) {
return this.withBlock(ctx, expression.subExpression.else);
} else {
return { resultType: "Value", value: { value: "UnitValue" } };
}
}
}
if (expression.subExpression.expressionType === "StructGetter") {
return this.withStructGetter(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "Block") {
return this.withBlock(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "Operation") {
return this.withOperation(ctx, expression.subExpression);
}
// not actually possible, but makes the type system happy
return { resultType: "Value", value: { value: "UnitValue" } };
};
withFunctionCall = (ctx: Context, fnCall: FunctionCall): ExpressionResult => {
const source = this.withExpression(ctx, fnCall.source);
if (source.resultType === "Return") {
return source;
}
const argValues: Value[] = [];
for (const arg of fnCall.arguments) {
const argValue = this.withExpression(ctx, arg);
if (argValue.resultType === "Return") {
return argValue;
}
argValues.push(argValue.value);
}
if (source.value.value !== "FunctionValue") {
throw Error("type error: function call source must be a function");
}
if (source.value.ref.functionType === "UserFunction") {
const fn = source.value.partial;
const fnCtx = contextFromModule(ctx.currentModule);
let i = 0;
for (const arg of source.value.partial) {
fnCtx.environment[source.value.ref.function.declaration.arguments[i].name.text] = {
namedEntity: "Variable",
value: arg,
};
i = i + 1;
}
for (const arg of argValues) {
fnCtx.environment[source.value.ref.function.declaration.arguments[i].name.text] = {
namedEntity: "Variable",
value: arg,
};
i = i + 1;
}
return { resultType: "Value", value: this.withFunction(fnCtx, source.value.ref.function) };
}
// builtin
let allValues = source.value.partial.concat(argValues);
return { resultType: "Value", value: source.value.ref.function(allValues) };
};
withStructGetter = (ctx: Context, structGetter: StructGetter): ExpressionResult => {
const source = this.withExpression(ctx, structGetter.source);
if (source.resultType === "Return") {
return source;
}
if (source.value.value !== "StructValue") {
throw Error("get attr of non-struct");
}
if (source.value.fields[structGetter.attribute.text]) {
return {
resultType: "Value",
value: source.value.fields[structGetter.attribute.text],
};
}
for (const impl of source.value.source.impls) {
for (const [name, method] of Object.entries(impl.functions)) {
if (name === structGetter.attribute.text) {
return {
resultType: "Value",
value: { value: "FunctionValue", partial: [source.value], ref: method },
};
}
}
}
// not actually possible, but makes the type system happy
return { resultType: "Value", value: { value: "UnitValue" } };
};
withOperation = (ctx: Context, op: Operation): ExpressionResult => {
const left = this.withExpression(ctx, op.left);
if (left.resultType === "Return") {
return left;
}
const right = this.withExpression(ctx, op.left);
if (right.resultType === "Return") {
return right;
}
if (left.value.value !== "NumericValue" || right.value.value !== "NumericValue") {
throw Error("Operation on a Nan");
}
if (op.op === "+") {
return {
resultType: "Value",
value: { value: "NumericValue", number: left.value.number + right.value.number },
};
}
if (op.op === "-") {
return {
resultType: "Value",
value: { value: "NumericValue", number: left.value.number - right.value.number },
};
}
if (op.op === "*") {
return {
resultType: "Value",
value: { value: "NumericValue", number: left.value.number * right.value.number },
};
}
return {
resultType: "Value",
value: { value: "NumericValue", number: left.value.number / right.value.number },
};
};
withPath = (ctx: Context, path: Path): Value => {
if (path.value.type == "Identifier") {
const variableValue = ctx.environment[path.value.name.text];
if (!variableValue || variableValue.namedEntity !== "Variable") {
throw Error(`not found: ${path.value.name.text}`);
}
return variableValue.value;
}
if (path.value.type == "Nested") {
return this.withPathItem(ctx, path) as Value;
}
throw Error(`Impossible path`);
};
withPathItem = (ctx: Context, path: Path): NamedType | Value => {
if (path.value.type == "Identifier") {
const envValue = ctx.environment[path.value.name.text];
if (!envValue || envValue.namedEntity !== "NamedType") {
throw Error(`not found: ${path.value.name.text}`);
}
return envValue;
}
if (path.value.type == "Nested") {
const envValue = this.withPathItem(ctx, path.value.parent) as NamedType;
if (envValue.isA == "Trait") {
throw Error(`Cannot get function impl from raw trait`);
}
for (const impl of envValue.impls) {
for (const [name, method] of Object.entries(impl.functions)) {
if (name == path.value.name.text) {
return { value: "FunctionValue", partial: [], ref: method };
}
}
}
}
throw Error(`Impossible path`);
};
}

View File

@@ -0,0 +1,272 @@
export interface Spanned {
spanStart: number;
spanEnd: number;
}
export interface Identifier extends Spanned {
text: 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 Path {
expressionType: "Path";
value:
| { type: "Identifier"; name: Identifier }
| { type: "Nested"; parent: Path; name: Identifier };
}
export interface Operation {
expressionType: "Operation";
left: Expression;
op: "+" | "-" | "*" | "/";
right: Expression;
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
| IfExpression
| Path
| 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: { type: "Identifier"; name: Identifier } | { type: "StructGetter"; source: StructGetter };
expression: Expression;
}
export type Statement = ReturnStatement | LetStatement | AssignmentStatement | Expression;
export interface Block {
expressionType: "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 const functionToType = (fn: FunctionDeclaration): FunctionTypeUsage => {
return {
typeUsage: "FunctionTypeUsage",
arguments: fn.arguments.map((arg) => arg.type),
returnType: fn.returnType,
};
};
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;
export const newVoid: () => TypeUsage = () => {
return {
typeUsage: "NamedTypeUsage",
name: { text: "Void", spanStart: 0, spanEnd: 0 },
};
};
export const newNever: () => TypeUsage = () => {
return {
typeUsage: "NamedTypeUsage",
name: { text: "Never", spanStart: 0, spanEnd: 0 },
};
};
function containsReturnExpression(expression: Expression) {
if (expression.subExpression.expressionType === "LiteralStruct") {
for (const field of expression.subExpression.fields) {
if (containsReturnExpression(field.expression)) {
return true;
}
}
}
if (expression.subExpression.expressionType === "IfExpression") {
if (containsReturn(expression.subExpression.block)) {
return true;
}
if (expression.subExpression.else && containsReturn(expression.subExpression.else)) {
return true;
}
}
if (expression.subExpression.expressionType === "Block") {
if (containsReturn(expression.subExpression)) {
return true;
}
}
if (expression.subExpression.expressionType === "Operation") {
if (containsReturnExpression(expression.subExpression.left)) {
return true;
}
if (containsReturnExpression(expression.subExpression.right)) {
return true;
}
}
return false;
}
export function containsReturn(block: Block) {
for (const statement of block.statements) {
if (statement.statementType === "ReturnStatement") {
return true;
}
if (statement.statementType === "AssignmentStatement") {
if (containsReturnExpression(statement.expression)) {
return true;
}
}
if (statement.statementType === "LetStatement") {
if (containsReturnExpression(statement.expression)) {
return true;
}
}
if (statement.statementType === "Expression") {
if (containsReturnExpression(statement)) {
return true;
}
}
}
return false;
}

View File

@@ -0,0 +1,72 @@
import * as ohm from "ohm-js";
export const boringGrammar = ohm.grammar(String.raw`
Boringlang {
ReturnStatement = "return" Expression ";"
LetStatement = "let" identifier (":" TypeUsage)? "=" Expression ";"
AssignmentStatement = identifier "=" Expression ";" -- identifier
| StructGetter "=" Expression ";" -- getter
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 | "_")*
StructGetter = Expression "." identifier
IfExpression = "if" "(" Expression ")" Block ("else" Block)?
Path = Path "::" identifier -- nested
| identifier -- base
PrimaryExpression = LiteralInt
| LiteralFloat
| LiteralBool
| LiteralString
| Path -- path
| "(" Expression ")" -- parens
StructExpression = LiteralStruct
| Block
| IfExpression
| PrimaryExpression
MemberExpression = MemberExpression "." identifier -- structGetter
| StructExpression
CallExpression = CallExpression "." identifier -- structGetter
| CallExpression "(" ListOf<Expression, ","> ")" -- functionCall
| MemberExpression "(" ListOf<Expression, ","> ")" -- memberFunctionCall
| MemberExpression
MultExpression = MultExpression "*" CallExpression -- mult
| MultExpression "/" CallExpression -- div
| CallExpression
AddExpression = Expression "+" MultExpression -- plus
| Expression "-" MultExpression -- minus
| MultExpression
Expression = AddExpression
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,444 @@
import {
AssignmentStatement,
Block,
containsReturn,
Expression,
Function,
FunctionArgument,
FunctionCall,
FunctionDeclaration,
FunctionTypeUsage,
Identifier,
IfExpression,
Impl,
LetStatement,
LiteralBool,
LiteralFloat,
LiteralInt,
LiteralString,
LiteralStruct,
Module,
ModuleItem,
NamedTypeUsage,
newNever,
Operation,
Path,
ReturnStatement,
Statement,
StructField,
StructGetter,
StructTypeDeclaration,
StructTypeField,
TraitTypeDeclaration,
TypeDeclaration,
TypeUsage,
} 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): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralInt",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
name: { text: "i64", spanStart: 0, spanEnd: 0 },
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
LiteralFloat(_1, _2, _3): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralFloat",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
name: { text: "f64", spanStart: 0, spanEnd: 0 },
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
LiteralBool(_): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralBool",
value: this.sourceString,
type: {
typeUsage: "NamedTypeUsage",
name: { text: "bool", spanStart: 0, spanEnd: 0 },
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
LiteralString(_1, text, _3): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralString",
value: text.sourceString,
type: {
typeUsage: "NamedTypeUsage",
name: { text: "String", spanStart: 0, spanEnd: 0 },
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
LiteralStructField(identifier, _2, expression): StructField {
return {
name: identifier.toAST(),
expression: expression.toAST(),
};
},
LiteralStruct(identifier, _2, fields, _4): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "LiteralStruct",
name: identifier.toAST(),
fields: fields.asIteration().children.map((c) => c.toAST()),
type: { typeUsage: "NamedTypeUsage", name: identifier.toAST() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
identifier(_1, _2): Identifier {
return {
text: this.sourceString,
spanStart: this.source.startIdx,
spanEnd: this.source.endIdx,
};
},
PrimaryExpression(literal): Expression {
return literal.toAST();
},
PrimaryExpression_path(path): Expression {
return {
statementType: "Expression",
subExpression: path.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
PrimaryExpression_parens(_1, term, _3): Expression {
return term.toAST();
},
StructExpression(expression): Expression {
return expression.toAST();
},
MemberExpression(expression): Expression {
return expression.toAST();
},
MemberExpression_structGetter(expression, _2, identifier): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "StructGetter",
source: expression.toAST(),
attribute: identifier.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
CallExpression(expression): Expression {
return expression.toAST();
},
CallExpression_structGetter(expression, _2, identifier): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "StructGetter",
source: expression.toAST(),
attribute: identifier.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
CallExpression_functionCall(expression, _2, args, _4): Expression {
const resolvedArgs = args.asIteration().children.map((c) => c.toAST());
return {
statementType: "Expression",
subExpression: {
expressionType: "FunctionCall",
source: expression.toAST(),
arguments: resolvedArgs,
type: {
typeUsage: "UnknownTypeUsage",
name: nextUnknown(),
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
CallExpression_memberFunctionCall(expression, _2, args, _4): Expression {
const resolvedArgs = args.asIteration().children.map((c) => c.toAST());
return {
statementType: "Expression",
subExpression: {
expressionType: "FunctionCall",
source: expression.toAST(),
arguments: resolvedArgs,
type: {
typeUsage: "UnknownTypeUsage",
name: nextUnknown(),
},
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
StructGetter(expression, _2, identifier): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "StructGetter",
source: expression.toAST(),
attribute: identifier.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
IfExpression(_1, _2, expression, _4, block, _6, elseBlock): Expression {
const eb = elseBlock.toAST();
return {
statementType: "Expression",
subExpression: {
expressionType: "IfExpression",
condition: expression.toAST(),
block: block.toAST(),
else: eb.length > 0 ? eb[0] : null,
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Path_base(identifier): Path {
return {
expressionType: "Path",
value: { type: "Identifier", name: identifier.toAST() },
};
},
Path_nested(basePath, _2, attrIdent): Path {
return {
expressionType: "Path",
value: { type: "Nested", parent: basePath.toAST(), name: attrIdent.toAST() },
};
},
MultExpression(expression): Expression {
return expression.toAST();
},
MultExpression_mult(factor, _2, term): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "Operation",
left: factor.toAST(),
op: "*",
right: term.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
MultExpression_div(factor, _2, term): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "Operation",
left: factor.toAST(),
op: "/",
right: term.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
AddExpression(expression): Expression {
return expression.toAST();
},
AddExpression_plus(expression, _2, factor): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "Operation",
left: expression.toAST(),
op: "+",
right: factor.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
AddExpression_minus(expression, _2, factor): Expression {
return {
statementType: "Expression",
subExpression: {
expressionType: "Operation",
left: expression.toAST(),
op: "-",
right: factor.toAST(),
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
},
type: { typeUsage: "UnknownTypeUsage", name: nextUnknown() },
};
},
Expression(expression): Expression {
return expression.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_identifier(variable, _2, expression, _4): AssignmentStatement {
return {
statementType: "AssignmentStatement",
source: { type: "Identifier", name: variable.toAST() },
expression: expression.toAST(),
};
},
AssignmentStatement_getter(variable, _2, expression, _4): AssignmentStatement {
return {
statementType: "AssignmentStatement",
source: { type: "StructGetter", 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();
if (finalExpression.length > 0) {
lines.push(finalExpression[0]);
}
const block: Block = {
expressionType: "Block",
statements: lines,
type: newNever(),
};
if (!containsReturn(block)) {
block.type = { typeUsage: "UnknownTypeUsage", name: nextUnknown() };
}
return block;
},
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,21 @@
import { Context } from "./context";
export function newContext(): Context {
const result: Context = {
currentFunctionReturn: null,
environment: {},
};
result.environment["i8"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["i16"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["i32"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["i64"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["f8"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["f16"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["f32"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["f64"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["String"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["Void"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
result.environment["Never"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
return result;
}

View File

@@ -0,0 +1,95 @@
import { FunctionTypeUsage, NamedTypeUsage, TypeUsage } from "../parse/ast";
interface EnvImpl {
trait: string | null;
functions: Record<string, FunctionTypeUsage>;
}
interface NamedType {
namedEntity: "NamedType";
isA: "Scalar" | "Trait" | "Struct";
fields: Record<string, TypeUsage>;
impls: EnvImpl[];
}
interface Variable {
namedEntity: "Variable";
type: TypeUsage;
}
type NamedEntity = NamedType | Variable;
export interface Context {
currentFunctionReturn: TypeUsage | null;
environment: Record<string, NamedEntity>;
}
export function getAttr(ctx: Context, name: string, field: string) {
const struct = ctx.environment[name];
if (!struct || struct.namedEntity !== "NamedType") {
throw Error(`Unknown type ${name}`);
}
if (struct.fields[field]) {
return struct.fields[field];
}
let results: TypeUsage[] = [];
for (const impl of struct.impls) {
if (impl.functions[field]) {
const fn = impl.functions[field];
if (
fn.arguments.length &&
fn.arguments[0].typeUsage == "NamedTypeUsage" &&
fn.arguments[0].name.text === name
) {
const fnCopy = deepCopy(fn);
fnCopy.arguments = fnCopy.arguments.slice(1);
results.push(fnCopy);
}
}
}
if (results.length === 0) {
throw Error(`${name} has no attribue ${field}`);
}
if (results.length > 1) {
throw Error(`${name} has multiple attribues ${field}, use universal function call syntax`);
}
return results[0];
}
export function typeExists(ctx: Context, type: TypeUsage) {
if (type.typeUsage === "NamedTypeUsage") {
if (
!ctx.environment[type.name.text] ||
ctx.environment[type.name.text].namedEntity !== "NamedType"
) {
throw Error(`${type.name.text} is not a type.`);
}
}
if (type.typeUsage === "FunctionTypeUsage") {
for (const arg of type.arguments) {
typeExists(ctx, arg);
}
typeExists(ctx, type.returnType);
}
}
export function replaceType(oldName: string, newType: TypeUsage, inType: TypeUsage) {
if (inType.typeUsage === "NamedTypeUsage") {
if (inType.name.text === oldName) {
return deepCopy(newType);
}
}
if (inType.typeUsage === "FunctionTypeUsage") {
const result = deepCopy(inType);
for (const [i, arg] of inType.arguments.entries()) {
result.arguments[i] = replaceType(oldName, newType, arg);
}
result.returnType = replaceType(oldName, newType, inType.returnType);
}
return deepCopy(inType);
}
export const deepCopy = <T>(o: T) => {
return JSON.parse(JSON.stringify(o)) as T;
};

View File

@@ -0,0 +1,62 @@
import { Impl, Module, TraitTypeDeclaration, TypeUsage } from "../parse/ast";
import { compareTypes } from "./type_system";
interface Context {
environment: Record<string, TraitTypeDeclaration>;
}
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.text] = item;
}
}
for (const item of module.items) {
if (item.moduleItem == "Impl") {
this.withImpl(ctx, item);
}
}
};
withTrait = (trait: TraitTypeDeclaration) => {
for (const fn of trait.functions) {
if (fn.arguments.length === 0) {
throw new Error("First argument of trait method must be Self");
}
const firstArg = fn.arguments[0];
if (firstArg.type.typeUsage !== "NamedTypeUsage" || firstArg.type.name.text !== "Self") {
throw new Error("First argument of trait method must be Self");
}
}
};
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.text}`);
}
if (impl.trait == null) {
return;
}
const trait = ctx.environment[impl.trait.name.text];
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.text} for ${impl.struct.name.text}`);
}
for (let i = 0; i < impl.functions.length; i++) {
if (impl.functions[i].declaration.name.text !== trait.functions[i].name.text) {
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,233 @@
import {
AssignmentStatement,
Block,
Expression,
Function,
FunctionCall,
FunctionDeclaration,
IfExpression,
Impl,
LetStatement,
LiteralStruct,
Module,
Operation,
ReturnStatement,
StructGetter,
StructTypeDeclaration,
TraitTypeDeclaration,
TypeUsage,
Path,
} from "../parse/ast";
import { deepCopy, replaceType } from "./context";
interface AliasContext {
environment: Record<string, TypeUsage>;
}
export class TypeAliasResolver {
withModule = (module: Module) => {
const ctx: AliasContext = { environment: {} };
const result = deepCopy(module);
for (const [i, item] of module.items.entries()) {
if (item.moduleItem === "TraitTypeDeclaration") {
let traitCtx = deepCopy(ctx);
traitCtx.environment["Self"] = { typeUsage: "NamedTypeUsage", name: item.name };
result.items[i] = this.withTraitTypeDeclaration(traitCtx, item);
}
if (item.moduleItem === "Impl") {
let implCtx = deepCopy(ctx);
implCtx.environment["Self"] = { typeUsage: "NamedTypeUsage", name: item.struct.name };
result.items[i] = this.withImpl(implCtx, item);
}
if (item.moduleItem === "StructTypeDeclaration") {
let structCtx = deepCopy(ctx);
structCtx.environment["Self"] = { typeUsage: "NamedTypeUsage", name: item.name };
result.items[i] = this.withStructDeclaration(structCtx, item);
}
}
return result;
};
withTraitTypeDeclaration = (ctx: AliasContext, trait: TraitTypeDeclaration) => {
const result = deepCopy(trait);
for (const [oldName, newType] of Object.entries(ctx.environment)) {
for (const [i, fn] of trait.functions.entries()) {
result.functions[i] = this.withFunctionDeclaration(ctx, fn);
}
}
return result;
};
withImpl = (ctx: AliasContext, impl: Impl) => {
const result = deepCopy(impl);
for (const [oldName, newType] of Object.entries(ctx.environment)) {
for (const [i, fn] of impl.functions.entries()) {
result.functions[i] = this.withFunction(ctx, fn);
}
}
return result;
};
withStructDeclaration = (ctx: AliasContext, struct: StructTypeDeclaration) => {
const result = deepCopy(struct);
for (const [oldName, newType] of Object.entries(ctx.environment)) {
for (const [i, field] of struct.fields.entries()) {
result.fields[i].type = replaceType(oldName, newType, field.type);
}
}
return result;
};
withFunctionDeclaration = (ctx: AliasContext, fn: FunctionDeclaration) => {
const result = deepCopy(fn);
for (const [oldName, newType] of Object.entries(ctx.environment)) {
for (const [i, arg] of fn.arguments.entries()) {
result.arguments[i].type = replaceType(oldName, newType, arg.type);
}
result.returnType = replaceType(oldName, newType, result.returnType);
}
return result;
};
withFunction = (ctx: AliasContext, fn: Function) => {
const result = deepCopy(fn);
result.declaration = this.withFunctionDeclaration(ctx, fn.declaration);
result.block = this.withBlock(ctx, fn.block);
return result;
};
withBlock = (ctx: AliasContext, block: Block) => {
const result = deepCopy(block);
for (const [i, statement] of block.statements.entries()) {
if (statement.statementType === "AssignmentStatement") {
result.statements[i] = this.withAssignmentStatement(ctx, statement);
}
if (statement.statementType === "LetStatement") {
result.statements[i] = this.withLetStatement(ctx, statement);
}
if (statement.statementType === "Expression") {
result.statements[i] = this.withExpression(ctx, statement);
}
if (statement.statementType === "ReturnStatement") {
result.statements[i] = this.withReturnStatement(ctx, statement);
}
for (const [oldName, newType] of Object.entries(ctx.environment)) {
result.type = replaceType(oldName, newType, block.type);
}
}
return result;
};
withAssignmentStatement = (ctx: AliasContext, statement: AssignmentStatement) => {
const result = deepCopy(statement);
if (statement.source.type == "StructGetter") {
result.source = {
type: "StructGetter",
source: this.withStructGetter(ctx, statement.source.source),
};
}
if (statement.source.type == "Identifier") {
result.source = deepCopy(result.source);
}
result.expression = this.withExpression(ctx, statement.expression);
return result;
};
withLetStatement = (ctx: AliasContext, statement: LetStatement) => {
const result = deepCopy(statement);
result.expression = this.withExpression(ctx, statement.expression);
return result;
};
withReturnStatement = (ctx: AliasContext, statement: ReturnStatement) => {
const result = deepCopy(statement);
result.source = this.withExpression(ctx, statement.source);
return result;
};
withExpression = (ctx: AliasContext, expression: Expression) => {
const result = deepCopy(expression);
if (expression.subExpression.expressionType === "LiteralStruct") {
result.subExpression = this.withLiteralStruct(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "FunctionCall") {
result.subExpression = this.withFunctionCall(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "Path") {
result.subExpression = deepCopy(expression.subExpression);
}
if (expression.subExpression.expressionType === "IfExpression") {
result.subExpression = this.withIfExpression(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "StructGetter") {
result.subExpression = this.withStructGetter(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "Block") {
result.subExpression = this.withBlock(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "Operation") {
result.subExpression = this.withOperation(ctx, expression.subExpression);
}
return result;
};
withLiteralStruct = (ctx: AliasContext, literalStruct: LiteralStruct) => {
const result = deepCopy(literalStruct);
for (const [i, field] of literalStruct.fields.entries()) {
result.fields[i].expression = this.withExpression(ctx, field.expression);
}
for (const [oldName, newType] of Object.entries(ctx.environment)) {
result.type = replaceType(oldName, newType, literalStruct.type);
if (result.name.text === oldName && newType.typeUsage === "NamedTypeUsage") {
result.name = newType.name;
}
}
return result;
};
withFunctionCall = (ctx: AliasContext, fnCall: FunctionCall) => {
const result = deepCopy(fnCall);
for (const [i, arg] of fnCall.arguments.entries()) {
result.arguments[i] = this.withExpression(ctx, arg);
}
result.source = this.withExpression(ctx, fnCall.source);
for (const [oldName, newType] of Object.entries(ctx.environment)) {
result.type = replaceType(oldName, newType, fnCall.type);
}
return result;
};
withIfExpression = (ctx: AliasContext, ifExpression: IfExpression) => {
const result = deepCopy(ifExpression);
result.condition = this.withExpression(ctx, ifExpression.condition);
result.block = this.withBlock(ctx, ifExpression.block);
if (ifExpression.else) {
result.else = this.withBlock(ctx, ifExpression.else);
}
for (const [oldName, newType] of Object.entries(ctx.environment)) {
result.type = replaceType(oldName, newType, ifExpression.type);
}
return result;
};
withStructGetter = (ctx: AliasContext, structGetter: StructGetter) => {
const result = deepCopy(structGetter);
result.source = this.withExpression(ctx, structGetter.source);
for (const [oldName, newType] of Object.entries(ctx.environment)) {
result.type = replaceType(oldName, newType, structGetter.type);
}
return result;
};
withOperation = (ctx: AliasContext, op: Operation) => {
const result = deepCopy(op);
result.left = this.withExpression(ctx, op.left);
result.right = this.withExpression(ctx, op.right);
for (const [oldName, newType] of Object.entries(ctx.environment)) {
result.type = replaceType(oldName, newType, op.type);
}
return result;
};
}

View File

@@ -0,0 +1,455 @@
import {
AssignmentStatement,
Block,
containsReturn,
Expression,
Function,
FunctionCall,
FunctionDeclaration,
functionToType,
IfExpression,
Impl,
LetStatement,
LiteralStruct,
Module,
newVoid,
Operation,
ReturnStatement,
StructGetter,
StructTypeDeclaration,
TraitTypeDeclaration,
TypeUsage,
Path,
Identifier,
FunctionTypeUsage,
} from "../parse/ast";
import { newContext } from "./builtins";
import { Context, deepCopy, typeExists } from "./context";
import { TypeSystem } from "./type_system";
export class TypeChecker {
withModule = (module: Module, typeSystem: TypeSystem) => {
const ctx: Context = newContext();
// add functions, structs, and traits to the context
for (const item of module.items) {
if (item.moduleItem === "StructTypeDeclaration") {
if (ctx.environment[item.name.text]) {
throw Error("Duplicate name of struct");
}
ctx.environment[item.name.text] = {
namedEntity: "NamedType",
isA: "Struct",
fields: Object.fromEntries(item.fields.map((field) => [field.name.text, field.type])),
impls: [],
};
}
if (item.moduleItem === "TraitTypeDeclaration") {
if (ctx.environment[item.name.text]) {
throw Error("Duplicate name of trait");
}
const functions: Record<string, FunctionTypeUsage> = {};
for (const fn of item.functions) {
functions[fn.name.text] = functionToType(fn);
}
ctx.environment[item.name.text] = {
namedEntity: "NamedType",
isA: "Trait",
fields: {},
impls: [
{
trait: item.name.text,
functions: functions,
},
],
};
}
if (item.moduleItem === "Function") {
if (ctx.environment[item.declaration.name.text]) {
throw Error("Duplicate name of function");
}
ctx.environment[item.declaration.name.text] = {
namedEntity: "Variable",
type: functionToType(item.declaration),
};
}
}
// now that structs and traits are added, add impls
for (const item of module.items) {
if (item.moduleItem === "Impl") {
const struct = ctx.environment[item.struct.name.text];
if (!struct || struct.namedEntity !== "NamedType" || struct.isA !== "Struct") {
throw Error("Impl for non-struct");
}
const functions: Record<string, FunctionTypeUsage> = {};
for (const fn of item.functions) {
functions[fn.declaration.name.text] = functionToType(fn.declaration);
}
struct.impls.push({
trait: item.trait?.name.text ?? null,
functions: functions,
});
}
}
typeSystem.context = deepCopy(ctx);
// environment set up, actually recurse.
for (const item of module.items) {
if (item.moduleItem === "Function") {
this.withFunction(ctx, item, typeSystem);
}
if (item.moduleItem === "Impl") {
this.withImpl(ctx, item, typeSystem);
}
if (item.moduleItem === "StructTypeDeclaration") {
this.withStructDeclaration(ctx, item, typeSystem);
}
if (item.moduleItem === "TraitTypeDeclaration") {
this.withTrait(ctx, item, typeSystem);
}
}
};
withFunction = (ctx: Context, fn: Function, typeSystem: TypeSystem) => {
this.withFunctionDeclaration(ctx, fn.declaration, typeSystem);
const fnCtx = deepCopy(ctx);
fnCtx.currentFunctionReturn = fn.declaration.returnType;
for (const arg of fn.declaration.arguments) {
fnCtx.environment[arg.name.text] = {
namedEntity: "Variable",
type: arg.type,
};
}
this.withBlock(fnCtx, fn.block, typeSystem);
typeSystem.compare({
left: fn.declaration.returnType,
operation: { operation: "equals" },
right: fn.block.type,
});
};
withFunctionDeclaration = (ctx: Context, def: FunctionDeclaration, typeSystem: TypeSystem) => {
for (const arg of def.arguments) {
typeExists(ctx, arg.type);
}
typeExists(ctx, def.returnType);
};
withImpl = (ctx: Context, impl: Impl, typeSystem: TypeSystem) => {
for (const fn of impl.functions) {
this.withFunction(ctx, fn, typeSystem);
}
};
withStructDeclaration = (ctx: Context, struct: StructTypeDeclaration, typeSystem: TypeSystem) => {
for (const field of struct.fields) {
typeExists(ctx, field.type);
}
};
withTrait = (ctx: Context, trait: TraitTypeDeclaration, typeSystem: TypeSystem) => {
for (const method of trait.functions) {
this.withFunctionDeclaration(ctx, method, typeSystem);
}
};
withBlock = (ctx: Context, block: Block, typeSystem: TypeSystem) => {
const blockCtx = deepCopy(ctx);
for (const statement of block.statements) {
if (statement.statementType === "AssignmentStatement") {
this.withAssignmentStatement(blockCtx, statement, typeSystem);
}
if (statement.statementType === "LetStatement") {
this.withLetStatement(blockCtx, statement, typeSystem);
blockCtx.environment[statement.variableName.text] = {
namedEntity: "Variable",
type: statement.type,
};
}
if (statement.statementType === "Expression") {
this.withExpression(blockCtx, statement, typeSystem);
}
if (statement.statementType === "ReturnStatement") {
this.withReturnStatement(blockCtx, statement, typeSystem);
}
if (!containsReturn(block)) {
const lastStatement = block.statements[block.statements.length - 1] ?? null;
if (lastStatement && lastStatement.statementType == "Expression") {
typeSystem.compare({
left: block.type,
operation: { operation: "equals" },
right: lastStatement.type,
});
} else {
typeSystem.compare({
left: block.type,
operation: { operation: "equals" },
right: newVoid(),
});
}
}
}
};
withAssignmentStatement = (
ctx: Context,
statement: AssignmentStatement,
typeSystem: TypeSystem,
) => {
if (statement.source.type == "StructGetter") {
this.withStructGetter(ctx, statement.source.source, typeSystem);
typeSystem.compare({
left: statement.source.source.type,
operation: { operation: "equals" },
right: statement.expression.type,
});
}
if (statement.source.type == "Identifier") {
typeSystem.compare({
left: this.withPath(ctx, {
expressionType: "Path",
value: { type: "Identifier", name: statement.source.name },
}),
operation: { operation: "equals" },
right: statement.expression.type,
});
}
this.withExpression(ctx, statement.expression, typeSystem);
};
withLetStatement = (ctx: Context, statement: LetStatement, typeSystem: TypeSystem) => {
this.withExpression(ctx, statement.expression, typeSystem);
typeSystem.compare({
left: statement.type,
operation: { operation: "equals" },
right: statement.expression.type,
});
};
withReturnStatement = (ctx: Context, statement: ReturnStatement, typeSystem: TypeSystem) => {
this.withExpression(ctx, statement.source, typeSystem);
if (ctx.currentFunctionReturn) {
typeSystem.compare({
left: ctx.currentFunctionReturn,
operation: { operation: "equals" },
right: statement.source.type,
});
}
};
withExpression = (ctx: Context, expression: Expression, typeSystem: TypeSystem) => {
if (expression.subExpression.expressionType === "LiteralInt") {
// LiteralInt always has type
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "LiteralFloat") {
// LiteralFloat always has type
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "LiteralString") {
// LiteralString always has type
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "LiteralBool") {
// LiteralBool always has type
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "LiteralStruct") {
this.withLiteralStruct(ctx, expression.subExpression, typeSystem);
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "FunctionCall") {
this.withFunctionCall(ctx, expression.subExpression, typeSystem);
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "Path") {
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: this.withPath(ctx, expression.subExpression),
});
}
if (expression.subExpression.expressionType === "IfExpression") {
this.withIfExpression(ctx, expression.subExpression, typeSystem);
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "StructGetter") {
this.withStructGetter(ctx, expression.subExpression, typeSystem);
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "Block") {
this.withBlock(ctx, expression.subExpression, typeSystem);
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
if (expression.subExpression.expressionType === "Operation") {
this.withOperation(ctx, expression.subExpression, typeSystem);
typeSystem.compare({
left: expression.type,
operation: { operation: "equals" },
right: expression.subExpression.type,
});
}
};
withLiteralStruct = (ctx: Context, literal: LiteralStruct, typeSystem: TypeSystem) => {
const definition = ctx.environment[literal.name.text];
if (!definition || definition.namedEntity !== "NamedType" || !(definition.isA === "Struct")) {
throw new Error(`${literal.name.text} not found.`);
}
if (Object.keys(definition.fields).length !== literal.fields.length) {
throw new Error(`${literal.name.text} has mismatched fields.`);
}
if (new Set(Object.keys(definition.fields)).size !== literal.fields.length) {
throw new Error(`${literal.name.text} has repeated fields.`);
}
for (const field of literal.fields) {
const definitionField = definition.fields[field.name.text];
if (!definitionField) throw new Error(`Unknown field ${field.name.text}`);
this.withExpression(ctx, field.expression, typeSystem);
typeSystem.compare({
left: definitionField,
operation: { operation: "equals" },
right: field.expression.type,
});
}
typeSystem.compare({
left: { typeUsage: "NamedTypeUsage", name: literal.name },
operation: { operation: "equals" },
right: literal.type,
});
};
withFunctionCall = (ctx: Context, fnCall: FunctionCall, typeSystem: TypeSystem) => {
this.withExpression(ctx, fnCall.source, typeSystem);
typeSystem.compare({
left: fnCall.source.type,
operation: { operation: "equals" },
right: {
typeUsage: "FunctionTypeUsage",
arguments: fnCall.arguments.map((arg) => arg.type),
returnType: fnCall.type,
},
});
for (const [i, arg] of fnCall.arguments.entries()) {
this.withExpression(ctx, arg, typeSystem);
}
};
withPath = (ctx: Context, path: Path): TypeUsage => {
const pathList: Identifier[] = [];
while (path.value.type == "Nested") {
pathList.unshift(path.value.name);
path = path.value.parent;
}
pathList.unshift(path.value.name);
if (pathList.length === 0 || pathList.length > 2) {
throw new Error(`Namespaces not yet supported`);
}
if (pathList.length === 1) {
const variable = ctx.environment[pathList[0].text];
if (!variable || variable.namedEntity === "NamedType") {
throw new Error(`${pathList[0].text} not found.`);
}
return variable.type;
}
let struct = ctx.environment[pathList[0].text];
if (!struct) {
throw new Error(`Unknown ${pathList[0].text}`);
}
if (struct.namedEntity == "Variable") {
throw new Error(`Cannot "::" variable ${pathList[0].text}`);
}
if (struct.fields[pathList[1].text]) {
return struct.fields[pathList[1].text];
}
for (const impl of struct.impls) {
if (impl.functions[pathList[1].text]) {
return impl.functions[pathList[1].text];
}
}
throw new Error(`Could not find attr ${pathList[1].text}`);
};
withIfExpression = (ctx: Context, ifExpression: IfExpression, typeSystem: TypeSystem) => {
this.withExpression(ctx, ifExpression.condition, typeSystem);
typeSystem.compare({
left: { typeUsage: "NamedTypeUsage", name: { text: "bool", spanStart: 0, spanEnd: 0 } },
operation: { operation: "equals" },
right: ifExpression.condition.type,
});
this.withBlock(ctx, ifExpression.block, typeSystem);
typeSystem.compare({
left: ifExpression.block.type,
operation: { operation: "equals" },
right: ifExpression.type,
});
if (ifExpression.else) {
this.withBlock(ctx, ifExpression.else, typeSystem);
typeSystem.compare({
left: ifExpression.else.type,
operation: { operation: "equals" },
right: ifExpression.type,
});
}
};
withStructGetter = (ctx: Context, structGetter: StructGetter, typeSystem: TypeSystem) => {
this.withExpression(ctx, structGetter.source, typeSystem);
typeSystem.compare({
left: structGetter.source.type,
operation: { operation: "field", name: structGetter.attribute.text },
right: structGetter.type,
});
};
withOperation = (ctx: Context, op: Operation, typeSystem: TypeSystem) => {
this.withExpression(ctx, op.left, typeSystem);
this.withExpression(ctx, op.right, typeSystem);
typeSystem.compare({
left: op.left.type,
operation: { operation: "equals" },
right: op.type,
});
typeSystem.compare({
left: op.right.type,
operation: { operation: "equals" },
right: op.type,
});
};
}

View File

@@ -0,0 +1,205 @@
import {
AssignmentStatement,
Block,
containsReturn,
Expression,
Function,
FunctionCall,
FunctionDeclaration,
functionToType,
IfExpression,
Impl,
LetStatement,
LiteralStruct,
Module,
newVoid,
Operation,
ReturnStatement,
StructGetter,
StructTypeDeclaration,
TraitTypeDeclaration,
TypeUsage,
Path,
} from "../parse/ast";
import { newContext } from "./builtins";
import { Context, deepCopy, typeExists } from "./context";
import { TypeSystem } from "./type_system";
export class TypeResolver {
withModule = (module: Module, typeSystem: TypeSystem) => {
const result = deepCopy(module);
// environment set up, actually recurse.
for (const [i, item] of module.items.entries()) {
if (item.moduleItem === "Function") {
result.items[i] = this.withFunction(item, typeSystem);
}
if (item.moduleItem === "Impl") {
result.items[i] = this.withImpl(item, typeSystem);
}
if (item.moduleItem === "StructTypeDeclaration") {
result.items[i] = this.withStructDeclaration(item, typeSystem);
}
if (item.moduleItem === "TraitTypeDeclaration") {
result.items[i] = this.withTrait(item, typeSystem);
}
}
return result;
};
withFunction = (fn: Function, typeSystem: TypeSystem) => {
const result = deepCopy(fn);
result.declaration = this.withFunctionDeclaration(fn.declaration, typeSystem);
result.block = this.withBlock(fn.block, typeSystem);
return result;
};
withFunctionDeclaration = (def: FunctionDeclaration, typeSystem: TypeSystem) => {
const result = deepCopy(def);
result.arguments = def.arguments.map((arg) => {
const resultArg = deepCopy(arg);
resultArg.type = typeSystem.resolveType(arg.type);
return resultArg;
});
return result;
};
withImpl = (impl: Impl, typeSystem: TypeSystem) => {
const result = deepCopy(impl);
for (const [i, fn] of impl.functions.entries()) {
result.functions[i] = this.withFunction(fn, typeSystem);
}
return result;
};
withStructDeclaration = (struct: StructTypeDeclaration, typeSystem: TypeSystem) => {
const result = deepCopy(struct);
for (const [i, field] of struct.fields.entries()) {
result.fields[i] = { name: field.name, type: typeSystem.resolveType(field.type) };
}
return result;
};
withTrait = (trait: TraitTypeDeclaration, typeSystem: TypeSystem) => {
const result = deepCopy(trait);
for (const [i, method] of trait.functions.entries()) {
result.functions[i] = this.withFunctionDeclaration(method, typeSystem);
}
return result;
};
withBlock = (block: Block, typeSystem: TypeSystem) => {
const result = deepCopy(block);
for (const [i, statement] of block.statements.entries()) {
if (statement.statementType === "AssignmentStatement") {
result.statements[i] = this.withAssignmentStatement(statement, typeSystem);
}
if (statement.statementType === "LetStatement") {
result.statements[i] = this.withLetStatement(statement, typeSystem);
}
if (statement.statementType === "Expression") {
result.statements[i] = this.withExpression(statement, typeSystem);
}
if (statement.statementType === "ReturnStatement") {
result.statements[i] = this.withReturnStatement(statement, typeSystem);
}
result.type = typeSystem.resolveType(block.type);
}
return result;
};
withAssignmentStatement = (statement: AssignmentStatement, typeSystem: TypeSystem) => {
const result = deepCopy(statement);
if (statement.source.type == "StructGetter") {
result.source = {
type: "StructGetter",
source: this.withStructGetter(statement.source.source, typeSystem),
};
}
result.expression = this.withExpression(statement.expression, typeSystem);
return result;
};
withLetStatement = (statement: LetStatement, typeSystem: TypeSystem) => {
const result = deepCopy(statement);
result.expression = this.withExpression(statement.expression, typeSystem);
result.type = typeSystem.resolveType(statement.type);
return result;
};
withReturnStatement = (statement: ReturnStatement, typeSystem: TypeSystem) => {
const result = deepCopy(statement);
result.source = this.withExpression(statement.source, typeSystem);
return result;
};
withExpression = (expression: Expression, typeSystem: TypeSystem) => {
const result = deepCopy(expression);
result.type = typeSystem.resolveType(expression.type);
if (expression.subExpression.expressionType === "LiteralStruct") {
result.subExpression = this.withLiteralStruct(expression.subExpression, typeSystem);
}
if (expression.subExpression.expressionType === "FunctionCall") {
result.subExpression = this.withFunctionCall(expression.subExpression, typeSystem);
}
if (expression.subExpression.expressionType === "Path") {
// paths do not have types
}
if (expression.subExpression.expressionType === "IfExpression") {
result.subExpression = this.withIfExpression(expression.subExpression, typeSystem);
}
if (expression.subExpression.expressionType === "StructGetter") {
result.subExpression = this.withStructGetter(expression.subExpression, typeSystem);
}
if (expression.subExpression.expressionType === "Block") {
result.subExpression = this.withBlock(expression.subExpression, typeSystem);
}
if (expression.subExpression.expressionType === "Operation") {
result.subExpression = this.withOperation(expression.subExpression, typeSystem);
}
return result;
};
withLiteralStruct = (literal: LiteralStruct, typeSystem: TypeSystem) => {
const result = deepCopy(literal);
for (const [i, field] of literal.fields.entries()) {
result.fields[i].expression = this.withExpression(field.expression, typeSystem);
}
result.type = typeSystem.resolveType(literal.type);
return result;
};
withFunctionCall = (fnCall: FunctionCall, typeSystem: TypeSystem) => {
const result = deepCopy(fnCall);
result.type = typeSystem.resolveType(fnCall.type);
result.source = this.withExpression(fnCall.source, typeSystem);
for (const [i, arg] of fnCall.arguments.entries()) {
result.arguments[i] = this.withExpression(arg, typeSystem);
}
return result;
};
withIfExpression = (ifExpression: IfExpression, typeSystem: TypeSystem) => {
const result = deepCopy(ifExpression);
result.type = typeSystem.resolveType(ifExpression.type);
result.condition = this.withExpression(ifExpression.condition, typeSystem);
result.block = this.withBlock(ifExpression.block, typeSystem);
result.else = ifExpression.else ? this.withBlock(ifExpression.else, typeSystem) : null;
return result;
};
withStructGetter = (structGetter: StructGetter, typeSystem: TypeSystem) => {
const result = deepCopy(structGetter);
result.type = typeSystem.resolveType(structGetter.type);
result.source = this.withExpression(structGetter.source, typeSystem);
return result;
};
withOperation = (op: Operation, typeSystem: TypeSystem) => {
const result = deepCopy(op);
result.type = typeSystem.resolveType(op.type);
result.left = this.withExpression(op.left, typeSystem);
result.right = this.withExpression(op.right, typeSystem);
return result;
};
}

View File

@@ -0,0 +1,203 @@
import { FunctionTypeUsage, TypeUsage, UnknownTypeUsage } from "../parse/ast";
import { newContext } from "./builtins";
import { Context, deepCopy, getAttr } from "./context";
export const compareTypes = (typeA: TypeUsage, typeB: TypeUsage) => {
if (typeA.typeUsage !== typeB.typeUsage) {
throw Error(
`Mismatched types: ${JSON.stringify(typeA, null, 2)} ${JSON.stringify(typeB, null, 2)}`,
);
}
if (typeA.typeUsage == "NamedTypeUsage" && typeB.typeUsage == "NamedTypeUsage") {
if (typeB.name.text === "Never") {
// never matches with everything
return;
}
if (typeA.name.text !== typeB.name.text) {
throw Error(
`Mismatched types: ${typeA.name.text}:${typeA.name.spanStart}:${typeA.name.spanEnd} ${typeB.name.text}:${typeB.name.spanStart}:${typeB.name.spanEnd}`,
);
}
}
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);
}
};
interface Equals {
operation: "equals";
}
interface Field {
operation: "field";
name: string;
}
interface Comparison {
left: TypeUsage;
operation: Equals | Field;
right: TypeUsage;
}
export class TypeSystem {
comparisons: Comparison[];
result: Record<string, TypeUsage>;
context: Context;
constructor() {
this.comparisons = [];
this.result = {};
this.context = newContext();
}
compare = (comparison: Comparison) => {
this.comparisons.push(comparison);
};
solve = () => {
let foundUpdate = false;
while (true) {
foundUpdate = false;
for (const comparison of this.comparisons) {
// if already found, just update
comparison.left = this.resolveType(comparison.left);
comparison.right = this.resolveType(comparison.right);
// equals
if (comparison.operation.operation === "equals") {
const [result, found] = this.equateTypes(comparison.left, comparison.right);
if (found) {
comparison.left = result;
comparison.right = result;
foundUpdate = true;
}
// check
if (
comparison.left.typeUsage !== "UnknownTypeUsage" &&
comparison.right.typeUsage !== "UnknownTypeUsage"
) {
compareTypes(comparison.left, comparison.right);
}
}
// field
if (comparison.operation.operation === "field") {
if (comparison.left.typeUsage === "UnknownTypeUsage") {
// cannot yet be resolved
continue;
}
if (comparison.left.typeUsage !== "NamedTypeUsage") {
throw Error("field on something that isn't a named type.");
}
// cannot be solved left
// solve right
if (comparison.right.typeUsage === "UnknownTypeUsage") {
foundUpdate = true;
const attrType = getAttr(
this.context,
comparison.left.name.text,
comparison.operation.name,
);
const [result, found] = this.equateTypes(attrType, comparison.right);
if (found) {
comparison.right = result;
foundUpdate = true;
}
}
// check
if (comparison.right.typeUsage !== "UnknownTypeUsage") {
const attrType = getAttr(
this.context,
comparison.left.name.text,
comparison.operation.name,
);
compareTypes(attrType, comparison.right);
}
}
}
let containsUnknown = false;
for (const comparison of this.comparisons) {
if (this.containsUnknown(comparison.left) || this.containsUnknown(comparison.right)) {
containsUnknown = true;
}
}
if (!foundUpdate && containsUnknown) {
throw Error("Type system failed to resolve all unknowns");
}
if (!foundUpdate) {
break;
}
}
return this.result;
};
equateTypes = (left: TypeUsage, right: TypeUsage): [TypeUsage, boolean] => {
if (left.typeUsage === "UnknownTypeUsage" && right.typeUsage !== "UnknownTypeUsage") {
this.result[left.name] = right;
return [right, true];
}
if (left.typeUsage !== "UnknownTypeUsage" && right.typeUsage === "UnknownTypeUsage") {
this.result[right.name] = left;
return [left, true];
}
if (left.typeUsage === "FunctionTypeUsage" && right.typeUsage === "FunctionTypeUsage") {
if (left.arguments.length !== right.arguments.length) {
throw Error(`Mismatched arg lengths: ${left.arguments.length} ${right.arguments.length}`);
}
let found = false;
let fnResult: FunctionTypeUsage = deepCopy(left);
for (let i = 0; i < left.arguments.length; i++) {
const [result, wasFound] = this.equateTypes(left.arguments[i], right.arguments[i]);
if (wasFound) {
found = true;
}
fnResult.arguments[i] = result;
}
const [result, wasFound] = this.equateTypes(left.returnType, right.returnType);
if (wasFound) {
found = true;
}
fnResult.returnType = result;
return [fnResult, found];
}
return [left, false];
};
resolveType = (type: TypeUsage): TypeUsage => {
if (type.typeUsage === "UnknownTypeUsage") {
if (this.result[type.name]) {
return this.result[type.name];
}
return type;
}
if (type.typeUsage === "NamedTypeUsage") {
return type;
}
return {
typeUsage: "FunctionTypeUsage",
arguments: type.arguments.map((arg) => this.resolveType(arg)),
returnType: this.resolveType(type.returnType),
};
};
containsUnknown = (type: TypeUsage) => {
if (type.typeUsage === "UnknownTypeUsage") {
return true;
}
if (type.typeUsage === "FunctionTypeUsage") {
for (const arg of type.arguments) {
if (this.containsUnknown(arg)) {
return true;
}
}
if (this.containsUnknown(type.returnType)) {
return true;
}
}
return false;
};
}

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/**/*"]
}