This commit is contained in:
Michael Zhang 2024-12-02 03:18:22 -06:00
commit 0e16de17f2
11 changed files with 2093 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

1
.tokeignore Normal file
View file

@ -0,0 +1 @@
examples/

620
Cargo.lock generated Normal file
View file

@ -0,0 +1,620 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
[[package]]
name = "aoclang"
version = "0.1.0"
dependencies = [
"anyhow",
"codespan-reporting",
"derive_more",
"lalrpop",
"lalrpop-util",
"once_cell",
]
[[package]]
name = "ascii-canvas"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891"
dependencies = [
"term",
]
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bit-set"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "cpufeatures"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "derive_more"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "ena"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5"
dependencies = [
"log",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "indexmap"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "keccak"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
dependencies = [
"cpufeatures",
]
[[package]]
name = "lalrpop"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06093b57658c723a21da679530e061a8c25340fa5a6f98e313b542268c7e2a1f"
dependencies = [
"ascii-canvas",
"bit-set",
"ena",
"itertools",
"lalrpop-util",
"petgraph",
"pico-args",
"regex",
"regex-syntax",
"sha3",
"string_cache",
"term",
"unicode-xid",
"walkdir",
]
[[package]]
name = "lalrpop-util"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feee752d43abd0f4807a921958ab4131f692a44d4d599733d4419c5d586176ce"
dependencies = [
"regex-automata",
"rustversion",
]
[[package]]
name = "libc"
version = "0.2.167"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "petgraph"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "phf_shared"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher",
]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[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.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustversion"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sha3"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
dependencies = [
"digest",
"keccak",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "string_cache"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
dependencies = [
"new_debug_unreachable",
"once_cell",
"parking_lot",
"phf_shared",
"precomputed-hash",
]
[[package]]
name = "syn"
version = "2.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "term"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4df4175de05129f31b80458c6df371a15e7fc3fd367272e6bf938e5c351c7ea0"
dependencies = [
"home",
"windows-sys 0.52.0",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

14
Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "aoclang"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.93"
codespan-reporting = "0.11.1"
derive_more = { version = "1.0.0", features = ["debug"] }
lalrpop-util = { version = "0.22.0", features = ["lexer"] }
once_cell = "1.20.2"
[build-dependencies]
lalrpop = "0.22.0"

3
build.rs Normal file
View file

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

1000
examples/1.txt Normal file

File diff suppressed because it is too large Load diff

29
examples/1a.aoc Normal file
View file

@ -0,0 +1,29 @@
input data = "1.txt";
let lines = data.splitlines();
let pairs = lines.map((s) => {
map(s.splitwhitespace(), (n) => { parseint(n) })
});
let lists = pairs.transpose();
let left = lists[0];
let right = lists[1];
let left = left.sort();
let right = right.sort();
let lists = [left, right];
let lists = lists.transpose();
let differences = lists.map((a) => {
let left = a[0];
let right = a[1];
abs(left - right)
});
print(differences);
let result = differences.sum();
print(result);

2
rustfmt.toml Normal file
View file

@ -0,0 +1,2 @@
tab_spaces = 2
max_width = 80

37
src/ast.rs Normal file
View file

@ -0,0 +1,37 @@
use anyhow::Result;
use derive_more::Debug;
#[derive(Debug, Clone)]
pub enum Stmt {
Input(String, String),
Let(String, Expr),
Print(Expr),
}
#[derive(Debug, Clone)]
pub enum Expr {
Block(Vec<Stmt>, Option<Box<Expr>>),
Array(Vec<Expr>),
Int(i64),
Ident(String),
BinOp(Box<Expr>, Op, Box<Expr>),
Field(Box<Expr>, String),
Call(Box<Expr>, Vec<Expr>),
Index(Box<Expr>, Box<Expr>),
Lambda(Vec<String>, Box<Expr>),
}
#[derive(Debug, Clone)]
pub enum Op {
Sub,
}
#[derive(Debug, Clone)]
pub enum Value {
Unit,
Int(i64),
Array(Vec<Value>),
String(String),
Function(Vec<String>, Expr),
Builtin(#[debug(skip)] fn(Vec<Value>) -> Result<Value>),
}

50
src/grammar.lalrpop Normal file
View file

@ -0,0 +1,50 @@
use crate::ast::*;
grammar;
pub Program = StmtSemi*;
StmtSemi: Stmt = <s:Stmt> ";" => s;
Stmt: Stmt = {
"input" <n:Ident> "=" <s:String> => Stmt::Input(n, s),
"let" <n:Ident> "=" <e:Expr> => Stmt::Let(n, e),
"print" "(" <e:Expr> ")" => Stmt::Print(e),
};
Expr: Expr = {
#[precedence(level = "0")]
<i:Ident> => Expr::Ident(i),
<n:Num> => Expr::Int(n),
"(" <n:Sep<",", Ident>> ")" "=>" <e:BlockExpr> => Expr::Lambda(n, Box::new(e)),
"[" <e:Sep<",", Expr>> "]" => Expr::Array(e),
#[precedence(level = "1")] #[assoc(side = "left")]
<l:Expr> "-" <r:Expr> => Expr::BinOp(Box::new(l), Op::Sub, Box::new(r)),
#[precedence(level = "2")]
<e:Expr> "." <n:Ident> => Expr::Field(Box::new(e), n),
#[precedence(level = "3")]
<e:Expr> "(" <a:Sep<",", Expr>> ")" => Expr::Call(Box::new(e), a),
<e:Expr> "[" <i:Expr> "]" => Expr::Index(Box::new(e), Box::new(i)),
};
BlockExpr: Expr = "{" <s:StmtSemi*> <e:Expr?> "}" => Expr::Block(s, e.map(Box::new));
Num: i64 = r"-?[0-9]+" => <>.parse::<i64>().unwrap();
Ident: String = r"[A-Za-z_][A-Za-z0-9_]*" => <>.to_owned();
String: String = r#"\"[^\"]*\""# => { let s = <>; s[1..s.len() - 1].to_owned() };
Sep<S, T>: Vec<T> = { // (1)
<mut v:K<T, S>*> <e:T?> => match e { // (2)
None => v,
Some(e) => {
v.push(e);
v
}
}
};
K<A, B>: A = <a:A> B => a;

336
src/main.rs Normal file
View file

@ -0,0 +1,336 @@
mod ast;
use std::{
collections::HashMap, env, fmt::format, fs, hash::Hash, os::unix::thread,
path::PathBuf, process::exit,
};
use anyhow::{bail, ensure, Context, Result};
use ast::Op;
use codespan_reporting::{
diagnostic::{Diagnostic, Label},
files::SimpleFiles,
term::{
self,
termcolor::{ColorChoice, StandardStream},
},
};
use lalrpop_util::{lalrpop_mod, ParseError};
use once_cell::unsync::{Lazy, OnceCell};
use crate::ast::{Expr, Stmt, Value};
lalrpop_mod!(grammar);
thread_local! {
static PARENT: OnceCell<PathBuf> = OnceCell::new();
}
fn main() -> Result<()> {
let args = env::args().collect::<Vec<_>>();
let path_str = &args[1];
let path = PathBuf::from(path_str);
let parent = path.parent().unwrap();
PARENT.with(|f| {
f.get_or_init(|| parent.to_path_buf());
});
let source = fs::read_to_string(&path)?;
let parser = grammar::ProgramParser::new();
let tree = match parser.parse(&source) {
Ok(v) => v,
Err(e) => {
let mut files = SimpleFiles::new();
let file_id = files.add(path_str, &source);
let diagnostic = match e {
ParseError::InvalidToken { location } => Diagnostic::error()
.with_message("invalid token")
.with_labels(vec![Label::primary(file_id, location..location)]),
ParseError::UnrecognizedEof { location, expected } => todo!(),
ParseError::UnrecognizedToken { token, expected } => {
Diagnostic::error()
.with_message(format!(
"unrecognized token {}, expecting [{}]",
token.1,
expected.join(", ")
))
.with_labels(vec![Label::primary(file_id, token.0..token.2)])
}
ParseError::ExtraToken { token } => Diagnostic::error()
.with_message(format!("extra token {}", token.1))
.with_labels(vec![Label::primary(file_id, token.0..token.2)]),
ParseError::User { error } => Diagnostic::error().with_message(error),
};
let writer = StandardStream::stderr(ColorChoice::Always);
let config = codespan_reporting::term::Config::default();
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
exit(1);
}
};
let mut ctx = HashMap::new();
insert_builtins(&mut ctx);
for item in tree {
eval_stmt(&mut ctx, &item)?;
}
Ok(())
}
fn eval_stmt(ctx: &mut HashMap<String, Value>, stmt: &Stmt) -> Result<()> {
match stmt {
Stmt::Input(n, p) => {
let path = PARENT.with(|f| f.get().unwrap().join(p).to_path_buf());
let data = fs::read_to_string(&path)
.with_context(|| format!("could not read {}", path.display()))?;
ctx.insert(n.to_string(), Value::String(data));
}
Stmt::Let(n, expr) => {
let val = eval_expr(ctx, &expr)?;
ctx.insert(n.to_string(), val);
}
Stmt::Print(expr) => {
let val = eval_expr(ctx, &expr)?;
println!("{val:?}");
}
}
Ok(())
}
fn eval_expr(ctx: &mut HashMap<String, Value>, expr: &Expr) -> Result<Value> {
match expr {
Expr::Ident(n) => match ctx.get(n) {
Some(v) => Ok(v.clone()),
None => bail!("unknown name {n}"),
},
Expr::Call(expr, args) if matches!(**expr, Expr::Field(_, _)) => {
if let Expr::Field(expr, name) = &**expr {
let mut args1 = vec![(**expr).clone()];
args1.extend(args.iter().cloned());
let args = args1;
Ok(eval_expr(
ctx,
&Expr::Call(Box::new(Expr::Ident(name.to_owned())), args),
)?)
} else {
unreachable!()
}
}
Expr::Field(expr, n) => todo!(),
Expr::Call(expr, args) => {
let v = eval_expr(ctx, expr)?;
match v {
Value::Function(params, body) => {
let mapping = params.iter().zip(args.iter()).collect::<Vec<_>>();
todo!()
}
Value::Builtin(func) => {
let mut args2 = Vec::new();
for arg in args {
args2.push(eval_expr(ctx, arg)?);
}
func(args2)
}
_ => todo!(),
}
}
Expr::Lambda(vec, expr) => {
// TODO: Do free variable analysis
Ok(Value::Function(vec.clone(), (**expr).clone()))
}
Expr::Block(vec, expr) => {
for stmt in vec {
eval_stmt(ctx, stmt)?;
}
if let Some(expr) = expr {
Ok(eval_expr(ctx, expr)?)
} else {
Ok(Value::Unit)
}
}
Expr::Index(expr, idx) => {
let idx = match eval_expr(ctx, idx)? {
Value::Int(v) => v,
_ => bail!("not a number: {idx:?}"),
};
let arr = match eval_expr(ctx, expr)? {
Value::Array(a) => a,
_ => bail!("not a list"),
};
Ok(arr[idx as usize].clone())
}
Expr::Int(n) => Ok(Value::Int(*n)),
Expr::Array(vec) => {
let mut arr = Vec::new();
for v in vec {
arr.push(eval_expr(ctx, v)?);
}
Ok(Value::Array(arr))
}
Expr::BinOp(left, op, right) => {
let left = eval_expr(ctx, left)?;
let right = eval_expr(ctx, right)?;
match op {
Op::Sub => match (&left, &right) {
(Value::Int(left), Value::Int(right)) => Ok(Value::Int(left - right)),
_ => bail!("both {left:?} , {right:?} must be ints"),
},
}
}
}
}
fn insert_builtins(ctx: &mut HashMap<String, Value>) {
fn splitlines(args: Vec<Value>) -> Result<Value> {
ensure!(args.len() == 1, "splitlines takes one argument");
let arg = &args[0];
match arg {
Value::String(s) => Ok(Value::Array(
s.lines().map(|s| Value::String(s.to_owned())).collect(),
)),
_ => bail!("splitlines takes a string, got {:?} instead", arg),
}
}
ctx.insert("splitlines".to_owned(), Value::Builtin(splitlines));
fn splitwhitespace(args: Vec<Value>) -> Result<Value> {
ensure!(args.len() == 1, "splitwhitespace takes one argument");
let arg = &args[0];
match arg {
Value::String(s) => Ok(Value::Array(
s.split_whitespace()
.map(|s| Value::String(s.to_owned()))
.collect(),
)),
_ => bail!("splitwhitespace takes a string, got {:?} instead", arg),
}
}
ctx.insert(
"splitwhitespace".to_owned(),
Value::Builtin(splitwhitespace),
);
fn map(args: Vec<Value>) -> Result<Value> {
ensure!(args.len() == 2, "map takes 2 arguments");
let list = &args[0];
let func = &args[1];
match (list, func) {
(Value::Array(a), Value::Function(s, f)) => {
let mut res = Vec::new();
let mut ctx = HashMap::new();
insert_builtins(&mut ctx);
for val in a {
ctx.insert(s[0].to_owned(), val.clone());
res.push(eval_expr(&mut ctx, f)?);
}
Ok(Value::Array(res))
// todo!(" s={s:?}, f={f:?}")
}
_ => bail!("wrong types"),
}
}
ctx.insert("map".to_owned(), Value::Builtin(map));
fn parseint(args: Vec<Value>) -> Result<Value> {
ensure!(args.len() == 1, "parseint takes one argument");
let arg = &args[0];
match arg {
Value::String(s) => Ok(Value::Int(s.parse::<i64>()?)),
_ => bail!("parseint takes a string, got {:?} instead", arg),
}
}
ctx.insert("parseint".to_owned(), Value::Builtin(parseint));
fn transpose(args: Vec<Value>) -> Result<Value> {
ensure!(args.len() == 1, "transpose takes one argument");
let arg = &args[0];
match arg {
Value::Array(s) => {
let mut res = Vec::new();
let mut first = true;
for row in s {
let row = match row {
Value::Array(v) => v,
_ => bail!("transpose takes an array of arrays"),
};
if first {
for _ in row {
res.push(vec![]);
}
first = false;
}
ensure!(
row.len() == res.len(),
"non-uniform number of elements per row, expected {}, got {}",
res.len(),
row.len()
);
for (i, e) in row.iter().enumerate() {
res[i].push(e.clone());
}
}
Ok(Value::Array(res.into_iter().map(Value::Array).collect()))
}
_ => bail!("transpose takes an array of arrays, got {:?} instead", arg),
}
}
ctx.insert("transpose".to_owned(), Value::Builtin(transpose));
fn sort(args: Vec<Value>) -> Result<Value> {
ensure!(args.len() == 1, "sort takes one argument");
let arg = &args[0];
match arg {
Value::Array(a) => {
let mut arr = Vec::new();
for v in a {
match v {
Value::Int(n) => arr.push(*n),
_ => bail!("not a number"),
}
}
arr.sort();
Ok(Value::Array(arr.into_iter().map(Value::Int).collect()))
}
_ => bail!("sort takes an array, got {:?} instead", arg),
}
}
ctx.insert("sort".to_owned(), Value::Builtin(sort));
fn abs(args: Vec<Value>) -> Result<Value> {
ensure!(args.len() == 1, "abs takes one argument");
let arg = &args[0];
match arg {
Value::Int(n) => Ok(Value::Int(n.abs())),
_ => bail!("abs takes an array, got {:?} instead", arg),
}
}
ctx.insert("abs".to_owned(), Value::Builtin(abs));
fn sum(args: Vec<Value>) -> Result<Value> {
ensure!(args.len() == 1, "sum takes one argument");
let arg = &args[0];
match arg {
Value::Array(a) => {
let mut sum = 0;
for v in a {
match v {
Value::Int(n) => sum += *n,
_ => bail!("not a number"),
}
}
Ok(Value::Int(sum))
}
_ => bail!("sum takes an array, got {:?} instead", arg),
}
}
ctx.insert("sum".to_owned(), Value::Builtin(sum));
}