start implementing
This commit is contained in:
parent
e27c3e624e
commit
e6d013c716
9 changed files with 846 additions and 1 deletions
289
Cargo.lock
generated
Normal file
289
Cargo.lock
generated
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "addr2line"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace-on-stack-overflow"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fd2d70527f3737a1ad17355e260706c1badebabd1fa06a7a053407380df841b"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"libc",
|
||||||
|
"nix",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chumsky"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
"stacker",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.23.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.35.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "persist"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace-on-stack-overflow",
|
||||||
|
"chumsky",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.85"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||||
|
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.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
|
[[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 = "syn"
|
||||||
|
version = "2.0.66"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[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 = "zerocopy"
|
||||||
|
version = "0.7.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
|
@ -6,3 +6,5 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
backtrace-on-stack-overflow = "0.3.0"
|
||||||
|
chumsky = "0.9.3"
|
||||||
|
|
10
run.sh
Executable file
10
run.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
|
|
||||||
|
echo "
|
||||||
|
let value = 4 * 3 + 2 * 1;
|
||||||
|
let expects = (4 * 3) + (2 * 1);
|
||||||
|
print(value, expects)
|
||||||
|
" | cargo run
|
2
rustfmt.toml
Normal file
2
rustfmt.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
hard_tabs = true
|
||||||
|
max_width = 120
|
3
src/lib/mod.rs
Normal file
3
src/lib/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod parse;
|
||||||
|
|
||||||
|
pub type Span = std::ops::Range<usize>;
|
304
src/lib/parse/ast/mod.rs
Normal file
304
src/lib/parse/ast/mod.rs
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
use crate::lib::parse::{token::Tok, AstParser};
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
any::{type_name, type_name_of_val},
|
||||||
|
fmt::Display,
|
||||||
|
};
|
||||||
|
|
||||||
|
use chumsky::{prelude::*, Parser as _};
|
||||||
|
use text::{ident, int, whitespace};
|
||||||
|
|
||||||
|
use super::token::Key;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Program {
|
||||||
|
body: Body,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn parser() -> impl AstParser<Self> {
|
||||||
|
let expr = Expression::parser();
|
||||||
|
Body::parser(expr).map(|body| Self { body }).then_ignore(end())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Statement {
|
||||||
|
Definition(Definition),
|
||||||
|
Assign(Assign),
|
||||||
|
Expression(Expression),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Statement {
|
||||||
|
fn parser(expr: impl AstParser<Expression> + 'static) -> impl AstParser<Self> {
|
||||||
|
let def = Definition::parser(expr.clone()).map(Self::Definition);
|
||||||
|
let assign = Assign::parser(expr).map(Self::Assign);
|
||||||
|
let expr = Expression::parser().map(Self::Expression);
|
||||||
|
def.or(assign).or(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Definition {
|
||||||
|
defined: Pattern,
|
||||||
|
value: Expression,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Definition {
|
||||||
|
fn parser(expr: impl AstParser<Expression> + 'static) -> impl AstParser<Self> {
|
||||||
|
just(Tok::Let)
|
||||||
|
.ignore_then(Pattern::parser())
|
||||||
|
.then_ignore(just(Tok::Key(Key::Assign)))
|
||||||
|
.then(expr)
|
||||||
|
.map(|(defined, value)| Self { defined, value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Assign {
|
||||||
|
assigned: Pattern,
|
||||||
|
value: Expression,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Assign {
|
||||||
|
fn parser(expr: impl AstParser<Expression> + 'static) -> impl AstParser<Self> {
|
||||||
|
Pattern::parser()
|
||||||
|
.then_ignore(just(Tok::Key(Key::Assign)))
|
||||||
|
.then(expr)
|
||||||
|
.map(|(assigned, value)| Self { assigned, value })
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Name(String);
|
||||||
|
|
||||||
|
impl Name {
|
||||||
|
fn parser() -> impl AstParser<Self> {
|
||||||
|
select! { Tok::Id(name) => name.clone() }.map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Pattern {
|
||||||
|
Name(Name),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pattern {
|
||||||
|
fn parser() -> impl AstParser<Self> {
|
||||||
|
Name::parser().map(Self::Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Expression {
|
||||||
|
Literal(Literal),
|
||||||
|
Variable(Name),
|
||||||
|
Block(Body),
|
||||||
|
Op(BoxEx, Op, BoxEx),
|
||||||
|
Access(BoxEx, Access),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expression {
|
||||||
|
#[allow(clippy::let_and_return)]
|
||||||
|
fn parser() -> impl AstParser<Self> {
|
||||||
|
recursive(|rec| {
|
||||||
|
let literal = Literal::parser().map(Self::Literal);
|
||||||
|
let variable = Name::parser().map(Self::Variable);
|
||||||
|
let value = literal.or(variable);
|
||||||
|
|
||||||
|
let closed = value.or(rec
|
||||||
|
.clone()
|
||||||
|
.boxed()
|
||||||
|
.delimited_by(just(Tok::Key(Key::BracketsL)), just(Tok::Key(Key::BracketsR))));
|
||||||
|
|
||||||
|
let access = closed
|
||||||
|
.then(Access::parser(rec.clone()).repeated())
|
||||||
|
.foldl(|e, a| Self::Access(BoxEx::new(e), a));
|
||||||
|
|
||||||
|
let product = access
|
||||||
|
.clone()
|
||||||
|
.then(Op::parser_mul().then(access).repeated())
|
||||||
|
.foldl(|a, (op, b)| Self::Op(BoxEx::new(a), op, BoxEx::new(b)));
|
||||||
|
|
||||||
|
let addition = product
|
||||||
|
.clone()
|
||||||
|
.then(Op::parser_add().then(product).repeated())
|
||||||
|
.foldl(|a, (op, b)| Self::Op(BoxEx::new(a), op, BoxEx::new(b)));
|
||||||
|
|
||||||
|
let comparisons = addition
|
||||||
|
.clone()
|
||||||
|
.then(Op::parser_cmp().then(addition).repeated())
|
||||||
|
.foldl(|a, (op, b)| Self::Op(BoxEx::new(a), op, BoxEx::new(b)));
|
||||||
|
|
||||||
|
comparisons
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct BoxEx(pub Box<Expression>);
|
||||||
|
|
||||||
|
impl BoxEx {
|
||||||
|
fn new(expr: Expression) -> Self {
|
||||||
|
Self(Box::new(expr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ExprParser<I: Clone>: Parser<I, Expression> {
|
||||||
|
fn boxify(self) -> chumsky::combinator::Map<Self, fn(Expression) -> BoxEx, Expression>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.map(|e| BoxEx(Box::new(e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T, I> ExprParser<I> for T
|
||||||
|
where
|
||||||
|
T: Parser<I, Expression>,
|
||||||
|
I: Clone,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Literal {
|
||||||
|
Number(f32),
|
||||||
|
String(String),
|
||||||
|
Bool(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::hash::Hash for Literal {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
core::mem::discriminant(self).hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Literal {}
|
||||||
|
|
||||||
|
fn parse_num(num: &str) -> Option<f32> {
|
||||||
|
if num.starts_with('.') {
|
||||||
|
format!("0{num}").parse().ok()
|
||||||
|
} else {
|
||||||
|
num.parse().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Literal {
|
||||||
|
fn parser() -> impl AstParser<Self> {
|
||||||
|
let num = select! { Tok::Num(num) => num }
|
||||||
|
.map(|s| parse_num(&s).unwrap())
|
||||||
|
.map(Self::Number);
|
||||||
|
let str = select! {Tok::Str(str) => str}.map(Self::String);
|
||||||
|
let bool = select! { Tok::Bool(bool) => bool }.map(Self::Bool);
|
||||||
|
num.or(str).or(bool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Access {
|
||||||
|
Invoke(Invoke),
|
||||||
|
Index(Index),
|
||||||
|
Property(Property),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Access {
|
||||||
|
fn parser(expr: impl AstParser<Expression>) -> impl AstParser<Self> {
|
||||||
|
Invoke::parser(expr.clone())
|
||||||
|
.map(Self::Invoke)
|
||||||
|
.or(Index::parser(expr).map(Self::Index))
|
||||||
|
.or(Property::parser().map(Self::Property))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Invoke {
|
||||||
|
arguments: Vec<Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Invoke {
|
||||||
|
fn parser(expr: impl AstParser<Expression>) -> impl AstParser<Self> {
|
||||||
|
expr.separated_by(just(Tok::Key(Key::Comma)))
|
||||||
|
.delimited_by(just(Tok::Key(Key::BracketsL)), just(Tok::Key(Key::BracketsR)))
|
||||||
|
.map(|arguments| Self { arguments })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Index {
|
||||||
|
index: BoxEx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index {
|
||||||
|
fn parser(expr: impl AstParser<Expression>) -> impl AstParser<Self> {
|
||||||
|
expr.delimited_by(just(Tok::Key(Key::SquBrackL)), just(Tok::Key(Key::SquBrackR)))
|
||||||
|
.boxify()
|
||||||
|
.map(|index| Self { index })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Property {
|
||||||
|
index: Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Property {
|
||||||
|
fn parser() -> impl AstParser<Self> {
|
||||||
|
Name::parser().map(|index| Self { index })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Op {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Div,
|
||||||
|
Mul,
|
||||||
|
Eq,
|
||||||
|
Neq,
|
||||||
|
Lt,
|
||||||
|
Gt,
|
||||||
|
Leq,
|
||||||
|
Geq,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Op {
|
||||||
|
fn parser_add() -> impl AstParser<Self> {
|
||||||
|
let add = just(Tok::Key(Key::Add)).to(Self::Add);
|
||||||
|
let sub = just(Tok::Key(Key::Sub)).to(Self::Sub);
|
||||||
|
add.or(sub)
|
||||||
|
}
|
||||||
|
fn parser_mul() -> impl AstParser<Self> {
|
||||||
|
let div = just(Tok::Key(Key::Div)).to(Self::Div);
|
||||||
|
let mul = just(Tok::Key(Key::Mul)).to(Self::Mul);
|
||||||
|
div.or(mul)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parser_cmp() -> impl AstParser<Self> {
|
||||||
|
let eq = just(Tok::Key(Key::Eq)).to(Self::Eq);
|
||||||
|
let neq = just(Tok::Key(Key::Neq)).to(Self::Neq);
|
||||||
|
let lt = just(Tok::Key(Key::AngBrackL)).to(Self::Lt);
|
||||||
|
let gt = just(Tok::Key(Key::AngBrackR)).to(Self::Gt);
|
||||||
|
let leq = just(Tok::Key(Key::Leq)).to(Self::Leq);
|
||||||
|
let geq = just(Tok::Key(Key::Geq)).to(Self::Geq);
|
||||||
|
eq.or(neq).or(lt).or(gt).or(leq).or(geq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Body {
|
||||||
|
statements: Vec<Statement>,
|
||||||
|
last_expr: Option<BoxEx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
fn parser(expr: impl AstParser<Expression> + 'static) -> impl AstParser<Self> {
|
||||||
|
let statements = Statement::parser(expr.clone())
|
||||||
|
.then_ignore(just(Key::Semi.tok()))
|
||||||
|
.repeated();
|
||||||
|
statements
|
||||||
|
.then(expr.boxify().or_not())
|
||||||
|
.map(|(statements, last_expr)| Self { statements, last_expr })
|
||||||
|
}
|
||||||
|
}
|
33
src/lib/parse/mod.rs
Normal file
33
src/lib/parse/mod.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
use chumsky::error::Simple;
|
||||||
|
use token::Tok;
|
||||||
|
|
||||||
|
pub mod ast;
|
||||||
|
pub mod token;
|
||||||
|
|
||||||
|
/// Parser over Generic input
|
||||||
|
pub trait AnyParser<I, O>: chumsky::Parser<I, O, Error = Simple<I>>
|
||||||
|
where
|
||||||
|
I: Clone + Hash + Eq,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
impl<T, I, O> AnyParser<I, O> for T
|
||||||
|
where
|
||||||
|
I: Clone + Hash + Eq,
|
||||||
|
T: chumsky::Parser<I, O, Error = Simple<I>>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type AstParseErr = Simple<Tok>;
|
||||||
|
/// Parser over Token Streams
|
||||||
|
pub trait AstParser<O: Clone>: AstParserNonClone<O> + Clone {}
|
||||||
|
impl<T, O> AstParser<O> for T
|
||||||
|
where
|
||||||
|
T: AstParserNonClone<O> + Clone,
|
||||||
|
O: Clone,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AstParserNonClone<O>: AnyParser<Tok, O> {}
|
||||||
|
impl<T, O> AstParserNonClone<O> for T where T: AnyParser<Tok, O> {}
|
185
src/lib/parse/token.rs
Normal file
185
src/lib/parse/token.rs
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use chumsky::{prelude::*, Parser as _};
|
||||||
|
use text::{ident, int, whitespace};
|
||||||
|
|
||||||
|
use crate::lib::Span;
|
||||||
|
|
||||||
|
use super::AnyParser;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Key {
|
||||||
|
/// `!`
|
||||||
|
Not,
|
||||||
|
/// `-`
|
||||||
|
Sub,
|
||||||
|
/// `+`
|
||||||
|
Add,
|
||||||
|
/// `*`
|
||||||
|
Mul,
|
||||||
|
/// `/`
|
||||||
|
Div,
|
||||||
|
/// `-=`
|
||||||
|
SubEq,
|
||||||
|
/// `+=`
|
||||||
|
AddEq,
|
||||||
|
/// `*=`
|
||||||
|
MulEq,
|
||||||
|
/// `/=`
|
||||||
|
DivEq,
|
||||||
|
/// `==`
|
||||||
|
Eq,
|
||||||
|
/// `!=`
|
||||||
|
Neq,
|
||||||
|
/// `<=`
|
||||||
|
Leq,
|
||||||
|
/// `>=`
|
||||||
|
Geq,
|
||||||
|
/// `(`
|
||||||
|
BracketsL,
|
||||||
|
/// `)`
|
||||||
|
BracketsR,
|
||||||
|
/// `{`
|
||||||
|
CurBrackL,
|
||||||
|
/// `}`
|
||||||
|
CurBrackR,
|
||||||
|
/// `[`
|
||||||
|
SquBrackL,
|
||||||
|
/// `]`
|
||||||
|
SquBrackR,
|
||||||
|
/// `<`
|
||||||
|
AngBrackL,
|
||||||
|
/// `>`
|
||||||
|
AngBrackR,
|
||||||
|
/// `:`
|
||||||
|
Colon,
|
||||||
|
/// `;`
|
||||||
|
Semi,
|
||||||
|
/// `,`
|
||||||
|
Comma,
|
||||||
|
/// `.`
|
||||||
|
Dot,
|
||||||
|
/// `=`
|
||||||
|
Assign,
|
||||||
|
/// `...`
|
||||||
|
Unfold,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
pub fn parser() -> impl AnyParser<char, Self> {
|
||||||
|
one_of("")
|
||||||
|
.to(Key::Add)
|
||||||
|
.or(just('!').to(Key::Not))
|
||||||
|
.or(just("+=").to(Key::AddEq))
|
||||||
|
.or(just("-=").to(Key::SubEq))
|
||||||
|
.or(just("*=").to(Key::MulEq))
|
||||||
|
.or(just("/=").to(Key::DivEq))
|
||||||
|
.or(just('-').to(Key::Add))
|
||||||
|
.or(just('+').to(Key::Sub))
|
||||||
|
.or(just('*').to(Key::Mul))
|
||||||
|
.or(just('/').to(Key::Div))
|
||||||
|
.or(just("<=").to(Key::Leq))
|
||||||
|
.or(just(">=").to(Key::Geq))
|
||||||
|
.or(just('(').to(Key::BracketsL))
|
||||||
|
.or(just(')').to(Key::BracketsR))
|
||||||
|
.or(just('{').to(Key::CurBrackL))
|
||||||
|
.or(just('}').to(Key::CurBrackR))
|
||||||
|
.or(just('[').to(Key::SquBrackL))
|
||||||
|
.or(just(']').to(Key::SquBrackR))
|
||||||
|
.or(just('<').to(Key::AngBrackL))
|
||||||
|
.or(just('>').to(Key::AngBrackR))
|
||||||
|
.or(just(':').to(Key::Colon))
|
||||||
|
.or(just(';').to(Key::Semi))
|
||||||
|
.or(just(',').to(Key::Comma))
|
||||||
|
.or(just("...").to(Key::Unfold))
|
||||||
|
.or(just('.').to(Key::Dot))
|
||||||
|
.or(just("==").to(Key::Eq))
|
||||||
|
.or(just("!=").to(Key::Neq))
|
||||||
|
.or(just("=").to(Key::Assign))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tok(self) -> Tok {
|
||||||
|
Tok::Key(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Tok {
|
||||||
|
Id(String),
|
||||||
|
Str(String),
|
||||||
|
Num(String),
|
||||||
|
Bool(bool),
|
||||||
|
Key(Key),
|
||||||
|
Let,
|
||||||
|
Fn,
|
||||||
|
Type,
|
||||||
|
If,
|
||||||
|
Else,
|
||||||
|
Loop,
|
||||||
|
While,
|
||||||
|
For,
|
||||||
|
In,
|
||||||
|
Use,
|
||||||
|
Pub,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Is,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Tok {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "Token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tok {
|
||||||
|
pub fn parser() -> impl AnyParser<char, (Tok, Span)> {
|
||||||
|
let key = Key::parser().map(Self::Key);
|
||||||
|
|
||||||
|
let integer = int(10);
|
||||||
|
let float = int(10)
|
||||||
|
.or_not()
|
||||||
|
.map(|op| op.unwrap_or("".into()))
|
||||||
|
.then(just('.').then(int(10)))
|
||||||
|
.map(|(int, (dot, rest))| format!("{int}{dot}{rest}"));
|
||||||
|
let num = integer.or(float).map(Self::Num);
|
||||||
|
|
||||||
|
let str = just('"')
|
||||||
|
.not()
|
||||||
|
.repeated()
|
||||||
|
.delimited_by(just('"'), just('"'))
|
||||||
|
.map(|e| e.iter().collect())
|
||||||
|
.map(Self::Str);
|
||||||
|
|
||||||
|
let id = ident().map(|id: String| match id.as_str() {
|
||||||
|
"true" => Self::Bool(true),
|
||||||
|
"false" => Self::Bool(false),
|
||||||
|
"if" => Self::If,
|
||||||
|
"else" => Self::Else,
|
||||||
|
"let" => Self::Let,
|
||||||
|
"fn" => Self::Fn,
|
||||||
|
"type" => Self::Type,
|
||||||
|
"loop" => Self::Loop,
|
||||||
|
"while" => Self::While,
|
||||||
|
"for" => Self::For,
|
||||||
|
"in" => Self::In,
|
||||||
|
"use" => Self::Use,
|
||||||
|
"pub" => Self::Pub,
|
||||||
|
"and" => Self::And,
|
||||||
|
"or" => Self::Or,
|
||||||
|
"is" => Self::Is,
|
||||||
|
_ => Self::Id(id),
|
||||||
|
});
|
||||||
|
|
||||||
|
key.or(str)
|
||||||
|
.or(num)
|
||||||
|
.or(id)
|
||||||
|
.then_ignore(whitespace())
|
||||||
|
.map_with_span(|tok, span| (tok, span))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_all() -> impl AnyParser<char, Vec<(Tok, Span)>> {
|
||||||
|
let toks = Self::parser().then_ignore(whitespace()).repeated();
|
||||||
|
whitespace().ignore_then(toks).then_ignore(end()).collect()
|
||||||
|
}
|
||||||
|
}
|
19
src/main.rs
19
src/main.rs
|
@ -1,3 +1,20 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(special_module_name)]
|
||||||
|
|
||||||
|
use std::io::{stdin, Read};
|
||||||
|
|
||||||
|
use chumsky::Parser;
|
||||||
|
use lib::parse::{ast::Program, token::Tok};
|
||||||
|
|
||||||
|
mod lib;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
unsafe { backtrace_on_stack_overflow::enable() };
|
||||||
|
|
||||||
|
let mut line = String::new();
|
||||||
|
stdin().read_to_string(&mut line);
|
||||||
|
let toks = Tok::parse_all().parse(line).unwrap();
|
||||||
|
let toks = toks.into_iter().map(|(t, r)| t).collect::<Vec<_>>();
|
||||||
|
let program = Program::parser().parse(toks);
|
||||||
|
dbg!(program);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue