Initial commit of hvm-lang with basic parser

This commit is contained in:
Nicolas Abril 2023-08-29 22:43:01 +02:00
commit c2eaa8502d
13 changed files with 1004 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

12
.rustfmt.toml Normal file
View File

@ -0,0 +1,12 @@
edition = "2021"
version = "Two"
max_width = 110
# won't add \r\n on windows machines, better on diffs
newline_style = "Unix"
use_small_heuristics = "Max"
tab_spaces = 2
imports_granularity = "Crate"
use_field_init_shorthand = true
use_try_shorthand = true
spaces_around_ranges = true
overflow_delimited_expr = true

459
Cargo.lock generated Normal file
View File

@ -0,0 +1,459 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
]
[[package]]
name = "anstream"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
[[package]]
name = "anstyle-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "ariadne"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd"
dependencies = [
"unicode-width",
"yansi",
]
[[package]]
name = "beef"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chumsky"
version = "1.0.0-alpha.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc3172a80699de358070dd99f80ea8badc6cdf8ac2417cb5a96e6d81bf5fe06d"
dependencies = [
"hashbrown",
"stacker",
]
[[package]]
name = "clap"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c8d502cbaec4595d2e7d5f61e318f05417bd2b66fdc3809498f0d3fdf0bea27"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5891c7bc0edb3e1c2204fc5e94009affabeb1821c9e5fdc3959536c5c0bb984d"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.29",
]
[[package]]
name = "clap_lex"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hvm-lang"
version = "0.1.0"
dependencies = [
"anyhow",
"ariadne",
"chumsky",
"clap",
"logos",
"shrinkwraprs",
]
[[package]]
name = "itertools"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "logos"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1"
dependencies = [
"logos-derive",
]
[[package]]
name = "logos-codegen"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68"
dependencies = [
"beef",
"fnv",
"proc-macro2",
"quote",
"regex-syntax",
"syn 2.0.29",
]
[[package]]
name = "logos-derive"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e"
dependencies = [
"logos-codegen",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "psm"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
dependencies = [
"cc",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "shrinkwraprs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63e6744142336dfb606fe2b068afa2e1cca1ee6a5d8377277a92945d81fa331"
dependencies = [
"bitflags",
"itertools",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "stacker"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
dependencies = [
"cc",
"cfg-if",
"libc",
"psm",
"winapi",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[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"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"

14
Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "hvm-lang"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.72"
ariadne = "0.3.0"
chumsky = "1.0.0-alpha.4"
clap = {version = "4.4.1", features = ["derive"]}
logos = "0.13.0"
shrinkwraprs = "0.3.0"

34
justfile Normal file
View File

@ -0,0 +1,34 @@
# To use this, first run `cargo install just`
# Then run the install subcommand: `just install`
# Running `just` is equivalent to running `just all` because it's the first command
export CARGO_TERM_COLOR := "always"
all: rustup-show test clippy fmt sort audit
rustup-show:
rustup show
test:
cargo test --workspace
clippy:
cargo clippy
fmt:
cargo fmt --all -- --check
sort:
cargo sort --check --workspace
audit:
cargo audit
install:
cargo install --locked cargo-sort cargo-audit
llcheck:
cargo watch -x llcheck
extras:
cargo install --locked cargo-watch cargo-limit amber

4
rust-toolchain.toml Normal file
View File

@ -0,0 +1,4 @@
[toolchain]
profile = "minimal"
channel = "nightly-2023-07-10"
components = ["rustfmt", "clippy"]

72
src/ast/mod.rs Normal file
View File

@ -0,0 +1,72 @@
use std::collections::HashMap;
use shrinkwraprs::Shrinkwrap;
#[derive(Debug, Clone, Default)]
pub struct DefinitionBook {
pub definitions: HashMap<Name, Definition>,
}
#[derive(Debug, Clone)]
pub struct Definition {
pub name: Name,
pub rules: Vec<Rule>,
}
#[derive(Debug, Clone)]
pub struct Rule {
pub name: Name,
pub pats: Vec<Pattern>,
pub body: Term,
}
#[derive(Debug, Clone)]
pub enum Pattern {
Ctr(Name, Vec<Pattern>),
Num(Number),
Var(Name),
}
#[derive(Debug, Clone)]
pub enum Term {
Lam { name: Name, body: Box<Term> },
Var { name: Name },
App { func: Box<Term>, arg: Box<Term> },
Dup { fst: Name, snd: Name, val: Box<Term>, next: Box<Term> },
Def { name: Name },
Num { val: Number },
NumOp { op: NumOper, fst: Box<Term>, snd: Box<Term> },
}
#[derive(Debug, PartialEq, Eq, Clone, Shrinkwrap, Hash)]
pub struct Name(pub String);
// TODO: Accept other number types
#[derive(Debug, PartialEq, Eq, Clone, Copy, Shrinkwrap)]
pub struct Number(pub u16);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum NumOper {
Add,
Sub,
Mul,
Div,
Mod,
And,
Or,
Xor,
Shl,
Shr,
Ltn,
Lte,
Gtn,
Gte,
Eql,
Neq,
}
impl DefinitionBook {
pub fn new() -> Self {
Default::default()
}
}

25
src/loader/mod.rs Normal file
View File

@ -0,0 +1,25 @@
use crate::{ast::DefinitionBook, parser::parse_definition_book};
use ariadne::{Color, Label, Report, ReportKind, Source};
use std::path::Path;
/// Reads a file and parses to a definition book.
pub fn load_file_to_book(path: &Path) -> anyhow::Result<DefinitionBook> {
let code = std::fs::read_to_string(path)?;
match parse_definition_book(&code) {
Ok(book) => Ok(book),
Err(errs) => {
for err in errs {
Report::build(ReportKind::Error, (), err.span().start)
.with_code(3)
.with_message(err.to_string())
.with_label(
Label::new(err.span().into_range()).with_message(err.reason().to_string()).with_color(Color::Red),
)
.finish()
.eprint(Source::from(code.clone()))
.unwrap();
}
Err(anyhow::anyhow!("Parsing error"))
}
}
}

22
src/main.rs Normal file
View File

@ -0,0 +1,22 @@
use clap::Parser;
use std::path::PathBuf;
mod ast;
mod loader;
mod parser;
mod to_core;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
path: PathBuf,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let book = loader::load_file_to_book(&args.path)?;
println!("{book:?}");
// let compiled = to_core::book_to_hvm_core(book)?;
// println!("{compiled}");
Ok(())
}

182
src/parser/lexer.rs Normal file
View File

@ -0,0 +1,182 @@
use logos::{FilterResult, Lexer, Logos};
use std::fmt;
use crate::ast::Number;
#[derive(Logos, Debug, PartialEq, Clone)]
#[logos(error=LexingError)]
pub enum Token {
#[regex("[_a-zA-Z][_a-zA-Z0-9]*", |lex| lex.slice().parse().ok())]
Name(String),
#[regex(r"\\")]
Lambda,
#[token("let")]
Let,
#[token("dup")]
Dup,
#[token("=")]
Equals,
#[regex("[0-9]+", |lex| lex.slice().parse().map(|x| Number(x)).ok())]
Number(Number),
#[token("+")]
Add,
#[token("-")]
Sub,
#[token("*")]
Mul,
#[token("/")]
Div,
#[token("%")]
Mod,
#[token("&")]
And,
#[token("|")]
Or,
#[token("^")]
Xor,
#[token("<<")]
Shl,
#[token(">>")]
Shr,
#[token("<")]
Ltn,
#[token("<=")]
Lte,
#[token(">")]
Gtn,
#[token(">=")]
Gte,
#[token("==")]
EqualsEquals,
#[token("!=")]
NotEquals,
#[token(";")]
Semicolon,
#[token("(")]
LParen,
#[token(")")]
RParen,
#[token("\n")]
NewLine,
#[regex("//.*", logos::skip)]
SingleLineComment,
#[token("/*", comment)]
MultiLineComment,
#[regex(r"[ \t\f\r]+", logos::skip)]
Whitespace,
Error(LexingError),
}
#[derive(Default, Debug, PartialEq, Clone)]
pub enum LexingError {
UnclosedComment,
#[default]
InvalidCharacter,
}
// Lexer for nested multi-line comments
#[derive(Logos)]
pub enum MultiLineComment {
#[token("/*")]
Open,
#[token("*/")]
Close,
#[regex("(?s).")]
Other,
}
fn comment(lexer: &mut Lexer<'_, Token>) -> FilterResult<(), LexingError> {
let start = lexer.remainder();
let mut comment = MultiLineComment::lexer(start);
let mut depth = 1; // Already matched an Open token, so count it
loop {
if let Some(token) = comment.next() {
match token {
Ok(MultiLineComment::Open) => depth += 1,
Ok(MultiLineComment::Close) => depth -= 1,
Ok(MultiLineComment::Other) => {}
Err(()) => unreachable!(),
}
} else {
// Unclosed comment
return FilterResult::Error(LexingError::UnclosedComment);
}
if depth <= 0 {
break;
}
}
let end = comment.remainder();
let span = (end as *const str as *const () as usize) - (start as *const str as *const () as usize);
lexer.bump(span);
FilterResult::Skip
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Name(s) => write!(f, "{}", s),
Self::Lambda => write!(f, r"λ"),
Self::Let => write!(f, "let"),
Self::Dup => write!(f, "dup"),
Self::Equals => write!(f, "="),
Self::Number(num) => write!(f, "{}", num.as_ref()),
Self::Add => write!(f, "+"),
Self::Sub => write!(f, "-"),
Self::Mul => write!(f, "*"),
Self::Div => write!(f, "/"),
Self::Mod => write!(f, "%"),
Self::And => write!(f, "&"),
Self::Or => write!(f, "|"),
Self::Xor => write!(f, "^"),
Self::Shl => write!(f, "<<"),
Self::Shr => write!(f, ">>"),
Self::Ltn => write!(f, "<"),
Self::Lte => write!(f, "<="),
Self::Gtn => write!(f, ">"),
Self::Gte => write!(f, ">="),
Self::NotEquals => write!(f, "!="),
Self::EqualsEquals => write!(f, "=="),
Self::Semicolon => write!(f, ";"),
Self::LParen => write!(f, "("),
Self::RParen => write!(f, ")"),
Self::NewLine => write!(f, "<NewLine>"),
Self::SingleLineComment => write!(f, "<SingleLineComment>"),
Self::MultiLineComment => write!(f, "<MultiLineComment>"),
Self::Whitespace => write!(f, "<Whitespace>"),
Self::Error(LexingError::InvalidCharacter) => write!(f, "<InvalidCharacter>"),
Self::Error(LexingError::UnclosedComment) => write!(f, "<UnclosedComment>"),
}
}
}

4
src/parser/mod.rs Normal file
View File

@ -0,0 +1,4 @@
mod lexer;
mod parser;
pub use parser::parse_definition_book;

170
src/parser/parser.rs Normal file
View File

@ -0,0 +1,170 @@
use crate::{
ast::{Definition, DefinitionBook, Name, NumOper, Rule, Term},
parser::lexer::Token,
};
use chumsky::{
extra,
input::{Emitter, Stream, ValueInput},
prelude::{Input, Rich},
primitive::{choice, just},
recursive::recursive,
select,
span::SimpleSpan,
IterParser, Parser,
};
use logos::Logos;
use std::collections::HashMap;
pub fn parse_definition_book(code: &str) -> Result<DefinitionBook, Vec<Rich<Token>>> {
let token_iter = Token::lexer(code).spanned().map(|(token, span)| match token {
Ok(t) => (t, SimpleSpan::from(span).into()),
Err(e) => (Token::Error(e), SimpleSpan::from(span).into()),
});
let token_stream = Stream::from_iter(token_iter).spanned(SimpleSpan::from(code.len() .. code.len()).into());
book_parser().parse(token_stream).into_result()
}
// TODO: Pattern matching on rules
// TODO: Other types of numbers
/// <Name> ::= <name_token> // [_a-zA-Z][_a-zA-Z0-9]*
/// <Number> ::= <number_token> // [0-9]+
/// <Var> ::= <Name>
/// <Nested> ::= "(" <newline_token>* <Term> <newline_token>* ")"
/// <Item> ::= <Var> | <Number> | <Nested>
/// <App> ::= <Item> <Item>+
/// <Lam> ::= ("λ"|"\") <Name> <Term>
/// <Dup> ::= "dup" <Name> <Name> "=" <Term> ";" <Term>
/// <Let> ::= "let" <Name> "=" <Term> ";" <Term>
/// <NumOp> ::= <numop_token> <Item> <Item>
/// <Term> ::= <Lam> | <App> | <Dup> | <Let> | <NumOp> | <Item>
/// <Rule> ::= "(" <Name> ")" "=" <newline_token>* <Term> <newline_token>*
/// <Def> ::= (<Rule> <NewLine>)+
/// <Book> ::= <Def>+
fn rule_parser<'a, I>() -> impl Parser<'a, I, Rule, extra::Err<Rich<'a, Token>>>
where
I: ValueInput<'a, Token = Token, Span = SimpleSpan>,
{
let new_line = just(Token::NewLine).repeated();
let name = select!(Token::Name(name) => Name(name.to_string()));
let number = select!(Token::Number(num) => Term::Num{val: num});
let num_oper = select! {
Token::Add => NumOper::Add,
Token::Sub => NumOper::Sub,
Token::Mul => NumOper::Mul,
Token::Div => NumOper::Div,
Token::Mod => NumOper::Mod,
Token::And => NumOper::And,
Token::Or => NumOper::Or,
Token::Xor => NumOper::Xor,
Token::Shl => NumOper::Shl,
Token::Shr => NumOper::Shr,
Token::Lte => NumOper::Lte,
Token::Ltn => NumOper::Ltn,
Token::Gte => NumOper::Gte,
Token::Gtn => NumOper::Gtn,
Token::EqualsEquals => NumOper::Eql,
Token::NotEquals => NumOper::Neq,
};
let var = name.map(|name| Term::Var { name });
let term = recursive(|term| {
let nested = term
.clone()
.delimited_by(just(Token::LParen).then(new_line.clone()), just(Token::RParen).then(new_line.clone()));
let item = choice((var, number, nested));
let app = item
.clone()
.foldl(item.clone().repeated(), |fun, arg| Term::App { func: Box::new(fun), arg: Box::new(arg) });
let lam = just(Token::Lambda)
.ignore_then(name)
.then(term.clone())
.map(|(name, body)| Term::Lam { name, body: Box::new(body) });
let dup = just(Token::Dup)
.ignore_then(name)
.then(name)
.then_ignore(just(Token::Equals))
.then(term.clone())
.then_ignore(just(Token::Semicolon))
.then(term.clone())
.map(|(((fst, snd), val), next)| Term::Dup { fst, snd, val: Box::new(val), next: Box::new(next) });
let let_ = just(Token::Let)
.ignore_then(name)
.then_ignore(just(Token::Equals))
.then(term.clone())
.then_ignore(just(Token::Semicolon))
.then(term.clone())
.map(|((name, body), next)| Term::App {
func: Box::new(Term::Lam { name, body: next.into() }),
arg: Box::new(body),
});
let num_op = num_oper.then(item.clone()).then(item.clone()).map(|((op, fst), snd)| Term::NumOp {
op,
fst: Box::new(fst),
snd: Box::new(snd),
});
choice((lam, app, dup, let_, num_op, item))
});
just(Token::LParen)
.ignore_then(name)
.then_ignore(just(Token::RParen))
.then_ignore(just(Token::Equals))
.then(term.delimited_by(new_line.clone(), new_line.clone()))
.map(|(name, body)| Rule { name, pats: vec![], body })
}
fn book_parser<'a, I>() -> impl Parser<'a, I, DefinitionBook, extra::Err<Rich<'a, Token>>>
where
I: ValueInput<'a, Token = Token, Span = SimpleSpan>,
{
fn rules_to_book(
rules: Vec<(Rule, SimpleSpan)>,
_span: SimpleSpan,
emitter: &mut Emitter<Rich<Token>>,
) -> DefinitionBook {
let mut book = DefinitionBook::new();
let mut crnt_def: Option<(Definition, SimpleSpan)> = None;
// Check for repeated defs (could be rules out of order or actually repeated names)
for (rule, rule_span) in rules {
if let Some((crnt_def, def_span)) = crnt_def.as_mut() {
if rule.name == crnt_def.name {
crnt_def.rules.push(rule);
def_span.end = rule_span.end;
} else {
let def = std::mem::replace(crnt_def, Definition { name: rule.name, rules: vec![] });
add_to_def_book(&mut book, def, *def_span, emitter);
}
} else {
crnt_def = Some((Definition { name: rule.name, rules: vec![] }, rule_span));
}
}
if let Some((def, span)) = crnt_def {
add_to_def_book(&mut book, def, span, emitter);
}
book
}
fn add_to_def_book(book: &mut DefinitionBook, def: Definition, span: SimpleSpan, emitter: &mut Emitter<Rich<Token>>) {
if book.definitions.contains_key(&def.name) {
emitter.emit(Rich::custom(span, format!("Repeated definition '{}'", def.name.as_ref())));
} else {
book.definitions.insert(def.name.clone(), def);
}
}
let parsed_rules =
rule_parser().map_with_span(|rule, span| (rule, span)).repeated().collect::<Vec<(Rule, SimpleSpan)>>();
parsed_rules.validate(move |rules, span, emitter| rules_to_book(rules, span, emitter))
}

5
src/to_core/mod.rs Normal file
View File

@ -0,0 +1,5 @@
use crate::ast::DefinitionBook;
pub fn book_to_hvm_core(book: DefinitionBook) -> anyhow::Result<String> {
todo!()
}