From e6d013c716f951948ca698a4d05b4562c071df33 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Fri, 7 Jun 2024 03:31:12 +0200 Subject: [PATCH] start implementing --- Cargo.lock | 289 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + run.sh | 10 ++ rustfmt.toml | 2 + src/lib/mod.rs | 3 + src/lib/parse/ast/mod.rs | 304 +++++++++++++++++++++++++++++++++++++++ src/lib/parse/mod.rs | 33 +++++ src/lib/parse/token.rs | 185 ++++++++++++++++++++++++ src/main.rs | 19 ++- 9 files changed, 846 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100755 run.sh create mode 100644 rustfmt.toml create mode 100644 src/lib/mod.rs create mode 100644 src/lib/parse/ast/mod.rs create mode 100644 src/lib/parse/mod.rs create mode 100644 src/lib/parse/token.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7b8ad9e --- /dev/null +++ b/Cargo.lock @@ -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", +] diff --git a/Cargo.toml b/Cargo.toml index d388ea5..5f0cd8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..5613cbc --- /dev/null +++ b/run.sh @@ -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 diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..768ff5c --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +hard_tabs = true +max_width = 120 diff --git a/src/lib/mod.rs b/src/lib/mod.rs new file mode 100644 index 0000000..b17c403 --- /dev/null +++ b/src/lib/mod.rs @@ -0,0 +1,3 @@ +pub mod parse; + +pub type Span = std::ops::Range; diff --git a/src/lib/parse/ast/mod.rs b/src/lib/parse/ast/mod.rs new file mode 100644 index 0000000..1c9e1ab --- /dev/null +++ b/src/lib/parse/ast/mod.rs @@ -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 { + 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 + 'static) -> impl AstParser { + 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 + 'static) -> impl AstParser { + 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 + 'static) -> impl AstParser { + 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 { + 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 { + 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 { + 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); + +impl BoxEx { + fn new(expr: Expression) -> Self { + Self(Box::new(expr)) + } +} + +trait ExprParser: Parser { + fn boxify(self) -> chumsky::combinator::Map BoxEx, Expression> + where + Self: Sized, + { + self.map(|e| BoxEx(Box::new(e))) + } +} +impl ExprParser for T +where + T: Parser, + I: Clone, +{ +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Literal { + Number(f32), + String(String), + Bool(bool), +} + +impl std::hash::Hash for Literal { + fn hash(&self, state: &mut H) { + core::mem::discriminant(self).hash(state); + } +} + +impl Eq for Literal {} + +fn parse_num(num: &str) -> Option { + if num.starts_with('.') { + format!("0{num}").parse().ok() + } else { + num.parse().ok() + } +} + +impl Literal { + fn parser() -> impl AstParser { + 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) -> impl AstParser { + 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, +} + +impl Invoke { + fn parser(expr: impl AstParser) -> impl AstParser { + 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) -> impl AstParser { + 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 { + 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 { + 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 { + 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 { + 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, + last_expr: Option, +} + +impl Body { + fn parser(expr: impl AstParser + 'static) -> impl AstParser { + 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 }) + } +} diff --git a/src/lib/parse/mod.rs b/src/lib/parse/mod.rs new file mode 100644 index 0000000..00ef725 --- /dev/null +++ b/src/lib/parse/mod.rs @@ -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: chumsky::Parser> +where + I: Clone + Hash + Eq, +{ +} +impl AnyParser for T +where + I: Clone + Hash + Eq, + T: chumsky::Parser>, +{ +} + +pub type AstParseErr = Simple; +/// Parser over Token Streams +pub trait AstParser: AstParserNonClone + Clone {} +impl AstParser for T +where + T: AstParserNonClone + Clone, + O: Clone, +{ +} + +pub trait AstParserNonClone: AnyParser {} +impl AstParserNonClone for T where T: AnyParser {} diff --git a/src/lib/parse/token.rs b/src/lib/parse/token.rs new file mode 100644 index 0000000..734345c --- /dev/null +++ b/src/lib/parse/token.rs @@ -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 { + 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 { + 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> { + let toks = Self::parser().then_ignore(whitespace()).repeated(); + whitespace().ignore_then(toks).then_ignore(end()).collect() + } +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..59c30e8 100644 --- a/src/main.rs +++ b/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::>(); + let program = Program::parser().parse(toks); + dbg!(program); }