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
|
||||
|
||||
[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() {
|
||||
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