From 98850ee1e9f24eb9fd7d3c2f9cb07de38cffa8e6 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 8 Mar 2024 12:34:09 +0100 Subject: [PATCH 01/67] lang: lexer --- Cargo.lock | 64 +++++++++++++++++++ Cargo.toml | 3 +- crates/lang/Cargo.toml | 12 ++++ crates/lang/src/lib.rs | 1 + crates/lang/src/tokens.rs | 45 ++++++++++++++ crates/lang/src/tokens/tests.rs | 107 ++++++++++++++++++++++++++++++++ 6 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 crates/lang/Cargo.toml create mode 100644 crates/lang/src/lib.rs create mode 100644 crates/lang/src/tokens.rs create mode 100644 crates/lang/src/tokens/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 7447d0d..5cd0ff9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,6 +94,12 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "bit_field" version = "0.10.2" @@ -321,6 +327,12 @@ dependencies = [ "spin", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "getrandom" version = "0.2.12" @@ -400,6 +412,19 @@ dependencies = [ "rayon", ] +[[package]] +name = "lang" +version = "0.1.0" +dependencies = [ + "logos", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "lebe" version = "0.5.2" @@ -433,6 +458,39 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "logos" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161971eb88a0da7ae0c333e1063467c5b5727e7fb6b710b8db4814eade3a42e8" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e31badd9de5131fdf4921f6473d457e3dd85b11b7f091ceb50e4df7c3eeb12a" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c2a69b3eb68d5bd595107c9ee58d7e07fe2bb5e360cc85b0f084dedac80de0a" +dependencies = [ + "logos-codegen", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -580,6 +638,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "ron" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 6d7f53d..82e4afd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,8 @@ members = [ "crates/app", "crates/eval", - "crates/ir", + "crates/ir", + "crates/lang", ] resolver = "2" diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml new file mode 100644 index 0000000..1182341 --- /dev/null +++ b/crates/lang/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "lang" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +logos = "0.14" + +[lints] +workspace = true diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs new file mode 100644 index 0000000..5c76635 --- /dev/null +++ b/crates/lang/src/lib.rs @@ -0,0 +1 @@ +pub mod tokens; diff --git a/crates/lang/src/tokens.rs b/crates/lang/src/tokens.rs new file mode 100644 index 0000000..e21b961 --- /dev/null +++ b/crates/lang/src/tokens.rs @@ -0,0 +1,45 @@ +use logos::Logos; + +#[derive(Logos, Debug, PartialEq, Eq)] +#[logos(skip r"[ \t\n\f]+")] +pub enum Token<'a> { + #[regex("[a-zA-Z0-9_\\-]+", |lex| lex.slice())] + Word(&'a str), + #[regex("\\$[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] + VarIdent(&'a str), + #[token("@..")] + InputSpread, + #[regex("\\@[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] + InputIdent(&'a str), + #[token(",")] + Comma, + #[token("|")] + Pipe, + #[token("@|")] + MappingPipe, + #[token("!|")] + NullPipe, + #[token("@")] + At, + #[token(">")] + GreaterThan, + #[token("=")] + Equals, + #[token(":")] + Colon, + #[token("[")] + BracketOpen, + #[token("]")] + BracketClose, + #[token("(")] + ParenOpen, + #[token(")")] + ParenClose, + #[token("{")] + BraceOpen, + #[token("}")] + BraceClose, +} + +#[cfg(test)] +mod tests; diff --git a/crates/lang/src/tokens/tests.rs b/crates/lang/src/tokens/tests.rs new file mode 100644 index 0000000..fe4270e --- /dev/null +++ b/crates/lang/src/tokens/tests.rs @@ -0,0 +1,107 @@ +use logos::Logos; + +use super::Token; + +/// generates tests for the lexer to avoid writing boilerplate +macro_rules! lexer_test { + ($name:ident, $input:literal, $out:expr) => { + #[test] + fn $name() { + let lex = Token::lexer($input); + let toks = lex.map(|tok| tok.unwrap()).collect::>(); + assert_eq!(toks, $out); + } + }; +} + +lexer_test! { + test_lex_simple_pipeline, + "streamer | processor | sink", + [ + Token::Word("streamer"), + Token::Pipe, + Token::Word("processor"), + Token::Pipe, + Token::Word("sink") + ] +} + +lexer_test! { + test_lex_var_ident, + "$identifier", + [ Token::VarIdent("identifier") ] +} + +lexer_test! { + test_lex_subgroup, + "subgroup(first, second) = a | b { 1: $first } | c { 1: $second }", + [ + Token::Word("subgroup"), + Token::ParenOpen, + Token::Word("first"), + Token::Comma, + Token::Word("second"), + Token::ParenClose, + Token::Equals, + Token::Word("a"), + Token::Pipe, + Token::Word("b"), + Token::BraceOpen, + Token::Word("1"), + Token::Colon, + Token::VarIdent("first"), + Token::BraceClose, + Token::Pipe, + Token::Word("c"), + Token::BraceOpen, + Token::Word("1"), + Token::Colon, + Token::VarIdent("second"), + Token::BraceClose + ] +} + +lexer_test! { + text_lex_crossing_pipeline_reordering, + "a >first, second|second, first> c", + [ + Token::Word("a"), + Token::GreaterThan, + Token::Word("first"), + Token::Comma, + Token::Word("second"), + Token::Pipe, + Token::Word("second"), + Token::Comma, + Token::Word("first"), + Token::GreaterThan, + Token::Word("c") + ] +} + +lexer_test! { + test_lex_crossing_input_args, + "a >second| c { second: @first }", + [ + Token::Word("a"), + Token::GreaterThan, + Token::Word("second"), + Token::Pipe, + Token::Word("c"), + Token::BraceOpen, + Token::Word("second"), + Token::Colon, + Token::InputIdent("first"), + Token::BraceClose + ] +} + +lexer_test! { + test_lex_map_io_named, + "a @| c", + [ + Token::Word("a"), + Token::MappingPipe, + Token::Word("c") + ] +} -- 2.46.0 From 69f0baf425bbdb744d85b6a8449910b0d6edb2ea Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 15 Mar 2024 16:44:47 +0100 Subject: [PATCH 02/67] svg-filters: init --- Cargo.lock | 136 ++++++++++++++++++ Cargo.toml | 2 +- crates/svg-filters/Cargo.toml | 15 ++ crates/svg-filters/src/lib.rs | 16 +++ crates/svg-filters/src/types.rs | 20 +++ crates/svg-filters/src/types/length.rs | 14 ++ crates/svg-filters/src/types/nodes.rs | 13 ++ .../svg-filters/src/types/nodes/primitives.rs | 51 +++++++ .../src/types/nodes/primitives/blend.rs | 25 ++++ .../types/nodes/primitives/color_matrix.rs | 19 +++ .../nodes/primitives/component_transfer.rs | 27 ++++ .../src/types/nodes/primitives/composite.rs | 13 ++ .../types/nodes/primitives/convolve_matrix.rs | 18 +++ .../nodes/primitives/diffuse_lighting.rs | 2 + .../nodes/primitives/displacement_map.rs | 13 ++ .../src/types/nodes/primitives/flood.rs | 7 + .../types/nodes/primitives/gaussian_blur.rs | 4 + .../src/types/nodes/primitives/image.rs | 2 + .../src/types/nodes/primitives/merge.rs | 2 + .../src/types/nodes/primitives/morphology.rs | 10 ++ .../src/types/nodes/primitives/offset.rs | 5 + .../nodes/primitives/specular_lighting.rs | 2 + .../src/types/nodes/primitives/tile.rs | 2 + .../src/types/nodes/primitives/turbulence.rs | 18 +++ .../src/types/nodes/standard_input.rs | 10 ++ 25 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 crates/svg-filters/Cargo.toml create mode 100644 crates/svg-filters/src/lib.rs create mode 100644 crates/svg-filters/src/types.rs create mode 100644 crates/svg-filters/src/types/length.rs create mode 100644 crates/svg-filters/src/types/nodes.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/blend.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/color_matrix.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/component_transfer.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/composite.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/convolve_matrix.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/diffuse_lighting.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/displacement_map.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/flood.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/image.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/merge.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/morphology.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/offset.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/specular_lighting.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/tile.rs create mode 100644 crates/svg-filters/src/types/nodes/primitives/turbulence.rs create mode 100644 crates/svg-filters/src/types/nodes/standard_input.rs diff --git a/Cargo.lock b/Cargo.lock index 5cd0ff9..a8b1f50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,6 +237,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "phf", +] + [[package]] name = "deranged" version = "0.3.11" @@ -273,6 +282,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "eval" version = "0.1.0" @@ -308,6 +323,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.28" @@ -363,6 +384,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" version = "0.4.1" @@ -388,6 +415,16 @@ dependencies = [ "tiff", ] +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "ir" version = "0.1.0" @@ -491,6 +528,12 @@ dependencies = [ "logos-codegen", ] +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -552,6 +595,58 @@ version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "png" version = "0.17.10" @@ -589,6 +684,16 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quote" version = "1.0.33" @@ -598,6 +703,21 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + [[package]] name = "rayon" version = "1.8.0" @@ -705,6 +825,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "smallvec" version = "1.11.2" @@ -726,6 +852,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "svg-filters" +version = "0.1.0" +dependencies = [ + "csscolorparser", + "indexmap", + "petgraph", + "quick-xml", +] + [[package]] name = "syn" version = "2.0.41" diff --git a/Cargo.toml b/Cargo.toml index 82e4afd..2ca8bdc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [ "crates/app", "crates/eval", "crates/ir", - "crates/lang", + "crates/lang", "crates/svg-filters", ] resolver = "2" diff --git a/crates/svg-filters/Cargo.toml b/crates/svg-filters/Cargo.toml new file mode 100644 index 0000000..fbe3aaf --- /dev/null +++ b/crates/svg-filters/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "svg-filters" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +csscolorparser = "0.6.2" +indexmap = "2.2.5" +petgraph = "0.6.4" +quick-xml = { version = "0.31.0", features = ["serialize"] } + +[lints] +workspace = true diff --git a/crates/svg-filters/src/lib.rs b/crates/svg-filters/src/lib.rs new file mode 100644 index 0000000..11b6708 --- /dev/null +++ b/crates/svg-filters/src/lib.rs @@ -0,0 +1,16 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +pub mod types; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/svg-filters/src/types.rs b/crates/svg-filters/src/types.rs new file mode 100644 index 0000000..94ecb4b --- /dev/null +++ b/crates/svg-filters/src/types.rs @@ -0,0 +1,20 @@ +use std::collections::HashMap; + +use petgraph::graphmap::DiGraphMap; + +pub mod length; +pub mod nodes; + +use self::{ + length::{Coordinate, Length}, + nodes::Node, +}; + +pub struct Filter<'a> { + graph: DiGraphMap, +} +pub enum Edge<'a> { + Named(&'a str, u8), + /// For standard inputs such as SourceGraphic etc., which we'll just be representing as nodes for simplicity + Unnamed(u8), +} diff --git a/crates/svg-filters/src/types/length.rs b/crates/svg-filters/src/types/length.rs new file mode 100644 index 0000000..4db3fd9 --- /dev/null +++ b/crates/svg-filters/src/types/length.rs @@ -0,0 +1,14 @@ +pub struct Length(f32, Unit); +pub type Coordinate = Length; + +pub enum Unit { + None, + Em, + Ex, + Px, + In, + Cm, + Mm, + Pt, + Pc, +} diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs new file mode 100644 index 0000000..3571e76 --- /dev/null +++ b/crates/svg-filters/src/types/nodes.rs @@ -0,0 +1,13 @@ +use self::{primitives::Primitive, standard_input::StandardInput}; + +use super::length::{Coordinate, Length}; + +pub mod standard_input; + +pub mod primitives; + +pub enum Node { + /// We represent those as Nodes because they're essentially magical nodes + StandardInput(StandardInput), + Primitive(Primitive), +} diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs new file mode 100644 index 0000000..433f190 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -0,0 +1,51 @@ +use crate::types::length::{Coordinate, Length}; + +mod blend; +mod color_matrix; +mod component_transfer; +mod composite; +mod convolve_matrix; +mod diffuse_lighting; +mod displacement_map; +mod flood; +mod gaussian_blur; +mod image; +mod merge; +mod morphology; +mod offset; +mod specular_lighting; +mod tile; +mod turbulence; + +pub struct Primitive { + primitive: FePrimitive, + common_attrs: CommonAttrs, +} + +struct CommonAttrs { + x: Coordinate, + y: Coordinate, + width: Length, + height: Length, +} + +/// svg filter effects primitives +enum FePrimitive { + // 2 inputs + Blend(blend::Blend), + ColorMatrix(color_matrix::ColorMatrix), + ComponentTransfer(component_transfer::ComponentTransfer), + Composite(composite::Composite), + ConvolveMatrix(convolve_matrix::ConvolveMatrix), + DiffuseLighting(diffuse_lighting::DiffuseLighting), + DisplacementMap(displacement_map::DisplacementMap), + Flood(flood::Flood), + GaussianBlur(gaussian_blur::GaussianBlur), + Image(image::Image), + Merge(merge::Merge), + Morphology(morphology::Morphology), + Offset(offset::Offset), + SpecularLighting(specular_lighting::SpecularLighting), + Tile(tile::Tile), + Turbulence(turbulence::Turbulence), +} diff --git a/crates/svg-filters/src/types/nodes/primitives/blend.rs b/crates/svg-filters/src/types/nodes/primitives/blend.rs new file mode 100644 index 0000000..c8972de --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/blend.rs @@ -0,0 +1,25 @@ +/// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement) +pub struct Blend { + mode: BlendMode, +} + +/// as according to https://drafts.fxtf.org/compositing-1/#blending +enum BlendMode { + Normal, + Multiply, + Screen, + Overlay, + Darken, + Lighten, + ColorDodge, + ColorBurn, + HardLight, + SoftLight, + Difference, + Exclusion, + + Hue, + Saturation, + Color, + Luminosity, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs new file mode 100644 index 0000000..bdc945e --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs @@ -0,0 +1,19 @@ +/// [feColorMatrix](https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement) +pub struct ColorMatrix { + cm_type: ColorMatrixType, +} + +enum ColorMatrixType { + Matrix { + values: [f32; 20], + }, + Saturate { + // is the values attribute anyway tho + value: f32, + }, + HueRotate { + // also the values attribute + degrees: f32, + }, + LuminanceToAlpha, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs new file mode 100644 index 0000000..6c58d87 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs @@ -0,0 +1,27 @@ +/// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement) +pub struct ComponentTransfer { + func_r: TransferFunction, + func_g: TransferFunction, + func_b: TransferFunction, + func_a: TransferFunction, +} + +/// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements) +enum TransferFunction { + Identity, + Table { + table_values: Vec, + }, + Discrete { + table_values: Vec, + }, + Linear { + slope: f32, + intercept: f32, + }, + Gamma { + amplitude: f32, + exponent: f32, + offset: f32, + }, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/composite.rs b/crates/svg-filters/src/types/nodes/primitives/composite.rs new file mode 100644 index 0000000..ae95447 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/composite.rs @@ -0,0 +1,13 @@ +/// [feComposite](https://www.w3.org/TR/SVG11/filters.html#feCompositeElement) +pub struct Composite { + operator: CompositeOperator, +} + +enum CompositeOperator { + Over, + In, + Out, + Atop, + Xor, + Arithmetic { k1: f32, k2: f32, k3: f32, k4: f32 }, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/convolve_matrix.rs b/crates/svg-filters/src/types/nodes/primitives/convolve_matrix.rs new file mode 100644 index 0000000..a619ac9 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/convolve_matrix.rs @@ -0,0 +1,18 @@ +pub struct ConvolveMatrix { + order: (u16, u16), + // must be checked to be `order.0 * order.1` + kernel_matrix: Vec, + divisor: f32, + bias: f32, + target_x: i32, + target_y: i32, + edge_mode: EdgeMode, + kernel_unit_length: (f32, f32), + preserve_alpha: bool, +} + +enum EdgeMode { + None, + Duplicate, + Wrap, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/diffuse_lighting.rs b/crates/svg-filters/src/types/nodes/primitives/diffuse_lighting.rs new file mode 100644 index 0000000..18a0485 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/diffuse_lighting.rs @@ -0,0 +1,2 @@ +// TODO +pub struct DiffuseLighting; diff --git a/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs b/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs new file mode 100644 index 0000000..e59a520 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs @@ -0,0 +1,13 @@ +/// [feDisplacementMap](https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement) +pub struct DisplacementMap { + scale: f32, + x_channel_selector: Channel, + y_channel_selector: Channel, +} + +enum Channel { + A, + R, + G, + B, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/flood.rs b/crates/svg-filters/src/types/nodes/primitives/flood.rs new file mode 100644 index 0000000..6858c71 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/flood.rs @@ -0,0 +1,7 @@ +use csscolorparser::Color; + +/// [feFlood](https://www.w3.org/TR/SVG11/filters.html#feFloodElement) +pub struct Flood { + flood_color: Color, + flood_opacity: f32, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs new file mode 100644 index 0000000..5ad8047 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs @@ -0,0 +1,4 @@ +/// [feGaussianBlur](https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement) +pub struct GaussianBlur { + std_deviation: (f32, f32), +} diff --git a/crates/svg-filters/src/types/nodes/primitives/image.rs b/crates/svg-filters/src/types/nodes/primitives/image.rs new file mode 100644 index 0000000..d068c89 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/image.rs @@ -0,0 +1,2 @@ +// TODO +pub struct Image; diff --git a/crates/svg-filters/src/types/nodes/primitives/merge.rs b/crates/svg-filters/src/types/nodes/primitives/merge.rs new file mode 100644 index 0000000..a65df6e --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/merge.rs @@ -0,0 +1,2 @@ +// TODO +pub struct Merge; diff --git a/crates/svg-filters/src/types/nodes/primitives/morphology.rs b/crates/svg-filters/src/types/nodes/primitives/morphology.rs new file mode 100644 index 0000000..6fd50e1 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/morphology.rs @@ -0,0 +1,10 @@ +/// [feMorphology](https://www.w3.org/TR/SVG11/filters.html#feMorphologyElement) +pub struct Morphology { + operator: Operator, + radius: (f32, f32), +} + +enum Operator { + Erode, + Dilate, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/offset.rs b/crates/svg-filters/src/types/nodes/primitives/offset.rs new file mode 100644 index 0000000..1473b0e --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/offset.rs @@ -0,0 +1,5 @@ +/// [feOffset](https://www.w3.org/TR/SVG11/filters.html#feOffsetElement) +pub struct Offset { + dx: f32, + dy: f32, +} diff --git a/crates/svg-filters/src/types/nodes/primitives/specular_lighting.rs b/crates/svg-filters/src/types/nodes/primitives/specular_lighting.rs new file mode 100644 index 0000000..ab41f49 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/specular_lighting.rs @@ -0,0 +1,2 @@ +// TODO +pub struct SpecularLighting; diff --git a/crates/svg-filters/src/types/nodes/primitives/tile.rs b/crates/svg-filters/src/types/nodes/primitives/tile.rs new file mode 100644 index 0000000..b943271 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/tile.rs @@ -0,0 +1,2 @@ +/// [feTile](https://www.w3.org/TR/SVG11/filters.html#feTileElement) +pub struct Tile; diff --git a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs new file mode 100644 index 0000000..998b472 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs @@ -0,0 +1,18 @@ +/// [feTurbulence](https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement) +pub struct Turbulence { + base_frequency: (f32, f32), + num_octaves: (u16), + seed: u32, + stich_tiles: StitchTiles, + // attr name: type + noise_type: NoiseType, +} + +enum StitchTiles { + Stitch, + NoStitch, +} +enum NoiseType { + Turbulence, + FractalNoise, +} diff --git a/crates/svg-filters/src/types/nodes/standard_input.rs b/crates/svg-filters/src/types/nodes/standard_input.rs new file mode 100644 index 0000000..e1ae7d1 --- /dev/null +++ b/crates/svg-filters/src/types/nodes/standard_input.rs @@ -0,0 +1,10 @@ +/// [svg filter effect standard input](https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute) +/// technically not a node, but for implementation simplicity... yeah +pub enum StandardInput { + SourceGraphic, + SourceAlpha, + BackgroundImage, + BackgroundAlpha, + FillPaint, + StrokePaint, +} -- 2.46.0 From 56848a1b059da24f09855a027ac101693b83086c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 15 Mar 2024 19:52:40 +0100 Subject: [PATCH 03/67] svg-filters: add basic graph and stuffs --- Cargo.toml | 3 +- crates/app/Cargo.toml | 1 + crates/svg-filters/src/lib.rs | 17 +-- crates/svg-filters/src/main.rs | 54 +++++++++ crates/svg-filters/src/types.rs | 70 ++++++++++- crates/svg-filters/src/types/length.rs | 3 + crates/svg-filters/src/types/nodes.rs | 112 +++++++++++++++++- .../svg-filters/src/types/nodes/primitives.rs | 47 ++++---- .../src/types/nodes/primitives/blend.rs | 20 +++- .../types/nodes/primitives/color_matrix.rs | 24 ++-- .../nodes/primitives/component_transfer.rs | 2 + .../src/types/nodes/primitives/composite.rs | 16 ++- .../types/nodes/primitives/convolve_matrix.rs | 2 + .../nodes/primitives/diffuse_lighting.rs | 1 + .../nodes/primitives/displacement_map.rs | 2 + .../src/types/nodes/primitives/flood.rs | 1 + .../types/nodes/primitives/gaussian_blur.rs | 17 ++- .../src/types/nodes/primitives/image.rs | 1 + .../src/types/nodes/primitives/merge.rs | 1 + .../src/types/nodes/primitives/morphology.rs | 2 + .../src/types/nodes/primitives/offset.rs | 7 ++ .../nodes/primitives/specular_lighting.rs | 1 + .../src/types/nodes/primitives/tile.rs | 1 + .../src/types/nodes/primitives/turbulence.rs | 3 + .../src/types/nodes/standard_input.rs | 1 + 25 files changed, 340 insertions(+), 69 deletions(-) create mode 100644 crates/svg-filters/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 2ca8bdc..cbd514c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ members = [ "crates/app", "crates/eval", "crates/ir", - "crates/lang", "crates/svg-filters", + "crates/lang", + "crates/svg-filters", ] resolver = "2" diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml index 2caaaf4..10182c2 100644 --- a/crates/app/Cargo.toml +++ b/crates/app/Cargo.toml @@ -2,6 +2,7 @@ name = "app" version = "0.1.0" edition = "2021" +default-run = "app" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/svg-filters/src/lib.rs b/crates/svg-filters/src/lib.rs index 11b6708..5081a19 100644 --- a/crates/svg-filters/src/lib.rs +++ b/crates/svg-filters/src/lib.rs @@ -1,16 +1,5 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - pub mod types; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub use types::nodes::Node; +pub use types::Edge; +pub use types::Filter; diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs new file mode 100644 index 0000000..d628d3e --- /dev/null +++ b/crates/svg-filters/src/main.rs @@ -0,0 +1,54 @@ +use svg_filters::{types::nodes::primitives::color_matrix::ColorMatrixType, Edge, Filter, Node}; + +fn main() { + let mut filter = Filter::new(); + + // + // + // + // + + // + + // + // + // + + // + // + // + + let chan_r = filter.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ + 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + ])))); + let chan_g = filter.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ + 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + ])))); + let chan_b = filter.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ + 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., + ])))); + + let offset_r = filter.add_node(Node::offset(25., 0.)); + let offset_b = filter.add_node(Node::offset(-25., 0.)); + let blur_r = filter.add_node(Node::gaussian_blur_xy(5, 0)); + let blur_b = filter.add_node(Node::gaussian_blur_xy(5, 0)); + + let composite_rb = filter.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); + let composite_final = filter.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); + + filter.graph.extend_with_edges(&[ + (filter.source_graphic(), chan_r, Edge::new()), + (filter.source_graphic(), chan_b, Edge::new()), + (filter.source_graphic(), chan_g, Edge::new()), + (chan_r, offset_r, Edge::new()), + (offset_r, blur_r, Edge::new()), + (chan_b, offset_b, Edge::new()), + (offset_b, blur_b, Edge::new()), + (blur_r, composite_rb, Edge::in_idx(0)), + (blur_b, composite_rb, Edge::in_idx(1)), + (composite_rb, composite_final, Edge::in_idx(0)), + (chan_g, composite_final, Edge::in_idx(1)), + ]); + + println!("{filter:#?}") +} diff --git a/crates/svg-filters/src/types.rs b/crates/svg-filters/src/types.rs index 94ecb4b..31e0954 100644 --- a/crates/svg-filters/src/types.rs +++ b/crates/svg-filters/src/types.rs @@ -1,20 +1,80 @@ use std::collections::HashMap; -use petgraph::graphmap::DiGraphMap; +use petgraph::{ + adj::EdgeIndex, data::Build, graph::DiGraph, graphmap::DiGraphMap, prelude::NodeIndex, +}; pub mod length; pub mod nodes; +use crate::types::nodes::primitives::color_matrix::{ColorMatrix, ColorMatrixType}; + use self::{ length::{Coordinate, Length}, nodes::Node, }; +#[derive(Debug)] pub struct Filter<'a> { - graph: DiGraphMap, + pub graph: DiGraph>, + source_graphic_idx: NodeIndex, } -pub enum Edge<'a> { - Named(&'a str, u8), + +impl Filter<'_> { + pub fn new() -> Self { + let mut graph = DiGraph::new(); + let source_graphic_idx = graph.add_node(Node::StdInput( + nodes::standard_input::StandardInput::SourceGraphic, + )); + + Self { + graph, + source_graphic_idx, + } + } + + pub fn add_node(&mut self, node: Node) -> NodeIndex { + self.graph.add_node(node) + } + + pub fn source_graphic(&self) -> NodeIndex { + self.source_graphic_idx + } +} + +impl Default for Filter<'_> { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug, Clone)] +pub struct Edge<'a> { + edge_type: EdgeType<'a>, + /// the index of the `in` attribute on the target element + /// if None, just `in` + in_idx: Option, +} + +impl Edge<'_> { + pub fn new() -> Self { + Self { + edge_type: EdgeType::Unnamed, + in_idx: None, + } + } + + pub fn in_idx(idx: u8) -> Self { + Self { + edge_type: EdgeType::Unnamed, + in_idx: Some(idx), + } + } +} + +#[derive(Debug, Clone)] +pub enum EdgeType<'a> { + Named(&'a str), /// For standard inputs such as SourceGraphic etc., which we'll just be representing as nodes for simplicity - Unnamed(u8), + Unnamed, } diff --git a/crates/svg-filters/src/types/length.rs b/crates/svg-filters/src/types/length.rs index 4db3fd9..1186181 100644 --- a/crates/svg-filters/src/types/length.rs +++ b/crates/svg-filters/src/types/length.rs @@ -1,7 +1,10 @@ +#[derive(Default, Debug)] pub struct Length(f32, Unit); pub type Coordinate = Length; +#[derive(Default, Debug)] pub enum Unit { + #[default] None, Em, Ex, diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index 3571e76..29982bc 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -1,13 +1,113 @@ -use self::{primitives::Primitive, standard_input::StandardInput}; +use self::{ + primitives::{ + blend::{Blend, BlendMode}, + color_matrix::{ColorMatrix, ColorMatrixType}, + composite::{Composite, CompositeOperator}, + gaussian_blur::GaussianBlur, + offset::Offset, + FePrimitive, + }, + standard_input::StandardInput, +}; use super::length::{Coordinate, Length}; +pub mod primitives; pub mod standard_input; -pub mod primitives; - +#[derive(Debug)] pub enum Node { - /// We represent those as Nodes because they're essentially magical nodes - StandardInput(StandardInput), - Primitive(Primitive), + StdInput(StandardInput), + Primitive { + primitive: FePrimitive, + common_attrs: CommonAttrs, + }, +} + +impl Default for Node { + fn default() -> Self { + Self::StdInput(StandardInput::SourceGraphic) + } +} + +#[derive(Default, Debug)] +struct CommonAttrs { + x: Coordinate, + y: Coordinate, + width: Length, + height: Length, +} + +impl Node { + pub fn simple(el: FePrimitive) -> Node { + Node::Primitive { + primitive: el, + common_attrs: CommonAttrs::default(), + } + } + + pub fn input_count(&self) -> u8 { + match self { + Node::Primitive { + primitive: + FePrimitive::ColorMatrix(_) + | FePrimitive::ComponentTransfer(_) + | FePrimitive::ConvolveMatrix(_) + | FePrimitive::DiffuseLighting(_) + | FePrimitive::GaussianBlur(_) + | FePrimitive::Morphology(_) + | FePrimitive::Offset(_) + | FePrimitive::SpecularLighting(_) + | FePrimitive::Tile(_), + .. + } => 1, + + Node::Primitive { + primitive: + FePrimitive::Composite(_) | FePrimitive::Blend(_) | FePrimitive::DisplacementMap(_), + .. + } => 2, + + Node::StdInput(_) + | Node::Primitive { + primitive: + FePrimitive::Flood(_) | FePrimitive::Image(_) | FePrimitive::Turbulence(_), + .. + } => 0, + Node::Primitive { + primitive: FePrimitive::Merge(_), + .. + } => todo!(), + } + } + + pub fn blend(mode: BlendMode) -> Self { + Self::simple(FePrimitive::Blend(Blend::new(mode))) + } + + pub fn color_matrix(cm_type: ColorMatrixType) -> Self { + Self::simple(FePrimitive::ColorMatrix(ColorMatrix::new(cm_type))) + } + + pub fn composite(op: CompositeOperator) -> Self { + Self::simple(FePrimitive::Composite(Composite::new(op))) + } + + pub fn composite_arithmetic(k1: f32, k2: f32, k3: f32, k4: f32) -> Self { + Self::simple(FePrimitive::Composite(Composite::arithmetic( + k1, k2, k3, k4, + ))) + } + + pub fn gaussian_blur(v: u16) -> Self { + Self::simple(FePrimitive::GaussianBlur(GaussianBlur::single(v))) + } + + pub fn gaussian_blur_xy(x: u16, y: u16) -> Self { + Self::simple(FePrimitive::GaussianBlur(GaussianBlur::with_xy(x, y))) + } + + pub fn offset(dx: f32, dy: f32) -> Self { + Self::simple(FePrimitive::Offset(Offset::new(dx, dy))) + } } diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index 433f190..6be79b3 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -1,36 +1,29 @@ use crate::types::length::{Coordinate, Length}; -mod blend; -mod color_matrix; -mod component_transfer; -mod composite; -mod convolve_matrix; -mod diffuse_lighting; -mod displacement_map; -mod flood; -mod gaussian_blur; -mod image; -mod merge; -mod morphology; -mod offset; -mod specular_lighting; -mod tile; -mod turbulence; +use self::blend::BlendMode; -pub struct Primitive { - primitive: FePrimitive, - common_attrs: CommonAttrs, -} +use super::Node; -struct CommonAttrs { - x: Coordinate, - y: Coordinate, - width: Length, - height: Length, -} +pub mod blend; +pub mod color_matrix; +pub mod component_transfer; +pub mod composite; +pub mod convolve_matrix; +pub mod diffuse_lighting; +pub mod displacement_map; +pub mod flood; +pub mod gaussian_blur; +pub mod image; +pub mod merge; +pub mod morphology; +pub mod offset; +pub mod specular_lighting; +pub mod tile; +pub mod turbulence; /// svg filter effects primitives -enum FePrimitive { +#[derive(Debug)] +pub enum FePrimitive { // 2 inputs Blend(blend::Blend), ColorMatrix(color_matrix::ColorMatrix), diff --git a/crates/svg-filters/src/types/nodes/primitives/blend.rs b/crates/svg-filters/src/types/nodes/primitives/blend.rs index c8972de..242f763 100644 --- a/crates/svg-filters/src/types/nodes/primitives/blend.rs +++ b/crates/svg-filters/src/types/nodes/primitives/blend.rs @@ -1,10 +1,26 @@ /// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement) -pub struct Blend { +#[derive(Debug)] +pub(in crate::types::nodes) struct Blend { mode: BlendMode, } +impl Blend { + pub fn new(mode: BlendMode) -> Self { + Self { mode } + } +} + +impl Default for Blend { + fn default() -> Self { + Self { + mode: BlendMode::Normal, + } + } +} + /// as according to https://drafts.fxtf.org/compositing-1/#blending -enum BlendMode { +#[derive(Debug)] +pub enum BlendMode { Normal, Multiply, Screen, diff --git a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs index bdc945e..fbae6b2 100644 --- a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs +++ b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs @@ -1,19 +1,19 @@ /// [feColorMatrix](https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement) +#[derive(Debug)] pub struct ColorMatrix { cm_type: ColorMatrixType, } -enum ColorMatrixType { - Matrix { - values: [f32; 20], - }, - Saturate { - // is the values attribute anyway tho - value: f32, - }, - HueRotate { - // also the values attribute - degrees: f32, - }, +impl ColorMatrix { + pub fn new(cm_type: ColorMatrixType) -> Self { + Self { cm_type } + } +} + +#[derive(Debug)] +pub enum ColorMatrixType { + Matrix(Box<[f32; 20]>), + Saturate(f32), + HueRotate(f32), LuminanceToAlpha, } diff --git a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs index 6c58d87..3ccc039 100644 --- a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs +++ b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs @@ -1,4 +1,5 @@ /// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement) +#[derive(Debug)] pub struct ComponentTransfer { func_r: TransferFunction, func_g: TransferFunction, @@ -7,6 +8,7 @@ pub struct ComponentTransfer { } /// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements) +#[derive(Debug)] enum TransferFunction { Identity, Table { diff --git a/crates/svg-filters/src/types/nodes/primitives/composite.rs b/crates/svg-filters/src/types/nodes/primitives/composite.rs index ae95447..c86de26 100644 --- a/crates/svg-filters/src/types/nodes/primitives/composite.rs +++ b/crates/svg-filters/src/types/nodes/primitives/composite.rs @@ -1,9 +1,23 @@ /// [feComposite](https://www.w3.org/TR/SVG11/filters.html#feCompositeElement) +#[derive(Debug)] pub struct Composite { operator: CompositeOperator, } -enum CompositeOperator { +impl Composite { + pub fn new(op: CompositeOperator) -> Self { + Self { operator: op } + } + + pub fn arithmetic(k1: f32, k2: f32, k3: f32, k4: f32) -> Self { + Self { + operator: CompositeOperator::Arithmetic { k1, k2, k3, k4 }, + } + } +} + +#[derive(Debug)] +pub enum CompositeOperator { Over, In, Out, diff --git a/crates/svg-filters/src/types/nodes/primitives/convolve_matrix.rs b/crates/svg-filters/src/types/nodes/primitives/convolve_matrix.rs index a619ac9..1fe2ef4 100644 --- a/crates/svg-filters/src/types/nodes/primitives/convolve_matrix.rs +++ b/crates/svg-filters/src/types/nodes/primitives/convolve_matrix.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub struct ConvolveMatrix { order: (u16, u16), // must be checked to be `order.0 * order.1` @@ -11,6 +12,7 @@ pub struct ConvolveMatrix { preserve_alpha: bool, } +#[derive(Debug)] enum EdgeMode { None, Duplicate, diff --git a/crates/svg-filters/src/types/nodes/primitives/diffuse_lighting.rs b/crates/svg-filters/src/types/nodes/primitives/diffuse_lighting.rs index 18a0485..5d9cd6f 100644 --- a/crates/svg-filters/src/types/nodes/primitives/diffuse_lighting.rs +++ b/crates/svg-filters/src/types/nodes/primitives/diffuse_lighting.rs @@ -1,2 +1,3 @@ // TODO +#[derive(Debug)] pub struct DiffuseLighting; diff --git a/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs b/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs index e59a520..b6485ac 100644 --- a/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs +++ b/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs @@ -1,10 +1,12 @@ /// [feDisplacementMap](https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement) +#[derive(Debug)] pub struct DisplacementMap { scale: f32, x_channel_selector: Channel, y_channel_selector: Channel, } +#[derive(Debug)] enum Channel { A, R, diff --git a/crates/svg-filters/src/types/nodes/primitives/flood.rs b/crates/svg-filters/src/types/nodes/primitives/flood.rs index 6858c71..94f6eee 100644 --- a/crates/svg-filters/src/types/nodes/primitives/flood.rs +++ b/crates/svg-filters/src/types/nodes/primitives/flood.rs @@ -1,6 +1,7 @@ use csscolorparser::Color; /// [feFlood](https://www.w3.org/TR/SVG11/filters.html#feFloodElement) +#[derive(Debug)] pub struct Flood { flood_color: Color, flood_opacity: f32, diff --git a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs index 5ad8047..e6ca9c1 100644 --- a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs +++ b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs @@ -1,4 +1,19 @@ /// [feGaussianBlur](https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement) +#[derive(Debug)] pub struct GaussianBlur { - std_deviation: (f32, f32), + std_deviation: (u16, u16), +} + +impl GaussianBlur { + pub fn single(v: u16) -> Self { + Self { + std_deviation: (v, v), + } + } + + pub fn with_xy(x: u16, y: u16) -> Self { + Self { + std_deviation: (x, y), + } + } } diff --git a/crates/svg-filters/src/types/nodes/primitives/image.rs b/crates/svg-filters/src/types/nodes/primitives/image.rs index d068c89..c52c0b7 100644 --- a/crates/svg-filters/src/types/nodes/primitives/image.rs +++ b/crates/svg-filters/src/types/nodes/primitives/image.rs @@ -1,2 +1,3 @@ // TODO +#[derive(Debug)] pub struct Image; diff --git a/crates/svg-filters/src/types/nodes/primitives/merge.rs b/crates/svg-filters/src/types/nodes/primitives/merge.rs index a65df6e..ab26374 100644 --- a/crates/svg-filters/src/types/nodes/primitives/merge.rs +++ b/crates/svg-filters/src/types/nodes/primitives/merge.rs @@ -1,2 +1,3 @@ // TODO +#[derive(Debug)] pub struct Merge; diff --git a/crates/svg-filters/src/types/nodes/primitives/morphology.rs b/crates/svg-filters/src/types/nodes/primitives/morphology.rs index 6fd50e1..6b33825 100644 --- a/crates/svg-filters/src/types/nodes/primitives/morphology.rs +++ b/crates/svg-filters/src/types/nodes/primitives/morphology.rs @@ -1,9 +1,11 @@ /// [feMorphology](https://www.w3.org/TR/SVG11/filters.html#feMorphologyElement) +#[derive(Debug)] pub struct Morphology { operator: Operator, radius: (f32, f32), } +#[derive(Debug)] enum Operator { Erode, Dilate, diff --git a/crates/svg-filters/src/types/nodes/primitives/offset.rs b/crates/svg-filters/src/types/nodes/primitives/offset.rs index 1473b0e..96b47ef 100644 --- a/crates/svg-filters/src/types/nodes/primitives/offset.rs +++ b/crates/svg-filters/src/types/nodes/primitives/offset.rs @@ -1,5 +1,12 @@ /// [feOffset](https://www.w3.org/TR/SVG11/filters.html#feOffsetElement) +#[derive(Debug)] pub struct Offset { dx: f32, dy: f32, } + +impl Offset { + pub fn new(dx: f32, dy: f32) -> Self { + Self { dx, dy } + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives/specular_lighting.rs b/crates/svg-filters/src/types/nodes/primitives/specular_lighting.rs index ab41f49..16a6fcc 100644 --- a/crates/svg-filters/src/types/nodes/primitives/specular_lighting.rs +++ b/crates/svg-filters/src/types/nodes/primitives/specular_lighting.rs @@ -1,2 +1,3 @@ // TODO +#[derive(Debug)] pub struct SpecularLighting; diff --git a/crates/svg-filters/src/types/nodes/primitives/tile.rs b/crates/svg-filters/src/types/nodes/primitives/tile.rs index b943271..7a957c5 100644 --- a/crates/svg-filters/src/types/nodes/primitives/tile.rs +++ b/crates/svg-filters/src/types/nodes/primitives/tile.rs @@ -1,2 +1,3 @@ /// [feTile](https://www.w3.org/TR/SVG11/filters.html#feTileElement) +#[derive(Debug)] pub struct Tile; diff --git a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs index 998b472..ec815cb 100644 --- a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs +++ b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs @@ -1,4 +1,5 @@ /// [feTurbulence](https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement) +#[derive(Debug)] pub struct Turbulence { base_frequency: (f32, f32), num_octaves: (u16), @@ -8,10 +9,12 @@ pub struct Turbulence { noise_type: NoiseType, } +#[derive(Debug)] enum StitchTiles { Stitch, NoStitch, } +#[derive(Debug)] enum NoiseType { Turbulence, FractalNoise, diff --git a/crates/svg-filters/src/types/nodes/standard_input.rs b/crates/svg-filters/src/types/nodes/standard_input.rs index e1ae7d1..add10d0 100644 --- a/crates/svg-filters/src/types/nodes/standard_input.rs +++ b/crates/svg-filters/src/types/nodes/standard_input.rs @@ -1,5 +1,6 @@ /// [svg filter effect standard input](https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute) /// technically not a node, but for implementation simplicity... yeah +#[derive(Debug)] pub enum StandardInput { SourceGraphic, SourceAlpha, -- 2.46.0 From 01b188008956104d80f7494cda03a7731fbadf26 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 15 Mar 2024 22:45:28 +0100 Subject: [PATCH 04/67] svg-filters: get bare basic xml generation going --- crates/svg-filters/src/main.rs | 18 ++- crates/svg-filters/src/types.rs | 117 +++++++++++++++++- crates/svg-filters/src/types/nodes.rs | 6 +- .../svg-filters/src/types/nodes/primitives.rs | 59 +++++++++ .../src/types/nodes/primitives/blend.rs | 2 +- .../types/nodes/primitives/color_matrix.rs | 34 +++++ 6 files changed, 227 insertions(+), 9 deletions(-) diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index d628d3e..5531784 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -1,6 +1,20 @@ -use svg_filters::{types::nodes::primitives::color_matrix::ColorMatrixType, Edge, Filter, Node}; +use svg_filters::{ + types::nodes::{primitives::color_matrix::ColorMatrixType, standard_input::StandardInput}, + Edge, Filter, Node, +}; fn main() { + let mut supersimple = Filter::new(); + + let cm = supersimple.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ + 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + ])))); + + supersimple + .graph + .extend_with_edges(&[(supersimple.source_graphic(), cm)]); + dbg!(supersimple.to_svg()); + let mut filter = Filter::new(); // @@ -50,5 +64,5 @@ fn main() { (chan_g, composite_final, Edge::in_idx(1)), ]); - println!("{filter:#?}") + println!("Result: {}", filter.to_svg()) } diff --git a/crates/svg-filters/src/types.rs b/crates/svg-filters/src/types.rs index 31e0954..df679ba 100644 --- a/crates/svg-filters/src/types.rs +++ b/crates/svg-filters/src/types.rs @@ -1,8 +1,15 @@ -use std::collections::HashMap; +use std::{borrow::Cow, collections::HashMap, fmt::Debug, io::BufWriter, primitive}; use petgraph::{ - adj::EdgeIndex, data::Build, graph::DiGraph, graphmap::DiGraphMap, prelude::NodeIndex, + adj::EdgeIndex, + algo::{toposort, DfsSpace}, + data::{Build, DataMap}, + graph::DiGraph, + graphmap::DiGraphMap, + prelude::NodeIndex, + visit::NodeIndexable, }; +use quick_xml::{events::attributes::Attribute, name::QName, ElementWriter}; pub mod length; pub mod nodes; @@ -11,7 +18,10 @@ use crate::types::nodes::primitives::color_matrix::{ColorMatrix, ColorMatrixType use self::{ length::{Coordinate, Length}, - nodes::Node, + nodes::{ + primitives::{FePrimitive, WriteElement}, + Node, + }, }; #[derive(Debug)] @@ -40,6 +50,52 @@ impl Filter<'_> { pub fn source_graphic(&self) -> NodeIndex { self.source_graphic_idx } + + pub fn to_svg(&self) -> String { + let mut result = Vec::new(); + let mut writer = quick_xml::Writer::new(&mut result); + + let mut dfs_space = DfsSpace::new(&self.graph); + let sorted = toposort(&self.graph, Some(&mut dfs_space)).expect("No cycles! Bad user!"); + + sorted + .into_iter() + .filter_map(|node_idx| { + let node = self + .graph + .node_weight(node_idx) + .expect("toposorting will not return invalid indices"); + + if let Node::Primitive { + primitive, + common_attrs, + } = node + { + Some((node_idx, primitive, common_attrs)) + } else { + None + } + }) + .fold( + &mut writer, + |writer, (node_idx, primitive, common_attrs)| { + let el_writer = primitive.element_writer(writer); + create_input_elements( + &self.graph, + el_writer, + node_idx, + self.graph + .node_weight(node_idx) + .expect("cannot get invalid node_idx from toposort") + .input_count(), + ) + .write_empty() + .expect("should write successfully") + }, + ); + + String::from_utf8_lossy(&result).to_string() + } } impl Default for Filter<'_> { @@ -50,10 +106,10 @@ impl Default for Filter<'_> { #[derive(Debug, Clone)] pub struct Edge<'a> { - edge_type: EdgeType<'a>, + pub edge_type: EdgeType<'a>, /// the index of the `in` attribute on the target element /// if None, just `in` - in_idx: Option, + pub in_idx: Option, } impl Edge<'_> { @@ -72,9 +128,60 @@ impl Edge<'_> { } } +impl Default for Edge<'_> { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug, Clone)] pub enum EdgeType<'a> { Named(&'a str), /// For standard inputs such as SourceGraphic etc., which we'll just be representing as nodes for simplicity Unnamed, } +fn create_input_elements<'a>( + g: &'a DiGraph>, + mut el_writer: ElementWriter<'a, &'a mut Vec>, + node_idx: NodeIndex, + input_count: u8, +) -> ElementWriter<'a, &'a mut Vec> { + let inputs = g + .neighbors_directed(node_idx, petgraph::Direction::Incoming) + .collect::>(); + + if inputs.len() != input_count as usize { + // TODO: better error handling + panic!("input couns didnt match"); + } + + inputs + .into_iter() + .enumerate() + .fold(el_writer, |el_writer, (i, incoming_idx)| { + let incoming_node = g.node_weight(incoming_idx).expect("cannot fail here"); + + // find incoming edge and get weight + let Edge { edge_type, in_idx } = g + .edge_weight( + g.find_edge(incoming_idx, node_idx) + .expect("there should always be an edge"), + ) + .expect("once again, should always exist"); + + let in_attr_name = match in_idx { + None | Some(0) => "in".to_owned(), + Some(n) => format!("in{}", n + 1), + }; + + let v = match incoming_node { + Node::StdInput(std_in) => format!("{std_in:?}"), + Node::Primitive { + primitive, + common_attrs, + } => todo!(), + }; + + el_writer.with_attribute((in_attr_name.as_str(), v.as_str())) + }) +} diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index 29982bc..63e041c 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -1,3 +1,7 @@ +use core::panic; + +use quick_xml::events::attributes::Attribute; + use self::{ primitives::{ blend::{Blend, BlendMode}, @@ -31,7 +35,7 @@ impl Default for Node { } #[derive(Default, Debug)] -struct CommonAttrs { +pub(crate) struct CommonAttrs { x: Coordinate, y: Coordinate, width: Length, diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index 6be79b3..012fd9a 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -1,3 +1,5 @@ +use quick_xml::{events::attributes::Attribute, ElementWriter, Writer}; + use crate::types::length::{Coordinate, Length}; use self::blend::BlendMode; @@ -21,6 +23,19 @@ pub mod specular_lighting; pub mod tile; pub mod turbulence; +pub trait WriteElement { + fn attrs(&self) -> Vec; + fn tag_name(&self) -> &'static str; + fn element_writer<'writer, 'result>( + &self, + writer: &'writer mut Writer<&'result mut Vec>, + ) -> ElementWriter<'writer, &'result mut Vec> { + writer + .create_element(self.tag_name()) + .with_attributes(self.attrs()) + } +} + /// svg filter effects primitives #[derive(Debug)] pub enum FePrimitive { @@ -42,3 +57,47 @@ pub enum FePrimitive { Tile(tile::Tile), Turbulence(turbulence::Turbulence), } + +impl WriteElement for FePrimitive { + fn attrs(&self) -> std::vec::Vec> { + match self { + FePrimitive::Blend(_) => todo!(), + FePrimitive::ColorMatrix(cm) => cm.attrs(), + FePrimitive::ComponentTransfer(_) => todo!(), + FePrimitive::Composite(_) => todo!(), + FePrimitive::ConvolveMatrix(_) => todo!(), + FePrimitive::DiffuseLighting(_) => todo!(), + FePrimitive::DisplacementMap(_) => todo!(), + FePrimitive::Flood(_) => todo!(), + FePrimitive::GaussianBlur(_) => todo!(), + FePrimitive::Image(_) => todo!(), + FePrimitive::Merge(_) => todo!(), + FePrimitive::Morphology(_) => todo!(), + FePrimitive::Offset(_) => todo!(), + FePrimitive::SpecularLighting(_) => todo!(), + FePrimitive::Tile(_) => todo!(), + FePrimitive::Turbulence(_) => todo!(), + } + } + + fn tag_name(&self) -> &'static str { + match self { + FePrimitive::Blend(_) => todo!(), + FePrimitive::ColorMatrix(cm) => cm.tag_name(), + FePrimitive::ComponentTransfer(_) => todo!(), + FePrimitive::Composite(_) => todo!(), + FePrimitive::ConvolveMatrix(_) => todo!(), + FePrimitive::DiffuseLighting(_) => todo!(), + FePrimitive::DisplacementMap(_) => todo!(), + FePrimitive::Flood(_) => todo!(), + FePrimitive::GaussianBlur(_) => todo!(), + FePrimitive::Image(_) => todo!(), + FePrimitive::Merge(_) => todo!(), + FePrimitive::Morphology(_) => todo!(), + FePrimitive::Offset(_) => todo!(), + FePrimitive::SpecularLighting(_) => todo!(), + FePrimitive::Tile(_) => todo!(), + FePrimitive::Turbulence(_) => todo!(), + } + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives/blend.rs b/crates/svg-filters/src/types/nodes/primitives/blend.rs index 242f763..1aba6d0 100644 --- a/crates/svg-filters/src/types/nodes/primitives/blend.rs +++ b/crates/svg-filters/src/types/nodes/primitives/blend.rs @@ -1,6 +1,6 @@ /// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement) #[derive(Debug)] -pub(in crate::types::nodes) struct Blend { +pub struct Blend { mode: BlendMode, } diff --git a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs index fbae6b2..ef32b60 100644 --- a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs +++ b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs @@ -1,3 +1,9 @@ +use std::borrow::Cow; + +use quick_xml::{events::attributes::Attribute, name::QName, se::to_string}; + +use super::WriteElement; + /// [feColorMatrix](https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement) #[derive(Debug)] pub struct ColorMatrix { @@ -10,6 +16,34 @@ impl ColorMatrix { } } +impl WriteElement for ColorMatrix { + fn attrs(&self) -> Vec { + match &self.cm_type { + ColorMatrixType::Matrix(v) => vec![Attribute { + key: QName(b"values"), + value: Cow::from( + v.iter() + .map(|v| v.to_string()) + .reduce(|mut acc, e| { + acc.push(' '); + acc.push_str(&e); + acc + }) + .expect("Should be able to concatenate the thingies") + .into_bytes(), + ), + }], + ColorMatrixType::Saturate(v) => todo!(), + ColorMatrixType::HueRotate(v) => todo!(), + ColorMatrixType::LuminanceToAlpha => todo!(), + } + } + + fn tag_name(&self) -> &'static str { + "feColorMatrix" + } +} + #[derive(Debug)] pub enum ColorMatrixType { Matrix(Box<[f32; 20]>), -- 2.46.0 From a42ec014e550a4c3570e920c533ac5844e4fa499 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 16 Mar 2024 00:35:23 +0100 Subject: [PATCH 05/67] svg-filters: get svg generation working!!!! --- crates/svg-filters/src/lib.rs | 3 +- crates/svg-filters/src/main.rs | 35 ++-- crates/svg-filters/src/types.rs | 169 ++++++++++++------ .../svg-filters/src/types/nodes/primitives.rs | 16 +- .../src/types/nodes/primitives/composite.rs | 53 ++++++ .../types/nodes/primitives/gaussian_blur.rs | 21 +++ .../src/types/nodes/primitives/offset.rs | 25 +++ 7 files changed, 237 insertions(+), 85 deletions(-) diff --git a/crates/svg-filters/src/lib.rs b/crates/svg-filters/src/lib.rs index 5081a19..a87a611 100644 --- a/crates/svg-filters/src/lib.rs +++ b/crates/svg-filters/src/lib.rs @@ -1,5 +1,6 @@ -pub mod types; +#![feature(lint_reasons)] +pub mod types; pub use types::nodes::Node; pub use types::Edge; pub use types::Filter; diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index 5531784..fb6a782 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -4,17 +4,6 @@ use svg_filters::{ }; fn main() { - let mut supersimple = Filter::new(); - - let cm = supersimple.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ - 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., - ])))); - - supersimple - .graph - .extend_with_edges(&[(supersimple.source_graphic(), cm)]); - dbg!(supersimple.to_svg()); - let mut filter = Filter::new(); // @@ -51,18 +40,18 @@ fn main() { let composite_final = filter.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); filter.graph.extend_with_edges(&[ - (filter.source_graphic(), chan_r, Edge::new()), - (filter.source_graphic(), chan_b, Edge::new()), - (filter.source_graphic(), chan_g, Edge::new()), - (chan_r, offset_r, Edge::new()), - (offset_r, blur_r, Edge::new()), - (chan_b, offset_b, Edge::new()), - (offset_b, blur_b, Edge::new()), - (blur_r, composite_rb, Edge::in_idx(0)), - (blur_b, composite_rb, Edge::in_idx(1)), - (composite_rb, composite_final, Edge::in_idx(0)), - (chan_g, composite_final, Edge::in_idx(1)), + (filter.source_graphic(), chan_r, Edge::unnamed()), + (filter.source_graphic(), chan_b, Edge::unnamed()), + (filter.source_graphic(), chan_g, Edge::unnamed()), + (chan_r, offset_r, Edge::new("ro")), + (offset_r, blur_r, Edge::new("rob")), + (chan_b, offset_b, Edge::new("bo")), + (offset_b, blur_b, Edge::new("bob")), + (blur_r, composite_rb, Edge::new("robc").with_idx(0)), + (blur_b, composite_rb, Edge::new("bobc").with_idx(1)), + (composite_rb, composite_final, Edge::new("cf").with_idx(0)), + (chan_g, composite_final, Edge::new("gf").with_idx(1)), ]); - println!("Result: {}", filter.to_svg()) + println!("{}", filter.to_svg()) } diff --git a/crates/svg-filters/src/types.rs b/crates/svg-filters/src/types.rs index df679ba..325a869 100644 --- a/crates/svg-filters/src/types.rs +++ b/crates/svg-filters/src/types.rs @@ -1,4 +1,11 @@ -use std::{borrow::Cow, collections::HashMap, fmt::Debug, io::BufWriter, primitive}; +use core::panic; +use std::{ + borrow::Cow, + collections::{HashMap, HashSet}, + fmt::Debug, + io::BufWriter, + primitive, +}; use petgraph::{ adj::EdgeIndex, @@ -9,7 +16,7 @@ use petgraph::{ prelude::NodeIndex, visit::NodeIndexable, }; -use quick_xml::{events::attributes::Attribute, name::QName, ElementWriter}; +use quick_xml::{events::attributes::Attribute, name::QName, ElementWriter, Error}; pub mod length; pub mod nodes; @@ -53,47 +60,53 @@ impl Filter<'_> { pub fn to_svg(&self) -> String { let mut result = Vec::new(); - let mut writer = quick_xml::Writer::new(&mut result); + // let mut doc_writer = quick_xml::Writer::new(&mut result); + let mut doc_writer = quick_xml::Writer::new_with_indent(&mut result, b' ', 2); - let mut dfs_space = DfsSpace::new(&self.graph); - let sorted = toposort(&self.graph, Some(&mut dfs_space)).expect("No cycles! Bad user!"); + doc_writer + .create_element("filter") + .with_attribute(("id", "chromabb_gen")) + .write_inner_content(|writer| { + let mut dfs_space = DfsSpace::new(&self.graph); + let sorted = + toposort(&self.graph, Some(&mut dfs_space)).expect("No cycles! Bad user!"); - sorted - .into_iter() - .filter_map(|node_idx| { - let node = self - .graph - .node_weight(node_idx) - .expect("toposorting will not return invalid indices"); - - if let Node::Primitive { - primitive, - common_attrs, - } = node - { - Some((node_idx, primitive, common_attrs)) - } else { - None - } - }) - .fold( - &mut writer, - |writer, (node_idx, primitive, common_attrs)| { - let el_writer = primitive.element_writer(writer); - create_input_elements( - &self.graph, - el_writer, - node_idx, - self.graph + let v = sorted + .into_iter() + .filter_map(|node_idx| { + let node = self + .graph .node_weight(node_idx) - .expect("cannot get invalid node_idx from toposort") - .input_count(), - ) - .write_empty() - .expect("should write successfully") - }, - ); + .expect("toposorting will not return invalid indices"); + if let Node::Primitive { + primitive, + common_attrs, + } = node + { + Some((node_idx, primitive, common_attrs)) + } else { + None + } + }) + .try_fold(writer, |acc, (node_idx, primitive, common_attrs)| { + let mut el_writer = primitive.element_writer(&mut *acc); + el_writer = create_input_attr( + &self.graph, + el_writer, + node_idx, + self.graph + .node_weight(node_idx) + .expect("cannot get invalid node_idx from toposort") + .input_count(), + ); + create_output_attr(&self.graph, el_writer, node_idx).write_empty() + }) + .map(|_| ()); + + Ok::<(), Error>(()) + }) + .expect("shouldnt fail to write or something"); String::from_utf8_lossy(&result).to_string() } } @@ -112,25 +125,31 @@ pub struct Edge<'a> { pub in_idx: Option, } -impl Edge<'_> { - pub fn new() -> Self { +impl<'a> Edge<'a> { + pub fn new(name: &'a str) -> Self { Self { - edge_type: EdgeType::Unnamed, + edge_type: EdgeType::Named(name), in_idx: None, } } - pub fn in_idx(idx: u8) -> Self { + #[must_use] + pub fn with_idx(mut self, idx: u8) -> Self { + self.in_idx = Some(idx); + self + } + + pub fn unnamed() -> Self { Self { edge_type: EdgeType::Unnamed, - in_idx: Some(idx), + in_idx: None, } } } impl Default for Edge<'_> { fn default() -> Self { - Self::new() + Self::unnamed() } } @@ -140,12 +159,13 @@ pub enum EdgeType<'a> { /// For standard inputs such as SourceGraphic etc., which we'll just be representing as nodes for simplicity Unnamed, } -fn create_input_elements<'a>( - g: &'a DiGraph>, - mut el_writer: ElementWriter<'a, &'a mut Vec>, + +fn create_input_attr<'w, 'b>( + g: &'_ DiGraph>, + mut el_writer: ElementWriter<'w, &'b mut Vec>, node_idx: NodeIndex, input_count: u8, -) -> ElementWriter<'a, &'a mut Vec> { +) -> ElementWriter<'w, &'b mut Vec> { let inputs = g .neighbors_directed(node_idx, petgraph::Direction::Incoming) .collect::>(); @@ -176,12 +196,55 @@ fn create_input_elements<'a>( let v = match incoming_node { Node::StdInput(std_in) => format!("{std_in:?}"), - Node::Primitive { - primitive, - common_attrs, - } => todo!(), + Node::Primitive { .. } => { + if let EdgeType::Named(name) = edge_type { + (*name).to_owned() + } else { + panic!( + "unnamed edges should not be used for connections between primitives" + ) + } + } }; el_writer.with_attribute((in_attr_name.as_str(), v.as_str())) }) } + +#[allow(clippy::unwrap_used, reason = "all unwraps are for finding on options")] +fn create_output_attr<'w, 'b>( + g: &'_ DiGraph>, + mut el_writer: ElementWriter<'w, &'b mut Vec>, + node_idx: NodeIndex, +) -> ElementWriter<'w, &'b mut Vec> { + let output = g + .neighbors_directed(node_idx, petgraph::Direction::Outgoing) + .map(|neighbor_idx| { + let edge_idx = g.find_edge(node_idx, neighbor_idx).unwrap(); + + let Edge { + edge_type: EdgeType::Named(name), + .. + } = g.edge_weight(edge_idx).unwrap() + else { + panic!("Unnamed edge used for connection between primitives"); + }; + *name + }) + .collect::>(); + + if output.is_empty() { + el_writer + } else if output.len() == 1 { + el_writer.with_attribute(Attribute { + key: QName(b"result"), + value: Cow::from( + (*output.into_iter().collect::>().first().unwrap()) + .to_string() + .into_bytes(), + ), + }) + } else { + panic!("Can't have more then one named output: {output:?}") + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index 012fd9a..513f1a8 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -62,18 +62,18 @@ impl WriteElement for FePrimitive { fn attrs(&self) -> std::vec::Vec> { match self { FePrimitive::Blend(_) => todo!(), - FePrimitive::ColorMatrix(cm) => cm.attrs(), + FePrimitive::ColorMatrix(el) => el.attrs(), FePrimitive::ComponentTransfer(_) => todo!(), - FePrimitive::Composite(_) => todo!(), + FePrimitive::Composite(el) => el.attrs(), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(), FePrimitive::Flood(_) => todo!(), - FePrimitive::GaussianBlur(_) => todo!(), + FePrimitive::GaussianBlur(el) => el.attrs(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), FePrimitive::Morphology(_) => todo!(), - FePrimitive::Offset(_) => todo!(), + FePrimitive::Offset(el) => el.attrs(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), FePrimitive::Turbulence(_) => todo!(), @@ -83,18 +83,18 @@ impl WriteElement for FePrimitive { fn tag_name(&self) -> &'static str { match self { FePrimitive::Blend(_) => todo!(), - FePrimitive::ColorMatrix(cm) => cm.tag_name(), + FePrimitive::ColorMatrix(el) => el.tag_name(), FePrimitive::ComponentTransfer(_) => todo!(), - FePrimitive::Composite(_) => todo!(), + FePrimitive::Composite(el) => el.tag_name(), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(), FePrimitive::Flood(_) => todo!(), - FePrimitive::GaussianBlur(_) => todo!(), + FePrimitive::GaussianBlur(el) => el.tag_name(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), FePrimitive::Morphology(_) => todo!(), - FePrimitive::Offset(_) => todo!(), + FePrimitive::Offset(el) => el.tag_name(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), FePrimitive::Turbulence(_) => todo!(), diff --git a/crates/svg-filters/src/types/nodes/primitives/composite.rs b/crates/svg-filters/src/types/nodes/primitives/composite.rs index c86de26..e27664d 100644 --- a/crates/svg-filters/src/types/nodes/primitives/composite.rs +++ b/crates/svg-filters/src/types/nodes/primitives/composite.rs @@ -1,3 +1,9 @@ +use std::borrow::Cow; + +use quick_xml::{events::attributes::Attribute, name::QName}; + +use super::WriteElement; + /// [feComposite](https://www.w3.org/TR/SVG11/filters.html#feCompositeElement) #[derive(Debug)] pub struct Composite { @@ -25,3 +31,50 @@ pub enum CompositeOperator { Xor, Arithmetic { k1: f32, k2: f32, k3: f32, k4: f32 }, } + +impl WriteElement for Composite { + fn attrs(&self) -> Vec { + let (op_name, vals) = match self.operator { + CompositeOperator::Over => ("over", None), + CompositeOperator::In => ("in", None), + CompositeOperator::Out => ("out", None), + CompositeOperator::Atop => ("atop", None), + CompositeOperator::Xor => ("xor", None), + CompositeOperator::Arithmetic { k1, k2, k3, k4 } => { + ("arithmetic", Some([k1, k2, k3, k4])) + } + }; + + let mut r = vec![Attribute { + key: QName(b"operator"), + value: Cow::from(op_name.as_bytes()), + }]; + + if let Some([k1, k2, k3, k4]) = vals { + r.append(&mut vec![ + Attribute { + key: QName(b"k1"), + value: Cow::from(k1.to_string().into_bytes()), + }, + Attribute { + key: QName(b"k2"), + value: Cow::from(k2.to_string().into_bytes()), + }, + Attribute { + key: QName(b"k3"), + value: Cow::from(k3.to_string().into_bytes()), + }, + Attribute { + key: QName(b"k4"), + value: Cow::from(k4.to_string().into_bytes()), + }, + ]); + } + + r + } + + fn tag_name(&self) -> &'static str { + "feComposite" + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs index e6ca9c1..a0c42bd 100644 --- a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs +++ b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs @@ -1,3 +1,9 @@ +use std::borrow::Cow; + +use quick_xml::{events::attributes::Attribute, name::QName}; + +use super::WriteElement; + /// [feGaussianBlur](https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement) #[derive(Debug)] pub struct GaussianBlur { @@ -17,3 +23,18 @@ impl GaussianBlur { } } } + +impl WriteElement for GaussianBlur { + fn attrs(&self) -> Vec { + vec![Attribute { + key: QName(b"stdDeviation"), + value: Cow::from( + format!("{} {}", self.std_deviation.0, self.std_deviation.1).into_bytes(), + ), + }] + } + + fn tag_name(&self) -> &'static str { + "feGaussianBlur" + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives/offset.rs b/crates/svg-filters/src/types/nodes/primitives/offset.rs index 96b47ef..4cad35b 100644 --- a/crates/svg-filters/src/types/nodes/primitives/offset.rs +++ b/crates/svg-filters/src/types/nodes/primitives/offset.rs @@ -1,3 +1,9 @@ +use std::borrow::Cow; + +use quick_xml::{events::attributes::Attribute, name::QName}; + +use super::WriteElement; + /// [feOffset](https://www.w3.org/TR/SVG11/filters.html#feOffsetElement) #[derive(Debug)] pub struct Offset { @@ -10,3 +16,22 @@ impl Offset { Self { dx, dy } } } + +impl WriteElement for Offset { + fn attrs(&self) -> Vec { + vec![ + Attribute { + key: QName(b"dx"), + value: Cow::from(self.dx.to_string().into_bytes()), + }, + Attribute { + key: QName(b"dy"), + value: Cow::from(self.dy.to_string().into_bytes()), + }, + ] + } + + fn tag_name(&self) -> &'static str { + "feOffset" + } +} -- 2.46.0 From 5368951254e89a92122c7e90e9cb02a8bdc727c3 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 16 Mar 2024 20:52:45 +0100 Subject: [PATCH 06/67] svg-filters: rework codegen --- crates/svg-filters/src/codegen.rs | 229 ++++++++++++++++++++ crates/svg-filters/src/lib.rs | 6 +- crates/svg-filters/src/main.rs | 58 ++--- crates/svg-filters/src/types.rs | 284 +++++-------------------- crates/svg-filters/src/types/length.rs | 29 ++- crates/svg-filters/src/types/nodes.rs | 22 +- 6 files changed, 361 insertions(+), 267 deletions(-) create mode 100644 crates/svg-filters/src/codegen.rs diff --git a/crates/svg-filters/src/codegen.rs b/crates/svg-filters/src/codegen.rs new file mode 100644 index 0000000..ca0baa3 --- /dev/null +++ b/crates/svg-filters/src/codegen.rs @@ -0,0 +1,229 @@ +use std::{ + borrow::Cow, + cmp, + collections::{BTreeSet, HashMap, HashSet}, +}; + +use indexmap::IndexMap; +use petgraph::{ + algo::toposort, + graph::DiGraph, + prelude::{EdgeIndex, NodeIndex}, +}; +use quick_xml::{events::attributes::Attribute, name::QName, ElementWriter}; + +use crate::{ + types::{ + graph::{edge::Edge, FilterGraph}, + nodes::{primitives::WriteElement, CommonAttrs}, + }, + Node, +}; + +use self::error::CodegenError; + +pub struct SvgDocument { + filters: HashMap, +} + +impl SvgDocument { + pub fn new() -> Self { + Self { + filters: HashMap::new(), + } + } + + #[allow(clippy::unwrap_used, reason = "we literally just did the insertion")] + pub fn create_filter(&mut self, id: impl ToString) -> &mut FilterGraph { + let filter = FilterGraph::new(); + + self.filters.insert(id.to_string(), filter); + self.filters.get_mut(&id.to_string()).unwrap() + } + + pub fn generate_svg(&self) -> String { + let mut result = Vec::new(); + let mut doc_writer = quick_xml::Writer::new_with_indent(&mut result, b' ', 4); + + doc_writer + .create_element("svg") + .write_inner_content(|writer| { + self.filters + .iter() + .try_fold(writer, Self::gen_filter) + .map(|_| {}) + }); + + String::from_utf8_lossy(&result).to_string() + } + + fn gen_filter<'w, 'r>( + writer: &'w mut quick_xml::Writer<&'r mut Vec>, + (id, graph): (&String, &FilterGraph), + ) -> Result<&'w mut quick_xml::Writer<&'r mut Vec>, CodegenError> { + writer + .create_element("filter") + .with_attribute(("id", id.as_str())) + .write_inner_content(|writer| Self::graph_to_svg(writer, graph)) + } + + fn graph_to_svg( + writer: &mut quick_xml::Writer<&mut Vec>, + graph: &FilterGraph, + ) -> Result<(), CodegenError> { + let sorted = toposort(&graph.dag, None).expect("no cycles allowed in a DAG"); + sorted + .into_iter() + .filter_map(|node_idx| { + graph + .dag + .node_weight(node_idx) + .and_then(|node| node.primitive()) + .map(|(primitive, common_attrs)| (node_idx, primitive, common_attrs)) + }) + .try_fold(writer, |writer, (node_idx, primitive, common_attrs)| { + let mut el_writer = primitive.element_writer(writer); + el_writer = input_attrs(node_idx, &graph.dag).write_into(el_writer); + el_writer = output_attrs(node_idx, &graph.dag).write_into(el_writer); + el_writer = Attrs::from(*common_attrs).write_into(el_writer); + el_writer.write_empty() + })?; + + Ok(()) + } +} + +struct Attrs(IndexMap); + +impl Attrs { + fn write_into<'w, 'b>( + self, + el_writer: ElementWriter<'w, &'b mut Vec>, + ) -> ElementWriter<'w, &'b mut Vec> { + let attrs = self.0.iter().map(|(k, v)| (k.as_str(), v.as_str())); + el_writer.with_attributes(attrs) + } +} + +impl From for Attrs { + fn from(value: CommonAttrs) -> Self { + let CommonAttrs { + x, + y, + width, + height, + } = value; + Self(IndexMap::from([ + ("x".to_owned(), x.to_string()), + ("y".to_owned(), y.to_string()), + ("width".to_owned(), width.to_string()), + ("height".to_owned(), height.to_string()), + ])) + } +} + +#[allow( + clippy::unwrap_used, + reason = "in all cases of use of unwrap, it's values we got from the graph so we know it's safe" +)] +fn input_attrs(node_idx: NodeIndex, g: &DiGraph) -> Attrs { + let inputs: Vec = g + .neighbors_directed(node_idx, petgraph::Direction::Incoming) + .collect(); + let node = g.node_weight(node_idx).unwrap(); + + if node.input_count() as usize != inputs.len() { + todo!("proper error handling for wrong numbers of inputs") + } + + let mut inputs = inputs + .into_iter() + .map(|input_idx| { + let edge_idx = g.find_edge(input_idx, node_idx).unwrap(); + let edge = g.edge_weight(edge_idx).unwrap(); + let input_node = g.node_weight(input_idx).unwrap(); + + (input_node, edge_idx, edge) + }) + .collect::>(); + inputs.sort_by(|a, b| a.2.cmp(b.2)); + + let mut uniq = BTreeSet::new(); + let no_duplicates = inputs.iter().all(|(_, _, edge)| uniq.insert(edge)); + + if no_duplicates { + Attrs( + inputs + .into_iter() + .map(|(input, edge_idx, edge)| { + let name = match input { + Node::StdInput(s) => format!("{s:?}"), + Node::Primitive { .. } => format_edge_idx(edge_idx), + }; + (edge.to_string(), name) + }) + .collect(), + ) + } else { + todo!("better error handling for inputs with multiple values") + } +} + +#[allow( + clippy::unwrap_used, + reason = "in all cases of use of unwrap, it's values we got from the graph so we know it's safe" +)] +fn output_attrs(node_idx: NodeIndex, g: &DiGraph) -> Attrs { + let outputs: Vec = g + .neighbors_directed(node_idx, petgraph::Direction::Outgoing) + .collect(); + + match outputs.len().cmp(&1) { + cmp::Ordering::Less => Attrs(IndexMap::new()), + cmp::Ordering::Equal => { + let output = outputs.first().unwrap(); + let edge_idx = g.find_edge(node_idx, *output).unwrap(); + Attrs(IndexMap::from([( + "result".to_string(), + format_edge_idx(edge_idx), + )])) + } + cmp::Ordering::Greater => todo!("better error handling for too many outputs"), + } +} + +/// convenience method to avoid fuckups during future changes +fn format_edge_idx(idx: EdgeIndex) -> String { + format!("edge{}", idx.index()) +} + +mod error { + use std::{error::Error, fmt::Display}; + + #[derive(Debug)] + pub enum CodegenError { + QuickXmlError(quick_xml::Error), + } + + impl From for CodegenError { + fn from(value: quick_xml::Error) -> Self { + Self::QuickXmlError(value) + } + } + + impl Display for CodegenError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CodegenError::QuickXmlError(e) => e.fmt(f), + } + } + } + + impl Error for CodegenError {} +} + +impl Default for SvgDocument { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/svg-filters/src/lib.rs b/crates/svg-filters/src/lib.rs index a87a611..cc443d8 100644 --- a/crates/svg-filters/src/lib.rs +++ b/crates/svg-filters/src/lib.rs @@ -1,6 +1,8 @@ #![feature(lint_reasons)] +pub mod codegen; pub mod types; pub use types::nodes::Node; -pub use types::Edge; -pub use types::Filter; + +#[cfg(test)] +mod tests {} diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index fb6a782..7caef27 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -1,11 +1,12 @@ +use std::hint::black_box; + use svg_filters::{ - types::nodes::{primitives::color_matrix::ColorMatrixType, standard_input::StandardInput}, - Edge, Filter, Node, + codegen::SvgDocument, + types::{graph::edge::Edge, nodes::primitives::color_matrix::ColorMatrixType}, + Node, }; fn main() { - let mut filter = Filter::new(); - // // // @@ -21,37 +22,42 @@ fn main() { // // - let chan_r = filter.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ + let mut doc = SvgDocument::new(); + let chromabb = doc.create_filter("chromabb_gen"); + + let chan_r = chromabb.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., ])))); - let chan_g = filter.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ + let chan_g = chromabb.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., ])))); - let chan_b = filter.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ + let chan_b = chromabb.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., ])))); - let offset_r = filter.add_node(Node::offset(25., 0.)); - let offset_b = filter.add_node(Node::offset(-25., 0.)); - let blur_r = filter.add_node(Node::gaussian_blur_xy(5, 0)); - let blur_b = filter.add_node(Node::gaussian_blur_xy(5, 0)); + let offset_r = chromabb.add_node(Node::offset(25., 0.)); + let offset_b = chromabb.add_node(Node::offset(-25., 0.)); + let blur_r = chromabb.add_node(Node::gaussian_blur_xy(5, 0)); + let blur_b = chromabb.add_node(Node::gaussian_blur_xy(5, 0)); - let composite_rb = filter.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); - let composite_final = filter.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); + let composite_rb = chromabb.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); + let composite_final = chromabb.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); - filter.graph.extend_with_edges(&[ - (filter.source_graphic(), chan_r, Edge::unnamed()), - (filter.source_graphic(), chan_b, Edge::unnamed()), - (filter.source_graphic(), chan_g, Edge::unnamed()), - (chan_r, offset_r, Edge::new("ro")), - (offset_r, blur_r, Edge::new("rob")), - (chan_b, offset_b, Edge::new("bo")), - (offset_b, blur_b, Edge::new("bob")), - (blur_r, composite_rb, Edge::new("robc").with_idx(0)), - (blur_b, composite_rb, Edge::new("bobc").with_idx(1)), - (composite_rb, composite_final, Edge::new("cf").with_idx(0)), - (chan_g, composite_final, Edge::new("gf").with_idx(1)), + chromabb.dag.extend_with_edges(&[ + (chromabb.source_graphic(), chan_r), + (chromabb.source_graphic(), chan_b), + (chromabb.source_graphic(), chan_g), + (chan_r, offset_r), + (offset_r, blur_r), + (chan_b, offset_b), + (offset_b, blur_b), + ]); + chromabb.dag.extend_with_edges(&[ + (blur_r, composite_rb, Edge::new(0)), + (blur_b, composite_rb, Edge::new(1)), + (composite_rb, composite_final, Edge::new(0)), + (chan_g, composite_final, Edge::new(1)), ]); - println!("{}", filter.to_svg()) + black_box(doc.generate_svg()); } diff --git a/crates/svg-filters/src/types.rs b/crates/svg-filters/src/types.rs index 325a869..fa3941e 100644 --- a/crates/svg-filters/src/types.rs +++ b/crates/svg-filters/src/types.rs @@ -1,250 +1,70 @@ -use core::panic; -use std::{ - borrow::Cow, - collections::{HashMap, HashSet}, - fmt::Debug, - io::BufWriter, - primitive, -}; - -use petgraph::{ - adj::EdgeIndex, - algo::{toposort, DfsSpace}, - data::{Build, DataMap}, - graph::DiGraph, - graphmap::DiGraphMap, - prelude::NodeIndex, - visit::NodeIndexable, -}; -use quick_xml::{events::attributes::Attribute, name::QName, ElementWriter, Error}; - pub mod length; pub mod nodes; -use crate::types::nodes::primitives::color_matrix::{ColorMatrix, ColorMatrixType}; +// pub mod old; -use self::{ - length::{Coordinate, Length}, - nodes::{ - primitives::{FePrimitive, WriteElement}, - Node, - }, -}; +pub mod graph { + use std::iter::Iterator; -#[derive(Debug)] -pub struct Filter<'a> { - pub graph: DiGraph>, - source_graphic_idx: NodeIndex, -} + use petgraph::{data::Build, prelude::*}; -impl Filter<'_> { - pub fn new() -> Self { - let mut graph = DiGraph::new(); - let source_graphic_idx = graph.add_node(Node::StdInput( - nodes::standard_input::StandardInput::SourceGraphic, - )); + use crate::Node; - Self { - graph, - source_graphic_idx, + use self::edge::Edge; + + use super::nodes::standard_input::StandardInput; + + #[derive(Debug)] + pub struct FilterGraph { + pub dag: DiGraph, + source_graphic_idx: NodeIndex, + } + + impl FilterGraph { + pub fn new() -> Self { + let mut dag = DiGraph::new(); + let source_graphic_idx = dag.add_node(Node::StdInput(StandardInput::SourceGraphic)); + + Self { + dag, + source_graphic_idx, + } + } + + pub fn add_node(&mut self, node: Node) -> NodeIndex { + self.dag.add_node(node) + } + + pub fn source_graphic(&self) -> NodeIndex { + self.source_graphic_idx } } - pub fn add_node(&mut self, node: Node) -> NodeIndex { - self.graph.add_node(node) - } - - pub fn source_graphic(&self) -> NodeIndex { - self.source_graphic_idx - } - - pub fn to_svg(&self) -> String { - let mut result = Vec::new(); - // let mut doc_writer = quick_xml::Writer::new(&mut result); - let mut doc_writer = quick_xml::Writer::new_with_indent(&mut result, b' ', 2); - - doc_writer - .create_element("filter") - .with_attribute(("id", "chromabb_gen")) - .write_inner_content(|writer| { - let mut dfs_space = DfsSpace::new(&self.graph); - let sorted = - toposort(&self.graph, Some(&mut dfs_space)).expect("No cycles! Bad user!"); - - let v = sorted - .into_iter() - .filter_map(|node_idx| { - let node = self - .graph - .node_weight(node_idx) - .expect("toposorting will not return invalid indices"); - - if let Node::Primitive { - primitive, - common_attrs, - } = node - { - Some((node_idx, primitive, common_attrs)) - } else { - None - } - }) - .try_fold(writer, |acc, (node_idx, primitive, common_attrs)| { - let mut el_writer = primitive.element_writer(&mut *acc); - el_writer = create_input_attr( - &self.graph, - el_writer, - node_idx, - self.graph - .node_weight(node_idx) - .expect("cannot get invalid node_idx from toposort") - .input_count(), - ); - create_output_attr(&self.graph, el_writer, node_idx).write_empty() - }) - .map(|_| ()); - - Ok::<(), Error>(()) - }) - .expect("shouldnt fail to write or something"); - String::from_utf8_lossy(&result).to_string() - } -} - -impl Default for Filter<'_> { - fn default() -> Self { - Self::new() - } -} - -#[derive(Debug, Clone)] -pub struct Edge<'a> { - pub edge_type: EdgeType<'a>, - /// the index of the `in` attribute on the target element - /// if None, just `in` - pub in_idx: Option, -} - -impl<'a> Edge<'a> { - pub fn new(name: &'a str) -> Self { - Self { - edge_type: EdgeType::Named(name), - in_idx: None, + pub mod edge { + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + pub struct Edge { + input_idx: u8, } - } - #[must_use] - pub fn with_idx(mut self, idx: u8) -> Self { - self.in_idx = Some(idx); - self - } - - pub fn unnamed() -> Self { - Self { - edge_type: EdgeType::Unnamed, - in_idx: None, + impl Edge { + pub fn new(input_idx: u8) -> Self { + Self { input_idx } + } } - } -} -impl Default for Edge<'_> { - fn default() -> Self { - Self::unnamed() - } -} + impl Default for Edge { + fn default() -> Self { + Self { input_idx: 0 } + } + } -#[derive(Debug, Clone)] -pub enum EdgeType<'a> { - Named(&'a str), - /// For standard inputs such as SourceGraphic etc., which we'll just be representing as nodes for simplicity - Unnamed, -} - -fn create_input_attr<'w, 'b>( - g: &'_ DiGraph>, - mut el_writer: ElementWriter<'w, &'b mut Vec>, - node_idx: NodeIndex, - input_count: u8, -) -> ElementWriter<'w, &'b mut Vec> { - let inputs = g - .neighbors_directed(node_idx, petgraph::Direction::Incoming) - .collect::>(); - - if inputs.len() != input_count as usize { - // TODO: better error handling - panic!("input couns didnt match"); - } - - inputs - .into_iter() - .enumerate() - .fold(el_writer, |el_writer, (i, incoming_idx)| { - let incoming_node = g.node_weight(incoming_idx).expect("cannot fail here"); - - // find incoming edge and get weight - let Edge { edge_type, in_idx } = g - .edge_weight( - g.find_edge(incoming_idx, node_idx) - .expect("there should always be an edge"), - ) - .expect("once again, should always exist"); - - let in_attr_name = match in_idx { - None | Some(0) => "in".to_owned(), - Some(n) => format!("in{}", n + 1), - }; - - let v = match incoming_node { - Node::StdInput(std_in) => format!("{std_in:?}"), - Node::Primitive { .. } => { - if let EdgeType::Named(name) = edge_type { - (*name).to_owned() - } else { - panic!( - "unnamed edges should not be used for connections between primitives" - ) - } + impl ToString for Edge { + fn to_string(&self) -> String { + match self.input_idx { + 0 => "in".to_owned(), + n => format!("in{}", n + 1), } - }; - - el_writer.with_attribute((in_attr_name.as_str(), v.as_str())) - }) -} - -#[allow(clippy::unwrap_used, reason = "all unwraps are for finding on options")] -fn create_output_attr<'w, 'b>( - g: &'_ DiGraph>, - mut el_writer: ElementWriter<'w, &'b mut Vec>, - node_idx: NodeIndex, -) -> ElementWriter<'w, &'b mut Vec> { - let output = g - .neighbors_directed(node_idx, petgraph::Direction::Outgoing) - .map(|neighbor_idx| { - let edge_idx = g.find_edge(node_idx, neighbor_idx).unwrap(); - - let Edge { - edge_type: EdgeType::Named(name), - .. - } = g.edge_weight(edge_idx).unwrap() - else { - panic!("Unnamed edge used for connection between primitives"); - }; - *name - }) - .collect::>(); - - if output.is_empty() { - el_writer - } else if output.len() == 1 { - el_writer.with_attribute(Attribute { - key: QName(b"result"), - value: Cow::from( - (*output.into_iter().collect::>().first().unwrap()) - .to_string() - .into_bytes(), - ), - }) - } else { - panic!("Can't have more then one named output: {output:?}") + } + } } } diff --git a/crates/svg-filters/src/types/length.rs b/crates/svg-filters/src/types/length.rs index 1186181..10ddfcc 100644 --- a/crates/svg-filters/src/types/length.rs +++ b/crates/svg-filters/src/types/length.rs @@ -1,8 +1,17 @@ -#[derive(Default, Debug)] +use std::fmt::Display; + +#[derive(Default, Debug, Clone, Copy)] pub struct Length(f32, Unit); + +impl Display for Length { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}{}", self.0, self.1) + } +} + pub type Coordinate = Length; -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone, Copy)] pub enum Unit { #[default] None, @@ -15,3 +24,19 @@ pub enum Unit { Pt, Pc, } + +impl Display for Unit { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Unit::None => f.write_str(""), + Unit::Em => f.write_str("em"), + Unit::Ex => f.write_str("ex"), + Unit::Px => f.write_str("px"), + Unit::In => f.write_str("in"), + Unit::Cm => f.write_str("cm"), + Unit::Mm => f.write_str("mm"), + Unit::Pt => f.write_str("pt"), + Unit::Pc => f.write_str("pc"), + } + } +} diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index 63e041c..80cb5d0 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -34,12 +34,12 @@ impl Default for Node { } } -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone, Copy)] pub(crate) struct CommonAttrs { - x: Coordinate, - y: Coordinate, - width: Length, - height: Length, + pub x: Coordinate, + pub y: Coordinate, + pub width: Length, + pub height: Length, } impl Node { @@ -50,6 +50,18 @@ impl Node { } } + pub fn primitive(&self) -> Option<(&FePrimitive, &CommonAttrs)> { + if let Node::Primitive { + primitive, + common_attrs, + } = self + { + Some((primitive, common_attrs)) + } else { + None + } + } + pub fn input_count(&self) -> u8 { match self { Node::Primitive { -- 2.46.0 From bf60bdd81426536acb5fffac24d99f1c449e9a76 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 16 Mar 2024 23:57:09 +0100 Subject: [PATCH 07/67] svg-filters: simplify and refactor a bit --- crates/svg-filters/src/codegen.rs | 134 +++--------- crates/svg-filters/src/main.rs | 61 +++--- crates/svg-filters/src/types.rs | 66 +----- crates/svg-filters/src/types/graph.rs | 202 ++++++++++++++++++ crates/svg-filters/src/types/graph/edge.rs | 19 ++ crates/svg-filters/src/types/length.rs | 6 + crates/svg-filters/src/types/nodes.rs | 39 +++- .../svg-filters/src/types/nodes/primitives.rs | 36 +++- .../src/types/nodes/primitives/blend.rs | 46 ++++ .../types/nodes/primitives/color_matrix.rs | 12 +- .../src/types/nodes/primitives/flood.rs | 24 +++ .../src/types/nodes/primitives/morphology.rs | 34 +++ .../src/types/nodes/primitives/tile.rs | 12 ++ .../src/types/nodes/primitives/turbulence.rs | 66 +++++- .../src/types/nodes/standard_input.rs | 2 +- 15 files changed, 532 insertions(+), 227 deletions(-) create mode 100644 crates/svg-filters/src/types/graph.rs create mode 100644 crates/svg-filters/src/types/graph/edge.rs diff --git a/crates/svg-filters/src/codegen.rs b/crates/svg-filters/src/codegen.rs index ca0baa3..a8e7b23 100644 --- a/crates/svg-filters/src/codegen.rs +++ b/crates/svg-filters/src/codegen.rs @@ -1,7 +1,9 @@ use std::{ - borrow::Cow, cmp, - collections::{BTreeSet, HashMap, HashSet}, + collections::{BTreeSet, HashMap}, + fmt::Display, + io::Read, + ops::Not, }; use indexmap::IndexMap; @@ -10,11 +12,11 @@ use petgraph::{ graph::DiGraph, prelude::{EdgeIndex, NodeIndex}, }; -use quick_xml::{events::attributes::Attribute, name::QName, ElementWriter}; +use quick_xml::ElementWriter; use crate::{ types::{ - graph::{edge::Edge, FilterGraph}, + graph::{edge::Edge, FilterGraph, NodeInput}, nodes::{primitives::WriteElement, CommonAttrs}, }, Node, @@ -43,7 +45,7 @@ impl SvgDocument { pub fn generate_svg(&self) -> String { let mut result = Vec::new(); - let mut doc_writer = quick_xml::Writer::new_with_indent(&mut result, b' ', 4); + let mut doc_writer = quick_xml::Writer::new_with_indent(&mut result, b' ', 2); doc_writer .create_element("svg") @@ -82,121 +84,35 @@ impl SvgDocument { .map(|(primitive, common_attrs)| (node_idx, primitive, common_attrs)) }) .try_fold(writer, |writer, (node_idx, primitive, common_attrs)| { - let mut el_writer = primitive.element_writer(writer); - el_writer = input_attrs(node_idx, &graph.dag).write_into(el_writer); - el_writer = output_attrs(node_idx, &graph.dag).write_into(el_writer); - el_writer = Attrs::from(*common_attrs).write_into(el_writer); - el_writer.write_empty() + primitive.element_writer( + writer, + *common_attrs, + graph + .inputs(node_idx) + .into_iter() + .map(|v| v.to_string()) + .collect(), + graph + .outputs(node_idx) + .is_empty() + .not() + .then_some(format!("r{}", node_idx.index())), + ) })?; Ok(()) } } -struct Attrs(IndexMap); - -impl Attrs { - fn write_into<'w, 'b>( - self, - el_writer: ElementWriter<'w, &'b mut Vec>, - ) -> ElementWriter<'w, &'b mut Vec> { - let attrs = self.0.iter().map(|(k, v)| (k.as_str(), v.as_str())); - el_writer.with_attributes(attrs) - } -} - -impl From for Attrs { - fn from(value: CommonAttrs) -> Self { - let CommonAttrs { - x, - y, - width, - height, - } = value; - Self(IndexMap::from([ - ("x".to_owned(), x.to_string()), - ("y".to_owned(), y.to_string()), - ("width".to_owned(), width.to_string()), - ("height".to_owned(), height.to_string()), - ])) - } -} - -#[allow( - clippy::unwrap_used, - reason = "in all cases of use of unwrap, it's values we got from the graph so we know it's safe" -)] -fn input_attrs(node_idx: NodeIndex, g: &DiGraph) -> Attrs { - let inputs: Vec = g - .neighbors_directed(node_idx, petgraph::Direction::Incoming) - .collect(); - let node = g.node_weight(node_idx).unwrap(); - - if node.input_count() as usize != inputs.len() { - todo!("proper error handling for wrong numbers of inputs") - } - - let mut inputs = inputs - .into_iter() - .map(|input_idx| { - let edge_idx = g.find_edge(input_idx, node_idx).unwrap(); - let edge = g.edge_weight(edge_idx).unwrap(); - let input_node = g.node_weight(input_idx).unwrap(); - - (input_node, edge_idx, edge) - }) - .collect::>(); - inputs.sort_by(|a, b| a.2.cmp(b.2)); - - let mut uniq = BTreeSet::new(); - let no_duplicates = inputs.iter().all(|(_, _, edge)| uniq.insert(edge)); - - if no_duplicates { - Attrs( - inputs - .into_iter() - .map(|(input, edge_idx, edge)| { - let name = match input { - Node::StdInput(s) => format!("{s:?}"), - Node::Primitive { .. } => format_edge_idx(edge_idx), - }; - (edge.to_string(), name) - }) - .collect(), - ) - } else { - todo!("better error handling for inputs with multiple values") - } -} - -#[allow( - clippy::unwrap_used, - reason = "in all cases of use of unwrap, it's values we got from the graph so we know it's safe" -)] -fn output_attrs(node_idx: NodeIndex, g: &DiGraph) -> Attrs { - let outputs: Vec = g - .neighbors_directed(node_idx, petgraph::Direction::Outgoing) - .collect(); - - match outputs.len().cmp(&1) { - cmp::Ordering::Less => Attrs(IndexMap::new()), - cmp::Ordering::Equal => { - let output = outputs.first().unwrap(); - let edge_idx = g.find_edge(node_idx, *output).unwrap(); - Attrs(IndexMap::from([( - "result".to_string(), - format_edge_idx(edge_idx), - )])) - } - cmp::Ordering::Greater => todo!("better error handling for too many outputs"), - } -} - /// convenience method to avoid fuckups during future changes fn format_edge_idx(idx: EdgeIndex) -> String { format!("edge{}", idx.index()) } +fn format_node_idx(node_idx: NodeIndex) -> String { + format!("r{}", node_idx.index()) +} + mod error { use std::{error::Error, fmt::Display}; diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index 7caef27..c5684de 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -2,7 +2,10 @@ use std::hint::black_box; use svg_filters::{ codegen::SvgDocument, - types::{graph::edge::Edge, nodes::primitives::color_matrix::ColorMatrixType}, + types::{ + graph::edge::Edge, + nodes::{primitives::color_matrix::ColorMatrixType, standard_input::StandardInput}, + }, Node, }; @@ -25,39 +28,33 @@ fn main() { let mut doc = SvgDocument::new(); let chromabb = doc.create_filter("chromabb_gen"); - let chan_r = chromabb.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ - 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., - ])))); - let chan_g = chromabb.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ - 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., - ])))); - let chan_b = chromabb.add_node(Node::color_matrix(ColorMatrixType::Matrix(Box::new([ - 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., - ])))); + let chan_r = chromabb.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + ])), + ); + let offset_r = chromabb.offset(chan_r, 25., 0.); + let blur_r = chromabb.gaussian_blur_xy(offset_r, 5, 0); - let offset_r = chromabb.add_node(Node::offset(25., 0.)); - let offset_b = chromabb.add_node(Node::offset(-25., 0.)); - let blur_r = chromabb.add_node(Node::gaussian_blur_xy(5, 0)); - let blur_b = chromabb.add_node(Node::gaussian_blur_xy(5, 0)); + let chan_b = chromabb.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., + ])), + ); + let offset_b = chromabb.offset(chan_b, -25., 0.); + let blur_b = chromabb.gaussian_blur_xy(offset_b, 5, 0); - let composite_rb = chromabb.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); - let composite_final = chromabb.add_node(Node::composite_arithmetic(0., 1., 1., 0.)); + let composite_rb = chromabb.composite_arithmetic(blur_r, blur_b, 0., 1., 1., 0.); - chromabb.dag.extend_with_edges(&[ - (chromabb.source_graphic(), chan_r), - (chromabb.source_graphic(), chan_b), - (chromabb.source_graphic(), chan_g), - (chan_r, offset_r), - (offset_r, blur_r), - (chan_b, offset_b), - (offset_b, blur_b), - ]); - chromabb.dag.extend_with_edges(&[ - (blur_r, composite_rb, Edge::new(0)), - (blur_b, composite_rb, Edge::new(1)), - (composite_rb, composite_final, Edge::new(0)), - (chan_g, composite_final, Edge::new(1)), - ]); + let chan_g = chromabb.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + ])), + ); + chromabb.composite_arithmetic(composite_rb, chan_g, 0., 1., 1., 0.); - black_box(doc.generate_svg()); + println!("{}", doc.generate_svg()); } diff --git a/crates/svg-filters/src/types.rs b/crates/svg-filters/src/types.rs index fa3941e..1290907 100644 --- a/crates/svg-filters/src/types.rs +++ b/crates/svg-filters/src/types.rs @@ -3,68 +3,4 @@ pub mod nodes; // pub mod old; -pub mod graph { - use std::iter::Iterator; - - use petgraph::{data::Build, prelude::*}; - - use crate::Node; - - use self::edge::Edge; - - use super::nodes::standard_input::StandardInput; - - #[derive(Debug)] - pub struct FilterGraph { - pub dag: DiGraph, - source_graphic_idx: NodeIndex, - } - - impl FilterGraph { - pub fn new() -> Self { - let mut dag = DiGraph::new(); - let source_graphic_idx = dag.add_node(Node::StdInput(StandardInput::SourceGraphic)); - - Self { - dag, - source_graphic_idx, - } - } - - pub fn add_node(&mut self, node: Node) -> NodeIndex { - self.dag.add_node(node) - } - - pub fn source_graphic(&self) -> NodeIndex { - self.source_graphic_idx - } - } - - pub mod edge { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] - pub struct Edge { - input_idx: u8, - } - - impl Edge { - pub fn new(input_idx: u8) -> Self { - Self { input_idx } - } - } - - impl Default for Edge { - fn default() -> Self { - Self { input_idx: 0 } - } - } - - impl ToString for Edge { - fn to_string(&self) -> String { - match self.input_idx { - 0 => "in".to_owned(), - n => format!("in{}", n + 1), - } - } - } - } -} +pub mod graph; diff --git a/crates/svg-filters/src/types/graph.rs b/crates/svg-filters/src/types/graph.rs new file mode 100644 index 0000000..44281d8 --- /dev/null +++ b/crates/svg-filters/src/types/graph.rs @@ -0,0 +1,202 @@ +use std::fmt::{Debug, Display}; + +use petgraph::{data::Build, prelude::NodeIndex, prelude::*, visit::IntoNeighborsDirected}; + +use crate::Node; + +use self::edge::Edge; + +use super::nodes::standard_input::StandardInput; + +#[derive(Debug)] +pub struct FilterGraph { + pub dag: DiGraph, + source_graphic_idx: NodeIndex, + source_alpha_idx: NodeIndex, + background_image_idx: NodeIndex, + background_alpha_idx: NodeIndex, + fill_paint_idx: NodeIndex, + stroke_paint_idx: NodeIndex, +} + +impl Default for FilterGraph { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug, Clone, Copy)] +pub enum NodeInput { + Standard(StandardInput), + Idx(NodeIndex), +} + +impl Display for NodeInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + NodeInput::Standard(s) => Debug::fmt(s, f), + NodeInput::Idx(idx) => write!(f, "r{}", idx.index()), + } + } +} + +impl From for NodeInput { + fn from(value: StandardInput) -> Self { + Self::Standard(value) + } +} + +impl From for NodeInput { + fn from(value: NodeIndex) -> Self { + Self::Idx(value) + } +} + +impl FilterGraph { + pub fn new() -> Self { + let mut dag = DiGraph::new(); + + let source_graphic_idx = dag.add_node(Node::StdInput(StandardInput::SourceGraphic)); + let source_alpha_idx = dag.add_node(Node::StdInput(StandardInput::SourceAlpha)); + let background_image_idx = dag.add_node(Node::StdInput(StandardInput::BackgroundImage)); + let background_alpha_idx = dag.add_node(Node::StdInput(StandardInput::BackgroundAlpha)); + let fill_paint_idx = dag.add_node(Node::StdInput(StandardInput::FillPaint)); + let stroke_paint_idx = dag.add_node(Node::StdInput(StandardInput::StrokePaint)); + + Self { + dag, + source_graphic_idx, + source_alpha_idx, + background_image_idx, + background_alpha_idx, + fill_paint_idx, + stroke_paint_idx, + } + } + + pub fn add_node(&mut self, node: Node) -> NodeIndex { + self.dag.add_node(node) + } + + fn resolve_input(&self, input: NodeInput) -> NodeIndex { + match input { + NodeInput::Standard(StandardInput::SourceGraphic) => self.source_graphic_idx, + NodeInput::Standard(StandardInput::SourceAlpha) => self.source_alpha_idx, + NodeInput::Standard(StandardInput::BackgroundImage) => self.background_image_idx, + NodeInput::Standard(StandardInput::BackgroundAlpha) => self.background_alpha_idx, + NodeInput::Standard(StandardInput::FillPaint) => self.fill_paint_idx, + NodeInput::Standard(StandardInput::StrokePaint) => self.stroke_paint_idx, + NodeInput::Idx(i) => i, + } + } + + #[allow( + clippy::unwrap_used, + reason = "we only operate on values we know exist, so unwrapping is safe" + )] + pub fn inputs(&self, node_idx: NodeIndex) -> Vec { + let mut inputs = self + .dag + .neighbors_directed(node_idx, Direction::Incoming) + .map(|input_idx| (self.dag.find_edge(input_idx, node_idx).unwrap(), input_idx)) + .collect::>(); + + inputs.sort_by(|(a, _), (b, _)| a.cmp(b)); + + inputs + .into_iter() + .map( + |(_, input_idx)| match self.dag.node_weight(input_idx).unwrap() { + Node::StdInput(s) => NodeInput::Standard(*s), + Node::Primitive { .. } => NodeInput::Idx(input_idx), + }, + ) + .collect() + } + + pub fn outputs(&self, node_idx: NodeIndex) -> Vec { + self.dag + .neighbors_directed(node_idx, Direction::Outgoing) + .collect() + } + + pub fn source_graphic(&self) -> NodeIndex { + self.source_graphic_idx + } + pub fn source_alpha(&self) -> NodeIndex { + self.source_alpha_idx + } + pub fn background_image(&self) -> NodeIndex { + self.background_image_idx + } + pub fn background_alpha(&self) -> NodeIndex { + self.background_alpha_idx + } + pub fn fill_paint(&self) -> NodeIndex { + self.fill_paint_idx + } + pub fn stroke_paint(&self) -> NodeIndex { + self.stroke_paint_idx + } +} + +pub mod abstracted_inputs { + use petgraph::{data::Build, prelude::NodeIndex}; + + use crate::{types::nodes::primitives::color_matrix::ColorMatrixType, Node}; + + use super::{FilterGraph, NodeInput}; + + impl FilterGraph { + pub fn color_matrix( + &mut self, + r#in: impl Into, + cm_type: ColorMatrixType, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::color_matrix(cm_type)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + + pub fn offset(&mut self, r#in: impl Into, dx: f32, dy: f32) -> NodeIndex { + let node_idx = self.dag.add_node(Node::offset(dx, dy)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + + pub fn gaussian_blur_xy( + &mut self, + r#in: impl Into, + x: u16, + y: u16, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::gaussian_blur_xy(x, y)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + + pub fn composite_arithmetic( + &mut self, + r#in: impl Into, + in2: impl Into, + k1: f32, + k2: f32, + k3: f32, + k4: f32, + ) -> NodeIndex { + let node_idx = self + .dag + .add_node(Node::composite_arithmetic(k1, k2, k3, k4)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + self.dag + .add_edge(self.resolve_input(in2.into()), node_idx, ()); + node_idx + } + } +} + +pub mod edge; diff --git a/crates/svg-filters/src/types/graph/edge.rs b/crates/svg-filters/src/types/graph/edge.rs new file mode 100644 index 0000000..0783a34 --- /dev/null +++ b/crates/svg-filters/src/types/graph/edge.rs @@ -0,0 +1,19 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] +pub struct Edge { + input_idx: u8, +} + +impl Edge { + pub fn new(input_idx: u8) -> Self { + Self { input_idx } + } +} + +impl ToString for Edge { + fn to_string(&self) -> String { + match self.input_idx { + 0 => "in".to_owned(), + n => format!("in{}", n + 1), + } + } +} diff --git a/crates/svg-filters/src/types/length.rs b/crates/svg-filters/src/types/length.rs index 10ddfcc..ef9d2ef 100644 --- a/crates/svg-filters/src/types/length.rs +++ b/crates/svg-filters/src/types/length.rs @@ -3,6 +3,12 @@ use std::fmt::Display; #[derive(Default, Debug, Clone, Copy)] pub struct Length(f32, Unit); +impl Length { + pub fn is_zero(&self) -> bool { + self.0 == 0. + } +} + impl Display for Length { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}{}", self.0, self.1) diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index 80cb5d0..b24251f 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -1,6 +1,6 @@ -use core::panic; +use std::borrow::Cow; -use quick_xml::events::attributes::Attribute; +use quick_xml::{events::attributes::Attribute, name::QName}; use self::{ primitives::{ @@ -42,6 +42,41 @@ pub(crate) struct CommonAttrs { pub height: Length, } +impl From for Vec> { + fn from(val: CommonAttrs) -> Self { + let mut r = Vec::new(); + if !val.x.is_zero() { + r.push(Attribute { + key: QName(b"x"), + value: Cow::from(val.x.to_string().into_bytes()), + }); + } + + if !val.y.is_zero() { + r.push(Attribute { + key: QName(b"y"), + value: Cow::from(val.y.to_string().into_bytes()), + }); + } + + if !val.width.is_zero() { + r.push(Attribute { + key: QName(b"width"), + value: Cow::from(val.width.to_string().into_bytes()), + }); + } + + if !val.height.is_zero() { + r.push(Attribute { + key: QName(b"height"), + value: Cow::from(val.height.to_string().into_bytes()), + }); + } + + r + } +} + impl Node { pub fn simple(el: FePrimitive) -> Node { Node::Primitive { diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index 513f1a8..f43c820 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -1,10 +1,8 @@ +use std::convert::Into; + use quick_xml::{events::attributes::Attribute, ElementWriter, Writer}; -use crate::types::length::{Coordinate, Length}; - -use self::blend::BlendMode; - -use super::Node; +use super::CommonAttrs; pub mod blend; pub mod color_matrix; @@ -29,10 +27,34 @@ pub trait WriteElement { fn element_writer<'writer, 'result>( &self, writer: &'writer mut Writer<&'result mut Vec>, - ) -> ElementWriter<'writer, &'result mut Vec> { - writer + common: CommonAttrs, + inputs: Vec, + output: Option, + ) -> quick_xml::Result<&'writer mut quick_xml::Writer<&'result mut Vec>> { + let attrs: Vec<_> = inputs + .into_iter() + .enumerate() + .map(|(i, edge)| { + ( + match i { + 0 => "in".to_owned(), + n => format!("in{}", n + 1), + } + .into_bytes(), + edge.into_bytes(), + ) + }) + .collect(); + let mut el_writer = writer .create_element(self.tag_name()) + .with_attributes(Into::>>::into(common)) .with_attributes(self.attrs()) + .with_attributes(attrs.iter().map(|(k, v)| (&k[..], &v[..]))); + if let Some(output) = output { + el_writer = el_writer.with_attribute(("result", output.as_str())); + } + + el_writer.write_empty() } } diff --git a/crates/svg-filters/src/types/nodes/primitives/blend.rs b/crates/svg-filters/src/types/nodes/primitives/blend.rs index 1aba6d0..8df8ad6 100644 --- a/crates/svg-filters/src/types/nodes/primitives/blend.rs +++ b/crates/svg-filters/src/types/nodes/primitives/blend.rs @@ -1,3 +1,9 @@ +use std::{borrow::Cow, fmt::Display}; + +use quick_xml::{events::attributes::Attribute, name::QName}; + +use super::WriteElement; + /// [feBlend](https://www.w3.org/TR/SVG11/filters.html#feBlendElement) #[derive(Debug)] pub struct Blend { @@ -18,6 +24,23 @@ impl Default for Blend { } } +impl WriteElement for Blend { + fn attrs(&self) -> Vec { + if let BlendMode::Normal = self.mode { + Vec::new() + } else { + vec![Attribute { + key: QName(b"mode"), + value: Cow::from(self.mode.to_string().into_bytes()), + }] + } + } + + fn tag_name(&self) -> &'static str { + "feBlend" + } +} + /// as according to https://drafts.fxtf.org/compositing-1/#blending #[derive(Debug)] pub enum BlendMode { @@ -39,3 +62,26 @@ pub enum BlendMode { Color, Luminosity, } + +impl Display for BlendMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + BlendMode::Normal => "normal", + BlendMode::Multiply => "multiply", + BlendMode::Screen => "screen", + BlendMode::Overlay => "overlay", + BlendMode::Darken => "darken", + BlendMode::Lighten => "lighten", + BlendMode::ColorDodge => "color-dodge", + BlendMode::ColorBurn => "color-burn", + BlendMode::HardLight => "hard-light", + BlendMode::SoftLight => "soft-light", + BlendMode::Difference => "difference", + BlendMode::Exclusion => "exclusion", + BlendMode::Hue => "hue", + BlendMode::Saturation => "saturation", + BlendMode::Color => "color", + BlendMode::Luminosity => "luminosity", + }) + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs index ef32b60..4469500 100644 --- a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs +++ b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use quick_xml::{events::attributes::Attribute, name::QName, se::to_string}; +use quick_xml::{events::attributes::Attribute, name::QName}; use super::WriteElement; @@ -23,7 +23,7 @@ impl WriteElement for ColorMatrix { key: QName(b"values"), value: Cow::from( v.iter() - .map(|v| v.to_string()) + .map(std::string::ToString::to_string) .reduce(|mut acc, e| { acc.push(' '); acc.push_str(&e); @@ -33,9 +33,11 @@ impl WriteElement for ColorMatrix { .into_bytes(), ), }], - ColorMatrixType::Saturate(v) => todo!(), - ColorMatrixType::HueRotate(v) => todo!(), - ColorMatrixType::LuminanceToAlpha => todo!(), + ColorMatrixType::Saturate(v) | ColorMatrixType::HueRotate(v) => vec![Attribute { + key: QName(b"values"), + value: Cow::from(v.to_string().into_bytes()), + }], + ColorMatrixType::LuminanceToAlpha => Vec::new(), } } diff --git a/crates/svg-filters/src/types/nodes/primitives/flood.rs b/crates/svg-filters/src/types/nodes/primitives/flood.rs index 94f6eee..3a90590 100644 --- a/crates/svg-filters/src/types/nodes/primitives/flood.rs +++ b/crates/svg-filters/src/types/nodes/primitives/flood.rs @@ -1,4 +1,9 @@ +use std::borrow::Cow; + use csscolorparser::Color; +use quick_xml::{events::attributes::Attribute, name::QName}; + +use super::WriteElement; /// [feFlood](https://www.w3.org/TR/SVG11/filters.html#feFloodElement) #[derive(Debug)] @@ -6,3 +11,22 @@ pub struct Flood { flood_color: Color, flood_opacity: f32, } + +impl WriteElement for Flood { + fn attrs(&self) -> Vec { + vec![ + Attribute { + key: QName(b"flood-color"), + value: Cow::from(self.flood_color.to_hex_string().into_bytes()), + }, + Attribute { + key: QName(b"flood-opacity"), + value: Cow::from(self.flood_opacity.to_string().into_bytes()), + }, + ] + } + + fn tag_name(&self) -> &'static str { + "feFlood" + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives/morphology.rs b/crates/svg-filters/src/types/nodes/primitives/morphology.rs index 6b33825..dc11775 100644 --- a/crates/svg-filters/src/types/nodes/primitives/morphology.rs +++ b/crates/svg-filters/src/types/nodes/primitives/morphology.rs @@ -1,3 +1,9 @@ +use std::{borrow::Cow, fmt::Display}; + +use quick_xml::{events::attributes::Attribute, name::QName}; + +use super::WriteElement; + /// [feMorphology](https://www.w3.org/TR/SVG11/filters.html#feMorphologyElement) #[derive(Debug)] pub struct Morphology { @@ -5,8 +11,36 @@ pub struct Morphology { radius: (f32, f32), } +impl WriteElement for Morphology { + fn attrs(&self) -> Vec { + vec![ + Attribute { + key: QName(b"operator"), + value: Cow::from(self.operator.to_string().into_bytes()), + }, + Attribute { + key: QName(b"radius"), + value: Cow::from(format!("{} {}", self.radius.0, self.radius.1).into_bytes()), + }, + ] + } + + fn tag_name(&self) -> &'static str { + "feMorphology" + } +} + #[derive(Debug)] enum Operator { Erode, Dilate, } + +impl Display for Operator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Operator::Erode => "erode", + Operator::Dilate => "dilate", + }) + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives/tile.rs b/crates/svg-filters/src/types/nodes/primitives/tile.rs index 7a957c5..d83b65f 100644 --- a/crates/svg-filters/src/types/nodes/primitives/tile.rs +++ b/crates/svg-filters/src/types/nodes/primitives/tile.rs @@ -1,3 +1,15 @@ +use super::WriteElement; + /// [feTile](https://www.w3.org/TR/SVG11/filters.html#feTileElement) #[derive(Debug)] pub struct Tile; + +impl WriteElement for Tile { + fn attrs(&self) -> Vec { + Vec::new() + } + + fn tag_name(&self) -> &'static str { + "feTile" + } +} diff --git a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs index ec815cb..8bdcab0 100644 --- a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs +++ b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs @@ -1,21 +1,75 @@ +use std::borrow::Cow; + +use quick_xml::{ + events::attributes::{Attr, Attribute}, + name::QName, +}; + +use super::WriteElement; + /// [feTurbulence](https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement) #[derive(Debug)] pub struct Turbulence { base_frequency: (f32, f32), - num_octaves: (u16), + num_octaves: u16, seed: u32, - stich_tiles: StitchTiles, + stitch_tiles: StitchTiles, // attr name: type noise_type: NoiseType, } -#[derive(Debug)] -enum StitchTiles { +impl WriteElement for Turbulence { + fn attrs(&self) -> Vec { + let mut r = Vec::from([Attribute { + key: QName(b"baseFrequency"), + value: Cow::from( + format!("{} {}", self.base_frequency.0, self.base_frequency.1).into_bytes(), + ), + }]); + + if self.num_octaves != 1 { + r.push(Attribute { + key: QName(b"numOctaves"), + value: Cow::from(format!("{}", self.num_octaves).into_bytes()), + }); + } + + if self.seed != 0 { + r.push(Attribute { + key: QName(b"seed"), + value: Cow::from(self.seed.to_string().into_bytes()), + }); + } + + if self.stitch_tiles != StitchTiles::NoStitch { + r.push(Attribute { + key: QName(b"stitchTiles"), + value: Cow::from(b"stitch".to_vec()), + }); + } + + if self.noise_type != NoiseType::Turbulence { + r.push(Attribute { + key: QName(b"type"), + value: Cow::from(b"fractalNoise".to_vec()), + }); + } + + r + } + + fn tag_name(&self) -> &'static str { + "feTurbulence" + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum StitchTiles { Stitch, NoStitch, } -#[derive(Debug)] -enum NoiseType { +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum NoiseType { Turbulence, FractalNoise, } diff --git a/crates/svg-filters/src/types/nodes/standard_input.rs b/crates/svg-filters/src/types/nodes/standard_input.rs index add10d0..3004e19 100644 --- a/crates/svg-filters/src/types/nodes/standard_input.rs +++ b/crates/svg-filters/src/types/nodes/standard_input.rs @@ -1,6 +1,6 @@ /// [svg filter effect standard input](https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute) /// technically not a node, but for implementation simplicity... yeah -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum StandardInput { SourceGraphic, SourceAlpha, -- 2.46.0 From d87033d32024845534e414f060a0d007ee85d3b5 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 17 Mar 2024 00:49:53 +0100 Subject: [PATCH 08/67] svg-filters(codegen): add pretty and ugly printing --- crates/svg-filters/src/codegen.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/svg-filters/src/codegen.rs b/crates/svg-filters/src/codegen.rs index a8e7b23..cd93d25 100644 --- a/crates/svg-filters/src/codegen.rs +++ b/crates/svg-filters/src/codegen.rs @@ -43,10 +43,25 @@ impl SvgDocument { self.filters.get_mut(&id.to_string()).unwrap() } + pub fn generate_svg_pretty(&self) -> String { + let mut result = Vec::new(); + let doc_writer = quick_xml::Writer::new_with_indent(&mut result, b' ', 2); + + self.generate(doc_writer); + + String::from_utf8_lossy(&result).to_string() + } + pub fn generate_svg(&self) -> String { let mut result = Vec::new(); - let mut doc_writer = quick_xml::Writer::new_with_indent(&mut result, b' ', 2); + let doc_writer = quick_xml::Writer::new(&mut result); + self.generate(doc_writer); + + String::from_utf8_lossy(&result).to_string() + } + + fn generate(&self, mut doc_writer: quick_xml::Writer<&mut Vec>) { doc_writer .create_element("svg") .write_inner_content(|writer| { @@ -55,8 +70,6 @@ impl SvgDocument { .try_fold(writer, Self::gen_filter) .map(|_| {}) }); - - String::from_utf8_lossy(&result).to_string() } fn gen_filter<'w, 'r>( -- 2.46.0 From 77bcb54b5eefa2e345f0e81d1ccb8b608a2c3b9c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 17 Mar 2024 00:50:16 +0100 Subject: [PATCH 09/67] svg-filters(tests): start --- crates/svg-filters/src/lib.rs | 2 +- crates/svg-filters/src/tests.rs | 19 +++++++++ crates/svg-filters/src/tests/color_matrix.rs | 25 +++++++++++ crates/svg-filters/src/tests/complex.rs | 42 +++++++++++++++++++ crates/svg-filters/src/tests/gaussian_blur.rs | 13 ++++++ crates/svg-filters/src/tests/offset.rs | 14 +++++++ 6 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 crates/svg-filters/src/tests.rs create mode 100644 crates/svg-filters/src/tests/color_matrix.rs create mode 100644 crates/svg-filters/src/tests/complex.rs create mode 100644 crates/svg-filters/src/tests/gaussian_blur.rs create mode 100644 crates/svg-filters/src/tests/offset.rs diff --git a/crates/svg-filters/src/lib.rs b/crates/svg-filters/src/lib.rs index cc443d8..c3ddedc 100644 --- a/crates/svg-filters/src/lib.rs +++ b/crates/svg-filters/src/lib.rs @@ -5,4 +5,4 @@ pub mod types; pub use types::nodes::Node; #[cfg(test)] -mod tests {} +mod tests; diff --git a/crates/svg-filters/src/tests.rs b/crates/svg-filters/src/tests.rs new file mode 100644 index 0000000..c09a957 --- /dev/null +++ b/crates/svg-filters/src/tests.rs @@ -0,0 +1,19 @@ +mod color_matrix; + +mod complex; +mod gaussian_blur; +mod offset; + +mod blend {} +mod component_transfer {} +mod composite {} +mod convolve_matrix {} +mod diffuse_lighting {} +mod displacement_map {} +mod flood {} +mod image {} +mod merge {} +mod morphology {} +mod specular_lighting {} +mod tile {} +mod turbulence {} diff --git a/crates/svg-filters/src/tests/color_matrix.rs b/crates/svg-filters/src/tests/color_matrix.rs new file mode 100644 index 0000000..e32d507 --- /dev/null +++ b/crates/svg-filters/src/tests/color_matrix.rs @@ -0,0 +1,25 @@ +use crate::{ + codegen::SvgDocument, + types::nodes::{primitives::color_matrix::ColorMatrixType, standard_input::StandardInput}, +}; + +#[test] +fn test_greyscale_channel_extraction() { + let mut doc = SvgDocument::new(); + let greyscale = doc.create_filter("greyscale"); + + greyscale.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 1., 0., 0., 0., 0., // + 1., 0., 0., 0., 0., // + 1., 0., 0., 0., 0., // + 0., 0., 0., 1., 0., + ])), + ); + + assert_eq!( + doc.generate_svg(), + r#""# + ); +} diff --git a/crates/svg-filters/src/tests/complex.rs b/crates/svg-filters/src/tests/complex.rs new file mode 100644 index 0000000..9b28304 --- /dev/null +++ b/crates/svg-filters/src/tests/complex.rs @@ -0,0 +1,42 @@ +use crate::{ + codegen::SvgDocument, + types::nodes::{primitives::color_matrix::ColorMatrixType, standard_input::StandardInput}, +}; + +#[test] +fn test_chrom_abb() { + let mut doc = SvgDocument::new(); + let chromabb = doc.create_filter("chromabb_gen"); + + let chan_r = chromabb.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + ])), + ); + let offset_r = chromabb.offset(chan_r, 25., 0.); + let blur_r = chromabb.gaussian_blur_xy(offset_r, 5, 0); + + let chan_b = chromabb.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., + ])), + ); + let offset_b = chromabb.offset(chan_b, -25., 0.); + let blur_b = chromabb.gaussian_blur_xy(offset_b, 5, 0); + + let composite_rb = chromabb.composite_arithmetic(blur_r, blur_b, 0., 1., 1., 0.); + + let chan_g = chromabb.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + ])), + ); + chromabb.composite_arithmetic(composite_rb, chan_g, 0., 1., 1., 0.); + assert_eq!( + doc.generate_svg(), + r#""# + ); +} diff --git a/crates/svg-filters/src/tests/gaussian_blur.rs b/crates/svg-filters/src/tests/gaussian_blur.rs new file mode 100644 index 0000000..5739794 --- /dev/null +++ b/crates/svg-filters/src/tests/gaussian_blur.rs @@ -0,0 +1,13 @@ +use crate::{codegen::SvgDocument, types::nodes::standard_input::StandardInput}; + +#[test] +fn test_simple_blur() { + let mut doc = SvgDocument::new(); + let blur = doc.create_filter("blur"); + + blur.gaussian_blur_xy(StandardInput::SourceGraphic, 30, 30); + assert_eq!( + doc.generate_svg(), + r#""# + ) +} diff --git a/crates/svg-filters/src/tests/offset.rs b/crates/svg-filters/src/tests/offset.rs new file mode 100644 index 0000000..4d7319e --- /dev/null +++ b/crates/svg-filters/src/tests/offset.rs @@ -0,0 +1,14 @@ +use crate::{codegen::SvgDocument, types::nodes::standard_input::StandardInput}; + +#[test] +fn test_offset_simple() { + let mut doc = SvgDocument::new(); + let mut offset = doc.create_filter("offset"); + + offset.offset(StandardInput::SourceGraphic, 25., -25.); + + assert_eq!( + doc.generate_svg(), + r#""# + ) +} -- 2.46.0 From 384fef5a816854f52572b18bf010dc3b8894b2ef Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 17 Mar 2024 00:50:51 +0100 Subject: [PATCH 10/67] svg-filters(primitives): start with feComponentTransfer --- crates/svg-filters/src/types/nodes.rs | 19 ++++++++++++++++--- .../nodes/primitives/component_transfer.rs | 10 +++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index b24251f..a1ca752 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -6,6 +6,7 @@ use self::{ primitives::{ blend::{Blend, BlendMode}, color_matrix::{ColorMatrix, ColorMatrixType}, + component_transfer::{ComponentTransfer, TransferFn}, composite::{Composite, CompositeOperator}, gaussian_blur::GaussianBlur, offset::Offset, @@ -145,9 +146,7 @@ impl Node { } pub fn composite_arithmetic(k1: f32, k2: f32, k3: f32, k4: f32) -> Self { - Self::simple(FePrimitive::Composite(Composite::arithmetic( - k1, k2, k3, k4, - ))) + Self::composite(CompositeOperator::Arithmetic { k1, k2, k3, k4 }) } pub fn gaussian_blur(v: u16) -> Self { @@ -161,4 +160,18 @@ impl Node { pub fn offset(dx: f32, dy: f32) -> Self { Self::simple(FePrimitive::Offset(Offset::new(dx, dy))) } + + pub fn component_transfer_rgba( + r: TransferFn, + g: TransferFn, + b: TransferFn, + a: TransferFn, + ) -> Self { + Self::simple(FePrimitive::ComponentTransfer(ComponentTransfer { + func_r: r, + func_g: g, + func_b: b, + func_a: a, + })) + } } diff --git a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs index 3ccc039..abfd5a0 100644 --- a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs +++ b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs @@ -1,15 +1,15 @@ /// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement) #[derive(Debug)] pub struct ComponentTransfer { - func_r: TransferFunction, - func_g: TransferFunction, - func_b: TransferFunction, - func_a: TransferFunction, + pub func_r: TransferFn, + pub func_g: TransferFn, + pub func_b: TransferFn, + pub func_a: TransferFn, } /// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements) #[derive(Debug)] -enum TransferFunction { +pub enum TransferFn { Identity, Table { table_values: Vec, -- 2.46.0 From f59062cf88685f5e7847b8b09361d55ed41c25b3 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 17 Mar 2024 00:51:20 +0100 Subject: [PATCH 11/67] svg-filters: cleanup --- crates/svg-filters/src/main.rs | 45 +------------------ .../svg-filters/src/types/nodes/primitives.rs | 1 - 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index c5684de..aff0c94 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -10,51 +10,8 @@ use svg_filters::{ }; fn main() { - // - // - // - // - - // - - // - // - // - - // - // - // - let mut doc = SvgDocument::new(); - let chromabb = doc.create_filter("chromabb_gen"); - - let chan_r = chromabb.color_matrix( - StandardInput::SourceGraphic, - ColorMatrixType::Matrix(Box::new([ - 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., - ])), - ); - let offset_r = chromabb.offset(chan_r, 25., 0.); - let blur_r = chromabb.gaussian_blur_xy(offset_r, 5, 0); - - let chan_b = chromabb.color_matrix( - StandardInput::SourceGraphic, - ColorMatrixType::Matrix(Box::new([ - 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., - ])), - ); - let offset_b = chromabb.offset(chan_b, -25., 0.); - let blur_b = chromabb.gaussian_blur_xy(offset_b, 5, 0); - - let composite_rb = chromabb.composite_arithmetic(blur_r, blur_b, 0., 1., 1., 0.); - - let chan_g = chromabb.color_matrix( - StandardInput::SourceGraphic, - ColorMatrixType::Matrix(Box::new([ - 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., - ])), - ); - chromabb.composite_arithmetic(composite_rb, chan_g, 0., 1., 1., 0.); + eprintln!("{}", doc.generate_svg_pretty()); println!("{}", doc.generate_svg()); } diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index f43c820..c7526fd 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -61,7 +61,6 @@ pub trait WriteElement { /// svg filter effects primitives #[derive(Debug)] pub enum FePrimitive { - // 2 inputs Blend(blend::Blend), ColorMatrix(color_matrix::ColorMatrix), ComponentTransfer(component_transfer::ComponentTransfer), -- 2.46.0 From e17fffb66b87aad7f3ad8cc98ac3ab65a21f1be9 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 19 Mar 2024 15:15:36 +0100 Subject: [PATCH 12/67] svg-filters: blend node --- crates/svg-filters/src/tests.rs | 2 +- crates/svg-filters/src/tests/blend.rs | 20 +++++++++++++++ crates/svg-filters/src/tests/gaussian_blur.rs | 2 +- crates/svg-filters/src/tests/offset.rs | 4 +-- crates/svg-filters/src/types/graph.rs | 25 +++++++++++++++---- .../svg-filters/src/types/nodes/primitives.rs | 4 +-- 6 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 crates/svg-filters/src/tests/blend.rs diff --git a/crates/svg-filters/src/tests.rs b/crates/svg-filters/src/tests.rs index c09a957..72b970f 100644 --- a/crates/svg-filters/src/tests.rs +++ b/crates/svg-filters/src/tests.rs @@ -1,10 +1,10 @@ mod color_matrix; +mod blend; mod complex; mod gaussian_blur; mod offset; -mod blend {} mod component_transfer {} mod composite {} mod convolve_matrix {} diff --git a/crates/svg-filters/src/tests/blend.rs b/crates/svg-filters/src/tests/blend.rs new file mode 100644 index 0000000..71aea21 --- /dev/null +++ b/crates/svg-filters/src/tests/blend.rs @@ -0,0 +1,20 @@ +use crate::{ + codegen::SvgDocument, + types::nodes::{primitives::blend::BlendMode, standard_input::StandardInput}, +}; + +#[test] +fn test_offset_blend() { + let mut doc = SvgDocument::new(); + + let blend = doc.create_filter("blend"); + + let offset0 = blend.offset(StandardInput::SourceGraphic, 100., 0.); + let offset1 = blend.offset(StandardInput::SourceGraphic, -100., 0.); + blend.blend(offset0, offset1, BlendMode::Multiply); + + assert_eq!( + doc.generate_svg(), + r#""# + ); +} diff --git a/crates/svg-filters/src/tests/gaussian_blur.rs b/crates/svg-filters/src/tests/gaussian_blur.rs index 5739794..f148684 100644 --- a/crates/svg-filters/src/tests/gaussian_blur.rs +++ b/crates/svg-filters/src/tests/gaussian_blur.rs @@ -9,5 +9,5 @@ fn test_simple_blur() { assert_eq!( doc.generate_svg(), r#""# - ) + ); } diff --git a/crates/svg-filters/src/tests/offset.rs b/crates/svg-filters/src/tests/offset.rs index 4d7319e..380170e 100644 --- a/crates/svg-filters/src/tests/offset.rs +++ b/crates/svg-filters/src/tests/offset.rs @@ -3,12 +3,12 @@ use crate::{codegen::SvgDocument, types::nodes::standard_input::StandardInput}; #[test] fn test_offset_simple() { let mut doc = SvgDocument::new(); - let mut offset = doc.create_filter("offset"); + let offset = doc.create_filter("offset"); offset.offset(StandardInput::SourceGraphic, 25., -25.); assert_eq!( doc.generate_svg(), r#""# - ) + ); } diff --git a/crates/svg-filters/src/types/graph.rs b/crates/svg-filters/src/types/graph.rs index 44281d8..05d63c3 100644 --- a/crates/svg-filters/src/types/graph.rs +++ b/crates/svg-filters/src/types/graph.rs @@ -1,11 +1,9 @@ use std::fmt::{Debug, Display}; -use petgraph::{data::Build, prelude::NodeIndex, prelude::*, visit::IntoNeighborsDirected}; +use petgraph::{prelude::NodeIndex, prelude::*}; use crate::Node; -use self::edge::Edge; - use super::nodes::standard_input::StandardInput; #[derive(Debug)] @@ -141,9 +139,12 @@ impl FilterGraph { } pub mod abstracted_inputs { - use petgraph::{data::Build, prelude::NodeIndex}; + use petgraph::prelude::NodeIndex; - use crate::{types::nodes::primitives::color_matrix::ColorMatrixType, Node}; + use crate::{ + types::nodes::primitives::{blend::BlendMode, color_matrix::ColorMatrixType}, + Node, + }; use super::{FilterGraph, NodeInput}; @@ -178,6 +179,20 @@ pub mod abstracted_inputs { node_idx } + pub fn blend( + &mut self, + r#in: impl Into, + in2: impl Into, + mode: BlendMode, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::blend(mode)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + self.dag + .add_edge(self.resolve_input(in2.into()), node_idx, ()); + node_idx + } + pub fn composite_arithmetic( &mut self, r#in: impl Into, diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index c7526fd..7ba1c2d 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -82,7 +82,7 @@ pub enum FePrimitive { impl WriteElement for FePrimitive { fn attrs(&self) -> std::vec::Vec> { match self { - FePrimitive::Blend(_) => todo!(), + FePrimitive::Blend(el) => el.attrs(), FePrimitive::ColorMatrix(el) => el.attrs(), FePrimitive::ComponentTransfer(_) => todo!(), FePrimitive::Composite(el) => el.attrs(), @@ -103,7 +103,7 @@ impl WriteElement for FePrimitive { fn tag_name(&self) -> &'static str { match self { - FePrimitive::Blend(_) => todo!(), + FePrimitive::Blend(el) => el.tag_name(), FePrimitive::ColorMatrix(el) => el.tag_name(), FePrimitive::ComponentTransfer(_) => todo!(), FePrimitive::Composite(el) => el.tag_name(), -- 2.46.0 From dc7d76dc264dd0446c4c0eb85da240faa5f79916 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 19 Mar 2024 15:47:09 +0100 Subject: [PATCH 13/67] svg-filters: create `gen_attr` and `gen_attrs` convenience macros --- crates/svg-filters/src/main.rs | 11 ++++- crates/svg-filters/src/types/nodes.rs | 2 +- .../svg-filters/src/types/nodes/primitives.rs | 17 ++++++++ .../src/types/nodes/primitives/blend.rs | 5 +-- .../types/nodes/primitives/color_matrix.rs | 36 +++++++---------- .../nodes/primitives/component_transfer.rs | 12 ++++++ .../src/types/nodes/primitives/composite.rs | 40 +++++++++++-------- .../src/types/nodes/primitives/flood.rs | 12 ++---- .../types/nodes/primitives/gaussian_blur.rs | 7 +--- .../src/types/nodes/primitives/morphology.rs | 12 ++---- .../src/types/nodes/primitives/offset.rs | 11 +---- .../src/types/nodes/primitives/turbulence.rs | 37 ++++------------- 12 files changed, 94 insertions(+), 108 deletions(-) diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index aff0c94..1bf86c8 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -4,7 +4,10 @@ use svg_filters::{ codegen::SvgDocument, types::{ graph::edge::Edge, - nodes::{primitives::color_matrix::ColorMatrixType, standard_input::StandardInput}, + nodes::{ + primitives::{blend::BlendMode, color_matrix::ColorMatrixType}, + standard_input::StandardInput, + }, }, Node, }; @@ -12,6 +15,12 @@ use svg_filters::{ fn main() { let mut doc = SvgDocument::new(); + let blend = doc.create_filter("blend"); + + let offset0 = blend.offset(StandardInput::SourceGraphic, 100., 0.); + let offset1 = blend.offset(StandardInput::SourceGraphic, -100., 0.); + blend.blend(offset0, offset1, BlendMode::Multiply); + eprintln!("{}", doc.generate_svg_pretty()); println!("{}", doc.generate_svg()); } diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index a1ca752..656574d 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -36,7 +36,7 @@ impl Default for Node { } #[derive(Default, Debug, Clone, Copy)] -pub(crate) struct CommonAttrs { +pub struct CommonAttrs { pub x: Coordinate, pub y: Coordinate, pub width: Length, diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index 7ba1c2d..a596639 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -4,6 +4,23 @@ use quick_xml::{events::attributes::Attribute, ElementWriter, Writer}; use super::CommonAttrs; +macro_rules! gen_attr { + ($name:literal = $out:expr) => { + quick_xml::events::attributes::Attribute { + key: quick_xml::name::QName($name), + value: std::borrow::Cow::from(($out).to_string().into_bytes()), + } + }; +} + +macro_rules! gen_attrs { + ($($name:literal = $out:expr),+) => { + vec![ + $(gen_attr!($name = $out)),+ + ] + }; +} + pub mod blend; pub mod color_matrix; pub mod component_transfer; diff --git a/crates/svg-filters/src/types/nodes/primitives/blend.rs b/crates/svg-filters/src/types/nodes/primitives/blend.rs index 8df8ad6..1887042 100644 --- a/crates/svg-filters/src/types/nodes/primitives/blend.rs +++ b/crates/svg-filters/src/types/nodes/primitives/blend.rs @@ -29,10 +29,7 @@ impl WriteElement for Blend { if let BlendMode::Normal = self.mode { Vec::new() } else { - vec![Attribute { - key: QName(b"mode"), - value: Cow::from(self.mode.to_string().into_bytes()), - }] + gen_attrs![b"mode" = self.mode] } } diff --git a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs index 4469500..31e4130 100644 --- a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs +++ b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs @@ -1,7 +1,3 @@ -use std::borrow::Cow; - -use quick_xml::{events::attributes::Attribute, name::QName}; - use super::WriteElement; /// [feColorMatrix](https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement) @@ -19,24 +15,20 @@ impl ColorMatrix { impl WriteElement for ColorMatrix { fn attrs(&self) -> Vec { match &self.cm_type { - ColorMatrixType::Matrix(v) => vec![Attribute { - key: QName(b"values"), - value: Cow::from( - v.iter() - .map(std::string::ToString::to_string) - .reduce(|mut acc, e| { - acc.push(' '); - acc.push_str(&e); - acc - }) - .expect("Should be able to concatenate the thingies") - .into_bytes(), - ), - }], - ColorMatrixType::Saturate(v) | ColorMatrixType::HueRotate(v) => vec![Attribute { - key: QName(b"values"), - value: Cow::from(v.to_string().into_bytes()), - }], + ColorMatrixType::Matrix(v) => gen_attrs![ + b"values" = v + .iter() + .map(std::string::ToString::to_string) + .reduce(|mut acc, e| { + acc.push(' '); + acc.push_str(&e); + acc + }) + .expect("fixed length arr should always work") + ], + ColorMatrixType::Saturate(v) | ColorMatrixType::HueRotate(v) => { + gen_attrs![b"values" = v] + } ColorMatrixType::LuminanceToAlpha => Vec::new(), } } diff --git a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs index abfd5a0..d495ecc 100644 --- a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs +++ b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs @@ -1,3 +1,5 @@ +use super::WriteElement; + /// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement) #[derive(Debug)] pub struct ComponentTransfer { @@ -7,6 +9,16 @@ pub struct ComponentTransfer { pub func_a: TransferFn, } +impl WriteElement for ComponentTransfer { + fn attrs(&self) -> Vec { + Vec::new() + } + + fn tag_name(&self) -> &'static str { + "feComponentTransfer" + } +} + /// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements) #[derive(Debug)] pub enum TransferFn { diff --git a/crates/svg-filters/src/types/nodes/primitives/composite.rs b/crates/svg-filters/src/types/nodes/primitives/composite.rs index e27664d..b877fe8 100644 --- a/crates/svg-filters/src/types/nodes/primitives/composite.rs +++ b/crates/svg-filters/src/types/nodes/primitives/composite.rs @@ -51,23 +51,29 @@ impl WriteElement for Composite { }]; if let Some([k1, k2, k3, k4]) = vals { - r.append(&mut vec![ - Attribute { - key: QName(b"k1"), - value: Cow::from(k1.to_string().into_bytes()), - }, - Attribute { - key: QName(b"k2"), - value: Cow::from(k2.to_string().into_bytes()), - }, - Attribute { - key: QName(b"k3"), - value: Cow::from(k3.to_string().into_bytes()), - }, - Attribute { - key: QName(b"k4"), - value: Cow::from(k4.to_string().into_bytes()), - }, + // r.append(&mut vec![ + // Attribute { + // key: QName(b"k1"), + // value: Cow::from(k1.to_string().into_bytes()), + // }, + // Attribute { + // key: QName(b"k2"), + // value: Cow::from(k2.to_string().into_bytes()), + // }, + // Attribute { + // key: QName(b"k3"), + // value: Cow::from(k3.to_string().into_bytes()), + // }, + // Attribute { + // key: QName(b"k4"), + // value: Cow::from(k4.to_string().into_bytes()), + // }, + // ]); + r.append(&mut gen_attrs![ + b"k1" = k1, + b"k2" = k2, + b"k3" = k3, + b"k4" = k4 ]); } diff --git a/crates/svg-filters/src/types/nodes/primitives/flood.rs b/crates/svg-filters/src/types/nodes/primitives/flood.rs index 3a90590..3342ef7 100644 --- a/crates/svg-filters/src/types/nodes/primitives/flood.rs +++ b/crates/svg-filters/src/types/nodes/primitives/flood.rs @@ -14,15 +14,9 @@ pub struct Flood { impl WriteElement for Flood { fn attrs(&self) -> Vec { - vec![ - Attribute { - key: QName(b"flood-color"), - value: Cow::from(self.flood_color.to_hex_string().into_bytes()), - }, - Attribute { - key: QName(b"flood-opacity"), - value: Cow::from(self.flood_opacity.to_string().into_bytes()), - }, + gen_attrs![ + b"flood-color" = self.flood_color.to_hex_string(), + b"flood-opacity" = self.flood_opacity ] } diff --git a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs index a0c42bd..e255df4 100644 --- a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs +++ b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs @@ -26,12 +26,7 @@ impl GaussianBlur { impl WriteElement for GaussianBlur { fn attrs(&self) -> Vec { - vec![Attribute { - key: QName(b"stdDeviation"), - value: Cow::from( - format!("{} {}", self.std_deviation.0, self.std_deviation.1).into_bytes(), - ), - }] + gen_attrs![b"stdDeviation" = format!("{} {}", self.std_deviation.0, self.std_deviation.1)] } fn tag_name(&self) -> &'static str { diff --git a/crates/svg-filters/src/types/nodes/primitives/morphology.rs b/crates/svg-filters/src/types/nodes/primitives/morphology.rs index dc11775..f9087d6 100644 --- a/crates/svg-filters/src/types/nodes/primitives/morphology.rs +++ b/crates/svg-filters/src/types/nodes/primitives/morphology.rs @@ -13,15 +13,9 @@ pub struct Morphology { impl WriteElement for Morphology { fn attrs(&self) -> Vec { - vec![ - Attribute { - key: QName(b"operator"), - value: Cow::from(self.operator.to_string().into_bytes()), - }, - Attribute { - key: QName(b"radius"), - value: Cow::from(format!("{} {}", self.radius.0, self.radius.1).into_bytes()), - }, + gen_attrs![ + b"operator" = self.operator, + b"radius" = format!("{} {}", self.radius.0, self.radius.1) ] } diff --git a/crates/svg-filters/src/types/nodes/primitives/offset.rs b/crates/svg-filters/src/types/nodes/primitives/offset.rs index 4cad35b..fa17dca 100644 --- a/crates/svg-filters/src/types/nodes/primitives/offset.rs +++ b/crates/svg-filters/src/types/nodes/primitives/offset.rs @@ -19,16 +19,7 @@ impl Offset { impl WriteElement for Offset { fn attrs(&self) -> Vec { - vec![ - Attribute { - key: QName(b"dx"), - value: Cow::from(self.dx.to_string().into_bytes()), - }, - Attribute { - key: QName(b"dy"), - value: Cow::from(self.dy.to_string().into_bytes()), - }, - ] + gen_attrs![b"dx" = self.dx, b"dy" = self.dy] } fn tag_name(&self) -> &'static str { diff --git a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs index 8bdcab0..d538006 100644 --- a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs +++ b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs @@ -1,10 +1,3 @@ -use std::borrow::Cow; - -use quick_xml::{ - events::attributes::{Attr, Attribute}, - name::QName, -}; - use super::WriteElement; /// [feTurbulence](https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement) @@ -19,40 +12,26 @@ pub struct Turbulence { } impl WriteElement for Turbulence { + #[allow(clippy::str_to_string, reason = "in macro invocation")] fn attrs(&self) -> Vec { - let mut r = Vec::from([Attribute { - key: QName(b"baseFrequency"), - value: Cow::from( - format!("{} {}", self.base_frequency.0, self.base_frequency.1).into_bytes(), - ), - }]); + let mut r = gen_attrs!( + b"baseFrequency" = format!("{} {}", self.base_frequency.0, self.base_frequency.1) + ); if self.num_octaves != 1 { - r.push(Attribute { - key: QName(b"numOctaves"), - value: Cow::from(format!("{}", self.num_octaves).into_bytes()), - }); + r.push(gen_attr!(b"numOctaves" = self.num_octaves)); } if self.seed != 0 { - r.push(Attribute { - key: QName(b"seed"), - value: Cow::from(self.seed.to_string().into_bytes()), - }); + r.push(gen_attr!(b"seed" = self.seed)); } if self.stitch_tiles != StitchTiles::NoStitch { - r.push(Attribute { - key: QName(b"stitchTiles"), - value: Cow::from(b"stitch".to_vec()), - }); + r.push(gen_attr!(b"stitchTiles" = "stitch")); } if self.noise_type != NoiseType::Turbulence { - r.push(Attribute { - key: QName(b"type"), - value: Cow::from(b"fractalNoise".to_vec()), - }); + r.push(gen_attr!(b"type" = "fractalNoise")); } r -- 2.46.0 From aeeee54200d126aeaa850878fe3ca406684242e6 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 19 Mar 2024 18:43:30 +0100 Subject: [PATCH 14/67] svg-filters: new conditional attrs macro --- crates/svg-filters/src/lib.rs | 32 ++++++++++++ .../svg-filters/src/types/nodes/primitives.rs | 21 +------- .../src/types/nodes/primitives/turbulence.rs | 49 ++++++++++--------- 3 files changed, 61 insertions(+), 41 deletions(-) diff --git a/crates/svg-filters/src/lib.rs b/crates/svg-filters/src/lib.rs index c3ddedc..e1e0272 100644 --- a/crates/svg-filters/src/lib.rs +++ b/crates/svg-filters/src/lib.rs @@ -1,5 +1,37 @@ #![feature(lint_reasons)] +#[macro_use] +pub mod util { + macro_rules! gen_attr { + ($name:literal = $out:expr) => { + quick_xml::events::attributes::Attribute { + key: quick_xml::name::QName($name), + value: std::borrow::Cow::from(($out).to_string().into_bytes()), + } + }; + } + + macro_rules! gen_attrs { + ($($name:literal = $out:expr),+) => { + vec![ + $(gen_attr!($name = $out)),+ + ] + }; + } + + macro_rules! gen_attrs_with_conds { + ($($name:literal = $out:expr),+, $($cond:expr => $name_:literal = $out_:expr),+) => { + { + let mut r = gen_attrs![$($name = $out),+]; + $(if $cond { + r.push(gen_attr!($name_ = $out_)); + })+ + r + } + }; + } +} + pub mod codegen; pub mod types; pub use types::nodes::Node; diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index a596639..f3c101a 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -4,23 +4,6 @@ use quick_xml::{events::attributes::Attribute, ElementWriter, Writer}; use super::CommonAttrs; -macro_rules! gen_attr { - ($name:literal = $out:expr) => { - quick_xml::events::attributes::Attribute { - key: quick_xml::name::QName($name), - value: std::borrow::Cow::from(($out).to_string().into_bytes()), - } - }; -} - -macro_rules! gen_attrs { - ($($name:literal = $out:expr),+) => { - vec![ - $(gen_attr!($name = $out)),+ - ] - }; -} - pub mod blend; pub mod color_matrix; pub mod component_transfer; @@ -114,7 +97,7 @@ impl WriteElement for FePrimitive { FePrimitive::Offset(el) => el.attrs(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), - FePrimitive::Turbulence(_) => todo!(), + FePrimitive::Turbulence(el) => el.attrs(), } } @@ -135,7 +118,7 @@ impl WriteElement for FePrimitive { FePrimitive::Offset(el) => el.tag_name(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), - FePrimitive::Turbulence(_) => todo!(), + FePrimitive::Turbulence(el) => el.tag_name(), } } } diff --git a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs index d538006..2cc59e5 100644 --- a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs +++ b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs @@ -3,38 +3,43 @@ use super::WriteElement; /// [feTurbulence](https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement) #[derive(Debug)] pub struct Turbulence { - base_frequency: (f32, f32), - num_octaves: u16, - seed: u32, - stitch_tiles: StitchTiles, + pub base_frequency: (f32, f32), + pub num_octaves: u16, + pub seed: u32, + pub stitch_tiles: StitchTiles, // attr name: type - noise_type: NoiseType, + pub noise_type: NoiseType, } impl WriteElement for Turbulence { #[allow(clippy::str_to_string, reason = "in macro invocation")] fn attrs(&self) -> Vec { - let mut r = gen_attrs!( - b"baseFrequency" = format!("{} {}", self.base_frequency.0, self.base_frequency.1) - ); + // let mut r = gen_attrs!( + // b"baseFrequency" = format!("{} {}", self.base_frequency.0, self.base_frequency.1) + // ); - if self.num_octaves != 1 { - r.push(gen_attr!(b"numOctaves" = self.num_octaves)); - } + // if self.num_octaves != 1 { + // r.push(gen_attr!(b"numOctaves" = self.num_octaves)); + // } - if self.seed != 0 { - r.push(gen_attr!(b"seed" = self.seed)); - } + // if self.seed != 0 { + // r.push(gen_attr!(b"seed" = self.seed)); + // } - if self.stitch_tiles != StitchTiles::NoStitch { - r.push(gen_attr!(b"stitchTiles" = "stitch")); - } + // if self.stitch_tiles != StitchTiles::NoStitch { + // r.push(gen_attr!(b"stitchTiles" = "stitch")); + // } - if self.noise_type != NoiseType::Turbulence { - r.push(gen_attr!(b"type" = "fractalNoise")); - } - - r + // if self.noise_type != NoiseType::Turbulence { + // r.push(gen_attr!(b"type" = "fractalNoise")); + // } + gen_attrs_with_conds![ + b"baseFrequency" = format!("{} {}", self.base_frequency.0, self.base_frequency.1), + self.num_octaves != 1 => b"numOctaves" = self.num_octaves, + self.seed != 0 => b"seed" = self.seed, + self.stitch_tiles != StitchTiles::NoStitch => b"stitchTiles" = "stitch", + self.noise_type != NoiseType::Turbulence => b"type" = "fractalNoise" + ] } fn tag_name(&self) -> &'static str { -- 2.46.0 From c31a158d9b38fd7aafacb009485fc425efe5132d Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 20 Mar 2024 19:11:42 +0100 Subject: [PATCH 15/67] svg-filters: rework macro slightly --- crates/svg-filters/src/lib.rs | 16 ++++----- crates/svg-filters/src/main.rs | 19 +++++++--- crates/svg-filters/src/types/nodes.rs | 36 ++++--------------- .../src/types/nodes/primitives/blend.rs | 6 ++-- .../types/nodes/primitives/color_matrix.rs | 4 +-- .../src/types/nodes/primitives/composite.rs | 8 ++--- .../src/types/nodes/primitives/flood.rs | 7 ++-- .../types/nodes/primitives/gaussian_blur.rs | 2 +- .../src/types/nodes/primitives/morphology.rs | 9 ++--- .../src/types/nodes/primitives/offset.rs | 6 +--- .../src/types/nodes/primitives/turbulence.rs | 35 +++++------------- 11 files changed, 52 insertions(+), 96 deletions(-) diff --git a/crates/svg-filters/src/lib.rs b/crates/svg-filters/src/lib.rs index e1e0272..2afb148 100644 --- a/crates/svg-filters/src/lib.rs +++ b/crates/svg-filters/src/lib.rs @@ -12,23 +12,23 @@ pub mod util { } macro_rules! gen_attrs { - ($($name:literal = $out:expr),+) => { + ($($name:literal: $out:expr),+) => { vec![ $(gen_attr!($name = $out)),+ ] }; - } - - macro_rules! gen_attrs_with_conds { - ($($name:literal = $out:expr),+, $($cond:expr => $name_:literal = $out_:expr),+) => { - { - let mut r = gen_attrs![$($name = $out),+]; + ($($cond:expr => $name:literal: $out:expr),+) => { + { + let mut r = Vec::new(); $(if $cond { - r.push(gen_attr!($name_ = $out_)); + r.push(gen_attr!($name = $out)); })+ r } }; + ($other:ident; $($cond:expr => $name:literal: $out:expr),+) => { + $other.append(&mut gen_attrs![$($cond => $name: $out),+]); + }; } } diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index 1bf86c8..2fc8df3 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -5,7 +5,12 @@ use svg_filters::{ types::{ graph::edge::Edge, nodes::{ - primitives::{blend::BlendMode, color_matrix::ColorMatrixType}, + primitives::{ + blend::BlendMode, + color_matrix::ColorMatrixType, + turbulence::{NoiseType, StitchTiles, Turbulence}, + FePrimitive, + }, standard_input::StandardInput, }, }, @@ -15,11 +20,15 @@ use svg_filters::{ fn main() { let mut doc = SvgDocument::new(); - let blend = doc.create_filter("blend"); + let noise = doc.create_filter("noise"); - let offset0 = blend.offset(StandardInput::SourceGraphic, 100., 0.); - let offset1 = blend.offset(StandardInput::SourceGraphic, -100., 0.); - blend.blend(offset0, offset1, BlendMode::Multiply); + noise.add_node(Node::simple(FePrimitive::Turbulence(Turbulence { + base_frequency: (0.2, 0.2), + num_octaves: 1, + seed: 2, + stitch_tiles: StitchTiles::NoStitch, + noise_type: NoiseType::FractalNoise, + }))); eprintln!("{}", doc.generate_svg_pretty()); println!("{}", doc.generate_svg()); diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index 656574d..2edc63b 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -45,36 +45,12 @@ pub struct CommonAttrs { impl From for Vec> { fn from(val: CommonAttrs) -> Self { - let mut r = Vec::new(); - if !val.x.is_zero() { - r.push(Attribute { - key: QName(b"x"), - value: Cow::from(val.x.to_string().into_bytes()), - }); - } - - if !val.y.is_zero() { - r.push(Attribute { - key: QName(b"y"), - value: Cow::from(val.y.to_string().into_bytes()), - }); - } - - if !val.width.is_zero() { - r.push(Attribute { - key: QName(b"width"), - value: Cow::from(val.width.to_string().into_bytes()), - }); - } - - if !val.height.is_zero() { - r.push(Attribute { - key: QName(b"height"), - value: Cow::from(val.height.to_string().into_bytes()), - }); - } - - r + gen_attrs![ + !val.x.is_zero() => b"x": val.x, + !val.y.is_zero() => b"y": val.y, + !val.width.is_zero() => b"width": val.width, + !val.height.is_zero() => b"height": val.height + ] } } diff --git a/crates/svg-filters/src/types/nodes/primitives/blend.rs b/crates/svg-filters/src/types/nodes/primitives/blend.rs index 1887042..3e28401 100644 --- a/crates/svg-filters/src/types/nodes/primitives/blend.rs +++ b/crates/svg-filters/src/types/nodes/primitives/blend.rs @@ -1,6 +1,4 @@ -use std::{borrow::Cow, fmt::Display}; - -use quick_xml::{events::attributes::Attribute, name::QName}; +use std::fmt::Display; use super::WriteElement; @@ -29,7 +27,7 @@ impl WriteElement for Blend { if let BlendMode::Normal = self.mode { Vec::new() } else { - gen_attrs![b"mode" = self.mode] + gen_attrs![b"mode": self.mode] } } diff --git a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs index 31e4130..3a31b7d 100644 --- a/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs +++ b/crates/svg-filters/src/types/nodes/primitives/color_matrix.rs @@ -16,7 +16,7 @@ impl WriteElement for ColorMatrix { fn attrs(&self) -> Vec { match &self.cm_type { ColorMatrixType::Matrix(v) => gen_attrs![ - b"values" = v + b"values": v .iter() .map(std::string::ToString::to_string) .reduce(|mut acc, e| { @@ -27,7 +27,7 @@ impl WriteElement for ColorMatrix { .expect("fixed length arr should always work") ], ColorMatrixType::Saturate(v) | ColorMatrixType::HueRotate(v) => { - gen_attrs![b"values" = v] + gen_attrs![b"values": v] } ColorMatrixType::LuminanceToAlpha => Vec::new(), } diff --git a/crates/svg-filters/src/types/nodes/primitives/composite.rs b/crates/svg-filters/src/types/nodes/primitives/composite.rs index b877fe8..45f639c 100644 --- a/crates/svg-filters/src/types/nodes/primitives/composite.rs +++ b/crates/svg-filters/src/types/nodes/primitives/composite.rs @@ -70,10 +70,10 @@ impl WriteElement for Composite { // }, // ]); r.append(&mut gen_attrs![ - b"k1" = k1, - b"k2" = k2, - b"k3" = k3, - b"k4" = k4 + b"k1": k1, + b"k2": k2, + b"k3": k3, + b"k4": k4 ]); } diff --git a/crates/svg-filters/src/types/nodes/primitives/flood.rs b/crates/svg-filters/src/types/nodes/primitives/flood.rs index 3342ef7..4daa3c8 100644 --- a/crates/svg-filters/src/types/nodes/primitives/flood.rs +++ b/crates/svg-filters/src/types/nodes/primitives/flood.rs @@ -1,7 +1,4 @@ -use std::borrow::Cow; - use csscolorparser::Color; -use quick_xml::{events::attributes::Attribute, name::QName}; use super::WriteElement; @@ -15,8 +12,8 @@ pub struct Flood { impl WriteElement for Flood { fn attrs(&self) -> Vec { gen_attrs![ - b"flood-color" = self.flood_color.to_hex_string(), - b"flood-opacity" = self.flood_opacity + b"flood-color": self.flood_color.to_hex_string(), + b"flood-opacity": self.flood_opacity ] } diff --git a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs index e255df4..cd18f72 100644 --- a/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs +++ b/crates/svg-filters/src/types/nodes/primitives/gaussian_blur.rs @@ -26,7 +26,7 @@ impl GaussianBlur { impl WriteElement for GaussianBlur { fn attrs(&self) -> Vec { - gen_attrs![b"stdDeviation" = format!("{} {}", self.std_deviation.0, self.std_deviation.1)] + gen_attrs![b"stdDeviation": format!("{} {}", self.std_deviation.0, self.std_deviation.1)] } fn tag_name(&self) -> &'static str { diff --git a/crates/svg-filters/src/types/nodes/primitives/morphology.rs b/crates/svg-filters/src/types/nodes/primitives/morphology.rs index f9087d6..f6b7bbf 100644 --- a/crates/svg-filters/src/types/nodes/primitives/morphology.rs +++ b/crates/svg-filters/src/types/nodes/primitives/morphology.rs @@ -1,8 +1,5 @@ -use std::{borrow::Cow, fmt::Display}; - -use quick_xml::{events::attributes::Attribute, name::QName}; - use super::WriteElement; +use std::fmt::Display; /// [feMorphology](https://www.w3.org/TR/SVG11/filters.html#feMorphologyElement) #[derive(Debug)] @@ -14,8 +11,8 @@ pub struct Morphology { impl WriteElement for Morphology { fn attrs(&self) -> Vec { gen_attrs![ - b"operator" = self.operator, - b"radius" = format!("{} {}", self.radius.0, self.radius.1) + b"operator": self.operator, + b"radius": format!("{} {}", self.radius.0, self.radius.1) ] } diff --git a/crates/svg-filters/src/types/nodes/primitives/offset.rs b/crates/svg-filters/src/types/nodes/primitives/offset.rs index fa17dca..7ccf2fd 100644 --- a/crates/svg-filters/src/types/nodes/primitives/offset.rs +++ b/crates/svg-filters/src/types/nodes/primitives/offset.rs @@ -1,7 +1,3 @@ -use std::borrow::Cow; - -use quick_xml::{events::attributes::Attribute, name::QName}; - use super::WriteElement; /// [feOffset](https://www.w3.org/TR/SVG11/filters.html#feOffsetElement) @@ -19,7 +15,7 @@ impl Offset { impl WriteElement for Offset { fn attrs(&self) -> Vec { - gen_attrs![b"dx" = self.dx, b"dy" = self.dy] + gen_attrs![b"dx": self.dx, b"dy": self.dy] } fn tag_name(&self) -> &'static str { diff --git a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs index 2cc59e5..8fc4ae9 100644 --- a/crates/svg-filters/src/types/nodes/primitives/turbulence.rs +++ b/crates/svg-filters/src/types/nodes/primitives/turbulence.rs @@ -14,32 +14,15 @@ pub struct Turbulence { impl WriteElement for Turbulence { #[allow(clippy::str_to_string, reason = "in macro invocation")] fn attrs(&self) -> Vec { - // let mut r = gen_attrs!( - // b"baseFrequency" = format!("{} {}", self.base_frequency.0, self.base_frequency.1) - // ); - - // if self.num_octaves != 1 { - // r.push(gen_attr!(b"numOctaves" = self.num_octaves)); - // } - - // if self.seed != 0 { - // r.push(gen_attr!(b"seed" = self.seed)); - // } - - // if self.stitch_tiles != StitchTiles::NoStitch { - // r.push(gen_attr!(b"stitchTiles" = "stitch")); - // } - - // if self.noise_type != NoiseType::Turbulence { - // r.push(gen_attr!(b"type" = "fractalNoise")); - // } - gen_attrs_with_conds![ - b"baseFrequency" = format!("{} {}", self.base_frequency.0, self.base_frequency.1), - self.num_octaves != 1 => b"numOctaves" = self.num_octaves, - self.seed != 0 => b"seed" = self.seed, - self.stitch_tiles != StitchTiles::NoStitch => b"stitchTiles" = "stitch", - self.noise_type != NoiseType::Turbulence => b"type" = "fractalNoise" - ] + let mut r = gen_attrs![b"baseFrequency": format!("{} {}", self.base_frequency.0, self.base_frequency.1)]; + gen_attrs![ + r; + self.num_octaves != 1 => b"numOctaves": self.num_octaves, + self.seed != 0 => b"seed": self.seed, + self.stitch_tiles != StitchTiles::NoStitch => b"stitchTiles": "stitch", + self.noise_type != NoiseType::Turbulence => b"type": "fractalNoise" + ]; + r } fn tag_name(&self) -> &'static str { -- 2.46.0 From 9727ef82cafe1f50280ee2d097df23dd974f2053 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 22 Mar 2024 16:05:36 +0100 Subject: [PATCH 16/67] svg-filters: implement feComponentTransfer rendering --- crates/svg-filters/src/tests.rs | 39 +++++++- .../svg-filters/src/types/nodes/primitives.rs | 31 ++++++- .../nodes/primitives/component_transfer.rs | 93 +++++++++++++++++++ 3 files changed, 160 insertions(+), 3 deletions(-) diff --git a/crates/svg-filters/src/tests.rs b/crates/svg-filters/src/tests.rs index 72b970f..771ab3a 100644 --- a/crates/svg-filters/src/tests.rs +++ b/crates/svg-filters/src/tests.rs @@ -5,7 +5,44 @@ mod complex; mod gaussian_blur; mod offset; -mod component_transfer {} +mod component_transfer { + use crate::{ + codegen::SvgDocument, + types::nodes::primitives::{ + component_transfer::{ComponentTransfer, TransferFn}, + FePrimitive, + }, + Node, + }; + + #[test] + fn test_comp_trans_simple() { + let mut doc = SvgDocument::new(); + + let comptrans = doc.create_filter("comp_trans"); + + comptrans.add_node(Node::simple(FePrimitive::ComponentTransfer( + ComponentTransfer { + func_r: TransferFn::Table { + table_values: vec![0., 0.1, 0.4, 0.9], + }, + func_g: TransferFn::Discrete { + table_values: vec![0.1, 0.3, 0.5, 0.7, 0.9], + }, + func_b: TransferFn::Linear { + slope: 1.0, + intercept: 0.75, + }, + func_a: TransferFn::Identity, + }, + ))); + + assert_eq!( + doc.generate_svg(), + r#""# + ); + } +} mod composite {} mod convolve_matrix {} mod diffuse_lighting {} diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index f3c101a..0b6e758 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -84,7 +84,7 @@ impl WriteElement for FePrimitive { match self { FePrimitive::Blend(el) => el.attrs(), FePrimitive::ColorMatrix(el) => el.attrs(), - FePrimitive::ComponentTransfer(_) => todo!(), + FePrimitive::ComponentTransfer(el) => el.attrs(), FePrimitive::Composite(el) => el.attrs(), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), @@ -105,7 +105,7 @@ impl WriteElement for FePrimitive { match self { FePrimitive::Blend(el) => el.tag_name(), FePrimitive::ColorMatrix(el) => el.tag_name(), - FePrimitive::ComponentTransfer(_) => todo!(), + FePrimitive::ComponentTransfer(el) => el.tag_name(), FePrimitive::Composite(el) => el.tag_name(), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), @@ -121,4 +121,31 @@ impl WriteElement for FePrimitive { FePrimitive::Turbulence(el) => el.tag_name(), } } + + fn element_writer<'writer, 'result>( + &self, + writer: &'writer mut Writer<&'result mut Vec>, + common: CommonAttrs, + inputs: Vec, + output: Option, + ) -> quick_xml::Result<&'writer mut quick_xml::Writer<&'result mut Vec>> { + match self { + FePrimitive::Blend(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::ColorMatrix(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::ComponentTransfer(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::Composite(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::ConvolveMatrix(_) => todo!(), + FePrimitive::DiffuseLighting(_) => todo!(), + FePrimitive::DisplacementMap(_) => todo!(), + FePrimitive::Flood(_) => todo!(), + FePrimitive::GaussianBlur(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::Image(_) => todo!(), + FePrimitive::Merge(_) => todo!(), + FePrimitive::Morphology(_) => todo!(), + FePrimitive::Offset(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::SpecularLighting(_) => todo!(), + FePrimitive::Tile(_) => todo!(), + FePrimitive::Turbulence(el) => el.element_writer(writer, common, inputs, output), + } + } } diff --git a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs index d495ecc..1c37c50 100644 --- a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs +++ b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs @@ -1,3 +1,5 @@ +use quick_xml::{events::attributes::Attribute, Writer}; + use super::WriteElement; /// [feComponentTransfer](https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement) @@ -17,6 +19,44 @@ impl WriteElement for ComponentTransfer { fn tag_name(&self) -> &'static str { "feComponentTransfer" } + + fn element_writer<'writer, 'result>( + &self, + writer: &'writer mut quick_xml::Writer<&'result mut Vec>, + common: crate::types::nodes::CommonAttrs, + inputs: Vec, + output: Option, + ) -> quick_xml::Result<&'writer mut quick_xml::Writer<&'result mut Vec>> { + let inputs: Vec<_> = inputs + .into_iter() + .enumerate() + .map(|(i, edge)| { + ( + match i { + 0 => "in".to_owned(), + n => format!("in{}", n + 1), + } + .into_bytes(), + edge.into_bytes(), + ) + }) + .collect(); + let mut el_writer = writer + .create_element(self.tag_name()) + .with_attributes(inputs.iter().map(|(k, v)| (&k[..], &v[..]))) + .with_attributes(Into::>>::into(common)); + if let Some(output) = output { + el_writer = el_writer.with_attribute(("result", output.as_str())); + } + + el_writer.write_inner_content(|writer| { + self.func_r.write_self(writer, "feFuncR")?; + self.func_g.write_self(writer, "feFuncG")?; + self.func_b.write_self(writer, "feFuncB")?; + self.func_a.write_self(writer, "feFuncA")?; + Ok(()) + }) + } } /// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements) @@ -39,3 +79,56 @@ pub enum TransferFn { offset: f32, }, } + +impl TransferFn { + #[allow(clippy::str_to_string, reason = "inside macro call")] + fn write_self<'writer, 'result>( + &self, + target: &'writer mut Writer<&'result mut Vec>, + name: &'static str, + ) -> quick_xml::Result<&'writer mut Writer<&'result mut Vec>> { + target + .create_element(name) + .with_attributes(match self { + TransferFn::Identity => gen_attrs![b"type": "identity"], + TransferFn::Table { table_values } => gen_attrs![ + b"type": "table", + b"tableValues": table_values + .iter() + .map(std::string::ToString::to_string) + .reduce(|mut acc, e| { + acc.push(' '); + acc.push_str(&e); + acc + }).expect("empty tables disallowed") + ], + TransferFn::Discrete { table_values } => gen_attrs![ + b"type": "discrete", + b"tableValues": table_values + .iter() + .map(std::string::ToString::to_string) + .reduce(|mut acc, e| { + acc.push(' '); + acc.push_str(&e); + acc + }).expect("empty tables disallowed") + ], + TransferFn::Linear { slope, intercept } => gen_attrs![ + b"type": "linear", + b"slope": slope, + b"intercept": intercept + ], + TransferFn::Gamma { + amplitude, + exponent, + offset, + } => gen_attrs![ + b"type": "gamma", + b"amplitude": amplitude, + b"exponent": exponent, + b"offset": offset + ], + }) + .write_empty() + } +} -- 2.46.0 From 9ae8c2fbd3620f3b9d792aa859de535a24f583c6 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 22 Mar 2024 16:24:04 +0100 Subject: [PATCH 17/67] svg-filters: add nice abstractions for component transfer --- crates/svg-filters/src/types/graph.rs | 82 ++++++++++++++++++- crates/svg-filters/src/types/nodes.rs | 44 ++++++++++ .../svg-filters/src/types/nodes/primitives.rs | 21 +++-- .../nodes/primitives/component_transfer.rs | 2 +- 4 files changed, 135 insertions(+), 14 deletions(-) diff --git a/crates/svg-filters/src/types/graph.rs b/crates/svg-filters/src/types/graph.rs index 05d63c3..524ba92 100644 --- a/crates/svg-filters/src/types/graph.rs +++ b/crates/svg-filters/src/types/graph.rs @@ -139,10 +139,12 @@ impl FilterGraph { } pub mod abstracted_inputs { - use petgraph::prelude::NodeIndex; + use petgraph::{data::Build, prelude::NodeIndex}; use crate::{ - types::nodes::primitives::{blend::BlendMode, color_matrix::ColorMatrixType}, + types::nodes::primitives::{ + blend::BlendMode, color_matrix::ColorMatrixType, component_transfer::TransferFn, + }, Node, }; @@ -211,6 +213,82 @@ pub mod abstracted_inputs { .add_edge(self.resolve_input(in2.into()), node_idx, ()); node_idx } + + pub fn component_transfer_rgba( + &mut self, + r#in: impl Into, + r: TransferFn, + g: TransferFn, + b: TransferFn, + a: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_rgba(r, g, b, a)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_rgb( + &mut self, + r#in: impl Into, + r: TransferFn, + g: TransferFn, + b: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_rgb(r, g, b)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_r( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_r(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_g( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_g(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_b( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_b(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_a( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_a(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_single( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_single(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } } } diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index 2edc63b..c9f9334 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -150,4 +150,48 @@ impl Node { func_a: a, })) } + + pub fn component_transfer_rgb(r: TransferFn, g: TransferFn, b: TransferFn) -> Self { + Self::component_transfer_rgba(r, g, b, TransferFn::Identity) + } + + pub fn component_transfer_r(func: TransferFn) -> Self { + Self::component_transfer_rgba( + func, + TransferFn::Identity, + TransferFn::Identity, + TransferFn::Identity, + ) + } + + pub fn component_transfer_g(func: TransferFn) -> Self { + Self::component_transfer_rgba( + TransferFn::Identity, + func, + TransferFn::Identity, + TransferFn::Identity, + ) + } + + pub fn component_transfer_b(func: TransferFn) -> Self { + Self::component_transfer_rgba( + TransferFn::Identity, + TransferFn::Identity, + func, + TransferFn::Identity, + ) + } + + pub fn component_transfer_a(func: TransferFn) -> Self { + Self::component_transfer_rgba( + TransferFn::Identity, + TransferFn::Identity, + TransferFn::Identity, + func, + ) + } + + pub fn component_transfer_single(func: TransferFn) -> Self { + Self::component_transfer_rgb(func.clone(), func.clone(), func) + } } diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index 0b6e758..5cdd232 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -1,6 +1,5 @@ -use std::convert::Into; - use quick_xml::{events::attributes::Attribute, ElementWriter, Writer}; +use std::convert::Into; use super::CommonAttrs; @@ -86,18 +85,18 @@ impl WriteElement for FePrimitive { FePrimitive::ColorMatrix(el) => el.attrs(), FePrimitive::ComponentTransfer(el) => el.attrs(), FePrimitive::Composite(el) => el.attrs(), + FePrimitive::GaussianBlur(el) => el.attrs(), + FePrimitive::Offset(el) => el.attrs(), + FePrimitive::Turbulence(el) => el.attrs(), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(), FePrimitive::Flood(_) => todo!(), - FePrimitive::GaussianBlur(el) => el.attrs(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), FePrimitive::Morphology(_) => todo!(), - FePrimitive::Offset(el) => el.attrs(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), - FePrimitive::Turbulence(el) => el.attrs(), } } @@ -107,18 +106,18 @@ impl WriteElement for FePrimitive { FePrimitive::ColorMatrix(el) => el.tag_name(), FePrimitive::ComponentTransfer(el) => el.tag_name(), FePrimitive::Composite(el) => el.tag_name(), + FePrimitive::GaussianBlur(el) => el.tag_name(), + FePrimitive::Offset(el) => el.tag_name(), + FePrimitive::Turbulence(el) => el.tag_name(), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(), FePrimitive::Flood(_) => todo!(), - FePrimitive::GaussianBlur(el) => el.tag_name(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), FePrimitive::Morphology(_) => todo!(), - FePrimitive::Offset(el) => el.tag_name(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), - FePrimitive::Turbulence(el) => el.tag_name(), } } @@ -134,18 +133,18 @@ impl WriteElement for FePrimitive { FePrimitive::ColorMatrix(el) => el.element_writer(writer, common, inputs, output), FePrimitive::ComponentTransfer(el) => el.element_writer(writer, common, inputs, output), FePrimitive::Composite(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::Turbulence(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::GaussianBlur(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::Offset(el) => el.element_writer(writer, common, inputs, output), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(), FePrimitive::Flood(_) => todo!(), - FePrimitive::GaussianBlur(el) => el.element_writer(writer, common, inputs, output), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), FePrimitive::Morphology(_) => todo!(), - FePrimitive::Offset(el) => el.element_writer(writer, common, inputs, output), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), - FePrimitive::Turbulence(el) => el.element_writer(writer, common, inputs, output), } } } diff --git a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs index 1c37c50..3f8a3a3 100644 --- a/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs +++ b/crates/svg-filters/src/types/nodes/primitives/component_transfer.rs @@ -60,7 +60,7 @@ impl WriteElement for ComponentTransfer { } /// [transfer functions](https://www.w3.org/TR/SVG11/filters.html#transferFuncElements) -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum TransferFn { Identity, Table { -- 2.46.0 From 919a3bb377ba4654153861b8bbc6b90065f14442 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 22 Mar 2024 16:47:21 +0100 Subject: [PATCH 18/67] svg-filters: seperate out abstractions and add flood abstraction --- crates/svg-filters/src/types/graph.rs | 154 +---------------- .../src/types/graph/abstracted_inputs.rs | 155 ++++++++++++++++++ crates/svg-filters/src/types/nodes.rs | 13 ++ .../svg-filters/src/types/nodes/primitives.rs | 6 +- .../src/types/nodes/primitives/flood.rs | 4 +- 5 files changed, 174 insertions(+), 158 deletions(-) create mode 100644 crates/svg-filters/src/types/graph/abstracted_inputs.rs diff --git a/crates/svg-filters/src/types/graph.rs b/crates/svg-filters/src/types/graph.rs index 524ba92..4b63a87 100644 --- a/crates/svg-filters/src/types/graph.rs +++ b/crates/svg-filters/src/types/graph.rs @@ -138,158 +138,6 @@ impl FilterGraph { } } -pub mod abstracted_inputs { - use petgraph::{data::Build, prelude::NodeIndex}; - - use crate::{ - types::nodes::primitives::{ - blend::BlendMode, color_matrix::ColorMatrixType, component_transfer::TransferFn, - }, - Node, - }; - - use super::{FilterGraph, NodeInput}; - - impl FilterGraph { - pub fn color_matrix( - &mut self, - r#in: impl Into, - cm_type: ColorMatrixType, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::color_matrix(cm_type)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - - pub fn offset(&mut self, r#in: impl Into, dx: f32, dy: f32) -> NodeIndex { - let node_idx = self.dag.add_node(Node::offset(dx, dy)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - - pub fn gaussian_blur_xy( - &mut self, - r#in: impl Into, - x: u16, - y: u16, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::gaussian_blur_xy(x, y)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - - pub fn blend( - &mut self, - r#in: impl Into, - in2: impl Into, - mode: BlendMode, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::blend(mode)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - self.dag - .add_edge(self.resolve_input(in2.into()), node_idx, ()); - node_idx - } - - pub fn composite_arithmetic( - &mut self, - r#in: impl Into, - in2: impl Into, - k1: f32, - k2: f32, - k3: f32, - k4: f32, - ) -> NodeIndex { - let node_idx = self - .dag - .add_node(Node::composite_arithmetic(k1, k2, k3, k4)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - self.dag - .add_edge(self.resolve_input(in2.into()), node_idx, ()); - node_idx - } - - pub fn component_transfer_rgba( - &mut self, - r#in: impl Into, - r: TransferFn, - g: TransferFn, - b: TransferFn, - a: TransferFn, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::component_transfer_rgba(r, g, b, a)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - pub fn component_transfer_rgb( - &mut self, - r#in: impl Into, - r: TransferFn, - g: TransferFn, - b: TransferFn, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::component_transfer_rgb(r, g, b)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - pub fn component_transfer_r( - &mut self, - r#in: impl Into, - func: TransferFn, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::component_transfer_r(func)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - pub fn component_transfer_g( - &mut self, - r#in: impl Into, - func: TransferFn, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::component_transfer_g(func)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - pub fn component_transfer_b( - &mut self, - r#in: impl Into, - func: TransferFn, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::component_transfer_b(func)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - pub fn component_transfer_a( - &mut self, - r#in: impl Into, - func: TransferFn, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::component_transfer_a(func)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - pub fn component_transfer_single( - &mut self, - r#in: impl Into, - func: TransferFn, - ) -> NodeIndex { - let node_idx = self.dag.add_node(Node::component_transfer_single(func)); - self.dag - .add_edge(self.resolve_input(r#in.into()), node_idx, ()); - node_idx - } - } -} +pub mod abstracted_inputs; pub mod edge; diff --git a/crates/svg-filters/src/types/graph/abstracted_inputs.rs b/crates/svg-filters/src/types/graph/abstracted_inputs.rs new file mode 100644 index 0000000..ca576c5 --- /dev/null +++ b/crates/svg-filters/src/types/graph/abstracted_inputs.rs @@ -0,0 +1,155 @@ +use csscolorparser::Color; +use petgraph::{data::Build, prelude::NodeIndex}; + +use crate::{ + types::nodes::primitives::{ + blend::BlendMode, color_matrix::ColorMatrixType, component_transfer::TransferFn, + }, + Node, +}; + +use super::{FilterGraph, NodeInput}; + +impl FilterGraph { + pub fn color_matrix( + &mut self, + r#in: impl Into, + cm_type: ColorMatrixType, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::color_matrix(cm_type)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + + pub fn offset(&mut self, r#in: impl Into, dx: f32, dy: f32) -> NodeIndex { + let node_idx = self.dag.add_node(Node::offset(dx, dy)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + + pub fn gaussian_blur_xy(&mut self, r#in: impl Into, x: u16, y: u16) -> NodeIndex { + let node_idx = self.dag.add_node(Node::gaussian_blur_xy(x, y)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + + pub fn blend( + &mut self, + r#in: impl Into, + in2: impl Into, + mode: BlendMode, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::blend(mode)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + self.dag + .add_edge(self.resolve_input(in2.into()), node_idx, ()); + node_idx + } + + pub fn composite_arithmetic( + &mut self, + r#in: impl Into, + in2: impl Into, + k1: f32, + k2: f32, + k3: f32, + k4: f32, + ) -> NodeIndex { + let node_idx = self + .dag + .add_node(Node::composite_arithmetic(k1, k2, k3, k4)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + self.dag + .add_edge(self.resolve_input(in2.into()), node_idx, ()); + node_idx + } + + pub fn component_transfer_rgba( + &mut self, + r#in: impl Into, + r: TransferFn, + g: TransferFn, + b: TransferFn, + a: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_rgba(r, g, b, a)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_rgb( + &mut self, + r#in: impl Into, + r: TransferFn, + g: TransferFn, + b: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_rgb(r, g, b)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_r( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_r(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_g( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_g(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_b( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_b(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_a( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_a(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + pub fn component_transfer_single( + &mut self, + r#in: impl Into, + func: TransferFn, + ) -> NodeIndex { + let node_idx = self.dag.add_node(Node::component_transfer_single(func)); + self.dag + .add_edge(self.resolve_input(r#in.into()), node_idx, ()); + node_idx + } + + pub fn flood(&mut self, flood_color: Color, flood_opacity: f32) -> NodeIndex { + self.dag.add_node(Node::flood(flood_color, flood_opacity)) + } + + pub fn flood_opaque(&mut self, flood_color: Color) -> NodeIndex { + self.dag.add_node(Node::flood_opaque(flood_color)) + } +} diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index c9f9334..ba0340b 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use csscolorparser::Color; use quick_xml::{events::attributes::Attribute, name::QName}; use self::{ @@ -8,6 +9,7 @@ use self::{ color_matrix::{ColorMatrix, ColorMatrixType}, component_transfer::{ComponentTransfer, TransferFn}, composite::{Composite, CompositeOperator}, + flood::Flood, gaussian_blur::GaussianBlur, offset::Offset, FePrimitive, @@ -194,4 +196,15 @@ impl Node { pub fn component_transfer_single(func: TransferFn) -> Self { Self::component_transfer_rgb(func.clone(), func.clone(), func) } + + pub fn flood(flood_color: Color, flood_opacity: f32) -> Self { + Self::simple(FePrimitive::Flood(Flood { + flood_color, + flood_opacity, + })) + } + + pub fn flood_opaque(flood_color: Color) -> Self { + Self::flood(flood_color, 1.) + } } diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index 5cdd232..a46ee9b 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -91,7 +91,7 @@ impl WriteElement for FePrimitive { FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(), - FePrimitive::Flood(_) => todo!(), + FePrimitive::Flood(el) => el.attrs(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), FePrimitive::Morphology(_) => todo!(), @@ -112,7 +112,7 @@ impl WriteElement for FePrimitive { FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(), - FePrimitive::Flood(_) => todo!(), + FePrimitive::Flood(el) => el.tag_name(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), FePrimitive::Morphology(_) => todo!(), @@ -139,7 +139,7 @@ impl WriteElement for FePrimitive { FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(_) => todo!(), - FePrimitive::Flood(_) => todo!(), + FePrimitive::Flood(el) => el.element_writer(writer, common, inputs, output), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), FePrimitive::Morphology(_) => todo!(), diff --git a/crates/svg-filters/src/types/nodes/primitives/flood.rs b/crates/svg-filters/src/types/nodes/primitives/flood.rs index 4daa3c8..df912ef 100644 --- a/crates/svg-filters/src/types/nodes/primitives/flood.rs +++ b/crates/svg-filters/src/types/nodes/primitives/flood.rs @@ -5,8 +5,8 @@ use super::WriteElement; /// [feFlood](https://www.w3.org/TR/SVG11/filters.html#feFloodElement) #[derive(Debug)] pub struct Flood { - flood_color: Color, - flood_opacity: f32, + pub flood_color: Color, + pub flood_opacity: f32, } impl WriteElement for Flood { -- 2.46.0 From 0197df5ee29e7dcd633b11ac899ac48575a53db8 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 24 Mar 2024 15:49:41 +0100 Subject: [PATCH 19/67] svg-filters: add turbulence and displacement map --- .../src/types/graph/abstracted_inputs.rs | 43 ++++++++++++++++++- crates/svg-filters/src/types/nodes.rs | 27 ++++++++++++ .../svg-filters/src/types/nodes/primitives.rs | 6 +-- .../nodes/primitives/displacement_map.rs | 29 ++++++++++--- .../src/types/nodes/primitives/flood.rs | 2 + 5 files changed, 98 insertions(+), 9 deletions(-) diff --git a/crates/svg-filters/src/types/graph/abstracted_inputs.rs b/crates/svg-filters/src/types/graph/abstracted_inputs.rs index ca576c5..8d84707 100644 --- a/crates/svg-filters/src/types/graph/abstracted_inputs.rs +++ b/crates/svg-filters/src/types/graph/abstracted_inputs.rs @@ -3,7 +3,11 @@ use petgraph::{data::Build, prelude::NodeIndex}; use crate::{ types::nodes::primitives::{ - blend::BlendMode, color_matrix::ColorMatrixType, component_transfer::TransferFn, + blend::BlendMode, + color_matrix::ColorMatrixType, + component_transfer::TransferFn, + displacement_map::Channel, + turbulence::{NoiseType, StitchTiles}, }, Node, }; @@ -152,4 +156,41 @@ impl FilterGraph { pub fn flood_opaque(&mut self, flood_color: Color) -> NodeIndex { self.dag.add_node(Node::flood_opaque(flood_color)) } + + pub fn turbulence( + &mut self, + base_freq_x: f32, + base_freq_y: f32, + num_octaves: u16, + seed: u32, + stitch_tiles: StitchTiles, + noise_type: NoiseType, + ) -> NodeIndex { + self.dag.add_node(Node::turbulence( + base_freq_x, + base_freq_y, + num_octaves, + seed, + stitch_tiles, + noise_type, + )) + } + + pub fn displacement_map( + &mut self, + source_image: impl Into, + displacement_map: impl Into, + scale: f32, + x_channel: Channel, + y_channel: Channel, + ) -> NodeIndex { + let node_idx = self + .dag + .add_node(Node::displacement_map(scale, x_channel, y_channel)); + self.dag + .add_edge(self.resolve_input(source_image.into()), node_idx, ()); + self.dag + .add_edge(self.resolve_input(displacement_map.into()), node_idx, ()); + node_idx + } } diff --git a/crates/svg-filters/src/types/nodes.rs b/crates/svg-filters/src/types/nodes.rs index ba0340b..797e7e4 100644 --- a/crates/svg-filters/src/types/nodes.rs +++ b/crates/svg-filters/src/types/nodes.rs @@ -9,9 +9,11 @@ use self::{ color_matrix::{ColorMatrix, ColorMatrixType}, component_transfer::{ComponentTransfer, TransferFn}, composite::{Composite, CompositeOperator}, + displacement_map::{Channel, DisplacementMap}, flood::Flood, gaussian_blur::GaussianBlur, offset::Offset, + turbulence::{NoiseType, StitchTiles, Turbulence}, FePrimitive, }, standard_input::StandardInput, @@ -207,4 +209,29 @@ impl Node { pub fn flood_opaque(flood_color: Color) -> Self { Self::flood(flood_color, 1.) } + + pub fn turbulence( + base_freq_x: f32, + base_freq_y: f32, + num_octaves: u16, + seed: u32, + stitch_tiles: StitchTiles, + noise_type: NoiseType, + ) -> Self { + Self::simple(FePrimitive::Turbulence(Turbulence { + base_frequency: (base_freq_x, base_freq_y), + num_octaves, + seed, + stitch_tiles, + noise_type, + })) + } + + pub fn displacement_map(scale: f32, x_channel: Channel, y_channel: Channel) -> Self { + Self::simple(FePrimitive::DisplacementMap(DisplacementMap { + scale, + x_channel_selector: x_channel, + y_channel_selector: y_channel, + })) + } } diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index a46ee9b..171314f 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -90,7 +90,7 @@ impl WriteElement for FePrimitive { FePrimitive::Turbulence(el) => el.attrs(), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), - FePrimitive::DisplacementMap(_) => todo!(), + FePrimitive::DisplacementMap(el) => el.attrs(), FePrimitive::Flood(el) => el.attrs(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), @@ -111,7 +111,7 @@ impl WriteElement for FePrimitive { FePrimitive::Turbulence(el) => el.tag_name(), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), - FePrimitive::DisplacementMap(_) => todo!(), + FePrimitive::DisplacementMap(el) => el.tag_name(), FePrimitive::Flood(el) => el.tag_name(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), @@ -138,7 +138,7 @@ impl WriteElement for FePrimitive { FePrimitive::Offset(el) => el.element_writer(writer, common, inputs, output), FePrimitive::ConvolveMatrix(_) => todo!(), FePrimitive::DiffuseLighting(_) => todo!(), - FePrimitive::DisplacementMap(_) => todo!(), + FePrimitive::DisplacementMap(el) => el.element_writer(writer, common, inputs, output), FePrimitive::Flood(el) => el.element_writer(writer, common, inputs, output), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), diff --git a/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs b/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs index b6485ac..189291e 100644 --- a/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs +++ b/crates/svg-filters/src/types/nodes/primitives/displacement_map.rs @@ -1,13 +1,32 @@ +use super::WriteElement; + /// [feDisplacementMap](https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement) #[derive(Debug)] pub struct DisplacementMap { - scale: f32, - x_channel_selector: Channel, - y_channel_selector: Channel, + pub scale: f32, + pub x_channel_selector: Channel, + pub y_channel_selector: Channel, } -#[derive(Debug)] -enum Channel { +impl WriteElement for DisplacementMap { + fn attrs(&self) -> Vec { + let mut r = Vec::new(); + gen_attrs![ + r; + self.scale != 0. => b"scale": self.scale, + self.x_channel_selector != Channel::A => b"xChannelSelector": format!("{:?}", self.x_channel_selector), + self.y_channel_selector != Channel::A => b"yChannelSelector": format!("{:?}", self.y_channel_selector) + ]; + r + } + + fn tag_name(&self) -> &'static str { + "feDisplacementMap" + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Channel { A, R, G, diff --git a/crates/svg-filters/src/types/nodes/primitives/flood.rs b/crates/svg-filters/src/types/nodes/primitives/flood.rs index df912ef..3f10658 100644 --- a/crates/svg-filters/src/types/nodes/primitives/flood.rs +++ b/crates/svg-filters/src/types/nodes/primitives/flood.rs @@ -3,6 +3,8 @@ use csscolorparser::Color; use super::WriteElement; /// [feFlood](https://www.w3.org/TR/SVG11/filters.html#feFloodElement) +// NOTE: this doesn't work for some reason, but the examples from mdn don't either. +// might be a browser bug, def worth investigating #[derive(Debug)] pub struct Flood { pub flood_color: Color, -- 2.46.0 From 02c5e9e159f01bbc595638846e5db60707a6e6ae Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 24 Mar 2024 16:11:07 +0100 Subject: [PATCH 20/67] svg-filters: figured out flood not working in local test env --- crates/svg-filters/src/types/nodes/primitives/flood.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/svg-filters/src/types/nodes/primitives/flood.rs b/crates/svg-filters/src/types/nodes/primitives/flood.rs index 3f10658..df912ef 100644 --- a/crates/svg-filters/src/types/nodes/primitives/flood.rs +++ b/crates/svg-filters/src/types/nodes/primitives/flood.rs @@ -3,8 +3,6 @@ use csscolorparser::Color; use super::WriteElement; /// [feFlood](https://www.w3.org/TR/SVG11/filters.html#feFloodElement) -// NOTE: this doesn't work for some reason, but the examples from mdn don't either. -// might be a browser bug, def worth investigating #[derive(Debug)] pub struct Flood { pub flood_color: Color, -- 2.46.0 From ae86ae29ab663f365a290034c685e221528c28f9 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 24 Mar 2024 17:07:35 +0100 Subject: [PATCH 21/67] svg-filters: more testssss --- crates/svg-filters/src/tests.rs | 49 ++----------------- .../src/tests/component_transfer.rs | 36 ++++++++++++++ .../svg-filters/src/tests/displacement_map.rs | 32 ++++++++++++ crates/svg-filters/src/tests/flood.rs | 17 +++++++ crates/svg-filters/src/tests/turbulence.rs | 25 ++++++++++ .../svg-filters/src/types/nodes/primitives.rs | 18 +++---- 6 files changed, 124 insertions(+), 53 deletions(-) create mode 100644 crates/svg-filters/src/tests/component_transfer.rs create mode 100644 crates/svg-filters/src/tests/displacement_map.rs create mode 100644 crates/svg-filters/src/tests/flood.rs create mode 100644 crates/svg-filters/src/tests/turbulence.rs diff --git a/crates/svg-filters/src/tests.rs b/crates/svg-filters/src/tests.rs index 771ab3a..742c457 100644 --- a/crates/svg-filters/src/tests.rs +++ b/crates/svg-filters/src/tests.rs @@ -1,56 +1,17 @@ -mod color_matrix; - mod blend; +mod color_matrix; mod complex; +mod component_transfer; +mod displacement_map; +mod flood; mod gaussian_blur; mod offset; - -mod component_transfer { - use crate::{ - codegen::SvgDocument, - types::nodes::primitives::{ - component_transfer::{ComponentTransfer, TransferFn}, - FePrimitive, - }, - Node, - }; - - #[test] - fn test_comp_trans_simple() { - let mut doc = SvgDocument::new(); - - let comptrans = doc.create_filter("comp_trans"); - - comptrans.add_node(Node::simple(FePrimitive::ComponentTransfer( - ComponentTransfer { - func_r: TransferFn::Table { - table_values: vec![0., 0.1, 0.4, 0.9], - }, - func_g: TransferFn::Discrete { - table_values: vec![0.1, 0.3, 0.5, 0.7, 0.9], - }, - func_b: TransferFn::Linear { - slope: 1.0, - intercept: 0.75, - }, - func_a: TransferFn::Identity, - }, - ))); - - assert_eq!( - doc.generate_svg(), - r#""# - ); - } -} +mod turbulence; mod composite {} mod convolve_matrix {} mod diffuse_lighting {} -mod displacement_map {} -mod flood {} mod image {} mod merge {} mod morphology {} mod specular_lighting {} mod tile {} -mod turbulence {} diff --git a/crates/svg-filters/src/tests/component_transfer.rs b/crates/svg-filters/src/tests/component_transfer.rs new file mode 100644 index 0000000..8f24c6f --- /dev/null +++ b/crates/svg-filters/src/tests/component_transfer.rs @@ -0,0 +1,36 @@ +use crate::{ + codegen::SvgDocument, + types::nodes::primitives::{ + component_transfer::{ComponentTransfer, TransferFn}, + FePrimitive, + }, + Node, +}; + +#[test] +fn test_comp_trans_simple() { + let mut doc = SvgDocument::new(); + + let comptrans = doc.create_filter("comp_trans"); + + comptrans.add_node(Node::simple(FePrimitive::ComponentTransfer( + ComponentTransfer { + func_r: TransferFn::Table { + table_values: vec![0., 0.1, 0.4, 0.9], + }, + func_g: TransferFn::Discrete { + table_values: vec![0.1, 0.3, 0.5, 0.7, 0.9], + }, + func_b: TransferFn::Linear { + slope: 1.0, + intercept: 0.75, + }, + func_a: TransferFn::Identity, + }, + ))); + + assert_eq!( + doc.generate_svg(), + r#""# + ); +} diff --git a/crates/svg-filters/src/tests/displacement_map.rs b/crates/svg-filters/src/tests/displacement_map.rs new file mode 100644 index 0000000..3b41ff1 --- /dev/null +++ b/crates/svg-filters/src/tests/displacement_map.rs @@ -0,0 +1,32 @@ +use crate::{ + codegen::SvgDocument, + types::nodes::{ + primitives::{ + displacement_map::Channel, + turbulence::{NoiseType, StitchTiles}, + }, + standard_input::StandardInput, + }, +}; + +#[test] +fn test_displacement_map_simple() { + let mut doc = SvgDocument::new(); + + let displace = doc.create_filter("displace"); + + let simple_noise = + displace.turbulence(0.01, 0.01, 1, 0, StitchTiles::Stitch, NoiseType::Turbulence); + displace.displacement_map( + StandardInput::SourceGraphic, + simple_noise, + 128., + Channel::R, + Channel::R, + ); + + assert_eq!( + doc.generate_svg(), + r#""# + ); +} diff --git a/crates/svg-filters/src/tests/flood.rs b/crates/svg-filters/src/tests/flood.rs new file mode 100644 index 0000000..27ed8bc --- /dev/null +++ b/crates/svg-filters/src/tests/flood.rs @@ -0,0 +1,17 @@ +use csscolorparser::Color; + +use crate::codegen::SvgDocument; + +#[test] +fn test_flood_simple() { + let mut doc = SvgDocument::new(); + + let turbdispl = doc.create_filter("noiseDisplace"); + + turbdispl.flood(Color::new(0.9, 0.7, 0.85, 1.), 1.); + + assert_eq!( + doc.generate_svg(), + r##""## + ); +} diff --git a/crates/svg-filters/src/tests/turbulence.rs b/crates/svg-filters/src/tests/turbulence.rs new file mode 100644 index 0000000..dd53e7d --- /dev/null +++ b/crates/svg-filters/src/tests/turbulence.rs @@ -0,0 +1,25 @@ +use crate::{ + codegen::SvgDocument, + types::nodes::primitives::turbulence::{NoiseType, StitchTiles}, +}; + +#[test] +fn test_simple_turbulence() { + let mut doc = SvgDocument::new(); + + let noise = doc.create_filter("noise"); + + noise.turbulence( + 0.01, + 0.01, + 1, + 0, + StitchTiles::Stitch, + NoiseType::FractalNoise, + ); + + assert_eq!( + doc.generate_svg(), + r#""# + ); +} diff --git a/crates/svg-filters/src/types/nodes/primitives.rs b/crates/svg-filters/src/types/nodes/primitives.rs index 171314f..4675b8e 100644 --- a/crates/svg-filters/src/types/nodes/primitives.rs +++ b/crates/svg-filters/src/types/nodes/primitives.rs @@ -88,13 +88,13 @@ impl WriteElement for FePrimitive { FePrimitive::GaussianBlur(el) => el.attrs(), FePrimitive::Offset(el) => el.attrs(), FePrimitive::Turbulence(el) => el.attrs(), - FePrimitive::ConvolveMatrix(_) => todo!(), - FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(el) => el.attrs(), FePrimitive::Flood(el) => el.attrs(), + FePrimitive::Morphology(el) => el.attrs(), + FePrimitive::ConvolveMatrix(_) => todo!(), + FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), - FePrimitive::Morphology(_) => todo!(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), } @@ -109,13 +109,13 @@ impl WriteElement for FePrimitive { FePrimitive::GaussianBlur(el) => el.tag_name(), FePrimitive::Offset(el) => el.tag_name(), FePrimitive::Turbulence(el) => el.tag_name(), - FePrimitive::ConvolveMatrix(_) => todo!(), - FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(el) => el.tag_name(), FePrimitive::Flood(el) => el.tag_name(), + FePrimitive::Morphology(el) => el.tag_name(), + FePrimitive::ConvolveMatrix(_) => todo!(), + FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), - FePrimitive::Morphology(_) => todo!(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), } @@ -136,13 +136,13 @@ impl WriteElement for FePrimitive { FePrimitive::Turbulence(el) => el.element_writer(writer, common, inputs, output), FePrimitive::GaussianBlur(el) => el.element_writer(writer, common, inputs, output), FePrimitive::Offset(el) => el.element_writer(writer, common, inputs, output), - FePrimitive::ConvolveMatrix(_) => todo!(), - FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::DisplacementMap(el) => el.element_writer(writer, common, inputs, output), FePrimitive::Flood(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::Morphology(el) => el.element_writer(writer, common, inputs, output), + FePrimitive::ConvolveMatrix(_) => todo!(), + FePrimitive::DiffuseLighting(_) => todo!(), FePrimitive::Image(_) => todo!(), FePrimitive::Merge(_) => todo!(), - FePrimitive::Morphology(_) => todo!(), FePrimitive::SpecularLighting(_) => todo!(), FePrimitive::Tile(_) => todo!(), } -- 2.46.0 From ca84af4e1bd6b22e7e32c3f3864c4f2618c94a15 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 3 Apr 2024 00:08:00 +0200 Subject: [PATCH 22/67] lang: basic parser --- Cargo.lock | 153 ++++++++++++++++++++++++++++++++- Cargo.toml | 1 + crates/lang/Cargo.toml | 4 + crates/lang/src/lib.rs | 4 + crates/lang/src/main.rs | 17 ++++ crates/lang/src/parser.rs | 134 +++++++++++++++++++++++++++++ crates/lang/src/parser/ast.rs | 36 ++++++++ crates/lang/src/tokens.rs | 11 ++- crates/svg-filters/Cargo.toml | 2 +- crates/svg-filters/src/main.rs | 29 +------ testfiles/test.owo | 4 + 11 files changed, 362 insertions(+), 33 deletions(-) create mode 100644 crates/lang/src/main.rs create mode 100644 crates/lang/src/parser.rs create mode 100644 crates/lang/src/parser/ast.rs create mode 100644 testfiles/test.owo diff --git a/Cargo.lock b/Cargo.lock index a8b1f50..bc8692d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,33 @@ 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 = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "anstream" version = "0.6.5" @@ -133,12 +160,31 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + [[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c28d4e5dd9a9262a38b231153591da6ce1471b818233f4727985d3dd0ed93c" +dependencies = [ + "hashbrown", + "regex-automata", + "serde", + "stacker", + "unicode-ident", +] + [[package]] name = "clap" version = "4.4.12" @@ -389,6 +435,10 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -417,9 +467,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -453,7 +503,11 @@ dependencies = [ name = "lang" version = "0.1.0" dependencies = [ + "chumsky", + "clap", + "indexmap", "logos", + "petgraph", ] [[package]] @@ -515,7 +569,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "regex-syntax", + "regex-syntax 0.8.2", "syn", ] @@ -583,6 +637,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "option-ext" version = "0.2.0" @@ -675,6 +735,15 @@ 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 = "qoi" version = "0.4.1" @@ -758,6 +827,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex-automata" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "regex-syntax" version = "0.8.2" @@ -846,6 +932,19 @@ dependencies = [ "lock_api", ] +[[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" @@ -942,6 +1041,12 @@ 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 = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -954,6 +1059,28 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +[[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" @@ -1092,6 +1219,26 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zune-inflate" version = "0.2.54" diff --git a/Cargo.toml b/Cargo.toml index cbd514c..2d4a618 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ resolver = "2" [workspace.dependencies] clap = { version = "4", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } +petgraph = "0.6.4" # to enable all the lints below, this must be present in a workspace member's Cargo.toml: # [lints] diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 1182341..8126ffa 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -7,6 +7,10 @@ edition = "2021" [dependencies] logos = "0.14" +chumsky = "1.0.0-alpha.6" +petgraph = { workspace = true} +indexmap = "2.2.6" +clap = { version = "4", features = ["derive"] } [lints] workspace = true diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index 5c76635..6878991 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -1 +1,5 @@ +pub mod parser; pub mod tokens; +pub mod err_reporting { + pub struct GlobalReporter {} +} diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs new file mode 100644 index 0000000..3fef5ac --- /dev/null +++ b/crates/lang/src/main.rs @@ -0,0 +1,17 @@ +use std::{fs, path::PathBuf}; + +use clap::Parser; +use lang::parser::parse; + +#[derive(Parser)] +struct Args { + file: PathBuf, +} + +fn main() { + let args = Args::parse(); + let f = fs::read_to_string(args.file).expect("failed to read file"); + + println!("file: {f}\n"); + println!("parsed: {:?}", parse(&f)) +} diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs new file mode 100644 index 0000000..b1034fa --- /dev/null +++ b/crates/lang/src/parser.rs @@ -0,0 +1,134 @@ +use std::ops::Range; + +use chumsky::{ + error::Rich, + extra, + input::{Stream, ValueInput}, + prelude::*, + primitive::just, + recursive::recursive, + select, + span::SimpleSpan, + IterParser, Parser, +}; +use indexmap::IndexMap; +use logos::{Logos, Source}; + +use crate::tokens::Token; + +pub mod ast; +use self::ast::{Expr, File}; + +type Span = SimpleSpan; +type Spanned = (T, Span); + +pub fn parse<'src>(src: &'src str) -> ParseResult, Rich<'_, Token<'_>>> { + let toks: Vec<_> = Token::lexer(src) + .spanned() + .into_iter() + .map(|(t, s)| (t.expect("TODO: add lexer error(s)"), Span::from(s))) + .collect(); + let tok_stream = Stream::from_iter(toks).spanned((src.len()..src.len()).into()); + expr_parser().parse(tok_stream) +} + +fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src>, Span = Span>>( +) -> impl Parser<'tokens, I, File<'src>, extra::Err, Span>>> { + let word = select! { Token::Word(word) => word }; + + let expr = recursive(|expr| { + let var = select! { + Token::VarIdent(name) => (Expr::Var as fn(_) -> _, name), + Token::InputIdent(name) => (Expr::InputVar as fn(_) -> _, name) + } + .map_with(|(item_type, name), extra| item_type((name, extra.span()))); + + let attrset = word + .map_with(|n, e| (n, e.span())) + .then_ignore(just(Token::Colon)) + .then(expr) + .separated_by(just(Token::Comma)) + .collect::>() + .map(IndexMap::from_iter) + .delimited_by(just(Token::BracketOpen), just(Token::BracketClose)) + .map_with(|v, e| (v, e.span())); + + let node = word + .map_with(|v, e| (v, e.span())) + .then(attrset.clone().or_not()) + .map(|(name, params)| Expr::Node(name, params)) + .or(var) + .or(attrset.map(Expr::AttrSet)); + + let pipeline = node + .clone() + .then(choice(( + just(Token::Pipe).to(Expr::SimplePipe as fn(_, _) -> _), + just(Token::MappingPipe).to(Expr::MappingPipe as fn(_, _) -> _), + just(Token::NullPipe).to(Expr::NullPipe as fn(_, _) -> _), + ))) + .repeated() + .foldr(node, |(curr, pipe), next| { + pipe(Box::new(curr), Box::new(next)) + }); + + pipeline + }); + + let decl = just(Token::Def).ignore_then( + word.map_with(|n, e| (n, e.span())) + .then_ignore(just(Token::Equals)) + .then(expr.clone().map_with(|expr, extra| (expr, extra.span()))) + .then_ignore(just(Token::SemiColon)), + ); + + expr.map_with(|expr, extra| File { + decls: IndexMap::from_iter([(("main", (0..0).into()), (expr, extra.span()))]), + }) + .or(decl.repeated().collect::>().map(|decls| File { + decls: IndexMap::from_iter(decls), + })) +} + +#[cfg(test)] +mod tests { + use crate::parser::ast::{Expr, File}; + use crate::parser::parse; + use crate::tokens::Token; + use chumsky::input::Stream; + use chumsky::prelude::*; + use indexmap::IndexMap; + use logos::Logos; + + #[test] + fn test_parse_node_with_params() { + const INPUT: &str = "meow [ hello: $foo, world: @bar]"; + assert_eq!( + parse(INPUT).unwrap(), + File { + decls: IndexMap::from_iter([( + ("main", (0..0).into()), + ( + Expr::Node( + ("meow", (0..4).into()), + Some(( + IndexMap::from_iter([ + ( + ("hello", (7..12).into()), + Expr::Var(("foo", (14..18).into())) + ), + ( + ("world", (20..25).into()), + Expr::InputVar(("bar", (27..31).into())) + ) + ]), + (5..32).into() + )) + ), + (0..0).into() + ) + )]) + } + ); + } +} diff --git a/crates/lang/src/parser/ast.rs b/crates/lang/src/parser/ast.rs new file mode 100644 index 0000000..a276a75 --- /dev/null +++ b/crates/lang/src/parser/ast.rs @@ -0,0 +1,36 @@ +use std::collections::{BTreeMap, HashMap}; + +use indexmap::IndexMap; + +use super::Spanned; + +#[derive(Debug, PartialEq, Eq)] +pub struct File<'src> { + pub decls: IndexMap, Spanned>>, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Expr<'src> { + Node( + Spanned<&'src str>, + Option, Expr<'src>>>>, + ), + SimplePipe(Box>, Box>), + NamingPipe( + Box>, + (Vec>, Vec>), + Box>, + ), + MappingPipe(Box>, Box>), + NullPipe(Box>, Box>), + MultiPipe(IndexMap, Expr<'src>>), + LetIn( + IndexMap, Box>>, + Box>, + ), + // $ + Var(Spanned<&'src str>), + // @ + InputVar(Spanned<&'src str>), + AttrSet(Spanned, Expr<'src>>>), +} diff --git a/crates/lang/src/tokens.rs b/crates/lang/src/tokens.rs index e21b961..40280e7 100644 --- a/crates/lang/src/tokens.rs +++ b/crates/lang/src/tokens.rs @@ -1,8 +1,15 @@ use logos::Logos; -#[derive(Logos, Debug, PartialEq, Eq)] +#[derive(Logos, Debug, PartialEq, Eq, Clone)] #[logos(skip r"[ \t\n\f]+")] pub enum Token<'a> { + // hack! + // this isn't actually supposed to be in the language. + // i just can't figure out how to automatically choose between a top level declaration + // or a top level expression + // so a declaration needs the keyword def until i can figure this out + #[token("def")] + Def, #[regex("[a-zA-Z0-9_\\-]+", |lex| lex.slice())] Word(&'a str), #[regex("\\$[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] @@ -27,6 +34,8 @@ pub enum Token<'a> { Equals, #[token(":")] Colon, + #[token(";")] + SemiColon, #[token("[")] BracketOpen, #[token("]")] diff --git a/crates/svg-filters/Cargo.toml b/crates/svg-filters/Cargo.toml index fbe3aaf..7f41e66 100644 --- a/crates/svg-filters/Cargo.toml +++ b/crates/svg-filters/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] csscolorparser = "0.6.2" indexmap = "2.2.5" -petgraph = "0.6.4" +petgraph = { workspace = true } quick-xml = { version = "0.31.0", features = ["serialize"] } [lints] diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index 2fc8df3..5ce0102 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -1,35 +1,8 @@ -use std::hint::black_box; - -use svg_filters::{ - codegen::SvgDocument, - types::{ - graph::edge::Edge, - nodes::{ - primitives::{ - blend::BlendMode, - color_matrix::ColorMatrixType, - turbulence::{NoiseType, StitchTiles, Turbulence}, - FePrimitive, - }, - standard_input::StandardInput, - }, - }, - Node, -}; +use svg_filters::codegen::SvgDocument; fn main() { let mut doc = SvgDocument::new(); - let noise = doc.create_filter("noise"); - - noise.add_node(Node::simple(FePrimitive::Turbulence(Turbulence { - base_frequency: (0.2, 0.2), - num_octaves: 1, - seed: 2, - stitch_tiles: StitchTiles::NoStitch, - noise_type: NoiseType::FractalNoise, - }))); - eprintln!("{}", doc.generate_svg_pretty()); println!("{}", doc.generate_svg()); } diff --git a/testfiles/test.owo b/testfiles/test.owo new file mode 100644 index 0000000..d9d05da --- /dev/null +++ b/testfiles/test.owo @@ -0,0 +1,4 @@ +def main = meow | test; +def test = meow [ hello: $foo, world: @bar]; + +def uwu = owo; -- 2.46.0 From de008263ca37ca78018520b1e2a674092f97493c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 3 Apr 2024 00:28:45 +0200 Subject: [PATCH 23/67] lang: add test --- crates/lang/src/parser.rs | 109 +++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index b1034fa..10fa46e 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -125,10 +125,117 @@ mod tests { (5..32).into() )) ), - (0..0).into() + (0..32).into() ) )]) } ); } + + fn test_parse_multiple_top_level_complex() { + const INPUT: &str = r#"def main = meow + | uwu + [ foo: @bar + , hello: world @| test [ more: params ] | yay + ] + !| awa + @| nya + | rawr; + +def test = meow + [ hello: $foo + , world: @bar + ]; +"#; + assert_eq!( + parse(INPUT).unwrap(), + File { + decls: IndexMap::from_iter([ + ( + ("main", (4..8).into()), + ( + Expr::SimplePipe( + Box::new(Expr::Node(("meow", (11..15).into()), None)), + Box::new(Expr::NullPipe( + Box::new(Expr::Node( + ("uwu", (20..23).into()), + Some(( + IndexMap::from_iter([ + ( + ("foo", (29..32).into()), + Expr::InputVar(("bar", (34..38).into())) + ), + ( + ("hello", (44..49).into()), + Expr::MappingPipe( + Box::new(Expr::Node( + ("world", (51..56).into()), + None + )), + Box::new(Expr::SimplePipe( + Box::new(Expr::Node( + ("test", (60..64).into()), + Some(( + IndexMap::from_iter([( + ("more", (67..71).into()), + Expr::Node( + ( + "params", + (73..79).into() + ), + None + ) + )]), + (65..81).into() + )) + )), + Box::new(Expr::Node( + ("yay", (84..87).into()), + None + )) + )) + ) + ) + ]), + (27..92).into() + )) + )), + Box::new(Expr::MappingPipe( + Box::new(Expr::Node(("awa", (97..100).into()), None)), + Box::new(Expr::SimplePipe( + Box::new(Expr::Node(("nya", (106..109).into()), None)), + Box::new(Expr::Node(("rawr", (114..118).into()), None)) + )) + )) + )) + ), + (11..118).into() + ), + ), + ( + ("test", (125..129).into()), + ( + Expr::Node( + ("meow", (132..136).into()), + Some(( + IndexMap::from_iter([ + ( + ("hello", (141..146).into()), + Expr::Var(("foo", (148..152).into())) + ), + ( + ("world", (156..161).into()), + Expr::InputVar(("bar", (163..167).into())) + ) + ]), + (139..171).into() + )) + ), + (132..171).into() + ) + ) + ]) + } + ); + } } -- 2.46.0 From ae60db77216343d7f259a733ccd2c1e53f95dd0c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 3 Apr 2024 00:30:11 +0200 Subject: [PATCH 24/67] lang: extract tests into file --- crates/lang/src/parser.rs | 152 +------------------------------- crates/lang/src/parser/tests.rs | 143 ++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 150 deletions(-) create mode 100644 crates/lang/src/parser/tests.rs diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 10fa46e..9641dbb 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -17,6 +17,8 @@ use logos::{Logos, Source}; use crate::tokens::Token; pub mod ast; +#[cfg(test)] +mod tests; use self::ast::{Expr, File}; type Span = SimpleSpan; @@ -89,153 +91,3 @@ fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src decls: IndexMap::from_iter(decls), })) } - -#[cfg(test)] -mod tests { - use crate::parser::ast::{Expr, File}; - use crate::parser::parse; - use crate::tokens::Token; - use chumsky::input::Stream; - use chumsky::prelude::*; - use indexmap::IndexMap; - use logos::Logos; - - #[test] - fn test_parse_node_with_params() { - const INPUT: &str = "meow [ hello: $foo, world: @bar]"; - assert_eq!( - parse(INPUT).unwrap(), - File { - decls: IndexMap::from_iter([( - ("main", (0..0).into()), - ( - Expr::Node( - ("meow", (0..4).into()), - Some(( - IndexMap::from_iter([ - ( - ("hello", (7..12).into()), - Expr::Var(("foo", (14..18).into())) - ), - ( - ("world", (20..25).into()), - Expr::InputVar(("bar", (27..31).into())) - ) - ]), - (5..32).into() - )) - ), - (0..32).into() - ) - )]) - } - ); - } - - fn test_parse_multiple_top_level_complex() { - const INPUT: &str = r#"def main = meow - | uwu - [ foo: @bar - , hello: world @| test [ more: params ] | yay - ] - !| awa - @| nya - | rawr; - -def test = meow - [ hello: $foo - , world: @bar - ]; -"#; - assert_eq!( - parse(INPUT).unwrap(), - File { - decls: IndexMap::from_iter([ - ( - ("main", (4..8).into()), - ( - Expr::SimplePipe( - Box::new(Expr::Node(("meow", (11..15).into()), None)), - Box::new(Expr::NullPipe( - Box::new(Expr::Node( - ("uwu", (20..23).into()), - Some(( - IndexMap::from_iter([ - ( - ("foo", (29..32).into()), - Expr::InputVar(("bar", (34..38).into())) - ), - ( - ("hello", (44..49).into()), - Expr::MappingPipe( - Box::new(Expr::Node( - ("world", (51..56).into()), - None - )), - Box::new(Expr::SimplePipe( - Box::new(Expr::Node( - ("test", (60..64).into()), - Some(( - IndexMap::from_iter([( - ("more", (67..71).into()), - Expr::Node( - ( - "params", - (73..79).into() - ), - None - ) - )]), - (65..81).into() - )) - )), - Box::new(Expr::Node( - ("yay", (84..87).into()), - None - )) - )) - ) - ) - ]), - (27..92).into() - )) - )), - Box::new(Expr::MappingPipe( - Box::new(Expr::Node(("awa", (97..100).into()), None)), - Box::new(Expr::SimplePipe( - Box::new(Expr::Node(("nya", (106..109).into()), None)), - Box::new(Expr::Node(("rawr", (114..118).into()), None)) - )) - )) - )) - ), - (11..118).into() - ), - ), - ( - ("test", (125..129).into()), - ( - Expr::Node( - ("meow", (132..136).into()), - Some(( - IndexMap::from_iter([ - ( - ("hello", (141..146).into()), - Expr::Var(("foo", (148..152).into())) - ), - ( - ("world", (156..161).into()), - Expr::InputVar(("bar", (163..167).into())) - ) - ]), - (139..171).into() - )) - ), - (132..171).into() - ) - ) - ]) - } - ); - } -} diff --git a/crates/lang/src/parser/tests.rs b/crates/lang/src/parser/tests.rs new file mode 100644 index 0000000..308b09b --- /dev/null +++ b/crates/lang/src/parser/tests.rs @@ -0,0 +1,143 @@ +use crate::parser::ast::{Expr, File}; +use crate::parser::parse; +use crate::tokens::Token; +use chumsky::input::Stream; +use chumsky::prelude::*; +use indexmap::IndexMap; +use logos::Logos; + +#[test] +fn test_parse_node_with_params() { + const INPUT: &str = "meow [ hello: $foo, world: @bar]"; + assert_eq!( + parse(INPUT).unwrap(), + File { + decls: IndexMap::from_iter([( + ("main", (0..0).into()), + ( + Expr::Node( + ("meow", (0..4).into()), + Some(( + IndexMap::from_iter([ + ( + ("hello", (7..12).into()), + Expr::Var(("foo", (14..18).into())) + ), + ( + ("world", (20..25).into()), + Expr::InputVar(("bar", (27..31).into())) + ) + ]), + (5..32).into() + )) + ), + (0..32).into() + ) + )]) + } + ); +} + +fn test_parse_multiple_top_level_complex() { + const INPUT: &str = r#"def main = meow + | uwu + [ foo: @bar + , hello: world @| test [ more: params ] | yay + ] + !| awa + @| nya + | rawr; + +def test = meow + [ hello: $foo + , world: @bar + ]; +"#; + assert_eq!( + parse(INPUT).unwrap(), + File { + decls: IndexMap::from_iter([ + ( + ("main", (4..8).into()), + ( + Expr::SimplePipe( + Box::new(Expr::Node(("meow", (11..15).into()), None)), + Box::new(Expr::NullPipe( + Box::new(Expr::Node( + ("uwu", (20..23).into()), + Some(( + IndexMap::from_iter([ + ( + ("foo", (29..32).into()), + Expr::InputVar(("bar", (34..38).into())) + ), + ( + ("hello", (44..49).into()), + Expr::MappingPipe( + Box::new(Expr::Node( + ("world", (51..56).into()), + None + )), + Box::new(Expr::SimplePipe( + Box::new(Expr::Node( + ("test", (60..64).into()), + Some(( + IndexMap::from_iter([( + ("more", (67..71).into()), + Expr::Node( + ("params", (73..79).into()), + None + ) + )]), + (65..81).into() + )) + )), + Box::new(Expr::Node( + ("yay", (84..87).into()), + None + )) + )) + ) + ) + ]), + (27..92).into() + )) + )), + Box::new(Expr::MappingPipe( + Box::new(Expr::Node(("awa", (97..100).into()), None)), + Box::new(Expr::SimplePipe( + Box::new(Expr::Node(("nya", (106..109).into()), None)), + Box::new(Expr::Node(("rawr", (114..118).into()), None)) + )) + )) + )) + ), + (11..118).into() + ), + ), + ( + ("test", (125..129).into()), + ( + Expr::Node( + ("meow", (132..136).into()), + Some(( + IndexMap::from_iter([ + ( + ("hello", (141..146).into()), + Expr::Var(("foo", (148..152).into())) + ), + ( + ("world", (156..161).into()), + Expr::InputVar(("bar", (163..167).into())) + ) + ]), + (139..171).into() + )) + ), + (132..171).into() + ) + ) + ]) + } + ); +} -- 2.46.0 From 84448af7149c083812ea81f861fd4c67b79b0b3d Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 3 Apr 2024 17:00:20 +0200 Subject: [PATCH 25/67] lang: funky basic error reporting stuff --- Cargo.lock | 1 + crates/lang/Cargo.toml | 3 +- crates/lang/src/err_reporting.rs | 87 ++++++++++++++++++++++++++++++++ crates/lang/src/lib.rs | 4 +- crates/lang/src/main.rs | 21 ++++++-- crates/lang/src/parser.rs | 15 ++++-- crates/lang/src/parser/tests.rs | 4 +- testfiles/test.owo | 5 +- 8 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 crates/lang/src/err_reporting.rs diff --git a/Cargo.lock b/Cargo.lock index bc8692d..e762ead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -503,6 +503,7 @@ dependencies = [ name = "lang" version = "0.1.0" dependencies = [ + "ariadne", "chumsky", "clap", "indexmap", diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 8126ffa..1cd8654 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -7,10 +7,11 @@ edition = "2021" [dependencies] logos = "0.14" -chumsky = "1.0.0-alpha.6" +chumsky = {version= "1.0.0-alpha.6", features=["label"]} petgraph = { workspace = true} indexmap = "2.2.6" clap = { version = "4", features = ["derive"] } +ariadne = "0.4.0" [lints] workspace = true diff --git a/crates/lang/src/err_reporting.rs b/crates/lang/src/err_reporting.rs new file mode 100644 index 0000000..ee66b7a --- /dev/null +++ b/crates/lang/src/err_reporting.rs @@ -0,0 +1,87 @@ +use std::{collections::HashMap, fs}; + +use ariadne::{sources, Label, Report, Source}; +use chumsky::{ + error::{self, Rich}, + ParseResult, +}; +use indexmap::IndexMap; + +use crate::{ + parser::{ast::File, Span}, + tokens::Token, +}; + +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum Stage { + Lex, + Parse, +} + +impl Stage { + fn variants() -> [Stage; 2] { + [Stage::Lex, Stage::Parse] + } +} + +pub struct ErrorCollector<'filename, 'tokens, 'src> { + files: HashMap<&'filename str, &'src str>, + raw_errors: IndexMap<(&'filename str, Stage), Vec, Span>>>, +} + +impl<'filename, 'tokens, 'src> ErrorCollector<'filename, 'tokens, 'src> { + pub fn new(files: Vec<(&'filename str, &'src str)>) -> Self { + Self { + files: HashMap::from_iter(files.clone()), + raw_errors: files + .iter() + .flat_map(|(name, _)| Stage::variants().map(|s| (name, s))) + .map(|(name, stage)| ((*name, stage), Vec::new())) + .collect(), + } + } + + pub fn insert_many( + &mut self, + file: &'filename str, + curr_stage: Stage, + mut errs: Vec, Span>>, + ) { + let err_vec = self + .raw_errors + .get_mut(&(file, curr_stage)) + .expect("filename should exist"); + err_vec.append(&mut errs); + } + + pub fn analyze_and_report(self) { + let ErrorCollector { files, raw_errors } = self; + todo!() + } + + pub fn report_raw(self) { + let ErrorCollector { files, raw_errors } = self; + + for ((file, stage), errs) in raw_errors.into_iter() { + for err in errs { + Report::build(ariadne::ReportKind::Error, file, err.span().start) + .with_message(format!("error at stage {stage:?}, {:?}", err.reason())) + .with_label( + Label::new((file, err.span().into_range())).with_message(format!( + "found: {:?}", + err.found().expect("errors should have a reason") + )), + ) + .with_help(format!( + "expected: {:?}", + err.expected().collect::>() + )) + .finish() + .print((file, Source::from(files[file]))); + } + } + } +} + +#[derive(Debug, PartialEq, Eq)] +struct Loc<'filename>(&'filename str, Span); diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index 6878991..94a1430 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -1,5 +1,3 @@ +pub mod err_reporting; pub mod parser; pub mod tokens; -pub mod err_reporting { - pub struct GlobalReporter {} -} diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 3fef5ac..3e70973 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -1,17 +1,32 @@ use std::{fs, path::PathBuf}; use clap::Parser; -use lang::parser::parse; +use lang::{err_reporting::ErrorCollector, parser::parse}; #[derive(Parser)] struct Args { file: PathBuf, } +#[allow(clippy::unwrap_used)] fn main() { let args = Args::parse(); - let f = fs::read_to_string(args.file).expect("failed to read file"); + let n = args.file.clone(); + let f = fs::read_to_string(n.clone()).expect("failed to read file"); + let mut err_collector = ErrorCollector::new(vec![(n.to_str().unwrap(), &f)]); println!("file: {f}\n"); - println!("parsed: {:?}", parse(&f)) + let parse_res = parse(&f); + err_collector.insert_many( + args.file.to_str().unwrap(), + lang::err_reporting::Stage::Parse, + parse_res + .errors() + .into_iter() + .map(|e| e.to_owned()) + .collect::>(), + ); + + err_collector.report_raw(); + println!("res: {:?}", parse_res); } diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 9641dbb..aae1fbb 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -21,8 +21,8 @@ pub mod ast; mod tests; use self::ast::{Expr, File}; -type Span = SimpleSpan; -type Spanned = (T, Span); +pub type Span = SimpleSpan; +pub type Spanned = (T, Span); pub fn parse<'src>(src: &'src str) -> ParseResult, Rich<'_, Token<'_>>> { let toks: Vec<_> = Token::lexer(src) @@ -43,24 +43,29 @@ fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src Token::VarIdent(name) => (Expr::Var as fn(_) -> _, name), Token::InputIdent(name) => (Expr::InputVar as fn(_) -> _, name) } - .map_with(|(item_type, name), extra| item_type((name, extra.span()))); + .map_with(|(item_type, name), extra| item_type((name, extra.span()))) + .labelled("variable"); let attrset = word .map_with(|n, e| (n, e.span())) + .labelled("attr name") .then_ignore(just(Token::Colon)) .then(expr) + .labelled("attr body") .separated_by(just(Token::Comma)) .collect::>() .map(IndexMap::from_iter) .delimited_by(just(Token::BracketOpen), just(Token::BracketClose)) - .map_with(|v, e| (v, e.span())); + .map_with(|v, e| (v, e.span())) + .labelled("attrset"); let node = word .map_with(|v, e| (v, e.span())) .then(attrset.clone().or_not()) .map(|(name, params)| Expr::Node(name, params)) .or(var) - .or(attrset.map(Expr::AttrSet)); + .or(attrset.map(Expr::AttrSet)) + .labelled("node"); let pipeline = node .clone() diff --git a/crates/lang/src/parser/tests.rs b/crates/lang/src/parser/tests.rs index 308b09b..f8fbdc0 100644 --- a/crates/lang/src/parser/tests.rs +++ b/crates/lang/src/parser/tests.rs @@ -39,7 +39,7 @@ fn test_parse_node_with_params() { } fn test_parse_multiple_top_level_complex() { - const INPUT: &str = r#"def main = meow + const INPUT: &str = r"def main = meow | uwu [ foo: @bar , hello: world @| test [ more: params ] | yay @@ -52,7 +52,7 @@ def test = meow [ hello: $foo , world: @bar ]; -"#; +"; assert_eq!( parse(INPUT).unwrap(), File { diff --git a/testfiles/test.owo b/testfiles/test.owo index d9d05da..d35cae5 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,4 +1 @@ -def main = meow | test; -def test = meow [ hello: $foo, world: @bar]; - -def uwu = owo; +meow gay | uwu -- 2.46.0 From ace69b0094e454ff3617300d17cb2b7ef6870e96 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 3 Apr 2024 20:08:33 +0200 Subject: [PATCH 26/67] svg-filters: format matrices in complex test --- crates/svg-filters/src/tests/complex.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/svg-filters/src/tests/complex.rs b/crates/svg-filters/src/tests/complex.rs index 9b28304..2936bb4 100644 --- a/crates/svg-filters/src/tests/complex.rs +++ b/crates/svg-filters/src/tests/complex.rs @@ -11,7 +11,10 @@ fn test_chrom_abb() { let chan_r = chromabb.color_matrix( StandardInput::SourceGraphic, ColorMatrixType::Matrix(Box::new([ - 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + 1., 0., 0., 0., 0., // + 0., 0., 0., 0., 0., // + 0., 0., 0., 0., 0., // + 0., 0., 0., 1., 0., ])), ); let offset_r = chromabb.offset(chan_r, 25., 0.); @@ -20,7 +23,10 @@ fn test_chrom_abb() { let chan_b = chromabb.color_matrix( StandardInput::SourceGraphic, ColorMatrixType::Matrix(Box::new([ - 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., + 0., 0., 0., 0., 0., // + 0., 0., 0., 0., 0., // + 0., 0., 1., 0., 0., // + 0., 0., 0., 1., 0., ])), ); let offset_b = chromabb.offset(chan_b, -25., 0.); @@ -31,7 +37,10 @@ fn test_chrom_abb() { let chan_g = chromabb.color_matrix( StandardInput::SourceGraphic, ColorMatrixType::Matrix(Box::new([ - 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., + 0., 0., 0., 0., 0., // + 0., 1., 0., 0., 0., // + 0., 0., 0., 0., 0., // + 0., 0., 0., 1., 0., ])), ); chromabb.composite_arithmetic(composite_rb, chan_g, 0., 1., 1., 0.); -- 2.46.0 From b6e304fa784a2b331769456bf98b1f183adc9546 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 7 Apr 2024 00:55:12 +0200 Subject: [PATCH 27/67] lang: rework ast structure --- crates/lang/src/err_reporting.rs | 1 + crates/lang/src/parser.rs | 47 ++++-- crates/lang/src/parser/ast.rs | 40 +++-- crates/lang/src/parser/tests.rs | 266 +++++++++++++++---------------- testfiles/test.owo | 19 ++- 5 files changed, 209 insertions(+), 164 deletions(-) diff --git a/crates/lang/src/err_reporting.rs b/crates/lang/src/err_reporting.rs index ee66b7a..ebf7866 100644 --- a/crates/lang/src/err_reporting.rs +++ b/crates/lang/src/err_reporting.rs @@ -64,6 +64,7 @@ impl<'filename, 'tokens, 'src> ErrorCollector<'filename, 'tokens, 'src> { for ((file, stage), errs) in raw_errors.into_iter() { for err in errs { + eprintln!("e: {err:?}"); Report::build(ariadne::ReportKind::Error, file, err.span().start) .with_message(format!("error at stage {stage:?}, {:?}", err.reason())) .with_label( diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index aae1fbb..8843d11 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -2,14 +2,12 @@ use std::ops::Range; use chumsky::{ error::Rich, - extra, input::{Stream, ValueInput}, prelude::*, primitive::just, recursive::recursive, - select, span::SimpleSpan, - IterParser, Parser, + IterParser, }; use indexmap::IndexMap; use logos::{Logos, Source}; @@ -19,7 +17,7 @@ use crate::tokens::Token; pub mod ast; #[cfg(test)] mod tests; -use self::ast::{Expr, File}; +use self::ast::{Expr, Expression, File}; pub type Span = SimpleSpan; pub type Spanned = (T, Span); @@ -31,11 +29,13 @@ pub fn parse<'src>(src: &'src str) -> ParseResult, Rich<'_, Token<'_>>> .map(|(t, s)| (t.expect("TODO: add lexer error(s)"), Span::from(s))) .collect(); let tok_stream = Stream::from_iter(toks).spanned((src.len()..src.len()).into()); - expr_parser().parse(tok_stream) + parser().parse(tok_stream) } - -fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src>, Span = Span>>( -) -> impl Parser<'tokens, I, File<'src>, extra::Err, Span>>> { +pub(crate) fn parser< + 'tokens, + 'src: 'tokens, + I: ValueInput<'tokens, Token = Token<'src>, Span = Span>, +>() -> impl Parser<'tokens, I, File<'src>, extra::Err, Span>>> { let word = select! { Token::Word(word) => word }; let expr = recursive(|expr| { @@ -43,7 +43,7 @@ fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src Token::VarIdent(name) => (Expr::Var as fn(_) -> _, name), Token::InputIdent(name) => (Expr::InputVar as fn(_) -> _, name) } - .map_with(|(item_type, name), extra| item_type((name, extra.span()))) + .map_with(|(item_type, name), extra| Expression::new(item_type(name), extra.span())) .labelled("variable"); let attrset = word @@ -62,11 +62,15 @@ fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src let node = word .map_with(|v, e| (v, e.span())) .then(attrset.clone().or_not()) - .map(|(name, params)| Expr::Node(name, params)) + .map_with(|(name, params), extra| { + Expression::new(Expr::Node(name, params), extra.span()) + }) .or(var) - .or(attrset.map(Expr::AttrSet)) + .or(attrset + .map_with(|attrset, extra| Expression::new(Expr::AttrSet(attrset), extra.span()))) .labelled("node"); + #[allow(clippy::let_and_return)] let pipeline = node .clone() .then(choice(( @@ -75,8 +79,8 @@ fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src just(Token::NullPipe).to(Expr::NullPipe as fn(_, _) -> _), ))) .repeated() - .foldr(node, |(curr, pipe), next| { - pipe(Box::new(curr), Box::new(next)) + .foldr_with(node, |(curr, pipe), next, extra| { + Expression::new(pipe(Box::new(curr), Box::new(next)), extra.span()) }); pipeline @@ -85,14 +89,25 @@ fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src let decl = just(Token::Def).ignore_then( word.map_with(|n, e| (n, e.span())) .then_ignore(just(Token::Equals)) - .then(expr.clone().map_with(|expr, extra| (expr, extra.span()))) + .then(expr.clone().map(|expr| expr)) .then_ignore(just(Token::SemiColon)), ); - expr.map_with(|expr, extra| File { - decls: IndexMap::from_iter([(("main", (0..0).into()), (expr, extra.span()))]), + expr.map(|expr| File { + decls: IndexMap::from_iter([(("main", (0..0).into()), expr)]), }) .or(decl.repeated().collect::>().map(|decls| File { decls: IndexMap::from_iter(decls), })) } +pub mod asg { + use std::collections::BTreeMap; + + use petgraph::{adj::NodeIndex, data::Build, graph::DiGraph}; + + use super::{ast::Expr, Spanned}; + + pub struct Asg<'src> { + ast: Expr<'src>, + } +} diff --git a/crates/lang/src/parser/ast.rs b/crates/lang/src/parser/ast.rs index a276a75..e58c5aa 100644 --- a/crates/lang/src/parser/ast.rs +++ b/crates/lang/src/parser/ast.rs @@ -2,35 +2,47 @@ use std::collections::{BTreeMap, HashMap}; use indexmap::IndexMap; -use super::Spanned; +use super::{Span, Spanned}; #[derive(Debug, PartialEq, Eq)] pub struct File<'src> { - pub decls: IndexMap, Spanned>>, + pub decls: IndexMap, Expression<'src>>, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Expression<'src> { + pub expr: Expr<'src>, + pub span: Span, +} + +impl<'src> Expression<'src> { + pub fn new(expr: Expr<'src>, span: Span) -> Self { + Self { expr, span } + } } #[derive(Debug, PartialEq, Eq)] pub enum Expr<'src> { Node( Spanned<&'src str>, - Option, Expr<'src>>>>, + Option, Expression<'src>>>>, ), - SimplePipe(Box>, Box>), + SimplePipe(Box>, Box>), NamingPipe( - Box>, + Box>, (Vec>, Vec>), - Box>, + Box>, ), - MappingPipe(Box>, Box>), - NullPipe(Box>, Box>), - MultiPipe(IndexMap, Expr<'src>>), + MappingPipe(Box>, Box>), + NullPipe(Box>, Box>), + MultiPipe(IndexMap, Expression<'src>>), LetIn( - IndexMap, Box>>, - Box>, + IndexMap, Box>>, + Box>, ), // $ - Var(Spanned<&'src str>), + Var(&'src str), // @ - InputVar(Spanned<&'src str>), - AttrSet(Spanned, Expr<'src>>>), + InputVar(&'src str), + AttrSet(Spanned, Expression<'src>>>), } diff --git a/crates/lang/src/parser/tests.rs b/crates/lang/src/parser/tests.rs index f8fbdc0..2bd2779 100644 --- a/crates/lang/src/parser/tests.rs +++ b/crates/lang/src/parser/tests.rs @@ -6,138 +6,138 @@ use chumsky::prelude::*; use indexmap::IndexMap; use logos::Logos; -#[test] -fn test_parse_node_with_params() { - const INPUT: &str = "meow [ hello: $foo, world: @bar]"; - assert_eq!( - parse(INPUT).unwrap(), - File { - decls: IndexMap::from_iter([( - ("main", (0..0).into()), - ( - Expr::Node( - ("meow", (0..4).into()), - Some(( - IndexMap::from_iter([ - ( - ("hello", (7..12).into()), - Expr::Var(("foo", (14..18).into())) - ), - ( - ("world", (20..25).into()), - Expr::InputVar(("bar", (27..31).into())) - ) - ]), - (5..32).into() - )) - ), - (0..32).into() - ) - )]) - } - ); -} +// #[test] +// fn test_parse_node_with_params() { +// const INPUT: &str = "meow [ hello: $foo, world: @bar]"; +// assert_eq!( +// parse(INPUT).unwrap(), +// File { +// decls: IndexMap::from_iter([( +// ("main", (0..0).into()), +// ( +// Expr::Node( +// ("meow", (0..4).into()), +// Some(( +// IndexMap::from_iter([ +// ( +// ("hello", (7..12).into()), +// Expr::Var(("foo", (14..18).into())) +// ), +// ( +// ("world", (20..25).into()), +// Expr::InputVar(("bar", (27..31).into())) +// ) +// ]), +// (5..32).into() +// )) +// ), +// (0..32).into() +// ) +// )]) +// } +// ); +// } -fn test_parse_multiple_top_level_complex() { - const INPUT: &str = r"def main = meow - | uwu - [ foo: @bar - , hello: world @| test [ more: params ] | yay - ] - !| awa - @| nya - | rawr; +// fn test_parse_multiple_top_level_complex() { +// const INPUT: &str = r"def main = meow +// | uwu +// [ foo: @bar +// , hello: world @| test [ more: params ] | yay +// ] +// !| awa +// @| nya +// | rawr; -def test = meow - [ hello: $foo - , world: @bar - ]; -"; - assert_eq!( - parse(INPUT).unwrap(), - File { - decls: IndexMap::from_iter([ - ( - ("main", (4..8).into()), - ( - Expr::SimplePipe( - Box::new(Expr::Node(("meow", (11..15).into()), None)), - Box::new(Expr::NullPipe( - Box::new(Expr::Node( - ("uwu", (20..23).into()), - Some(( - IndexMap::from_iter([ - ( - ("foo", (29..32).into()), - Expr::InputVar(("bar", (34..38).into())) - ), - ( - ("hello", (44..49).into()), - Expr::MappingPipe( - Box::new(Expr::Node( - ("world", (51..56).into()), - None - )), - Box::new(Expr::SimplePipe( - Box::new(Expr::Node( - ("test", (60..64).into()), - Some(( - IndexMap::from_iter([( - ("more", (67..71).into()), - Expr::Node( - ("params", (73..79).into()), - None - ) - )]), - (65..81).into() - )) - )), - Box::new(Expr::Node( - ("yay", (84..87).into()), - None - )) - )) - ) - ) - ]), - (27..92).into() - )) - )), - Box::new(Expr::MappingPipe( - Box::new(Expr::Node(("awa", (97..100).into()), None)), - Box::new(Expr::SimplePipe( - Box::new(Expr::Node(("nya", (106..109).into()), None)), - Box::new(Expr::Node(("rawr", (114..118).into()), None)) - )) - )) - )) - ), - (11..118).into() - ), - ), - ( - ("test", (125..129).into()), - ( - Expr::Node( - ("meow", (132..136).into()), - Some(( - IndexMap::from_iter([ - ( - ("hello", (141..146).into()), - Expr::Var(("foo", (148..152).into())) - ), - ( - ("world", (156..161).into()), - Expr::InputVar(("bar", (163..167).into())) - ) - ]), - (139..171).into() - )) - ), - (132..171).into() - ) - ) - ]) - } - ); -} +// def test = meow +// [ hello: $foo +// , world: @bar +// ]; +// "; +// assert_eq!( +// parse(INPUT).unwrap(), +// File { +// decls: IndexMap::from_iter([ +// ( +// ("main", (4..8).into()), +// ( +// Expr::SimplePipe( +// Box::new(Expr::Node(("meow", (11..15).into()), None)), +// Box::new(Expr::NullPipe( +// Box::new(Expr::Node( +// ("uwu", (20..23).into()), +// Some(( +// IndexMap::from_iter([ +// ( +// ("foo", (29..32).into()), +// Expr::InputVar(("bar", (34..38).into())) +// ), +// ( +// ("hello", (44..49).into()), +// Expr::MappingPipe( +// Box::new(Expr::Node( +// ("world", (51..56).into()), +// None +// )), +// Box::new(Expr::SimplePipe( +// Box::new(Expr::Node( +// ("test", (60..64).into()), +// Some(( +// IndexMap::from_iter([( +// ("more", (67..71).into()), +// Expr::Node( +// ("params", (73..79).into()), +// None +// ) +// )]), +// (65..81).into() +// )) +// )), +// Box::new(Expr::Node( +// ("yay", (84..87).into()), +// None +// )) +// )) +// ) +// ) +// ]), +// (27..92).into() +// )) +// )), +// Box::new(Expr::MappingPipe( +// Box::new(Expr::Node(("awa", (97..100).into()), None)), +// Box::new(Expr::SimplePipe( +// Box::new(Expr::Node(("nya", (106..109).into()), None)), +// Box::new(Expr::Node(("rawr", (114..118).into()), None)) +// )) +// )) +// )) +// ), +// (11..118).into() +// ), +// ), +// ( +// ("test", (125..129).into()), +// ( +// Expr::Node( +// ("meow", (132..136).into()), +// Some(( +// IndexMap::from_iter([ +// ( +// ("hello", (141..146).into()), +// Expr::Var(("foo", (148..152).into())) +// ), +// ( +// ("world", (156..161).into()), +// Expr::InputVar(("bar", (163..167).into())) +// ) +// ]), +// (139..171).into() +// )) +// ), +// (132..171).into() +// ) +// ) +// ]) +// } +// ); +// } diff --git a/testfiles/test.owo b/testfiles/test.owo index d35cae5..f767d2a 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1 +1,18 @@ -meow gay | uwu +def main = meow + | uwu + [ foo: @bar + , hello: world @| test [ more: params ] | yay + ] + !| awa + @| nya + | rawr; + +def test = meow + [ hello: $foo + , world: @bar + ]; + +def blah = [ + pipe1: meow | uwu, + pipe2: lorem | ipsum +] | flkjdsafkjl; -- 2.46.0 From 8d7401531e4633163e2ec2fd2eb14d7efd85a73c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 7 Apr 2024 01:04:02 +0200 Subject: [PATCH 28/67] lang: some small, unfinished stuff --- crates/lang/src/parser.rs | 11 ----------- crates/lang/src/parser/ast.rs | 18 +++++++++--------- crates/lang/src/tokens.rs | 4 ++++ 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 8843d11..8422bc0 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -100,14 +100,3 @@ pub(crate) fn parser< decls: IndexMap::from_iter(decls), })) } -pub mod asg { - use std::collections::BTreeMap; - - use petgraph::{adj::NodeIndex, data::Build, graph::DiGraph}; - - use super::{ast::Expr, Spanned}; - - pub struct Asg<'src> { - ast: Expr<'src>, - } -} diff --git a/crates/lang/src/parser/ast.rs b/crates/lang/src/parser/ast.rs index e58c5aa..02ce0ae 100644 --- a/crates/lang/src/parser/ast.rs +++ b/crates/lang/src/parser/ast.rs @@ -28,18 +28,18 @@ pub enum Expr<'src> { Option, Expression<'src>>>>, ), SimplePipe(Box>, Box>), - NamingPipe( - Box>, - (Vec>, Vec>), - Box>, - ), + // NamingPipe( + // Box>, + // (Vec>, Vec>), + // Box>, + // ), MappingPipe(Box>, Box>), NullPipe(Box>, Box>), MultiPipe(IndexMap, Expression<'src>>), - LetIn( - IndexMap, Box>>, - Box>, - ), + // LetIn( + // IndexMap, Box>>, + // Box>, + // ), // $ Var(&'src str), // @ diff --git a/crates/lang/src/tokens.rs b/crates/lang/src/tokens.rs index 40280e7..e881663 100644 --- a/crates/lang/src/tokens.rs +++ b/crates/lang/src/tokens.rs @@ -10,6 +10,10 @@ pub enum Token<'a> { // so a declaration needs the keyword def until i can figure this out #[token("def")] Def, + #[token("let")] + Let, + #[token("in")] + In, #[regex("[a-zA-Z0-9_\\-]+", |lex| lex.slice())] Word(&'a str), #[regex("\\$[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] -- 2.46.0 From 198c74c7ae4a6ffd3b82a3fd0368495686addcb7 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 8 Apr 2024 14:04:52 +0200 Subject: [PATCH 29/67] lang: make attrset delims braces --- crates/lang/src/parser.rs | 17 +++++++++++++++-- testfiles/test.owo | 14 +++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 8422bc0..8090f7b 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -25,7 +25,6 @@ pub type Spanned = (T, Span); pub fn parse<'src>(src: &'src str) -> ParseResult, Rich<'_, Token<'_>>> { let toks: Vec<_> = Token::lexer(src) .spanned() - .into_iter() .map(|(t, s)| (t.expect("TODO: add lexer error(s)"), Span::from(s))) .collect(); let tok_stream = Stream::from_iter(toks).spanned((src.len()..src.len()).into()); @@ -55,7 +54,7 @@ pub(crate) fn parser< .separated_by(just(Token::Comma)) .collect::>() .map(IndexMap::from_iter) - .delimited_by(just(Token::BracketOpen), just(Token::BracketClose)) + .delimited_by(just(Token::BraceOpen), just(Token::BraceClose)) .map_with(|v, e| (v, e.span())) .labelled("attrset"); @@ -100,3 +99,17 @@ pub(crate) fn parser< decls: IndexMap::from_iter(decls), })) } + +pub mod asg { + use petgraph::graph::DiGraph; + + use super::Spanned; + + pub struct Asg<'src> { + graph: DiGraph, String>, + } + + enum AsgNode<'src> { + Node(Spanned<&'src str>), + } +} diff --git a/testfiles/test.owo b/testfiles/test.owo index f767d2a..11930d6 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,18 +1,18 @@ def main = meow | uwu - [ foo: @bar - , hello: world @| test [ more: params ] | yay - ] + { foo: @bar + , hello: world @| test { more: params } | yay + } !| awa @| nya | rawr; def test = meow - [ hello: $foo + { hello: $foo , world: @bar - ]; + }; -def blah = [ +def blah = { pipe1: meow | uwu, pipe2: lorem | ipsum -] | flkjdsafkjl; +} | flkjdsafkjl; -- 2.46.0 From bfd4b3765f35a98112b691d2f29c2c3446fe4d5c Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 8 Apr 2024 15:43:42 +0200 Subject: [PATCH 30/67] lang: state with confusing error --- crates/lang/src/parser.rs | 4 ++++ crates/lang/src/parser/ast.rs | 15 ++++++++++--- crates/lang/src/tokens.rs | 18 +++++++++++++++- crates/lang/src/tokens/tests.rs | 38 ++++++++++++++++++++++++++++++--- testfiles/test.owo | 23 ++++++-------------- 5 files changed, 74 insertions(+), 24 deletions(-) diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 8090f7b..7c0e406 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -38,6 +38,10 @@ pub(crate) fn parser< let word = select! { Token::Word(word) => word }; let expr = recursive(|expr| { + let lit = select! { + Token::Int(i) = e => Expression::new(Expr::Lit(ast::Lit::Int(i.parse().unwrap())), e.span()), + Token::Float(f) = e => Expression::new(Expr::Lit(ast::Lit::Float(f.parse().unwrap())), e.span()), + }; let var = select! { Token::VarIdent(name) => (Expr::Var as fn(_) -> _, name), Token::InputIdent(name) => (Expr::InputVar as fn(_) -> _, name) diff --git a/crates/lang/src/parser/ast.rs b/crates/lang/src/parser/ast.rs index 02ce0ae..d21ad49 100644 --- a/crates/lang/src/parser/ast.rs +++ b/crates/lang/src/parser/ast.rs @@ -4,12 +4,12 @@ use indexmap::IndexMap; use super::{Span, Spanned}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq)] pub struct File<'src> { pub decls: IndexMap, Expression<'src>>, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq)] pub struct Expression<'src> { pub expr: Expr<'src>, pub span: Span, @@ -21,7 +21,7 @@ impl<'src> Expression<'src> { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq)] pub enum Expr<'src> { Node( Spanned<&'src str>, @@ -45,4 +45,13 @@ pub enum Expr<'src> { // @ InputVar(&'src str), AttrSet(Spanned, Expression<'src>>>), + Lit(Lit<'src>), +} + +#[derive(Debug, PartialEq)] +pub enum Lit<'src> { + // TODO: more bigger better number types + Int(i64), + Float(f64), + String(&'src str), } diff --git a/crates/lang/src/tokens.rs b/crates/lang/src/tokens.rs index e881663..6caa57c 100644 --- a/crates/lang/src/tokens.rs +++ b/crates/lang/src/tokens.rs @@ -14,7 +14,23 @@ pub enum Token<'a> { Let, #[token("in")] In, - #[regex("[a-zA-Z0-9_\\-]+", |lex| lex.slice())] + #[regex("[\\d]+", |lex| lex.slice())] + Int(&'a str), + #[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)", |lex| lex.slice())] + Float(&'a str), + // TODO: more bigger better more complex string lexing + // TODO: templating? + #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#, |lex| lex.slice())] + String(&'a str), + #[token("+")] + Plus, + #[token("-")] + Minus, + #[token("*")] + Mult, + #[token("/")] + Div, + #[regex("[a-zA-Z_]+[a-zA-Z0-9_\\-]*", |lex| lex.slice())] Word(&'a str), #[regex("\\$[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] VarIdent(&'a str), diff --git a/crates/lang/src/tokens/tests.rs b/crates/lang/src/tokens/tests.rs index fe4270e..2b0454f 100644 --- a/crates/lang/src/tokens/tests.rs +++ b/crates/lang/src/tokens/tests.rs @@ -34,7 +34,7 @@ lexer_test! { lexer_test! { test_lex_subgroup, - "subgroup(first, second) = a | b { 1: $first } | c { 1: $second }", + "subgroup(first, second) = a | b { in1: $first } | c { in1: $second }", [ Token::Word("subgroup"), Token::ParenOpen, @@ -47,14 +47,14 @@ lexer_test! { Token::Pipe, Token::Word("b"), Token::BraceOpen, - Token::Word("1"), + Token::Word("in1"), Token::Colon, Token::VarIdent("first"), Token::BraceClose, Token::Pipe, Token::Word("c"), Token::BraceOpen, - Token::Word("1"), + Token::Word("in1"), Token::Colon, Token::VarIdent("second"), Token::BraceClose @@ -105,3 +105,35 @@ lexer_test! { Token::Word("c") ] } + +lexer_test! { + test_lex_int_literal, + "42", + [ + Token::Int("42") + ] +} + +lexer_test! { + test_lex_float_literal_0, + "1.5", + [ + Token::Float("1.5") + ] +} + +lexer_test! { + test_lex_float_literal_1, + "42.", + [ + Token::Float("42.") + ] +} + +lexer_test! { + test_lex_float_literal_2, + ".42", + [ + Token::Float(".42") + ] +} diff --git a/testfiles/test.owo b/testfiles/test.owo index 11930d6..809a6e1 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,18 +1,7 @@ -def main = meow - | uwu - { foo: @bar - , hello: world @| test { more: params } | yay - } - !| awa - @| nya - | rawr; +def blend1 = [ + open "test.png", + open "test2.png" + ] + | blend multiply 0.6 -def test = meow - { hello: $foo - , world: @bar - }; - -def blah = { - pipe1: meow | uwu, - pipe2: lorem | ipsum -} | flkjdsafkjl; +def blend2 = open "test.png" | blend multiply 0.6 [ open test2.png ] -- 2.46.0 From 881a987b2f9ce45955e99ac4b19b828a897ae326 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 8 Apr 2024 23:27:56 +0200 Subject: [PATCH 31/67] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'devenv': 'github:cachix/devenv/18ef9849d1ecac7a9a7920eb4f2e4adcf67a8c3a' (2024-01-09) → 'github:cachix/devenv/a71323c618664a6b7a39bc183b0ce22ac8511cf9' (2024-04-08) • Added input 'devenv/cachix': 'github:cachix/cachix/661bbb7f8b55722a0406456b15267b5426a3bda6' (2024-03-15) • Added input 'devenv/cachix/devenv': 'github:cachix/devenv/2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196' (2024-02-23) • Added input 'devenv/cachix/devenv/flake-compat': follows 'devenv/cachix/flake-compat' • Added input 'devenv/cachix/devenv/nix': 'github:domenkozar/nix/ecd0af0c1f56de32cbad14daa1d82a132bf298f8' (2024-02-22) • Added input 'devenv/cachix/devenv/nix/flake-compat': 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9' (2023-01-17) • Added input 'devenv/cachix/devenv/nix/nixpkgs': follows 'devenv/cachix/devenv/nixpkgs' • Added input 'devenv/cachix/devenv/nix/nixpkgs-regression': 'github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2' (2022-01-24) • Added input 'devenv/cachix/devenv/nixpkgs': 'github:NixOS/nixpkgs/9201b5ff357e781bf014d0330d18555695df7ba8' (2023-08-23) • Added input 'devenv/cachix/devenv/poetry2nix': 'github:nix-community/poetry2nix/d5006be9c2c2417dafb2e2e5034d83fabd207ee3' (2023-08-24) • Added input 'devenv/cachix/devenv/poetry2nix/flake-utils': 'github:numtide/flake-utils/919d646de7be200f3bf08cb76ae1f09402b6f9b4' (2023-07-11) • Added input 'devenv/cachix/devenv/poetry2nix/flake-utils/systems': 'github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e' (2023-04-09) • Added input 'devenv/cachix/devenv/poetry2nix/nix-github-actions': 'github:nix-community/nix-github-actions/165b1650b753316aa7f1787f3005a8d2da0f5301' (2023-07-09) • Added input 'devenv/cachix/devenv/poetry2nix/nix-github-actions/nixpkgs': follows 'devenv/cachix/devenv/poetry2nix/nixpkgs' • Added input 'devenv/cachix/devenv/poetry2nix/nixpkgs': follows 'devenv/cachix/devenv/nixpkgs' • Added input 'devenv/cachix/devenv/pre-commit-hooks': follows 'devenv/cachix/pre-commit-hooks' • Added input 'devenv/cachix/flake-compat': 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33' (2023-10-04) • Added input 'devenv/cachix/nixpkgs': follows 'devenv/nixpkgs' • Added input 'devenv/cachix/pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/5df5a70ad7575f6601d91f0efec95dd9bc619431' (2024-02-15) • Added input 'devenv/cachix/pre-commit-hooks/flake-compat': 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33' (2023-10-04) • Added input 'devenv/cachix/pre-commit-hooks/flake-utils': 'github:numtide/flake-utils/4022d587cbbfd70fe950c1e2083a02621806a725' (2023-12-04) • Added input 'devenv/cachix/pre-commit-hooks/flake-utils/systems': 'github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e' (2023-04-09) • Added input 'devenv/cachix/pre-commit-hooks/gitignore': 'github:hercules-ci/gitignore.nix/43e1aa1308018f37118e34d3a9cb4f5e75dc11d5' (2023-12-29) • Added input 'devenv/cachix/pre-commit-hooks/gitignore/nixpkgs': follows 'devenv/cachix/pre-commit-hooks/nixpkgs' • Added input 'devenv/cachix/pre-commit-hooks/nixpkgs': follows 'devenv/cachix/nixpkgs' • Added input 'devenv/cachix/pre-commit-hooks/nixpkgs-stable': 'github:NixOS/nixpkgs/3dc440faeee9e889fe2d1b4d25ad0f430d449356' (2024-01-10) • Updated input 'devenv/flake-compat': 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9' (2023-01-17) → 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33' (2023-10-04) • Updated input 'devenv/nix': 'github:domenkozar/nix/7c91803598ffbcfe4a55c44ac6d49b2cf07a527f' (2023-02-16) → 'github:domenkozar/nix/c5bbf14ecbd692eeabf4184cc8d50f79c2446549' (2024-03-15) • Added input 'devenv/nix/flake-compat': 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9' (2023-01-17) • Removed input 'devenv/nix/lowdown-src' • Updated input 'devenv/nixpkgs': 'github:NixOS/nixpkgs/126f49a01de5b7e35a43fd43f891ecf6d3a51459' (2023-03-15) → 'github:cachix/devenv-nixpkgs/829e73affeadfb4198a7105cbe3a03153d13edc9' (2024-03-12) • Updated input 'devenv/pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/ea96f0c05924341c551a797aaba8126334c505d2' (2024-01-08) → 'github:cachix/pre-commit-hooks.nix/e35aed5fda3cc79f88ed7f1795021e559582093a' (2024-04-02) • Updated input 'devenv/pre-commit-hooks/flake-utils': 'github:numtide/flake-utils/a1720a10a6cfe8234c0e93907ffe81be440f4cef' (2023-05-31) → 'github:numtide/flake-utils/b1d9ab70662946ef0850d488da1c9019f3a9752a' (2024-03-11) • Updated input 'devenv/pre-commit-hooks/gitignore': 'github:hercules-ci/gitignore.nix/a20de23b925fd8264fd7fad6454652e142fd7f73' (2022-08-14) → 'github:hercules-ci/gitignore.nix/637db329424fd7e46cf4185293b9cc8c88c95394' (2024-02-28) • Updated input 'devenv/pre-commit-hooks/nixpkgs-stable': 'github:NixOS/nixpkgs/c37ca420157f4abc31e26f436c1145f8951ff373' (2023-06-03) → 'github:NixOS/nixpkgs/614b4613980a522ba49f0d194531beddbb7220d3' (2024-03-17) • Updated input 'fenix': 'github:nix-community/fenix/93e89638c15512db65e931f26ce36edf8cfbb4a5' (2024-01-10) → 'github:nix-community/fenix/99c6241db5ca5363c05c8f4acbdf3a4e8fc42844' (2024-04-06) • Updated input 'fenix/nixpkgs': 'github:nixos/nixpkgs/46ae0210ce163b3cba6c7da08840c1d63de9c701' (2024-01-06) → 'github:nixos/nixpkgs/fd281bd6b7d3e32ddfa399853946f782553163b5' (2024-04-03) • Updated input 'fenix/rust-analyzer-src': 'github:rust-lang/rust-analyzer/ae6e73772432cfe35bb0ff6de6fdcfa908642b67' (2024-01-09) → 'github:rust-lang/rust-analyzer/8e581ac348e223488622f4d3003cb2bd412bf27e' (2024-04-03) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/317484b1ead87b9c1b8ac5261a8d2dd748a0492d' (2024-01-08) → 'github:NixOS/nixpkgs/ff0dbd94265ac470dda06a657d5fe49de93b4599' (2024-04-06) --- flake.lock | 468 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 405 insertions(+), 63 deletions(-) diff --git a/flake.lock b/flake.lock index fd2920d..43f154e 100644 --- a/flake.lock +++ b/flake.lock @@ -1,18 +1,43 @@ { "nodes": { - "devenv": { + "cachix": { "inputs": { - "flake-compat": "flake-compat", - "nix": "nix", - "nixpkgs": "nixpkgs", + "devenv": "devenv_2", + "flake-compat": "flake-compat_2", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1704835383, - "narHash": "sha256-SoC0rYR9iHW0dVOEmxNEfa8vk9dTK86P5iXTgHafmwM=", + "lastModified": 1710475558, + "narHash": "sha256-egKrPCKjy/cE+NqCj4hg2fNX/NwLCf0bRDInraYXDgs=", + "owner": "cachix", + "repo": "cachix", + "rev": "661bbb7f8b55722a0406456b15267b5426a3bda6", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat_4", + "nix": "nix_2", + "nixpkgs": "nixpkgs_2", + "pre-commit-hooks": "pre-commit-hooks_2" + }, + "locked": { + "lastModified": 1712579011, + "narHash": "sha256-trHgFNW8CW85c1OuAPBI+OGous53KkVhMemvcq7syDo=", "owner": "cachix", "repo": "devenv", - "rev": "18ef9849d1ecac7a9a7920eb4f2e4adcf67a8c3a", + "rev": "a71323c618664a6b7a39bc183b0ce22ac8511cf9", "type": "github" }, "original": { @@ -21,17 +46,48 @@ "type": "github" } }, + "devenv_2": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix", + "nixpkgs": "nixpkgs", + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "devenv", + "cachix", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "python-rewrite", + "repo": "devenv", + "type": "github" + } + }, "fenix": { "inputs": { - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs_3", "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1704867811, - "narHash": "sha256-pG4O1vPpNSMjz7p/5x+/OH4tXC0thzAPbJ55kI/W5dU=", + "lastModified": 1712384501, + "narHash": "sha256-AZmYmEnc1ZkSlxUJVUtGh9VFAqWPr+xtNIiBqD2eKfc=", "owner": "nix-community", "repo": "fenix", - "rev": "93e89638c15512db65e931f26ce36edf8cfbb4a5", + "rev": "99c6241db5ca5363c05c8f4acbdf3a4e8fc42844", "type": "github" }, "original": { @@ -56,16 +112,116 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_3": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_4": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_5": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { - "lastModified": 1685518550, - "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", "owner": "numtide", "repo": "flake-utils", - "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -78,16 +234,17 @@ "inputs": { "nixpkgs": [ "devenv", + "cachix", "pre-commit-hooks", "nixpkgs" ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1703887061, + "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", "type": "github" }, "original": { @@ -96,53 +253,109 @@ "type": "github" } }, - "lowdown-src": { - "flake": false, + "gitignore_2": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, "locked": { - "lastModified": 1633514407, - "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", - "owner": "kristapsdz", - "repo": "lowdown", - "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { - "owner": "kristapsdz", - "repo": "lowdown", + "owner": "hercules-ci", + "repo": "gitignore.nix", "type": "github" } }, "nix": { "inputs": { - "lowdown-src": "lowdown-src", + "flake-compat": "flake-compat", "nixpkgs": [ + "devenv", + "cachix", "devenv", "nixpkgs" ], "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1676545802, - "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "lastModified": 1708577783, + "narHash": "sha256-92xq7eXlxIT5zFNccLpjiP7sdQqQI30Gyui2p/PfKZM=", "owner": "domenkozar", "repo": "nix", - "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "rev": "ecd0af0c1f56de32cbad14daa1d82a132bf298f8", "type": "github" }, "original": { "owner": "domenkozar", - "ref": "relaxed-flakes", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { + "inputs": { + "flake-compat": "flake-compat_5", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_2" + }, + "locked": { + "lastModified": 1710500156, + "narHash": "sha256-zvCqeUO2GLOm7jnU23G4EzTZR7eylcJN+HJ5svjmubI=", + "owner": "domenkozar", + "repo": "nix", + "rev": "c5bbf14ecbd692eeabf4184cc8d50f79c2446549", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", "repo": "nix", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1678875422, - "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", "type": "github" }, "original": { @@ -168,45 +381,93 @@ "type": "github" } }, - "nixpkgs-stable": { + "nixpkgs-regression_2": { "locked": { - "lastModified": 1685801374, - "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1704874635, + "narHash": "sha256-YWuCrtsty5vVZvu+7BchAxmcYzTMfolSPP5io8+WYCg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3dc440faeee9e889fe2d1b4d25ad0f430d449356", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable_2": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1704538339, - "narHash": "sha256-1734d3mQuux9ySvwf6axRWZRBhtcZA9Q8eftD6EZg6U=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "46ae0210ce163b3cba6c7da08840c1d63de9c701", + "lastModified": 1710236354, + "narHash": "sha256-vWrciFdq49vve43g4pbi7NjmL4cwG1ifXnQx+dU3T5E=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "829e73affeadfb4198a7105cbe3a03153d13edc9", "type": "github" }, "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", "type": "github" } }, "nixpkgs_3": { "locked": { - "lastModified": 1704722960, - "narHash": "sha256-mKGJ3sPsT6//s+Knglai5YflJUF2DGj7Ai6Ynopz0kI=", + "lastModified": 1712163089, + "narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1712439257, + "narHash": "sha256-aSpiNepFOMk9932HOax0XwNxbA38GOUVOiXfUVPOrck=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "317484b1ead87b9c1b8ac5261a8d2dd748a0492d", + "rev": "ff0dbd94265ac470dda06a657d5fe49de93b4599", "type": "github" }, "original": { @@ -216,26 +477,77 @@ "type": "github" } }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, "pre-commit-hooks": { "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "flake-utils": "flake-utils", + "flake-compat": "flake-compat_3", + "flake-utils": "flake-utils_2", "gitignore": "gitignore", "nixpkgs": [ "devenv", + "cachix", "nixpkgs" ], "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1704725188, - "narHash": "sha256-qq8NbkhRZF1vVYQFt1s8Mbgo8knj+83+QlL5LBnYGpI=", + "lastModified": 1708018599, + "narHash": "sha256-M+Ng6+SePmA8g06CmUZWi1AjG2tFBX9WCXElBHEKnyM=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "ea96f0c05924341c551a797aaba8126334c505d2", + "rev": "5df5a70ad7575f6601d91f0efec95dd9bc619431", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_2": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils_3", + "gitignore": "gitignore_2", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_2" + }, + "locked": { + "lastModified": 1712055707, + "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", "type": "github" }, "original": { @@ -248,18 +560,18 @@ "inputs": { "devenv": "devenv", "fenix": "fenix", - "nixpkgs": "nixpkgs_3", - "systems": "systems_2" + "nixpkgs": "nixpkgs_4", + "systems": "systems_4" } }, "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1704833483, - "narHash": "sha256-Ox01mpYmjapNYaqOu4fMS/4Ma9NLd2rVNz6d4rJmcf4=", + "lastModified": 1712156296, + "narHash": "sha256-St7ZQrkrr5lmQX9wC1ZJAFxL8W7alswnyZk9d1se3Us=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "ae6e73772432cfe35bb0ff6de6fdcfa908642b67", + "rev": "8e581ac348e223488622f4d3003cb2bd412bf27e", "type": "github" }, "original": { @@ -298,6 +610,36 @@ "repo": "default", "type": "github" } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", -- 2.46.0 From 9da157ff4a3b843a2ed107bac074f0d61cfdfa97 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Thu, 11 Apr 2024 03:23:03 +0200 Subject: [PATCH 32/67] lang: massive amounts of parser and ast pain --- Cargo.lock | 52 ++- crates/lang/Cargo.toml | 4 +- crates/lang/src/main.rs | 37 +- crates/lang/src/parser.rs | 117 +++-- crates/lang/src/parser/ast.rs | 47 +- crates/lang/src/parser/ast/ast_tree.rs | 31 ++ crates/lang/src/parser/ast/lossless.rs | 19 + crates/lang/src/parser/ast/lossless/lex.rs | 118 +++++ crates/lang/src/parser/ast/lossless/parser.rs | 437 ++++++++++++++++++ crates/lang/src/parser/ast/raw_ast.rs | 50 ++ crates/lang/src/parser/tests.rs | 2 +- crates/lang/src/tokens.rs | 15 +- crates/lang/src/tokens/tests.rs | 14 +- flake.lock | 30 +- flake.nix | 86 ++-- testfiles/test.owo | 11 +- 16 files changed, 900 insertions(+), 170 deletions(-) create mode 100644 crates/lang/src/parser/ast/ast_tree.rs create mode 100644 crates/lang/src/parser/ast/lossless.rs create mode 100644 crates/lang/src/parser/ast/lossless/lex.rs create mode 100644 crates/lang/src/parser/ast/lossless/parser.rs create mode 100644 crates/lang/src/parser/ast/raw_ast.rs diff --git a/Cargo.lock b/Cargo.lock index e762ead..a25751d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,9 +174,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chumsky" -version = "1.0.0-alpha.6" +version = "1.0.0-alpha.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c28d4e5dd9a9262a38b231153591da6ce1471b818233f4727985d3dd0ed93c" +checksum = "c7b80276986f86789dc56ca6542d53bba9cda3c66091ebbe7bd96fc1bdf20f1f" dependencies = [ "hashbrown", "regex-automata", @@ -237,6 +237,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "countme" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" + [[package]] name = "crc32fast" version = "1.3.2" @@ -322,6 +328,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ego-tree" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" + [[package]] name = "either" version = "1.9.0" @@ -506,9 +518,11 @@ dependencies = [ "ariadne", "chumsky", "clap", + "ego-tree", "indexmap", "logos", "petgraph", + "rowan", ] [[package]] @@ -589,6 +603,15 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -863,6 +886,25 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "rowan" +version = "0.15.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49" +dependencies = [ + "countme", + "hashbrown", + "memoffset", + "rustc-hash", + "text-size", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "ryu" version = "1.0.16" @@ -973,6 +1015,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "text-size" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" + [[package]] name = "thiserror" version = "1.0.55" diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 1cd8654..8c1d3db 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -7,11 +7,13 @@ edition = "2021" [dependencies] logos = "0.14" -chumsky = {version= "1.0.0-alpha.6", features=["label"]} +chumsky = {version= "1.0.0-alpha.7", features=["label"]} petgraph = { workspace = true} indexmap = "2.2.6" clap = { version = "4", features = ["derive"] } ariadne = "0.4.0" +ego-tree = "0.6.2" +rowan = "0.15.15" [lints] workspace = true diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 3e70973..16ec4e4 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -1,7 +1,10 @@ use std::{fs, path::PathBuf}; use clap::Parser; -use lang::{err_reporting::ErrorCollector, parser::parse}; +use lang::{ + err_reporting::ErrorCollector, + parser::ast::lossless::{lex, parser}, +}; #[derive(Parser)] struct Args { @@ -13,20 +16,24 @@ fn main() { let args = Args::parse(); let n = args.file.clone(); let f = fs::read_to_string(n.clone()).expect("failed to read file"); - let mut err_collector = ErrorCollector::new(vec![(n.to_str().unwrap(), &f)]); + println!("toks: {:?}", lex::lex(&f)); + let parse_res = parser::parse(&f); + println!("parse: {:?}", parse_res); + // dbg!(lex::lex(&f)); + // let mut err_collector = ErrorCollector::new(vec![(n.to_str().unwrap(), &f)]); - println!("file: {f}\n"); - let parse_res = parse(&f); - err_collector.insert_many( - args.file.to_str().unwrap(), - lang::err_reporting::Stage::Parse, - parse_res - .errors() - .into_iter() - .map(|e| e.to_owned()) - .collect::>(), - ); + // println!("file: {f}\n"); + // let parse_res = parse(&f); + // err_collector.insert_many( + // args.file.to_str().unwrap(), + // lang::err_reporting::Stage::Parse, + // parse_res + // .errors() + // .into_iter() + // .map(|e| e.to_owned()) + // .collect::>(), + // ); - err_collector.report_raw(); - println!("res: {:?}", parse_res); + // err_collector.report_raw(); + // println!("res: {:?}", parse_res); } diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 7c0e406..b1ee34b 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -1,5 +1,3 @@ -use std::ops::Range; - use chumsky::{ error::Rich, input::{Stream, ValueInput}, @@ -10,19 +8,22 @@ use chumsky::{ IterParser, }; use indexmap::IndexMap; -use logos::{Logos, Source}; +use logos::Logos; use crate::tokens::Token; pub mod ast; #[cfg(test)] mod tests; -use self::ast::{Expr, Expression, File}; +use self::ast::{ + raw_ast::{RawExpr, RawExpression}, + File, +}; pub type Span = SimpleSpan; pub type Spanned = (T, Span); -pub fn parse<'src>(src: &'src str) -> ParseResult, Rich<'_, Token<'_>>> { +pub fn parse(src: &str) -> ParseResult, Rich<'_, Token<'_>>> { let toks: Vec<_> = Token::lexer(src) .spanned() .map(|(t, s)| (t.expect("TODO: add lexer error(s)"), Span::from(s))) @@ -35,22 +36,39 @@ pub(crate) fn parser< 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src>, Span = Span>, >() -> impl Parser<'tokens, I, File<'src>, extra::Err, Span>>> { - let word = select! { Token::Word(word) => word }; + let word = select! { Token::Word(word) = e => (word, e.span())}; let expr = recursive(|expr| { let lit = select! { - Token::Int(i) = e => Expression::new(Expr::Lit(ast::Lit::Int(i.parse().unwrap())), e.span()), - Token::Float(f) = e => Expression::new(Expr::Lit(ast::Lit::Float(f.parse().unwrap())), e.span()), + Token::Int(i) = e => RawExpression::new(RawExpr::Lit(ast::Lit::Int(i.parse().expect("TODO: handle better"))), e.span()), + Token::Float(f) = e => RawExpression::new(RawExpr::Lit(ast::Lit::Float(f.parse().expect("TODO: handle better"))), e.span()), + Token::String(s) = e => RawExpression::new(RawExpr::Lit(ast::Lit::String(s.strip_prefix('"').expect("a").strip_suffix('"').expect("b"))), e.span()) }; + let mat = just(Token::Mat) + .ignore_then(select! { Token::Dimensions(dimensions) = e => (dimensions, e.span())}) + .then( + lit.separated_by(just(Token::Comma)) + .collect::>() + .separated_by(just(Token::Semicolon)) + .collect::>() + .delimited_by(just(Token::BracketOpen), just(Token::BracketClose)), + ) + .map_with(|(dimensions, data), e| { + // TODO: Validation and proper error handling/reporting + // (validation = validating the matrix dimensions) + RawExpression::new( + RawExpr::Matrix(dimensions, data.into_iter().flatten().collect()), + e.span(), + ) + }); let var = select! { - Token::VarIdent(name) => (Expr::Var as fn(_) -> _, name), - Token::InputIdent(name) => (Expr::InputVar as fn(_) -> _, name) + Token::VarIdent(name) => (RawExpr::Var as fn(_) -> _, name), + Token::InputIdent(name) => (RawExpr::InputVar as fn(_) -> _, name) } - .map_with(|(item_type, name), extra| Expression::new(item_type(name), extra.span())) + .map_with(|(item_type, name), extra| RawExpression::new(item_type(name), extra.span())) .labelled("variable"); let attrset = word - .map_with(|n, e| (n, e.span())) .labelled("attr name") .then_ignore(just(Token::Colon)) .then(expr) @@ -63,57 +81,72 @@ pub(crate) fn parser< .labelled("attrset"); let node = word - .map_with(|v, e| (v, e.span())) + .repeated() + .collect() .then(attrset.clone().or_not()) .map_with(|(name, params), extra| { - Expression::new(Expr::Node(name, params), extra.span()) + RawExpression::new(RawExpr::Node(name, params), extra.span()) }) - .or(var) - .or(attrset - .map_with(|attrset, extra| Expression::new(Expr::AttrSet(attrset), extra.span()))) + // .or(var) + // .or(attrset + // .map_with(|attrset, extra| Expression::new(Expr::AttrSet(attrset), extra.span()))) + // .or(lit) + // .or(mat) .labelled("node"); + let atom = var + .or(lit) + .or(mat) + .or(attrset.map_with(|attrset, extra| { + RawExpression::new(RawExpr::AttrSet(attrset), extra.span()) + })) + .or(node.clone()); + #[allow(clippy::let_and_return)] - let pipeline = node + let pipeline = atom .clone() .then(choice(( - just(Token::Pipe).to(Expr::SimplePipe as fn(_, _) -> _), - just(Token::MappingPipe).to(Expr::MappingPipe as fn(_, _) -> _), - just(Token::NullPipe).to(Expr::NullPipe as fn(_, _) -> _), + just(Token::Pipe).to(RawExpr::SimplePipe as fn(_, _) -> _), + just(Token::MappingPipe).to(RawExpr::MappingPipe as fn(_, _) -> _), + just(Token::NullPipe).to(RawExpr::NullPipe as fn(_, _) -> _), ))) .repeated() - .foldr_with(node, |(curr, pipe), next, extra| { - Expression::new(pipe(Box::new(curr), Box::new(next)), extra.span()) + .foldr_with(atom, |(curr, pipe), next, extra| { + RawExpression::new(pipe(curr, next), extra.span()) }); pipeline }); - let decl = just(Token::Def).ignore_then( - word.map_with(|n, e| (n, e.span())) - .then_ignore(just(Token::Equals)) - .then(expr.clone().map(|expr| expr)) - .then_ignore(just(Token::SemiColon)), - ); + let decls = just(Token::Def) + .ignore_then( + word.then_ignore(just(Token::Equals)) + .then(expr.clone().map(|expr| expr)) + .then_ignore(just(Token::Semicolon)), + ) + .repeated() + .collect::>() + .map(|decls| File { + decls: IndexMap::from_iter(decls), + }); - expr.map(|expr| File { + let single_expr = expr.map(|expr| File { decls: IndexMap::from_iter([(("main", (0..0).into()), expr)]), - }) - .or(decl.repeated().collect::>().map(|decls| File { - decls: IndexMap::from_iter(decls), - })) + }); + + just(Token::Def).rewind().ignore_then(decls).or(single_expr) + // single_expr.or(decls) + + // expr.map(|expr| File { + // decls: IndexMap::from_iter([(("main", (0..0).into()), expr)]), + // }) + // .or(decl.repeated().collect::>().map(|decls| File { + // decls: IndexMap::from_iter(decls), + // })) } pub mod asg { use petgraph::graph::DiGraph; use super::Spanned; - - pub struct Asg<'src> { - graph: DiGraph, String>, - } - - enum AsgNode<'src> { - Node(Spanned<&'src str>), - } } diff --git a/crates/lang/src/parser/ast.rs b/crates/lang/src/parser/ast.rs index d21ad49..d45ef66 100644 --- a/crates/lang/src/parser/ast.rs +++ b/crates/lang/src/parser/ast.rs @@ -2,51 +2,14 @@ use std::collections::{BTreeMap, HashMap}; use indexmap::IndexMap; -use super::{Span, Spanned}; +use super::Spanned; #[derive(Debug, PartialEq)] pub struct File<'src> { - pub decls: IndexMap, Expression<'src>>, + pub decls: IndexMap, raw_ast::RawExpression<'src>>, } -#[derive(Debug, PartialEq)] -pub struct Expression<'src> { - pub expr: Expr<'src>, - pub span: Span, -} - -impl<'src> Expression<'src> { - pub fn new(expr: Expr<'src>, span: Span) -> Self { - Self { expr, span } - } -} - -#[derive(Debug, PartialEq)] -pub enum Expr<'src> { - Node( - Spanned<&'src str>, - Option, Expression<'src>>>>, - ), - SimplePipe(Box>, Box>), - // NamingPipe( - // Box>, - // (Vec>, Vec>), - // Box>, - // ), - MappingPipe(Box>, Box>), - NullPipe(Box>, Box>), - MultiPipe(IndexMap, Expression<'src>>), - // LetIn( - // IndexMap, Box>>, - // Box>, - // ), - // $ - Var(&'src str), - // @ - InputVar(&'src str), - AttrSet(Spanned, Expression<'src>>>), - Lit(Lit<'src>), -} +pub mod raw_ast; #[derive(Debug, PartialEq)] pub enum Lit<'src> { @@ -55,3 +18,7 @@ pub enum Lit<'src> { Float(f64), String(&'src str), } + +pub mod lossless; + +pub mod ast_tree; diff --git a/crates/lang/src/parser/ast/ast_tree.rs b/crates/lang/src/parser/ast/ast_tree.rs new file mode 100644 index 0000000..46db4e9 --- /dev/null +++ b/crates/lang/src/parser/ast/ast_tree.rs @@ -0,0 +1,31 @@ +use ego_tree::Tree; + +use crate::parser::Spanned; + +use super::{File, Lit}; + +pub struct Ast<'src> { + tree: Tree>, +} + +struct AstNode<'src> { + kind: NodeKind<'src>, +} + +enum NodeKind<'src> { + Decl, + Ident(&'src str), + Instr, + Expr, + MappingPipe, + NullPipe, + MultiPipe, + Var(&'src str), + InputVar(&'src str), + AttrSet, + Attr, + Lit(Lit<'src>), + Matrix, + Dimensions(u16, u16), + MatrixRow, +} diff --git a/crates/lang/src/parser/ast/lossless.rs b/crates/lang/src/parser/ast/lossless.rs new file mode 100644 index 0000000..0047441 --- /dev/null +++ b/crates/lang/src/parser/ast/lossless.rs @@ -0,0 +1,19 @@ +use self::lex::SyntaxKind; + +pub mod parser; + +pub mod lex; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum Lang {} +impl rowan::Language for Lang { + type Kind = SyntaxKind; + #[allow(unsafe_code)] + fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { + assert!(raw.0 <= SyntaxKind::ROOT as u16); + unsafe { std::mem::transmute::(raw.0) } + } + fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { + kind.into() + } +} diff --git a/crates/lang/src/parser/ast/lossless/lex.rs b/crates/lang/src/parser/ast/lossless/lex.rs new file mode 100644 index 0000000..c25608a --- /dev/null +++ b/crates/lang/src/parser/ast/lossless/lex.rs @@ -0,0 +1,118 @@ +use logos::Logos; + +use crate::parser::Span; + +pub fn lex(src: &str) -> Vec<(SyntaxKind, &str)> { + let mut lex = SyntaxKind::lexer(src); + let mut r = Vec::new(); + + while let Some(tok_res) = lex.next() { + r.push((tok_res.unwrap_or(SyntaxKind::LEX_ERR), lex.slice())) + } + + r.reverse(); + r +} + +#[derive(Logos, Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] +#[repr(u16)] +#[allow(non_camel_case_types)] +pub enum SyntaxKind { + #[token("def")] + DEF_KW = 0, + #[token("let")] + LET_KW, + #[token("in")] + IN_KW, + #[token("mat")] + MAT_KW, + #[regex("[\\d]+x[\\d]+")] + PAT_DIMENSIONS, + #[regex("[\\d]+")] + INT_NUM, + #[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)")] + FLOAT_NUM, + #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#)] + STRING, + MATRIX, + DECL, + LIST, + MAT_BODY, + PARENTHESIZED_EXPR, + EXPR, + #[token("(")] + L_PAREN, + #[token(")")] + R_PAREN, + #[token("{")] + L_CURLY, + #[token("}")] + R_CURLY, + #[token("[")] + L_BRACK, + #[token("]")] + R_BRACK, + #[token("<")] + L_ANGLE, + #[token(">")] + R_ANGLE, + #[token("+")] + PLUS, + #[token("-")] + MINUS, + #[token("*")] + STAR, + #[token("/")] + SLASH, + #[token("%")] + PERCENT, + #[token("^")] + CARET, + INSTR, + INSTR_NAME, + INSTR_PARAMS, + ATTR_SET, + ATTR, + ATTR_NAME, + ATTR_VALUE, + #[regex("[a-zA-Z_]+[a-zA-Z_\\-\\d]*")] + IDENT, + #[regex("\\$[a-zA-Z0-9_\\-]+")] + VAR, + #[regex("\\@[a-zA-Z0-9_\\-]+")] + INPUT_VAR, + #[token("$")] + DOLLAR, + #[token("@")] + AT, + #[token(",")] + COMMA, + #[token("|")] + PIPE, + #[token("@|")] + MAPPING_PIPE, + #[token("!|")] + NULL_PIPE, + #[token("=")] + EQ, + #[token(":")] + COLON, + #[token(";")] + SEMICOLON, + #[token(".")] + DOT, + #[token("!")] + BANG, + #[regex("[ \\t\\f]+")] + WHITESPACE, + #[token("\n")] + NEWLINE, + PARSE_ERR, + LEX_ERR, + ROOT, +} +impl From for rowan::SyntaxKind { + fn from(kind: SyntaxKind) -> Self { + Self(kind as u16) + } +} diff --git a/crates/lang/src/parser/ast/lossless/parser.rs b/crates/lang/src/parser/ast/lossless/parser.rs new file mode 100644 index 0000000..a3c30ca --- /dev/null +++ b/crates/lang/src/parser/ast/lossless/parser.rs @@ -0,0 +1,437 @@ +use std::borrow::Borrow; + +use chumsky::container::Container; +use rowan::{ + Checkpoint, GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken, +}; + +use crate::parser::{ + ast::lossless::{lex::SyntaxKind::*, Lang}, + Span, +}; + +use super::lex::{self, SyntaxKind}; + +#[derive(PartialEq, Eq)] +pub struct Parse { + pub green_node: GreenNode, +} + +impl std::fmt::Debug for Parse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + debug_print_green_node(NodeOrToken::Node(self.green_node.borrow()), f, 0) + } +} + +fn debug_print_green_node( + node: NodeOrToken<&GreenNodeData, &GreenTokenData>, + f: &mut std::fmt::Formatter<'_>, + lvl: i32, +) -> std::fmt::Result { + for _ in 0..lvl { + f.write_str(" ")?; + } + + match node { + NodeOrToken::Node(n) => { + writeln!(f, "{:?} {{", Lang::kind_from_raw(node.kind())); + for c in n.children() { + debug_print_green_node(c, f, lvl + 1)?; + } + for _ in 0..lvl { + f.write_str(" ")?; + } + f.write_str("}\n") + } + NodeOrToken::Token(t) => { + writeln!(f, "{:?} {:?};", Lang::kind_from_raw(t.kind()), t.text()) + } + } +} + +#[derive(Debug)] +struct Parser<'src> { + tokens: Vec<(SyntaxKind, &'src str)>, + builder: GreenNodeBuilder<'src>, + errors: Vec, +} + +#[derive(Debug, PartialEq, Eq)] +enum SyntaxError { + Expected(SyntaxKind), + AttrExpectedValue, + /// guessed if there's a newline and attr on next line without comma + /// should then suggest comma after attr + ExpectedCommaBetweenAttrs, +} + +pub fn parse(src: &str) -> Parse { + let mut tokens = lex::lex(src); + Parser { + tokens, + builder: GreenNodeBuilder::new(), + errors: Vec::new(), + } + .parse() +} + +impl Parser<'_> { + fn parse(mut self) -> Parse { + self.start_node(ROOT); + + match self.expr(None) { + expr::ExprRes::Ok => (), + expr::ExprRes::Eof => (), + expr::ExprRes::NoExpr => todo!(), + } + + self.builder.finish_node(); + Parse { + green_node: self.builder.finish(), + } + } + + fn start_node(&mut self, kind: SyntaxKind) { + self.builder.start_node(kind.into()); + } + fn finish_node(&mut self) { + self.builder.finish_node(); + } + + /// Advance one token, adding it to the current branch of the tree builder. + fn bump(&mut self) { + let (kind, text) = self.tokens.pop().unwrap(); + self.builder.token(kind.into(), text); + } + fn syntax_err(&mut self, err: SyntaxError) { + let (_, text) = self.tokens.pop().unwrap(); + self.builder.token(PARSE_ERR.into(), text); + self.errors.push(err); + } + fn syntax_err_by_checkpoint(&mut self, checkpoint: Checkpoint, err: SyntaxError) { + self.builder.start_node_at(checkpoint, PARSE_ERR.into()); + self.finish_node(); + self.errors.push(err); + } + fn expected(&mut self, expected: SyntaxKind) { + self.syntax_err(SyntaxError::Expected(expected)) + } + /// Peek at the first unprocessed token + fn current(&self) -> Option { + self.tokens.last().map(|(kind, _)| *kind) + } + fn next(&self) -> Option { + self.tokens + .get(self.tokens.len() - 2) + .map(|(kind, _)| *kind) + } + fn skip_ws(&mut self) { + while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) { + self.bump() + } + } + fn skip_ws_without_newlines(&mut self) { + while self.current() == Some(WHITESPACE) { + self.bump() + } + } +} + +mod expr { + use rowan::Checkpoint; + + use super::{attrset::AttrsetRes, instr::NodeRes, Parser}; + use crate::parser::{ast::lossless::lex::SyntaxKind::*, Span}; + impl Parser<'_> { + pub(super) fn expr(&mut self, start: Option) -> ExprRes { + self.skip_ws(); + let start = start.unwrap_or_else(|| self.builder.checkpoint()); + match self.current() { + Some(IDENT) => { + let expr_res = match self.instr() { + NodeRes::Ok => ExprRes::Ok, + NodeRes::Eof => ExprRes::Eof, + }; + self.builder.start_node_at(start, EXPR.into()); + self.finish_node(); + expr_res + } + Some(_) => self.atom(Some(start)), + None => ExprRes::Eof, + } + } + + pub(super) fn atom(&mut self, start: Option) -> ExprRes { + self.skip_ws(); + let start = start.unwrap_or_else(|| self.builder.checkpoint()); + match self.current() { + Some(INT_NUM | FLOAT_NUM | STRING) => { + self.bump(); + self.builder.start_node_at(start, EXPR.into()); + self.finish_node(); + ExprRes::Ok + } + Some(L_CURLY) => match self.attrset(start) { + AttrsetRes::Ok => ExprRes::Ok, + AttrsetRes::Eof => ExprRes::Eof, + }, + Some(L_PAREN) => { + self.builder.start_node_at(start, PARENTHESIZED_EXPR.into()); + self.bump(); + self.expr(None); + self.skip_ws(); + match self.current() { + Some(R_PAREN) => ExprRes::Ok, + Some(_) => todo!(), + None => ExprRes::Eof, + } + } + Some(_) => ExprRes::NoExpr, + None => ExprRes::Eof, + } + } + } + + pub enum ExprRes { + Ok, + Eof, + /// isnt an expression + NoExpr, + } +} + +mod attrset { + use chumsky::container::Container; + use rowan::Checkpoint; + + use super::{expr::ExprRes, instr::NodeRes, Parser}; + use crate::parser::{ + ast::lossless::{lex::SyntaxKind::*, parser::SyntaxError}, + Span, + }; + impl Parser<'_> { + pub(super) fn attrset(&mut self, checkpoint: Checkpoint) -> AttrsetRes { + assert_eq!(self.current(), Some(L_CURLY)); + self.bump(); + self.skip_ws(); + match self.current() { + Some(R_CURLY) => { + self.builder.start_node_at(checkpoint, ATTR_SET.into()); + self.bump(); + self.finish_node(); + AttrsetRes::Ok + } + Some(_) => { + self.builder.start_node_at(checkpoint, ATTR_SET.into()); + let res = match self.attrs() { + AttrRes::Eof => AttrsetRes::Eof, + AttrRes::RCurly | AttrRes::Ok => { + println!("curr: {:?}", self.current()); + AttrsetRes::Ok + } + }; + self.finish_node(); + res + } + None => AttrsetRes::Eof, + } + // self.start_node(ATTR); + } + + fn attrs(&mut self) -> AttrRes { + let mut res = AttrRes::Ok; + + while res == AttrRes::Ok { + println!("it: {:?}", self.tokens.last()); + match self.attr() { + AttrRes::Ok => { + self.skip_ws_without_newlines(); + println!( + "a: {:?}, {:?}", + self.tokens.last(), + self.tokens.get(self.tokens.len() - 2) + ); + println!("errs: {:?}", self.errors); + res = AttrRes::Ok; + let checkpoint_previous_end = self.builder.checkpoint(); + res = match self.current() { + Some(COMMA) => { + self.bump(); + AttrRes::Ok + } + Some(R_CURLY) => { + self.bump(); + res = AttrRes::Ok; + break; + } + Some(NEWLINE) => { + self.skip_ws(); + println!( + "b: {:?}, {:?}", + self.tokens.last(), + self.tokens.get(self.tokens.len() - 2) + ); + match self.current() { + Some(COMMA) => { + self.bump(); + AttrRes::Ok + } + Some(R_CURLY) => { + self.bump(); + res = AttrRes::Ok; + break; + } + Some(IDENT) => { + println!("wtf"); + self.syntax_err_by_checkpoint( + checkpoint_previous_end, + SyntaxError::ExpectedCommaBetweenAttrs, + ); + // self.syntax_err(SyntaxError::ExpectedCommaBetweenAttrs); + AttrRes::Ok + } + Some(_) => { + self.bump(); + AttrRes::Ok + } + None => { + res = AttrRes::Eof; + break; + } + } + } + Some(_) => { + self.bump(); + println!( + "c: {:?}, {:?}", + self.tokens.last(), + self.tokens.get(self.tokens.len() - 2) + ); + AttrRes::Ok + } + None => { + res = AttrRes::Eof; + break; + } + } + } + AttrRes::Eof => { + res = AttrRes::Eof; + break; + } + AttrRes::RCurly => { + res = AttrRes::RCurly; + break; + } + } + } + println!("toks_left: {:?}", self.tokens); + res + } + + fn attr(&mut self) -> AttrRes { + self.skip_ws(); + self.start_node(ATTR); + self.start_node(ATTR_NAME); + match self.current() { + Some(IDENT) => self.bump(), + Some(R_CURLY) => return AttrRes::Ok, + Some(_) => self.expected(IDENT), + None => return AttrRes::Eof, + } + self.finish_node(); + self.skip_ws(); + match self.current() { + Some(COLON) => self.bump(), + Some(R_CURLY) => { + self.expected(COLON); + return AttrRes::RCurly; + } + Some(_) => self.expected(COLON), + None => return AttrRes::Eof, + } + self.skip_ws(); + self.start_node(ATTR_VALUE); + match self.expr(None) { + ExprRes::Ok => self.bump(), + ExprRes::Eof => return AttrRes::Eof, + ExprRes::NoExpr => match self.current() { + Some(COMMA) => self.syntax_err(SyntaxError::AttrExpectedValue), + Some(R_CURLY) => { + self.syntax_err(SyntaxError::AttrExpectedValue); + return AttrRes::RCurly; + } + Some(_) => self.expected(EXPR), + None => unreachable!(), + }, + } + self.finish_node(); + self.finish_node(); + AttrRes::Ok + } + } + + #[derive(PartialEq, Eq)] + pub enum AttrsetRes { + Ok, + Eof, + } + + #[derive(PartialEq, Eq)] + enum AttrRes { + Ok, + Eof, + RCurly, + } +} + +mod instr { + use super::Parser; + use crate::parser::{ + ast::lossless::{lex::SyntaxKind::*, parser::expr::ExprRes}, + Span, + }; + + impl Parser<'_> { + pub(super) fn instr(&mut self) -> NodeRes { + assert_eq!(self.current(), Some(IDENT)); + self.skip_ws(); + self.start_node(INSTR); + self.instr_name(); + + // used to count positionals + let mut i = 0; + let params_checkpoint = self.builder.checkpoint(); + loop { + match self.expr(None) { + ExprRes::Ok => { + i += 1; + continue; + } + ExprRes::NoExpr | ExprRes::Eof => break, + } + } + if i >= 1 { + self.builder + .start_node_at(params_checkpoint, INSTR_PARAMS.into()); + self.finish_node(); + } + self.finish_node(); + NodeRes::Ok + } + + fn instr_name(&mut self) { + self.start_node(INSTR_NAME); + while self.current() == Some(IDENT) { + self.bump(); + self.skip_ws_without_newlines(); + } + self.finish_node(); + } + } + + pub(super) enum NodeRes { + Ok, + Eof, + } +} diff --git a/crates/lang/src/parser/ast/raw_ast.rs b/crates/lang/src/parser/ast/raw_ast.rs new file mode 100644 index 0000000..a0ec749 --- /dev/null +++ b/crates/lang/src/parser/ast/raw_ast.rs @@ -0,0 +1,50 @@ +use indexmap::IndexMap; + +use super::super::Spanned; + +use super::super::Span; +use super::Lit; + +#[derive(Debug, PartialEq)] +pub struct RawExpression<'src> { + pub expr: Box>, + pub span: Span, +} + +impl<'src> RawExpression<'src> { + pub fn new(expr: RawExpr<'src>, span: Span) -> Self { + Self { + expr: Box::new(expr), + span, + } + } +} + +#[derive(Debug, PartialEq)] +pub enum RawExpr<'src> { + Node( + Vec>, + Option, RawExpression<'src>>>>, + ), + SimplePipe(RawExpression<'src>, RawExpression<'src>), + // NamingPipe( + // Box>, + // (Vec>, Vec>), + // Box>, + // ), + MappingPipe(RawExpression<'src>, RawExpression<'src>), + NullPipe(RawExpression<'src>, RawExpression<'src>), + MultiPipe(IndexMap, RawExpression<'src>>), + // LetIn( + // IndexMap, Box>>, + // Box>, + // ), + // $ + Var(&'src str), + // @ + InputVar(&'src str), + AttrSet(Spanned, RawExpression<'src>>>), + Lit(Lit<'src>), + Matrix(Spanned<(u16, u16)>, Vec>), + List(Vec>), +} diff --git a/crates/lang/src/parser/tests.rs b/crates/lang/src/parser/tests.rs index 2bd2779..5bd221f 100644 --- a/crates/lang/src/parser/tests.rs +++ b/crates/lang/src/parser/tests.rs @@ -1,4 +1,4 @@ -use crate::parser::ast::{Expr, File}; +use crate::parser::ast::File; use crate::parser::parse; use crate::tokens::Token; use chumsky::input::Stream; diff --git a/crates/lang/src/tokens.rs b/crates/lang/src/tokens.rs index 6caa57c..3314916 100644 --- a/crates/lang/src/tokens.rs +++ b/crates/lang/src/tokens.rs @@ -14,6 +14,14 @@ pub enum Token<'a> { Let, #[token("in")] In, + #[token("mat")] + Mat, + #[regex("[\\d]+x[\\d]+", |lex| { + let (x, y) = lex.slice().split_once('x').expect("shouldn't fail to split"); + // TODO: handle overflows etc + (x.parse().expect("should only match valid u16s"), y.parse().expect("should only match valid u16s")) + })] + Dimensions((u16, u16)), #[regex("[\\d]+", |lex| lex.slice())] Int(&'a str), #[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)", |lex| lex.slice())] @@ -30,12 +38,11 @@ pub enum Token<'a> { Mult, #[token("/")] Div, - #[regex("[a-zA-Z_]+[a-zA-Z0-9_\\-]*", |lex| lex.slice())] + // TODO: figure out how to allow numbers in words? + #[regex("[a-zA-Z_]+[a-zA-Z_\\-\\d]*", |lex| lex.slice().trim())] Word(&'a str), #[regex("\\$[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] VarIdent(&'a str), - #[token("@..")] - InputSpread, #[regex("\\@[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] InputIdent(&'a str), #[token(",")] @@ -55,7 +62,7 @@ pub enum Token<'a> { #[token(":")] Colon, #[token(";")] - SemiColon, + Semicolon, #[token("[")] BracketOpen, #[token("]")] diff --git a/crates/lang/src/tokens/tests.rs b/crates/lang/src/tokens/tests.rs index 2b0454f..3b35ace 100644 --- a/crates/lang/src/tokens/tests.rs +++ b/crates/lang/src/tokens/tests.rs @@ -34,7 +34,7 @@ lexer_test! { lexer_test! { test_lex_subgroup, - "subgroup(first, second) = a | b { in1: $first } | c { in1: $second }", + "subgroup(first, second) = a | b [ $first ] | c [ $second ]", [ Token::Word("subgroup"), Token::ParenOpen, @@ -46,18 +46,14 @@ lexer_test! { Token::Word("a"), Token::Pipe, Token::Word("b"), - Token::BraceOpen, - Token::Word("in1"), - Token::Colon, + Token::BracketOpen, Token::VarIdent("first"), - Token::BraceClose, + Token::BracketClose, Token::Pipe, Token::Word("c"), - Token::BraceOpen, - Token::Word("in1"), - Token::Colon, + Token::BracketOpen, Token::VarIdent("second"), - Token::BraceClose + Token::BracketClose ] } diff --git a/flake.lock b/flake.lock index 43f154e..7c3a06d 100644 --- a/flake.lock +++ b/flake.lock @@ -33,11 +33,11 @@ "pre-commit-hooks": "pre-commit-hooks_2" }, "locked": { - "lastModified": 1712579011, - "narHash": "sha256-trHgFNW8CW85c1OuAPBI+OGous53KkVhMemvcq7syDo=", + "lastModified": 1712724616, + "narHash": "sha256-qs9uEbrOpp6oXcDOp5cpilyU52t78ZpEPATtaHRVLIU=", "owner": "cachix", "repo": "devenv", - "rev": "a71323c618664a6b7a39bc183b0ce22ac8511cf9", + "rev": "d1a11d14dbe96a03c7f9068e4d3af05f283734e0", "type": "github" }, "original": { @@ -83,11 +83,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1712384501, - "narHash": "sha256-AZmYmEnc1ZkSlxUJVUtGh9VFAqWPr+xtNIiBqD2eKfc=", + "lastModified": 1712730246, + "narHash": "sha256-iB8bFj+07RHpmt+XuGGvYQk2Iwm12u6+DklGq/+Tg5s=", "owner": "nix-community", "repo": "fenix", - "rev": "99c6241db5ca5363c05c8f4acbdf3a4e8fc42844", + "rev": "d402ae4a5e5676722290470f61a5e8e3155b5487", "type": "github" }, "original": { @@ -447,11 +447,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1712163089, - "narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=", + "lastModified": 1712608508, + "narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", + "rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6", "type": "github" }, "original": { @@ -463,11 +463,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1712439257, - "narHash": "sha256-aSpiNepFOMk9932HOax0XwNxbA38GOUVOiXfUVPOrck=", + "lastModified": 1712608508, + "narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ff0dbd94265ac470dda06a657d5fe49de93b4599", + "rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6", "type": "github" }, "original": { @@ -567,11 +567,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1712156296, - "narHash": "sha256-St7ZQrkrr5lmQX9wC1ZJAFxL8W7alswnyZk9d1se3Us=", + "lastModified": 1712663608, + "narHash": "sha256-tN9ZL6kGppmHg84lxlpAlaN+kXWNctKK7Yitq/iXDEw=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "8e581ac348e223488622f4d3003cb2bd412bf27e", + "rev": "a5feb4f05f09adca661c869b1bf2324898cbaa43", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index da80ec2..9d43d6e 100644 --- a/flake.nix +++ b/flake.nix @@ -11,41 +11,59 @@ extra-substituters = "https://devenv.cachix.org"; }; - outputs = { self, nixpkgs, devenv, systems, ... } @ inputs: - let - forEachSystem = nixpkgs.lib.genAttrs (import systems); - in - { - devShells = forEachSystem - (system: - let - pkgs = nixpkgs.legacyPackages.${system}; - in - { - default = devenv.lib.mkShell { - inherit inputs pkgs; - modules = [ - ({pkgs, config, ...}: { - languages.rust = { - enable = true; - channel = "nightly"; - }; + outputs = { + self, + nixpkgs, + devenv, + systems, + ... + } @ inputs: let + forEachSystem = nixpkgs.lib.genAttrs (import systems); + in { + devShells = + forEachSystem + (system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + default = devenv.lib.mkShell { + inherit inputs pkgs; + modules = [ + ({ + pkgs, + config, + ... + }: { + languages.rust = { + enable = true; + channel = "nightly"; + components = [ + "rustc" + "cargo" + "clippy" + "rustfmt" + "rust-src" + ]; + }; - pre-commit.hooks = { - clippy.enable = true; - rustfmt.enable = true; - }; + pre-commit.hooks = { + clippy.enable = true; + rustfmt.enable = true; + }; - packages = with pkgs; [ - just nushell - ripgrep - typst typst-lsp - mold - cargo-nextest cargo-watch - ]; - }) + packages = with pkgs; [ + just + nushell + ripgrep + typst + typst-lsp + mold + cargo-nextest + cargo-watch + rust-analyzer ]; - }; - }); - }; + }) + ]; + }; + }); + }; } diff --git a/testfiles/test.owo b/testfiles/test.owo index 809a6e1..3662b45 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,7 +1,4 @@ -def blend1 = [ - open "test.png", - open "test2.png" - ] - | blend multiply 0.6 - -def blend2 = open "test.png" | blend multiply 0.6 [ open test2.png ] +meow mew meow 5 3.14 "uwu" { + meow: test 24 + another: hi "hello", +} "awa" -- 2.46.0 From 2d59a7f560bf81d24c0fc418a614f858efb454b2 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 12 Apr 2024 01:02:07 +0200 Subject: [PATCH 33/67] lang: start implementing parser combinators (i have no idea what i'm doing) also, the current test.owo crashes for some reason. this is a headache. manual/imperative parsers are a nightmare. --- crates/lang/src/parser/ast/lossless/parser.rs | 18 +++++++++++++++++- flake.nix | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/lang/src/parser/ast/lossless/parser.rs b/crates/lang/src/parser/ast/lossless/parser.rs index a3c30ca..95bb7d7 100644 --- a/crates/lang/src/parser/ast/lossless/parser.rs +++ b/crates/lang/src/parser/ast/lossless/parser.rs @@ -1,6 +1,5 @@ use std::borrow::Borrow; -use chumsky::container::Container; use rowan::{ Checkpoint, GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken, }; @@ -12,6 +11,23 @@ use crate::parser::{ use super::lex::{self, SyntaxKind}; +mod parsers { + use rowan::GreenNode; + + use crate::parser::ast::lossless::lex::SyntaxKind; + + use super::SyntaxError; + + struct ParseResult { + green_node: GreenNode, + errors: Vec, + } + + trait Parser { + fn parse<'src>(input: &[(SyntaxKind, &'src str)]) -> ParseResult; + } +} + #[derive(PartialEq, Eq)] pub struct Parse { pub green_node: GreenNode, diff --git a/flake.nix b/flake.nix index 9d43d6e..3c8d815 100644 --- a/flake.nix +++ b/flake.nix @@ -46,7 +46,7 @@ }; pre-commit.hooks = { - clippy.enable = true; + clippy.enable = false; rustfmt.enable = true; }; -- 2.46.0 From f7b61f9e0ef1b2085d4c4ad7ada721d4cec36be3 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 12 Apr 2024 15:43:34 +0200 Subject: [PATCH 34/67] lang: start working on parser that parses to events --- crates/lang/src/main.rs | 12 ++++-- crates/lang/src/parser/ast/lossless/parser.rs | 37 ++++++++++++++----- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 16ec4e4..203e1eb 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -3,7 +3,10 @@ use std::{fs, path::PathBuf}; use clap::Parser; use lang::{ err_reporting::ErrorCollector, - parser::ast::lossless::{lex, parser}, + parser::ast::lossless::{ + lex, + parser::{self, parser_to_events::to_events}, + }, }; #[derive(Parser)] @@ -17,8 +20,11 @@ fn main() { let n = args.file.clone(); let f = fs::read_to_string(n.clone()).expect("failed to read file"); println!("toks: {:?}", lex::lex(&f)); - let parse_res = parser::parse(&f); - println!("parse: {:?}", parse_res); + println!("evs: {:?}", to_events(&f)); + + // let parse_res = parser::parse(&f); + // println!("parse: {:?}", parse_res); + // dbg!(lex::lex(&f)); // let mut err_collector = ErrorCollector::new(vec![(n.to_str().unwrap(), &f)]); diff --git a/crates/lang/src/parser/ast/lossless/parser.rs b/crates/lang/src/parser/ast/lossless/parser.rs index 95bb7d7..b5f5e85 100644 --- a/crates/lang/src/parser/ast/lossless/parser.rs +++ b/crates/lang/src/parser/ast/lossless/parser.rs @@ -11,20 +11,37 @@ use crate::parser::{ use super::lex::{self, SyntaxKind}; -mod parsers { - use rowan::GreenNode; +pub mod parser_to_events { + use chumsky::Parser; - use crate::parser::ast::lossless::lex::SyntaxKind; + use crate::parser::ast::lossless::lex::SyntaxKind::{self, *}; - use super::SyntaxError; - - struct ParseResult { - green_node: GreenNode, - errors: Vec, + #[derive(Debug, PartialEq, Eq)] + pub(super) enum Event { + StartNode, + StartErr(SyntaxError), + EatToken, + FinishNode, + FinishErr, } - trait Parser { - fn parse<'src>(input: &[(SyntaxKind, &'src str)]) -> ParseResult; + #[derive(Debug, PartialEq, Eq)] + enum SyntaxError { + Expected(SyntaxKind), + AttrExpectedValue, + /// guessed if there's a newline and attr on next line without comma + /// should then suggest comma after attr + ExpectedCommaBetweenAttrs, + } + + pub fn to_events(src: &str) -> Vec { + let mut tokens = lex::lex(src); + parser().parse(tokens) + } + + pub fn parser() -> impl Parser<'static, SyntaxKind, Vec> { + let whitespace = just(WHITESPACE).or(NEWLINE).repeated().collect::>(); + whitespace } } -- 2.46.0 From 1711d17fa6f89e6b9adc4884f0d35b9955b02acd Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 12 Apr 2024 20:55:55 +0200 Subject: [PATCH 35/67] lang: parsing to events now --- crates/lang/Cargo.toml | 2 +- crates/lang/src/lib.rs | 1 + crates/lang/src/main.rs | 4 +- crates/lang/src/parser/ast/lossless/lex.rs | 1 - crates/lang/src/parser/ast/lossless/parser.rs | 461 +++++------------- flake.lock | 54 +- flake.nix | 31 +- testfiles/test.owo | 5 +- 8 files changed, 176 insertions(+), 383 deletions(-) diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 8c1d3db..3e03209 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] logos = "0.14" -chumsky = {version= "1.0.0-alpha.7", features=["label"]} +chumsky = {version= "1.0.0-alpha.7", features=["label", "extension"]} petgraph = { workspace = true} indexmap = "2.2.6" clap = { version = "4", features = ["derive"] } diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index 94a1430..ebf22d3 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(type_alias_impl_trait)] pub mod err_reporting; pub mod parser; pub mod tokens; diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 203e1eb..346862e 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -5,7 +5,7 @@ use lang::{ err_reporting::ErrorCollector, parser::ast::lossless::{ lex, - parser::{self, parser_to_events::to_events}, + parser::{self, parse}, }, }; @@ -20,7 +20,7 @@ fn main() { let n = args.file.clone(); let f = fs::read_to_string(n.clone()).expect("failed to read file"); println!("toks: {:?}", lex::lex(&f)); - println!("evs: {:?}", to_events(&f)); + println!("parse res: {:?}", parse(&f)); // let parse_res = parser::parse(&f); // println!("parse: {:?}", parse_res); diff --git a/crates/lang/src/parser/ast/lossless/lex.rs b/crates/lang/src/parser/ast/lossless/lex.rs index c25608a..e2a867b 100644 --- a/crates/lang/src/parser/ast/lossless/lex.rs +++ b/crates/lang/src/parser/ast/lossless/lex.rs @@ -10,7 +10,6 @@ pub fn lex(src: &str) -> Vec<(SyntaxKind, &str)> { r.push((tok_res.unwrap_or(SyntaxKind::LEX_ERR), lex.slice())) } - r.reverse(); r } diff --git a/crates/lang/src/parser/ast/lossless/parser.rs b/crates/lang/src/parser/ast/lossless/parser.rs index b5f5e85..adcb08e 100644 --- a/crates/lang/src/parser/ast/lossless/parser.rs +++ b/crates/lang/src/parser/ast/lossless/parser.rs @@ -9,24 +9,29 @@ use crate::parser::{ Span, }; +use self::parser_to_events::{to_events, Event}; + use super::lex::{self, SyntaxKind}; pub mod parser_to_events { - use chumsky::Parser; + use chumsky::prelude::*; - use crate::parser::ast::lossless::lex::SyntaxKind::{self, *}; + use crate::parser::ast::lossless::lex::{ + self, + SyntaxKind::{self, *}, + }; - #[derive(Debug, PartialEq, Eq)] - pub(super) enum Event { - StartNode, + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub enum Event { + StartNode(SyntaxKind), StartErr(SyntaxError), EatToken, FinishNode, FinishErr, } - #[derive(Debug, PartialEq, Eq)] - enum SyntaxError { + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub enum SyntaxError { Expected(SyntaxKind), AttrExpectedValue, /// guessed if there's a newline and attr on next line without comma @@ -34,14 +39,107 @@ pub mod parser_to_events { ExpectedCommaBetweenAttrs, } - pub fn to_events(src: &str) -> Vec { - let mut tokens = lex::lex(src); - parser().parse(tokens) + pub fn to_events(tokens: &[(SyntaxKind, &str)]) -> Vec { + let mut only_toks: Vec = tokens.iter().map(|(t, _)| *t).collect(); + let res = parser().parse(&only_toks); + res.unwrap() } - pub fn parser() -> impl Parser<'static, SyntaxKind, Vec> { - let whitespace = just(WHITESPACE).or(NEWLINE).repeated().collect::>(); - whitespace + macro_rules! padded { + ($parser:expr) => {{ + let ws = one_of([WHITESPACE, NEWLINE]) + .to(Event::EatToken) + .repeated() + .collect::>(); + ws.then($parser) + .then(ws) + .map(|((mut before, mut c), mut after)| { + before.append(&mut c); + before.append(&mut after); + before + }) + }}; + } + + pub fn parser<'toks>() -> impl Parser<'toks, &'toks [SyntaxKind], Vec> { + let ws = one_of([WHITESPACE, NEWLINE]) + .to(Event::EatToken) + .repeated() + .collect::>(); + // let ws_without_newlines = just(WHITESPACE) + // .to(Event::EatToken) + // .repeated() + // .collect::>(); + let parenthesized = |c| { + just(L_PAREN) + .to(vec![Event::EatToken]) + .then(c) + .then(just(R_PAREN).to(vec![Event::EatToken])) + .map(|((mut before, mut c), mut after)| { + before.append(&mut c); + before.append(&mut after); + before + }) + }; + + let expr = recursive(|expr| { + let lit = one_of([INT_NUM, FLOAT_NUM, STRING]).to(vec![ + Event::StartNode(EXPR), + Event::EatToken, + Event::FinishNode, + ]); + + let atom = lit.clone().or(parenthesized(expr)); + + let ident = just(IDENT).to(vec![Event::EatToken]); + let instr_name = ident + .clone() + .map(|mut v| { + v.insert(0, Event::StartNode(INSTR_NAME)); + v + }) + .foldl( + ws.then(ident).repeated(), + |mut ident, (mut ws, mut next)| { + ident.append(&mut ws); + ident.append(&mut next); + ident + }, + ) + .map(|mut v| { + v.push(Event::FinishNode); + v + }); + let instr = padded!(instr_name) + .then( + atom.clone() + .map(|mut v| { + v.insert(0, Event::StartNode(INSTR_PARAMS)); + v + }) + .foldl( + ws.then(atom.clone()).repeated(), + |mut cur, (mut ws, mut next)| { + cur.append(&mut ws); + cur.append(&mut next); + cur + }, + ) + .map(|mut v| { + v.push(Event::FinishNode); + v + }), + ) + .map(|(mut name, mut params)| { + name.insert(0, Event::StartNode(INSTR)); + name.append(&mut params); + name.push(Event::FinishNode); + name + }); + padded!(instr.or(lit).or(atom)) + }); + expr + // .map(|(lit, mut ev)| lit.append(&mut ev)); } } @@ -99,7 +197,7 @@ enum SyntaxError { } pub fn parse(src: &str) -> Parse { - let mut tokens = lex::lex(src); + let tokens = lex::lex(src); Parser { tokens, builder: GreenNodeBuilder::new(), @@ -110,12 +208,19 @@ pub fn parse(src: &str) -> Parse { impl Parser<'_> { fn parse(mut self) -> Parse { - self.start_node(ROOT); + let evs = to_events(&self.tokens); + self.builder.start_node(ROOT.into()); - match self.expr(None) { - expr::ExprRes::Ok => (), - expr::ExprRes::Eof => (), - expr::ExprRes::NoExpr => todo!(), + self.tokens.reverse(); + + for ev in evs { + match ev { + Event::StartNode(kind) => self.builder.start_node(kind.into()), + Event::StartErr(SyntaxError) => todo!(), + Event::EatToken => self.bump(), + Event::FinishNode => self.builder.finish_node(), + Event::FinishErr => todo!(), + } } self.builder.finish_node(); @@ -124,13 +229,6 @@ impl Parser<'_> { } } - fn start_node(&mut self, kind: SyntaxKind) { - self.builder.start_node(kind.into()); - } - fn finish_node(&mut self) { - self.builder.finish_node(); - } - /// Advance one token, adding it to the current branch of the tree builder. fn bump(&mut self) { let (kind, text) = self.tokens.pop().unwrap(); @@ -143,7 +241,7 @@ impl Parser<'_> { } fn syntax_err_by_checkpoint(&mut self, checkpoint: Checkpoint, err: SyntaxError) { self.builder.start_node_at(checkpoint, PARSE_ERR.into()); - self.finish_node(); + self.builder.finish_node(); self.errors.push(err); } fn expected(&mut self, expected: SyntaxKind) { @@ -158,313 +256,4 @@ impl Parser<'_> { .get(self.tokens.len() - 2) .map(|(kind, _)| *kind) } - fn skip_ws(&mut self) { - while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) { - self.bump() - } - } - fn skip_ws_without_newlines(&mut self) { - while self.current() == Some(WHITESPACE) { - self.bump() - } - } -} - -mod expr { - use rowan::Checkpoint; - - use super::{attrset::AttrsetRes, instr::NodeRes, Parser}; - use crate::parser::{ast::lossless::lex::SyntaxKind::*, Span}; - impl Parser<'_> { - pub(super) fn expr(&mut self, start: Option) -> ExprRes { - self.skip_ws(); - let start = start.unwrap_or_else(|| self.builder.checkpoint()); - match self.current() { - Some(IDENT) => { - let expr_res = match self.instr() { - NodeRes::Ok => ExprRes::Ok, - NodeRes::Eof => ExprRes::Eof, - }; - self.builder.start_node_at(start, EXPR.into()); - self.finish_node(); - expr_res - } - Some(_) => self.atom(Some(start)), - None => ExprRes::Eof, - } - } - - pub(super) fn atom(&mut self, start: Option) -> ExprRes { - self.skip_ws(); - let start = start.unwrap_or_else(|| self.builder.checkpoint()); - match self.current() { - Some(INT_NUM | FLOAT_NUM | STRING) => { - self.bump(); - self.builder.start_node_at(start, EXPR.into()); - self.finish_node(); - ExprRes::Ok - } - Some(L_CURLY) => match self.attrset(start) { - AttrsetRes::Ok => ExprRes::Ok, - AttrsetRes::Eof => ExprRes::Eof, - }, - Some(L_PAREN) => { - self.builder.start_node_at(start, PARENTHESIZED_EXPR.into()); - self.bump(); - self.expr(None); - self.skip_ws(); - match self.current() { - Some(R_PAREN) => ExprRes::Ok, - Some(_) => todo!(), - None => ExprRes::Eof, - } - } - Some(_) => ExprRes::NoExpr, - None => ExprRes::Eof, - } - } - } - - pub enum ExprRes { - Ok, - Eof, - /// isnt an expression - NoExpr, - } -} - -mod attrset { - use chumsky::container::Container; - use rowan::Checkpoint; - - use super::{expr::ExprRes, instr::NodeRes, Parser}; - use crate::parser::{ - ast::lossless::{lex::SyntaxKind::*, parser::SyntaxError}, - Span, - }; - impl Parser<'_> { - pub(super) fn attrset(&mut self, checkpoint: Checkpoint) -> AttrsetRes { - assert_eq!(self.current(), Some(L_CURLY)); - self.bump(); - self.skip_ws(); - match self.current() { - Some(R_CURLY) => { - self.builder.start_node_at(checkpoint, ATTR_SET.into()); - self.bump(); - self.finish_node(); - AttrsetRes::Ok - } - Some(_) => { - self.builder.start_node_at(checkpoint, ATTR_SET.into()); - let res = match self.attrs() { - AttrRes::Eof => AttrsetRes::Eof, - AttrRes::RCurly | AttrRes::Ok => { - println!("curr: {:?}", self.current()); - AttrsetRes::Ok - } - }; - self.finish_node(); - res - } - None => AttrsetRes::Eof, - } - // self.start_node(ATTR); - } - - fn attrs(&mut self) -> AttrRes { - let mut res = AttrRes::Ok; - - while res == AttrRes::Ok { - println!("it: {:?}", self.tokens.last()); - match self.attr() { - AttrRes::Ok => { - self.skip_ws_without_newlines(); - println!( - "a: {:?}, {:?}", - self.tokens.last(), - self.tokens.get(self.tokens.len() - 2) - ); - println!("errs: {:?}", self.errors); - res = AttrRes::Ok; - let checkpoint_previous_end = self.builder.checkpoint(); - res = match self.current() { - Some(COMMA) => { - self.bump(); - AttrRes::Ok - } - Some(R_CURLY) => { - self.bump(); - res = AttrRes::Ok; - break; - } - Some(NEWLINE) => { - self.skip_ws(); - println!( - "b: {:?}, {:?}", - self.tokens.last(), - self.tokens.get(self.tokens.len() - 2) - ); - match self.current() { - Some(COMMA) => { - self.bump(); - AttrRes::Ok - } - Some(R_CURLY) => { - self.bump(); - res = AttrRes::Ok; - break; - } - Some(IDENT) => { - println!("wtf"); - self.syntax_err_by_checkpoint( - checkpoint_previous_end, - SyntaxError::ExpectedCommaBetweenAttrs, - ); - // self.syntax_err(SyntaxError::ExpectedCommaBetweenAttrs); - AttrRes::Ok - } - Some(_) => { - self.bump(); - AttrRes::Ok - } - None => { - res = AttrRes::Eof; - break; - } - } - } - Some(_) => { - self.bump(); - println!( - "c: {:?}, {:?}", - self.tokens.last(), - self.tokens.get(self.tokens.len() - 2) - ); - AttrRes::Ok - } - None => { - res = AttrRes::Eof; - break; - } - } - } - AttrRes::Eof => { - res = AttrRes::Eof; - break; - } - AttrRes::RCurly => { - res = AttrRes::RCurly; - break; - } - } - } - println!("toks_left: {:?}", self.tokens); - res - } - - fn attr(&mut self) -> AttrRes { - self.skip_ws(); - self.start_node(ATTR); - self.start_node(ATTR_NAME); - match self.current() { - Some(IDENT) => self.bump(), - Some(R_CURLY) => return AttrRes::Ok, - Some(_) => self.expected(IDENT), - None => return AttrRes::Eof, - } - self.finish_node(); - self.skip_ws(); - match self.current() { - Some(COLON) => self.bump(), - Some(R_CURLY) => { - self.expected(COLON); - return AttrRes::RCurly; - } - Some(_) => self.expected(COLON), - None => return AttrRes::Eof, - } - self.skip_ws(); - self.start_node(ATTR_VALUE); - match self.expr(None) { - ExprRes::Ok => self.bump(), - ExprRes::Eof => return AttrRes::Eof, - ExprRes::NoExpr => match self.current() { - Some(COMMA) => self.syntax_err(SyntaxError::AttrExpectedValue), - Some(R_CURLY) => { - self.syntax_err(SyntaxError::AttrExpectedValue); - return AttrRes::RCurly; - } - Some(_) => self.expected(EXPR), - None => unreachable!(), - }, - } - self.finish_node(); - self.finish_node(); - AttrRes::Ok - } - } - - #[derive(PartialEq, Eq)] - pub enum AttrsetRes { - Ok, - Eof, - } - - #[derive(PartialEq, Eq)] - enum AttrRes { - Ok, - Eof, - RCurly, - } -} - -mod instr { - use super::Parser; - use crate::parser::{ - ast::lossless::{lex::SyntaxKind::*, parser::expr::ExprRes}, - Span, - }; - - impl Parser<'_> { - pub(super) fn instr(&mut self) -> NodeRes { - assert_eq!(self.current(), Some(IDENT)); - self.skip_ws(); - self.start_node(INSTR); - self.instr_name(); - - // used to count positionals - let mut i = 0; - let params_checkpoint = self.builder.checkpoint(); - loop { - match self.expr(None) { - ExprRes::Ok => { - i += 1; - continue; - } - ExprRes::NoExpr | ExprRes::Eof => break, - } - } - if i >= 1 { - self.builder - .start_node_at(params_checkpoint, INSTR_PARAMS.into()); - self.finish_node(); - } - self.finish_node(); - NodeRes::Ok - } - - fn instr_name(&mut self) { - self.start_node(INSTR_NAME); - while self.current() == Some(IDENT) { - self.bump(); - self.skip_ws_without_newlines(); - } - self.finish_node(); - } - } - - pub(super) enum NodeRes { - Ok, - Eof, - } } diff --git a/flake.lock b/flake.lock index 7c3a06d..9719001 100644 --- a/flake.lock +++ b/flake.lock @@ -11,11 +11,11 @@ "pre-commit-hooks": "pre-commit-hooks" }, "locked": { - "lastModified": 1710475558, - "narHash": "sha256-egKrPCKjy/cE+NqCj4hg2fNX/NwLCf0bRDInraYXDgs=", + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", "owner": "cachix", "repo": "cachix", - "rev": "661bbb7f8b55722a0406456b15267b5426a3bda6", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", "type": "github" }, "original": { @@ -33,11 +33,11 @@ "pre-commit-hooks": "pre-commit-hooks_2" }, "locked": { - "lastModified": 1712724616, - "narHash": "sha256-qs9uEbrOpp6oXcDOp5cpilyU52t78ZpEPATtaHRVLIU=", + "lastModified": 1712925466, + "narHash": "sha256-MJ6VxGNu/ftbn8SErJjBz80FUNXkZfcObHg/JP7wwAc=", "owner": "cachix", "repo": "devenv", - "rev": "d1a11d14dbe96a03c7f9068e4d3af05f283734e0", + "rev": "1af93652caf48bfeef6ba7d1cf59fc66e506e5c2", "type": "github" }, "original": { @@ -83,11 +83,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1712730246, - "narHash": "sha256-iB8bFj+07RHpmt+XuGGvYQk2Iwm12u6+DklGq/+Tg5s=", + "lastModified": 1712903033, + "narHash": "sha256-KcvsEm0h1mIwBHFAzWFBjGihnbf2fxpAaXOdVbUfAI4=", "owner": "nix-community", "repo": "fenix", - "rev": "d402ae4a5e5676722290470f61a5e8e3155b5487", + "rev": "c739f83545e625227f4d0af7fe2a71e69931fa4c", "type": "github" }, "original": { @@ -335,11 +335,11 @@ "nixpkgs-regression": "nixpkgs-regression_2" }, "locked": { - "lastModified": 1710500156, - "narHash": "sha256-zvCqeUO2GLOm7jnU23G4EzTZR7eylcJN+HJ5svjmubI=", + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", "owner": "domenkozar", "repo": "nix", - "rev": "c5bbf14ecbd692eeabf4184cc8d50f79c2446549", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", "type": "github" }, "original": { @@ -431,11 +431,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1710236354, - "narHash": "sha256-vWrciFdq49vve43g4pbi7NjmL4cwG1ifXnQx+dU3T5E=", + "lastModified": 1710796454, + "narHash": "sha256-lQlICw60RhH8sHTDD/tJiiJrlAfNn8FDI9c+7G2F0SE=", "owner": "cachix", "repo": "devenv-nixpkgs", - "rev": "829e73affeadfb4198a7105cbe3a03153d13edc9", + "rev": "06fb0f1c643aee3ae6838dda3b37ef0abc3c763b", "type": "github" }, "original": { @@ -447,11 +447,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1712608508, - "narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=", + "lastModified": 1712791164, + "narHash": "sha256-3sbWO1mbpWsLepZGbWaMovSO7ndZeFqDSdX0hZ9nVyw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6", + "rev": "1042fd8b148a9105f3c0aca3a6177fd1d9360ba5", "type": "github" }, "original": { @@ -463,11 +463,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1712608508, - "narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=", + "lastModified": 1712791164, + "narHash": "sha256-3sbWO1mbpWsLepZGbWaMovSO7ndZeFqDSdX0hZ9nVyw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6", + "rev": "1042fd8b148a9105f3c0aca3a6177fd1d9360ba5", "type": "github" }, "original": { @@ -543,11 +543,11 @@ "nixpkgs-stable": "nixpkgs-stable_2" }, "locked": { - "lastModified": 1712055707, - "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", + "lastModified": 1712897695, + "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", + "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", "type": "github" }, "original": { @@ -567,11 +567,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1712663608, - "narHash": "sha256-tN9ZL6kGppmHg84lxlpAlaN+kXWNctKK7Yitq/iXDEw=", + "lastModified": 1712818880, + "narHash": "sha256-VDxsvgj/bNypHq48tQWtc3VRbWvzlFjzKf9ZZIVO10Y=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "a5feb4f05f09adca661c869b1bf2324898cbaa43", + "rev": "657b33b0cb9bd49085202e91ad5b4676532c9140", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3c8d815..caad7d6 100644 --- a/flake.nix +++ b/flake.nix @@ -15,6 +15,7 @@ self, nixpkgs, devenv, + fenix, systems, ... } @ inputs: let @@ -24,6 +25,11 @@ forEachSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; + toolchain = with fenix.packages.${system}; + combine [ + default.toolchain + rust-analyzer + ]; in { default = devenv.lib.mkShell { inherit inputs pkgs; @@ -33,17 +39,18 @@ config, ... }: { - languages.rust = { - enable = true; - channel = "nightly"; - components = [ - "rustc" - "cargo" - "clippy" - "rustfmt" - "rust-src" - ]; - }; + # languages.rust = { + # enable = true; + # channel = "nightly"; + # components = [ + # "rustc" + # "cargo" + # "clippy" + # "rustfmt" + # "rust-src" + # "rust-analyzer" + # ]; + # }; pre-commit.hooks = { clippy.enable = false; @@ -59,7 +66,7 @@ mold cargo-nextest cargo-watch - rust-analyzer + toolchain ]; }) ]; diff --git a/testfiles/test.owo b/testfiles/test.owo index 3662b45..78cbbda 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,4 +1 @@ -meow mew meow 5 3.14 "uwu" { - meow: test 24 - another: hi "hello", -} "awa" +hello world test 42 3.14 "uwu" -- 2.46.0 From be637846b15bc019f888ec9e56a5d6680d272843 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Fri, 12 Apr 2024 21:31:55 +0200 Subject: [PATCH 36/67] lang: kinda fun parsing things that can now parse attribute sets with one attribute --- crates/lang/src/parser/ast/lossless/parser.rs | 66 ++++++++++++++----- testfiles/test.owo | 8 ++- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/crates/lang/src/parser/ast/lossless/parser.rs b/crates/lang/src/parser/ast/lossless/parser.rs index adcb08e..dc7868d 100644 --- a/crates/lang/src/parser/ast/lossless/parser.rs +++ b/crates/lang/src/parser/ast/lossless/parser.rs @@ -40,7 +40,7 @@ pub mod parser_to_events { } pub fn to_events(tokens: &[(SyntaxKind, &str)]) -> Vec { - let mut only_toks: Vec = tokens.iter().map(|(t, _)| *t).collect(); + let only_toks: Vec = tokens.iter().map(|(t, _)| *t).collect(); let res = parser().parse(&only_toks); res.unwrap() } @@ -60,27 +60,28 @@ pub mod parser_to_events { }) }}; } + macro_rules! parenthesized { + ($parser:expr) => { + just(L_PAREN) + .to(vec![Event::EatToken]) + .then($parser) + .then(just(R_PAREN).to(vec![Event::EatToken])) + .map(|((mut before, mut c), mut after)| { + before.insert(0, Event::StartNode(PARENTHESIZED_EXPR)); + before.append(&mut c); + before.append(&mut after); + before.push(Event::FinishNode); + before + }) + }; + } pub fn parser<'toks>() -> impl Parser<'toks, &'toks [SyntaxKind], Vec> { let ws = one_of([WHITESPACE, NEWLINE]) .to(Event::EatToken) .repeated() .collect::>(); - // let ws_without_newlines = just(WHITESPACE) - // .to(Event::EatToken) - // .repeated() - // .collect::>(); - let parenthesized = |c| { - just(L_PAREN) - .to(vec![Event::EatToken]) - .then(c) - .then(just(R_PAREN).to(vec![Event::EatToken])) - .map(|((mut before, mut c), mut after)| { - before.append(&mut c); - before.append(&mut after); - before - }) - }; + let ident = just(IDENT).to(vec![Event::EatToken]); let expr = recursive(|expr| { let lit = one_of([INT_NUM, FLOAT_NUM, STRING]).to(vec![ @@ -88,10 +89,39 @@ pub mod parser_to_events { Event::EatToken, Event::FinishNode, ]); + let attrset = just(L_CURLY) + .then( + padded!(just(IDENT).to(vec![ + Event::StartNode(ATTR), + Event::StartNode(ATTR_NAME), + Event::EatToken, + Event::FinishNode + ])) + .then(just(COLON)) + .then(padded!(expr.clone().map(|mut exp: Vec| { + exp.insert(0, Event::StartNode(ATTR_VALUE)); + exp.push(Event::FinishNode); + exp.push(Event::FinishNode); + exp + }))) + .map(|((mut name, _), mut value)| { + // colon + name.push(Event::EatToken); + name.append(&mut value); + name + }), + ) + .then(just(R_CURLY)) + .map(|((_, mut attrs), _)| { + attrs.insert(0, Event::StartNode(ATTR_SET)); + attrs.insert(0, Event::EatToken); + attrs.push(Event::EatToken); + attrs.push(Event::FinishNode); + attrs + }); - let atom = lit.clone().or(parenthesized(expr)); + let atom = lit.clone().or(attrset).or(parenthesized!(expr)); - let ident = just(IDENT).to(vec![Event::EatToken]); let instr_name = ident .clone() .map(|mut v| { diff --git a/testfiles/test.owo b/testfiles/test.owo index 78cbbda..dc95a8d 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1 +1,7 @@ -hello world test 42 3.14 "uwu" +hello world test + 42 + (another command 3.14 "meow") + "uwu" + { + some: attrs 42 (meow gay 1) + } -- 2.46.0 From 6d8b79e8f7b812c3cc51875470fdb934d58fccd6 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 15 Apr 2024 16:22:33 +0200 Subject: [PATCH 37/67] lang: apparently add event debug printer --- crates/lang/src/parser/ast/lossless/parser.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/lang/src/parser/ast/lossless/parser.rs b/crates/lang/src/parser/ast/lossless/parser.rs index dc7868d..3a9c11d 100644 --- a/crates/lang/src/parser/ast/lossless/parser.rs +++ b/crates/lang/src/parser/ast/lossless/parser.rs @@ -240,6 +240,7 @@ impl Parser<'_> { fn parse(mut self) -> Parse { let evs = to_events(&self.tokens); self.builder.start_node(ROOT.into()); + println!("evs: {evs:?}"); self.tokens.reverse(); -- 2.46.0 From 381ab45edcf496ec1ed6b564602e41a0a52ecf66 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 24 Apr 2024 11:07:38 +0200 Subject: [PATCH 38/67] lang: rewrite parser --- Cargo.lock | 215 +++++-------- crates/lang/Cargo.toml | 3 +- crates/lang/src/err_reporting.rs | 88 ------ crates/lang/src/lib.rs | 4 +- crates/lang/src/main.rs | 24 +- crates/lang/src/parser.rs | 257 ++++++++-------- crates/lang/src/parser/ast.rs | 24 -- crates/lang/src/parser/ast/ast_tree.rs | 31 -- crates/lang/src/parser/ast/lossless.rs | 19 -- crates/lang/src/parser/ast/lossless/parser.rs | 290 ------------------ crates/lang/src/parser/ast/raw_ast.rs | 50 --- crates/lang/src/parser/error.rs | 6 + crates/lang/src/parser/events.rs | 23 ++ crates/lang/src/parser/grammar.rs | 14 + crates/lang/src/parser/grammar/expression.rs | 14 + .../parser/grammar/expression/instruction.rs | 30 ++ .../lang/src/parser/grammar/expression/lit.rs | 20 ++ crates/lang/src/parser/input.rs | 61 ++++ crates/lang/src/parser/output.rs | 113 +++++++ crates/lang/src/parser/parser.rs | 6 + .../{ast/lossless/lex.rs => syntax_kind.rs} | 27 +- crates/lang/src/parser/tests.rs | 142 --------- crates/lang/src/tokens.rs | 81 ----- crates/lang/src/tokens/tests.rs | 135 -------- testfiles/test.owo | 8 +- 25 files changed, 524 insertions(+), 1161 deletions(-) delete mode 100644 crates/lang/src/err_reporting.rs delete mode 100644 crates/lang/src/parser/ast.rs delete mode 100644 crates/lang/src/parser/ast/ast_tree.rs delete mode 100644 crates/lang/src/parser/ast/lossless.rs delete mode 100644 crates/lang/src/parser/ast/lossless/parser.rs delete mode 100644 crates/lang/src/parser/ast/raw_ast.rs create mode 100644 crates/lang/src/parser/error.rs create mode 100644 crates/lang/src/parser/events.rs create mode 100644 crates/lang/src/parser/grammar.rs create mode 100644 crates/lang/src/parser/grammar/expression.rs create mode 100644 crates/lang/src/parser/grammar/expression/instruction.rs create mode 100644 crates/lang/src/parser/grammar/expression/lit.rs create mode 100644 crates/lang/src/parser/input.rs create mode 100644 crates/lang/src/parser/output.rs create mode 100644 crates/lang/src/parser/parser.rs rename crates/lang/src/parser/{ast/lossless/lex.rs => syntax_kind.rs} (74%) delete mode 100644 crates/lang/src/tokens.rs delete mode 100644 crates/lang/src/tokens/tests.rs diff --git a/Cargo.lock b/Cargo.lock index a25751d..d759c0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,33 +8,6 @@ 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 = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - [[package]] name = "anstream" version = "0.6.5" @@ -160,31 +133,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cc" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" - [[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.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b80276986f86789dc56ca6542d53bba9cda3c66091ebbe7bd96fc1bdf20f1f" -dependencies = [ - "hashbrown", - "regex-automata", - "serde", - "stacker", - "unicode-ident", -] - [[package]] name = "clap" version = "4.4.12" @@ -298,6 +252,40 @@ dependencies = [ "phf", ] +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "deranged" version = "0.3.11" @@ -328,6 +316,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "drop_bomb" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" + [[package]] name = "ego-tree" version = "0.6.2" @@ -340,6 +334,27 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -447,10 +462,6 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "heck" @@ -458,6 +469,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "image" version = "0.24.7" @@ -516,9 +533,10 @@ name = "lang" version = "0.1.0" dependencies = [ "ariadne", - "chumsky", "clap", + "drop_bomb", "ego-tree", + "enumset", "indexmap", "logos", "petgraph", @@ -584,7 +602,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.8.2", + "regex-syntax", "syn", ] @@ -661,12 +679,6 @@ dependencies = [ "libc", ] -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - [[package]] name = "option-ext" version = "0.2.0" @@ -759,15 +771,6 @@ 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 = "qoi" version = "0.4.1" @@ -851,23 +854,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "regex-automata" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.5", -] - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - [[package]] name = "regex-syntax" version = "0.8.2" @@ -975,19 +961,6 @@ dependencies = [ "lock_api", ] -[[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" @@ -1090,12 +1063,6 @@ 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 = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1108,28 +1075,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" -[[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" @@ -1268,26 +1213,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zune-inflate" version = "0.2.54" diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 3e03209..fba2bcf 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -7,13 +7,14 @@ edition = "2021" [dependencies] logos = "0.14" -chumsky = {version= "1.0.0-alpha.7", features=["label", "extension"]} petgraph = { workspace = true} indexmap = "2.2.6" clap = { version = "4", features = ["derive"] } ariadne = "0.4.0" ego-tree = "0.6.2" rowan = "0.15.15" +drop_bomb = "0.1.5" +enumset = "1.1.3" [lints] workspace = true diff --git a/crates/lang/src/err_reporting.rs b/crates/lang/src/err_reporting.rs deleted file mode 100644 index ebf7866..0000000 --- a/crates/lang/src/err_reporting.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::{collections::HashMap, fs}; - -use ariadne::{sources, Label, Report, Source}; -use chumsky::{ - error::{self, Rich}, - ParseResult, -}; -use indexmap::IndexMap; - -use crate::{ - parser::{ast::File, Span}, - tokens::Token, -}; - -#[derive(Debug, PartialEq, Eq, Hash)] -pub enum Stage { - Lex, - Parse, -} - -impl Stage { - fn variants() -> [Stage; 2] { - [Stage::Lex, Stage::Parse] - } -} - -pub struct ErrorCollector<'filename, 'tokens, 'src> { - files: HashMap<&'filename str, &'src str>, - raw_errors: IndexMap<(&'filename str, Stage), Vec, Span>>>, -} - -impl<'filename, 'tokens, 'src> ErrorCollector<'filename, 'tokens, 'src> { - pub fn new(files: Vec<(&'filename str, &'src str)>) -> Self { - Self { - files: HashMap::from_iter(files.clone()), - raw_errors: files - .iter() - .flat_map(|(name, _)| Stage::variants().map(|s| (name, s))) - .map(|(name, stage)| ((*name, stage), Vec::new())) - .collect(), - } - } - - pub fn insert_many( - &mut self, - file: &'filename str, - curr_stage: Stage, - mut errs: Vec, Span>>, - ) { - let err_vec = self - .raw_errors - .get_mut(&(file, curr_stage)) - .expect("filename should exist"); - err_vec.append(&mut errs); - } - - pub fn analyze_and_report(self) { - let ErrorCollector { files, raw_errors } = self; - todo!() - } - - pub fn report_raw(self) { - let ErrorCollector { files, raw_errors } = self; - - for ((file, stage), errs) in raw_errors.into_iter() { - for err in errs { - eprintln!("e: {err:?}"); - Report::build(ariadne::ReportKind::Error, file, err.span().start) - .with_message(format!("error at stage {stage:?}, {:?}", err.reason())) - .with_label( - Label::new((file, err.span().into_range())).with_message(format!( - "found: {:?}", - err.found().expect("errors should have a reason") - )), - ) - .with_help(format!( - "expected: {:?}", - err.expected().collect::>() - )) - .finish() - .print((file, Source::from(files[file]))); - } - } - } -} - -#[derive(Debug, PartialEq, Eq)] -struct Loc<'filename>(&'filename str, Span); diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index ebf22d3..1b08789 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -1,4 +1,2 @@ -#![feature(type_alias_impl_trait)] -pub mod err_reporting; +#![feature(type_alias_impl_trait, lint_reasons)] pub mod parser; -pub mod tokens; diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 346862e..6b1caa0 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -1,12 +1,9 @@ +use clap::Parser; use std::{fs, path::PathBuf}; -use clap::Parser; -use lang::{ - err_reporting::ErrorCollector, - parser::ast::lossless::{ - lex, - parser::{self, parse}, - }, +use lang::parser::{ + parser::{self, grammar, input, output::Output}, + syntax_kind, }; #[derive(Parser)] @@ -19,8 +16,17 @@ fn main() { let args = Args::parse(); let n = args.file.clone(); let f = fs::read_to_string(n.clone()).expect("failed to read file"); - println!("toks: {:?}", lex::lex(&f)); - println!("parse res: {:?}", parse(&f)); + + let toks = dbg!(syntax_kind::lex(&f)); + let input = input::Input::new(&toks); + let mut parser = parser::Parser::new(input); + + grammar::source_file(&mut parser); + + let p_out = dbg!(parser.finish()); + let o = Output::from_parser_output(toks, p_out); + + println!("Out: {:?}", o); // let parse_res = parser::parse(&f); // println!("parse: {:?}", parse_res); diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index b1ee34b..e850ab0 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -1,152 +1,143 @@ -use chumsky::{ - error::Rich, - input::{Stream, ValueInput}, - prelude::*, - primitive::just, - recursive::recursive, - span::SimpleSpan, - IterParser, -}; -use indexmap::IndexMap; -use logos::Logos; +use drop_bomb::DropBomb; -use crate::tokens::Token; +use self::{error::SyntaxError, events::Event, input::Input, syntax_kind::SyntaxKind}; -pub mod ast; +pub mod syntax_kind; #[cfg(test)] mod tests; -use self::ast::{ - raw_ast::{RawExpr, RawExpression}, - File, -}; -pub type Span = SimpleSpan; -pub type Spanned = (T, Span); +pub mod error; +pub mod events; +pub mod grammar; +pub mod input; +pub mod output; -pub fn parse(src: &str) -> ParseResult, Rich<'_, Token<'_>>> { - let toks: Vec<_> = Token::lexer(src) - .spanned() - .map(|(t, s)| (t.expect("TODO: add lexer error(s)"), Span::from(s))) - .collect(); - let tok_stream = Stream::from_iter(toks).spanned((src.len()..src.len()).into()); - parser().parse(tok_stream) +pub struct Parser<'src, 'toks> { + input: Input<'src, 'toks>, + pos: usize, + events: Vec, + errors: Vec, } -pub(crate) fn parser< - 'tokens, - 'src: 'tokens, - I: ValueInput<'tokens, Token = Token<'src>, Span = Span>, ->() -> impl Parser<'tokens, I, File<'src>, extra::Err, Span>>> { - let word = select! { Token::Word(word) = e => (word, e.span())}; - let expr = recursive(|expr| { - let lit = select! { - Token::Int(i) = e => RawExpression::new(RawExpr::Lit(ast::Lit::Int(i.parse().expect("TODO: handle better"))), e.span()), - Token::Float(f) = e => RawExpression::new(RawExpr::Lit(ast::Lit::Float(f.parse().expect("TODO: handle better"))), e.span()), - Token::String(s) = e => RawExpression::new(RawExpr::Lit(ast::Lit::String(s.strip_prefix('"').expect("a").strip_suffix('"').expect("b"))), e.span()) - }; - let mat = just(Token::Mat) - .ignore_then(select! { Token::Dimensions(dimensions) = e => (dimensions, e.span())}) - .then( - lit.separated_by(just(Token::Comma)) - .collect::>() - .separated_by(just(Token::Semicolon)) - .collect::>() - .delimited_by(just(Token::BracketOpen), just(Token::BracketClose)), - ) - .map_with(|(dimensions, data), e| { - // TODO: Validation and proper error handling/reporting - // (validation = validating the matrix dimensions) - RawExpression::new( - RawExpr::Matrix(dimensions, data.into_iter().flatten().collect()), - e.span(), - ) - }); - let var = select! { - Token::VarIdent(name) => (RawExpr::Var as fn(_) -> _, name), - Token::InputIdent(name) => (RawExpr::InputVar as fn(_) -> _, name) +impl<'src, 'toks> Parser<'src, 'toks> { + pub fn new(input: Input<'src, 'toks>) -> Self { + Self { + input, + pos: 0, + events: Vec::new(), + errors: Vec::new(), } - .map_with(|(item_type, name), extra| RawExpression::new(item_type(name), extra.span())) - .labelled("variable"); + } - let attrset = word - .labelled("attr name") - .then_ignore(just(Token::Colon)) - .then(expr) - .labelled("attr body") - .separated_by(just(Token::Comma)) - .collect::>() - .map(IndexMap::from_iter) - .delimited_by(just(Token::BraceOpen), just(Token::BraceClose)) - .map_with(|v, e| (v, e.span())) - .labelled("attrset"); + pub fn finish(self) -> (Vec, Vec) { + (self.events, self.errors) + } - let node = word - .repeated() - .collect() - .then(attrset.clone().or_not()) - .map_with(|(name, params), extra| { - RawExpression::new(RawExpr::Node(name, params), extra.span()) - }) - // .or(var) - // .or(attrset - // .map_with(|attrset, extra| Expression::new(Expr::AttrSet(attrset), extra.span()))) - // .or(lit) - // .or(mat) - .labelled("node"); + pub(crate) fn nth(&self, n: usize) -> SyntaxKind { + self.input.kind(self.pos + n) + } - let atom = var - .or(lit) - .or(mat) - .or(attrset.map_with(|attrset, extra| { - RawExpression::new(RawExpr::AttrSet(attrset), extra.span()) - })) - .or(node.clone()); - - #[allow(clippy::let_and_return)] - let pipeline = atom - .clone() - .then(choice(( - just(Token::Pipe).to(RawExpr::SimplePipe as fn(_, _) -> _), - just(Token::MappingPipe).to(RawExpr::MappingPipe as fn(_, _) -> _), - just(Token::NullPipe).to(RawExpr::NullPipe as fn(_, _) -> _), - ))) - .repeated() - .foldr_with(atom, |(curr, pipe), next, extra| { - RawExpression::new(pipe(curr, next), extra.span()) - }); - - pipeline - }); - - let decls = just(Token::Def) - .ignore_then( - word.then_ignore(just(Token::Equals)) - .then(expr.clone().map(|expr| expr)) - .then_ignore(just(Token::Semicolon)), - ) - .repeated() - .collect::>() - .map(|decls| File { - decls: IndexMap::from_iter(decls), + pub fn eat_succeeding_ws(&mut self) { + self.push_ev(Event::Eat { + count: self.input.meaningless_tail_len(), }); + } - let single_expr = expr.map(|expr| File { - decls: IndexMap::from_iter([(("main", (0..0).into()), expr)]), - }); + pub(crate) fn current(&self) -> SyntaxKind { + self.input.kind(self.pos) + } - just(Token::Def).rewind().ignore_then(decls).or(single_expr) - // single_expr.or(decls) + pub(crate) fn start(&mut self) -> Marker { + let pos = self.events.len(); + self.push_ev(Event::tombstone()); + Marker::new(pos) + } - // expr.map(|expr| File { - // decls: IndexMap::from_iter([(("main", (0..0).into()), expr)]), - // }) - // .or(decl.repeated().collect::>().map(|decls| File { - // decls: IndexMap::from_iter(decls), - // })) + pub(crate) fn at(&self, kind: SyntaxKind) -> bool { + self.nth_at(0, kind) + } + + pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool { + if !self.at(kind) { + return false; + } + + self.do_bump(); + true + } + + pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool { + self.nth(n) == kind + } + + fn do_bump(&mut self) { + self.push_ev(Event::Eat { + count: self.input.preceding_meaningless(self.pos), + }); + self.pos += 1; + } + + fn push_ev(&mut self, event: Event) { + self.events.push(event) + } } -pub mod asg { - use petgraph::graph::DiGraph; - - use super::Spanned; +pub(crate) struct Marker { + pos: usize, + bomb: DropBomb, +} + +impl Marker { + pub(crate) fn new(pos: usize) -> Self { + Self { + pos, + bomb: DropBomb::new("Marker must be completed or abandoned"), + } + } + pub(crate) fn complete(mut self, p: &mut Parser<'_, '_>, kind: SyntaxKind) -> CompletedMarker { + self.bomb.defuse(); + match &mut p.events[self.pos] { + Event::Start { kind: slot, .. } => *slot = kind, + _ => unreachable!(), + } + p.push_ev(Event::Finish); + + CompletedMarker { + pos: self.pos, + kind, + } + } + + pub(crate) fn abandon(mut self, p: &mut Parser<'_, '_>) { + self.bomb.defuse(); + if self.pos == p.events.len() - 1 { + match p.events.pop() { + Some(Event::Start { + kind: SyntaxKind::TOMBSTONE, + forward_parent: None, + }) => (), + _ => unreachable!(), + } + } + } +} + +pub(crate) struct CompletedMarker { + pos: usize, + kind: SyntaxKind, +} + +impl CompletedMarker { + pub(crate) fn precede(self, p: &mut Parser<'_, '_>) -> Marker { + let new_pos = p.start(); + + match &mut p.events[self.pos] { + Event::Start { forward_parent, .. } => { + *forward_parent = Some(new_pos.pos - self.pos); + } + _ => unreachable!(), + } + + new_pos + } } diff --git a/crates/lang/src/parser/ast.rs b/crates/lang/src/parser/ast.rs deleted file mode 100644 index d45ef66..0000000 --- a/crates/lang/src/parser/ast.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::collections::{BTreeMap, HashMap}; - -use indexmap::IndexMap; - -use super::Spanned; - -#[derive(Debug, PartialEq)] -pub struct File<'src> { - pub decls: IndexMap, raw_ast::RawExpression<'src>>, -} - -pub mod raw_ast; - -#[derive(Debug, PartialEq)] -pub enum Lit<'src> { - // TODO: more bigger better number types - Int(i64), - Float(f64), - String(&'src str), -} - -pub mod lossless; - -pub mod ast_tree; diff --git a/crates/lang/src/parser/ast/ast_tree.rs b/crates/lang/src/parser/ast/ast_tree.rs deleted file mode 100644 index 46db4e9..0000000 --- a/crates/lang/src/parser/ast/ast_tree.rs +++ /dev/null @@ -1,31 +0,0 @@ -use ego_tree::Tree; - -use crate::parser::Spanned; - -use super::{File, Lit}; - -pub struct Ast<'src> { - tree: Tree>, -} - -struct AstNode<'src> { - kind: NodeKind<'src>, -} - -enum NodeKind<'src> { - Decl, - Ident(&'src str), - Instr, - Expr, - MappingPipe, - NullPipe, - MultiPipe, - Var(&'src str), - InputVar(&'src str), - AttrSet, - Attr, - Lit(Lit<'src>), - Matrix, - Dimensions(u16, u16), - MatrixRow, -} diff --git a/crates/lang/src/parser/ast/lossless.rs b/crates/lang/src/parser/ast/lossless.rs deleted file mode 100644 index 0047441..0000000 --- a/crates/lang/src/parser/ast/lossless.rs +++ /dev/null @@ -1,19 +0,0 @@ -use self::lex::SyntaxKind; - -pub mod parser; - -pub mod lex; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum Lang {} -impl rowan::Language for Lang { - type Kind = SyntaxKind; - #[allow(unsafe_code)] - fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { - assert!(raw.0 <= SyntaxKind::ROOT as u16); - unsafe { std::mem::transmute::(raw.0) } - } - fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { - kind.into() - } -} diff --git a/crates/lang/src/parser/ast/lossless/parser.rs b/crates/lang/src/parser/ast/lossless/parser.rs deleted file mode 100644 index 3a9c11d..0000000 --- a/crates/lang/src/parser/ast/lossless/parser.rs +++ /dev/null @@ -1,290 +0,0 @@ -use std::borrow::Borrow; - -use rowan::{ - Checkpoint, GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken, -}; - -use crate::parser::{ - ast::lossless::{lex::SyntaxKind::*, Lang}, - Span, -}; - -use self::parser_to_events::{to_events, Event}; - -use super::lex::{self, SyntaxKind}; - -pub mod parser_to_events { - use chumsky::prelude::*; - - use crate::parser::ast::lossless::lex::{ - self, - SyntaxKind::{self, *}, - }; - - #[derive(Debug, PartialEq, Eq, Clone, Copy)] - pub enum Event { - StartNode(SyntaxKind), - StartErr(SyntaxError), - EatToken, - FinishNode, - FinishErr, - } - - #[derive(Debug, PartialEq, Eq, Clone, Copy)] - pub enum SyntaxError { - Expected(SyntaxKind), - AttrExpectedValue, - /// guessed if there's a newline and attr on next line without comma - /// should then suggest comma after attr - ExpectedCommaBetweenAttrs, - } - - pub fn to_events(tokens: &[(SyntaxKind, &str)]) -> Vec { - let only_toks: Vec = tokens.iter().map(|(t, _)| *t).collect(); - let res = parser().parse(&only_toks); - res.unwrap() - } - - macro_rules! padded { - ($parser:expr) => {{ - let ws = one_of([WHITESPACE, NEWLINE]) - .to(Event::EatToken) - .repeated() - .collect::>(); - ws.then($parser) - .then(ws) - .map(|((mut before, mut c), mut after)| { - before.append(&mut c); - before.append(&mut after); - before - }) - }}; - } - macro_rules! parenthesized { - ($parser:expr) => { - just(L_PAREN) - .to(vec![Event::EatToken]) - .then($parser) - .then(just(R_PAREN).to(vec![Event::EatToken])) - .map(|((mut before, mut c), mut after)| { - before.insert(0, Event::StartNode(PARENTHESIZED_EXPR)); - before.append(&mut c); - before.append(&mut after); - before.push(Event::FinishNode); - before - }) - }; - } - - pub fn parser<'toks>() -> impl Parser<'toks, &'toks [SyntaxKind], Vec> { - let ws = one_of([WHITESPACE, NEWLINE]) - .to(Event::EatToken) - .repeated() - .collect::>(); - let ident = just(IDENT).to(vec![Event::EatToken]); - - let expr = recursive(|expr| { - let lit = one_of([INT_NUM, FLOAT_NUM, STRING]).to(vec![ - Event::StartNode(EXPR), - Event::EatToken, - Event::FinishNode, - ]); - let attrset = just(L_CURLY) - .then( - padded!(just(IDENT).to(vec![ - Event::StartNode(ATTR), - Event::StartNode(ATTR_NAME), - Event::EatToken, - Event::FinishNode - ])) - .then(just(COLON)) - .then(padded!(expr.clone().map(|mut exp: Vec| { - exp.insert(0, Event::StartNode(ATTR_VALUE)); - exp.push(Event::FinishNode); - exp.push(Event::FinishNode); - exp - }))) - .map(|((mut name, _), mut value)| { - // colon - name.push(Event::EatToken); - name.append(&mut value); - name - }), - ) - .then(just(R_CURLY)) - .map(|((_, mut attrs), _)| { - attrs.insert(0, Event::StartNode(ATTR_SET)); - attrs.insert(0, Event::EatToken); - attrs.push(Event::EatToken); - attrs.push(Event::FinishNode); - attrs - }); - - let atom = lit.clone().or(attrset).or(parenthesized!(expr)); - - let instr_name = ident - .clone() - .map(|mut v| { - v.insert(0, Event::StartNode(INSTR_NAME)); - v - }) - .foldl( - ws.then(ident).repeated(), - |mut ident, (mut ws, mut next)| { - ident.append(&mut ws); - ident.append(&mut next); - ident - }, - ) - .map(|mut v| { - v.push(Event::FinishNode); - v - }); - let instr = padded!(instr_name) - .then( - atom.clone() - .map(|mut v| { - v.insert(0, Event::StartNode(INSTR_PARAMS)); - v - }) - .foldl( - ws.then(atom.clone()).repeated(), - |mut cur, (mut ws, mut next)| { - cur.append(&mut ws); - cur.append(&mut next); - cur - }, - ) - .map(|mut v| { - v.push(Event::FinishNode); - v - }), - ) - .map(|(mut name, mut params)| { - name.insert(0, Event::StartNode(INSTR)); - name.append(&mut params); - name.push(Event::FinishNode); - name - }); - padded!(instr.or(lit).or(atom)) - }); - expr - // .map(|(lit, mut ev)| lit.append(&mut ev)); - } -} - -#[derive(PartialEq, Eq)] -pub struct Parse { - pub green_node: GreenNode, -} - -impl std::fmt::Debug for Parse { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - debug_print_green_node(NodeOrToken::Node(self.green_node.borrow()), f, 0) - } -} - -fn debug_print_green_node( - node: NodeOrToken<&GreenNodeData, &GreenTokenData>, - f: &mut std::fmt::Formatter<'_>, - lvl: i32, -) -> std::fmt::Result { - for _ in 0..lvl { - f.write_str(" ")?; - } - - match node { - NodeOrToken::Node(n) => { - writeln!(f, "{:?} {{", Lang::kind_from_raw(node.kind())); - for c in n.children() { - debug_print_green_node(c, f, lvl + 1)?; - } - for _ in 0..lvl { - f.write_str(" ")?; - } - f.write_str("}\n") - } - NodeOrToken::Token(t) => { - writeln!(f, "{:?} {:?};", Lang::kind_from_raw(t.kind()), t.text()) - } - } -} - -#[derive(Debug)] -struct Parser<'src> { - tokens: Vec<(SyntaxKind, &'src str)>, - builder: GreenNodeBuilder<'src>, - errors: Vec, -} - -#[derive(Debug, PartialEq, Eq)] -enum SyntaxError { - Expected(SyntaxKind), - AttrExpectedValue, - /// guessed if there's a newline and attr on next line without comma - /// should then suggest comma after attr - ExpectedCommaBetweenAttrs, -} - -pub fn parse(src: &str) -> Parse { - let tokens = lex::lex(src); - Parser { - tokens, - builder: GreenNodeBuilder::new(), - errors: Vec::new(), - } - .parse() -} - -impl Parser<'_> { - fn parse(mut self) -> Parse { - let evs = to_events(&self.tokens); - self.builder.start_node(ROOT.into()); - println!("evs: {evs:?}"); - - self.tokens.reverse(); - - for ev in evs { - match ev { - Event::StartNode(kind) => self.builder.start_node(kind.into()), - Event::StartErr(SyntaxError) => todo!(), - Event::EatToken => self.bump(), - Event::FinishNode => self.builder.finish_node(), - Event::FinishErr => todo!(), - } - } - - self.builder.finish_node(); - Parse { - green_node: self.builder.finish(), - } - } - - /// Advance one token, adding it to the current branch of the tree builder. - fn bump(&mut self) { - let (kind, text) = self.tokens.pop().unwrap(); - self.builder.token(kind.into(), text); - } - fn syntax_err(&mut self, err: SyntaxError) { - let (_, text) = self.tokens.pop().unwrap(); - self.builder.token(PARSE_ERR.into(), text); - self.errors.push(err); - } - fn syntax_err_by_checkpoint(&mut self, checkpoint: Checkpoint, err: SyntaxError) { - self.builder.start_node_at(checkpoint, PARSE_ERR.into()); - self.builder.finish_node(); - self.errors.push(err); - } - fn expected(&mut self, expected: SyntaxKind) { - self.syntax_err(SyntaxError::Expected(expected)) - } - /// Peek at the first unprocessed token - fn current(&self) -> Option { - self.tokens.last().map(|(kind, _)| *kind) - } - fn next(&self) -> Option { - self.tokens - .get(self.tokens.len() - 2) - .map(|(kind, _)| *kind) - } -} diff --git a/crates/lang/src/parser/ast/raw_ast.rs b/crates/lang/src/parser/ast/raw_ast.rs deleted file mode 100644 index a0ec749..0000000 --- a/crates/lang/src/parser/ast/raw_ast.rs +++ /dev/null @@ -1,50 +0,0 @@ -use indexmap::IndexMap; - -use super::super::Spanned; - -use super::super::Span; -use super::Lit; - -#[derive(Debug, PartialEq)] -pub struct RawExpression<'src> { - pub expr: Box>, - pub span: Span, -} - -impl<'src> RawExpression<'src> { - pub fn new(expr: RawExpr<'src>, span: Span) -> Self { - Self { - expr: Box::new(expr), - span, - } - } -} - -#[derive(Debug, PartialEq)] -pub enum RawExpr<'src> { - Node( - Vec>, - Option, RawExpression<'src>>>>, - ), - SimplePipe(RawExpression<'src>, RawExpression<'src>), - // NamingPipe( - // Box>, - // (Vec>, Vec>), - // Box>, - // ), - MappingPipe(RawExpression<'src>, RawExpression<'src>), - NullPipe(RawExpression<'src>, RawExpression<'src>), - MultiPipe(IndexMap, RawExpression<'src>>), - // LetIn( - // IndexMap, Box>>, - // Box>, - // ), - // $ - Var(&'src str), - // @ - InputVar(&'src str), - AttrSet(Spanned, RawExpression<'src>>>), - Lit(Lit<'src>), - Matrix(Spanned<(u16, u16)>, Vec>), - List(Vec>), -} diff --git a/crates/lang/src/parser/error.rs b/crates/lang/src/parser/error.rs new file mode 100644 index 0000000..698ecaf --- /dev/null +++ b/crates/lang/src/parser/error.rs @@ -0,0 +1,6 @@ +use crate::parser::syntax_kind::SyntaxKind; + +#[derive(Debug)] +pub enum SyntaxError { + Expected(Vec), +} diff --git a/crates/lang/src/parser/events.rs b/crates/lang/src/parser/events.rs new file mode 100644 index 0000000..6fc5b2c --- /dev/null +++ b/crates/lang/src/parser/events.rs @@ -0,0 +1,23 @@ +use crate::parser::syntax_kind::SyntaxKind; + +#[derive(Debug)] +pub enum Event { + Start { + kind: SyntaxKind, + forward_parent: Option, + }, + Finish, + Eat { + count: usize, + }, + Error, +} + +impl Event { + pub(crate) fn tombstone() -> Self { + Self::Start { + kind: SyntaxKind::TOMBSTONE, + forward_parent: None, + } + } +} diff --git a/crates/lang/src/parser/grammar.rs b/crates/lang/src/parser/grammar.rs new file mode 100644 index 0000000..f301d4e --- /dev/null +++ b/crates/lang/src/parser/grammar.rs @@ -0,0 +1,14 @@ +use crate::parser::syntax_kind::SyntaxKind::*; + +use super::Parser; + +mod expression; + +pub fn source_file(p: &mut Parser) { + let root = p.start(); + + expression::expression(p); + p.eat_succeeding_ws(); + + root.complete(p, ROOT); +} diff --git a/crates/lang/src/parser/grammar/expression.rs b/crates/lang/src/parser/grammar/expression.rs new file mode 100644 index 0000000..18323a5 --- /dev/null +++ b/crates/lang/src/parser/grammar/expression.rs @@ -0,0 +1,14 @@ +use crate::parser::{syntax_kind::SyntaxKind::*, Parser}; + +use self::{instruction::instr, lit::literal}; + +mod instruction; +mod lit; + +pub fn expression(p: &mut Parser) { + let expr = p.start(); + + instr(p); + + expr.complete(p, EXPR); +} diff --git a/crates/lang/src/parser/grammar/expression/instruction.rs b/crates/lang/src/parser/grammar/expression/instruction.rs new file mode 100644 index 0000000..136f4ed --- /dev/null +++ b/crates/lang/src/parser/grammar/expression/instruction.rs @@ -0,0 +1,30 @@ +use crate::parser::{syntax_kind::SyntaxKind::*, Parser}; + +use super::lit::literal; + +pub fn instr(p: &mut Parser) { + let instr = p.start(); + + instr_name(p); + instr_params(p); + + instr.complete(p, INSTR); +} + +fn instr_name(p: &mut Parser) { + let instr_name = p.start(); + + while p.at(IDENT) { + p.do_bump(); + } + + instr_name.complete(p, INSTR_NAME); +} + +fn instr_params(p: &mut Parser) { + if let Some(start) = literal(p) { + while literal(p).is_some() {} + + start.precede(p).complete(p, INSTR_PARAMS); + } +} diff --git a/crates/lang/src/parser/grammar/expression/lit.rs b/crates/lang/src/parser/grammar/expression/lit.rs new file mode 100644 index 0000000..bb48360 --- /dev/null +++ b/crates/lang/src/parser/grammar/expression/lit.rs @@ -0,0 +1,20 @@ +use enumset::enum_set; + +use crate::parser::{ + syntax_kind::{SyntaxKind::*, TokenSet}, + CompletedMarker, Parser, +}; + +const LIT_TOKENS: TokenSet = enum_set!(INT_NUM | FLOAT_NUM | STRING); + +pub fn literal(p: &mut Parser) -> Option { + if !LIT_TOKENS.contains(p.current()) { + return None; + } + + let lit = p.start(); + + p.do_bump(); + + Some(lit.complete(p, LITERAL)) +} diff --git a/crates/lang/src/parser/input.rs b/crates/lang/src/parser/input.rs new file mode 100644 index 0000000..c61fc87 --- /dev/null +++ b/crates/lang/src/parser/input.rs @@ -0,0 +1,61 @@ +use crate::parser::syntax_kind::SyntaxKind; + +pub struct Input<'src, 'toks> { + raw: &'toks Vec<(SyntaxKind, &'src str)>, + /// indices of the "meaningful" tokens (not whitespace etc) + /// includes newlines because those might indeed help with finding errors + meaningful: Vec, + /// indices of newlines for the purpose of easily querying them + /// can be helpful with missing commas etc + newlines: Vec, +} + +impl<'src, 'toks> Input<'src, 'toks> { + pub fn new(raw_toks: &'toks Vec<(SyntaxKind, &'src str)>) -> Self { + let meaningful = raw_toks + .iter() + .enumerate() + .filter_map(|(i, tok)| match tok.0 { + SyntaxKind::WHITESPACE | SyntaxKind::NEWLINE => None, + _ => Some(i), + }) + .collect(); + let newlines = raw_toks + .iter() + .enumerate() + .filter_map(|(i, tok)| match tok.0 { + SyntaxKind::NEWLINE => Some(i), + _ => None, + }) + .collect(); + + Self { + raw: raw_toks, + meaningful, + newlines, + } + } + + #[allow(clippy::unwrap_used, reason = "meaningful indices cannot be invalid")] + pub(crate) fn kind(&self, idx: usize) -> SyntaxKind { + let Some(meaningful_idx) = self.meaningful.get(idx) else { + return SyntaxKind::EOF; + }; + + self.raw.get(*meaningful_idx).unwrap().0 + } + + pub(crate) fn preceding_meaningless(&self, idx: usize) -> usize { + assert!(self.meaningful.len() > idx); + + if idx == 0 { + 1 + } else { + self.meaningful[idx] - self.meaningful[idx - 1] + } + } + + pub(crate) fn meaningless_tail_len(&self) -> usize { + self.raw.len() - (self.meaningful.last().unwrap() + 1) + } +} diff --git a/crates/lang/src/parser/output.rs b/crates/lang/src/parser/output.rs new file mode 100644 index 0000000..75019bb --- /dev/null +++ b/crates/lang/src/parser/output.rs @@ -0,0 +1,113 @@ +use rowan::{GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken}; +use std::mem; + +use crate::parser::syntax_kind::{Lang, SyntaxKind}; + +use super::{error::SyntaxError, events::Event}; + +pub struct Output { + pub green_node: GreenNode, + pub errors: Vec, +} +impl std::fmt::Debug for Output { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + debug_print_green_node(NodeOrToken::Node(&self.green_node), f, 0) + } +} + +fn debug_print_green_node( + node: NodeOrToken<&GreenNodeData, &GreenTokenData>, + f: &mut std::fmt::Formatter<'_>, + lvl: i32, +) -> std::fmt::Result { + for _ in 0..lvl { + f.write_str(" ")?; + } + + match node { + NodeOrToken::Node(n) => { + writeln!(f, "{:?} {{", Lang::kind_from_raw(node.kind()))?; + for c in n.children() { + debug_print_green_node(c, f, lvl + 1)?; + } + for _ in 0..lvl { + f.write_str(" ")?; + } + f.write_str("}\n") + } + NodeOrToken::Token(t) => { + writeln!(f, "{:?} {:?};", Lang::kind_from_raw(t.kind()), t.text()) + } + } +} + +impl Output { + pub fn from_parser_output( + mut raw_toks: Vec<(SyntaxKind, &str)>, + (mut events, errs): (Vec, Vec), + ) -> Self { + let mut builder = GreenNodeBuilder::new(); + let mut fw_parents = Vec::new(); + raw_toks.reverse(); + + for i in 0..events.len() { + match mem::replace(&mut events[i], Event::tombstone()) { + Event::Start { + kind, + forward_parent, + } => { + if kind == SyntaxKind::TOMBSTONE && forward_parent.is_none() { + continue; + } + + fw_parents.push(kind); + let mut idx = i; + let mut fp = forward_parent; + while let Some(fwd) = fp { + idx += fwd as usize; + fp = match mem::replace(&mut events[idx], Event::tombstone()) { + Event::Start { + kind, + forward_parent, + } => { + fw_parents.push(kind); + forward_parent + } + _ => unreachable!(), + } + } + + // remove whitespace bc it's ugly + while let Some((SyntaxKind::WHITESPACE | SyntaxKind::NEWLINE, _)) = + raw_toks.last() + { + match events.iter_mut().find(|ev| matches!(ev, Event::Eat { .. })) { + Some(Event::Eat { count }) => *count -= 1, + _ => unreachable!(), + } + + let (tok, text): (SyntaxKind, &str) = raw_toks.pop().unwrap(); + builder.token(tok.into(), text); + } + + for kind in fw_parents.drain(..).rev() { + if kind != SyntaxKind::TOMBSTONE { + builder.start_node(kind.into()); + } + } + } + Event::Finish => builder.finish_node(), + Event::Eat { count } => (0..count).for_each(|_| { + let (tok, text): (SyntaxKind, &str) = raw_toks.pop().unwrap(); + builder.token(tok.into(), text); + }), + Event::Error => todo!(), + } + } + + Self { + green_node: builder.finish(), + errors: errs, + } + } +} diff --git a/crates/lang/src/parser/parser.rs b/crates/lang/src/parser/parser.rs new file mode 100644 index 0000000..9ee53da --- /dev/null +++ b/crates/lang/src/parser/parser.rs @@ -0,0 +1,6 @@ +//! The parser architecture is *heavily* inspired (and partially copied and adapted) from the amazing rust-analyzer +use drop_bomb::DropBomb; + +use self::{error::SyntaxError, events::Event, input::Input}; + +use super::syntax_kind::SyntaxKind; diff --git a/crates/lang/src/parser/ast/lossless/lex.rs b/crates/lang/src/parser/syntax_kind.rs similarity index 74% rename from crates/lang/src/parser/ast/lossless/lex.rs rename to crates/lang/src/parser/syntax_kind.rs index e2a867b..e37254f 100644 --- a/crates/lang/src/parser/ast/lossless/lex.rs +++ b/crates/lang/src/parser/syntax_kind.rs @@ -1,7 +1,6 @@ +use enumset::EnumSet; use logos::Logos; -use crate::parser::Span; - pub fn lex(src: &str) -> Vec<(SyntaxKind, &str)> { let mut lex = SyntaxKind::lexer(src); let mut r = Vec::new(); @@ -13,8 +12,9 @@ pub fn lex(src: &str) -> Vec<(SyntaxKind, &str)> { r } -#[derive(Logos, Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] +#[derive(enumset::EnumSetType, Logos, Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] #[repr(u16)] +#[enumset(no_super_impls)] #[allow(non_camel_case_types)] pub enum SyntaxKind { #[token("def")] @@ -39,6 +39,7 @@ pub enum SyntaxKind { MAT_BODY, PARENTHESIZED_EXPR, EXPR, + LITERAL, #[token("(")] L_PAREN, #[token(")")] @@ -109,9 +110,29 @@ pub enum SyntaxKind { PARSE_ERR, LEX_ERR, ROOT, + EOF, + TOMBSTONE, + ERROR, } + +pub type TokenSet = EnumSet; + impl From for rowan::SyntaxKind { fn from(kind: SyntaxKind) -> Self { Self(kind as u16) } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Lang {} +impl rowan::Language for Lang { + type Kind = SyntaxKind; + #[allow(unsafe_code)] + fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { + assert!(raw.0 <= SyntaxKind::ROOT as u16); + unsafe { std::mem::transmute::(raw.0) } + } + fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { + kind.into() + } +} diff --git a/crates/lang/src/parser/tests.rs b/crates/lang/src/parser/tests.rs index 5bd221f..8b13789 100644 --- a/crates/lang/src/parser/tests.rs +++ b/crates/lang/src/parser/tests.rs @@ -1,143 +1 @@ -use crate::parser::ast::File; -use crate::parser::parse; -use crate::tokens::Token; -use chumsky::input::Stream; -use chumsky::prelude::*; -use indexmap::IndexMap; -use logos::Logos; -// #[test] -// fn test_parse_node_with_params() { -// const INPUT: &str = "meow [ hello: $foo, world: @bar]"; -// assert_eq!( -// parse(INPUT).unwrap(), -// File { -// decls: IndexMap::from_iter([( -// ("main", (0..0).into()), -// ( -// Expr::Node( -// ("meow", (0..4).into()), -// Some(( -// IndexMap::from_iter([ -// ( -// ("hello", (7..12).into()), -// Expr::Var(("foo", (14..18).into())) -// ), -// ( -// ("world", (20..25).into()), -// Expr::InputVar(("bar", (27..31).into())) -// ) -// ]), -// (5..32).into() -// )) -// ), -// (0..32).into() -// ) -// )]) -// } -// ); -// } - -// fn test_parse_multiple_top_level_complex() { -// const INPUT: &str = r"def main = meow -// | uwu -// [ foo: @bar -// , hello: world @| test [ more: params ] | yay -// ] -// !| awa -// @| nya -// | rawr; - -// def test = meow -// [ hello: $foo -// , world: @bar -// ]; -// "; -// assert_eq!( -// parse(INPUT).unwrap(), -// File { -// decls: IndexMap::from_iter([ -// ( -// ("main", (4..8).into()), -// ( -// Expr::SimplePipe( -// Box::new(Expr::Node(("meow", (11..15).into()), None)), -// Box::new(Expr::NullPipe( -// Box::new(Expr::Node( -// ("uwu", (20..23).into()), -// Some(( -// IndexMap::from_iter([ -// ( -// ("foo", (29..32).into()), -// Expr::InputVar(("bar", (34..38).into())) -// ), -// ( -// ("hello", (44..49).into()), -// Expr::MappingPipe( -// Box::new(Expr::Node( -// ("world", (51..56).into()), -// None -// )), -// Box::new(Expr::SimplePipe( -// Box::new(Expr::Node( -// ("test", (60..64).into()), -// Some(( -// IndexMap::from_iter([( -// ("more", (67..71).into()), -// Expr::Node( -// ("params", (73..79).into()), -// None -// ) -// )]), -// (65..81).into() -// )) -// )), -// Box::new(Expr::Node( -// ("yay", (84..87).into()), -// None -// )) -// )) -// ) -// ) -// ]), -// (27..92).into() -// )) -// )), -// Box::new(Expr::MappingPipe( -// Box::new(Expr::Node(("awa", (97..100).into()), None)), -// Box::new(Expr::SimplePipe( -// Box::new(Expr::Node(("nya", (106..109).into()), None)), -// Box::new(Expr::Node(("rawr", (114..118).into()), None)) -// )) -// )) -// )) -// ), -// (11..118).into() -// ), -// ), -// ( -// ("test", (125..129).into()), -// ( -// Expr::Node( -// ("meow", (132..136).into()), -// Some(( -// IndexMap::from_iter([ -// ( -// ("hello", (141..146).into()), -// Expr::Var(("foo", (148..152).into())) -// ), -// ( -// ("world", (156..161).into()), -// Expr::InputVar(("bar", (163..167).into())) -// ) -// ]), -// (139..171).into() -// )) -// ), -// (132..171).into() -// ) -// ) -// ]) -// } -// ); -// } diff --git a/crates/lang/src/tokens.rs b/crates/lang/src/tokens.rs deleted file mode 100644 index 3314916..0000000 --- a/crates/lang/src/tokens.rs +++ /dev/null @@ -1,81 +0,0 @@ -use logos::Logos; - -#[derive(Logos, Debug, PartialEq, Eq, Clone)] -#[logos(skip r"[ \t\n\f]+")] -pub enum Token<'a> { - // hack! - // this isn't actually supposed to be in the language. - // i just can't figure out how to automatically choose between a top level declaration - // or a top level expression - // so a declaration needs the keyword def until i can figure this out - #[token("def")] - Def, - #[token("let")] - Let, - #[token("in")] - In, - #[token("mat")] - Mat, - #[regex("[\\d]+x[\\d]+", |lex| { - let (x, y) = lex.slice().split_once('x').expect("shouldn't fail to split"); - // TODO: handle overflows etc - (x.parse().expect("should only match valid u16s"), y.parse().expect("should only match valid u16s")) - })] - Dimensions((u16, u16)), - #[regex("[\\d]+", |lex| lex.slice())] - Int(&'a str), - #[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)", |lex| lex.slice())] - Float(&'a str), - // TODO: more bigger better more complex string lexing - // TODO: templating? - #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#, |lex| lex.slice())] - String(&'a str), - #[token("+")] - Plus, - #[token("-")] - Minus, - #[token("*")] - Mult, - #[token("/")] - Div, - // TODO: figure out how to allow numbers in words? - #[regex("[a-zA-Z_]+[a-zA-Z_\\-\\d]*", |lex| lex.slice().trim())] - Word(&'a str), - #[regex("\\$[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] - VarIdent(&'a str), - #[regex("\\@[a-zA-Z0-9_\\-]+", |lex| &lex.slice()[1..])] - InputIdent(&'a str), - #[token(",")] - Comma, - #[token("|")] - Pipe, - #[token("@|")] - MappingPipe, - #[token("!|")] - NullPipe, - #[token("@")] - At, - #[token(">")] - GreaterThan, - #[token("=")] - Equals, - #[token(":")] - Colon, - #[token(";")] - Semicolon, - #[token("[")] - BracketOpen, - #[token("]")] - BracketClose, - #[token("(")] - ParenOpen, - #[token(")")] - ParenClose, - #[token("{")] - BraceOpen, - #[token("}")] - BraceClose, -} - -#[cfg(test)] -mod tests; diff --git a/crates/lang/src/tokens/tests.rs b/crates/lang/src/tokens/tests.rs deleted file mode 100644 index 3b35ace..0000000 --- a/crates/lang/src/tokens/tests.rs +++ /dev/null @@ -1,135 +0,0 @@ -use logos::Logos; - -use super::Token; - -/// generates tests for the lexer to avoid writing boilerplate -macro_rules! lexer_test { - ($name:ident, $input:literal, $out:expr) => { - #[test] - fn $name() { - let lex = Token::lexer($input); - let toks = lex.map(|tok| tok.unwrap()).collect::>(); - assert_eq!(toks, $out); - } - }; -} - -lexer_test! { - test_lex_simple_pipeline, - "streamer | processor | sink", - [ - Token::Word("streamer"), - Token::Pipe, - Token::Word("processor"), - Token::Pipe, - Token::Word("sink") - ] -} - -lexer_test! { - test_lex_var_ident, - "$identifier", - [ Token::VarIdent("identifier") ] -} - -lexer_test! { - test_lex_subgroup, - "subgroup(first, second) = a | b [ $first ] | c [ $second ]", - [ - Token::Word("subgroup"), - Token::ParenOpen, - Token::Word("first"), - Token::Comma, - Token::Word("second"), - Token::ParenClose, - Token::Equals, - Token::Word("a"), - Token::Pipe, - Token::Word("b"), - Token::BracketOpen, - Token::VarIdent("first"), - Token::BracketClose, - Token::Pipe, - Token::Word("c"), - Token::BracketOpen, - Token::VarIdent("second"), - Token::BracketClose - ] -} - -lexer_test! { - text_lex_crossing_pipeline_reordering, - "a >first, second|second, first> c", - [ - Token::Word("a"), - Token::GreaterThan, - Token::Word("first"), - Token::Comma, - Token::Word("second"), - Token::Pipe, - Token::Word("second"), - Token::Comma, - Token::Word("first"), - Token::GreaterThan, - Token::Word("c") - ] -} - -lexer_test! { - test_lex_crossing_input_args, - "a >second| c { second: @first }", - [ - Token::Word("a"), - Token::GreaterThan, - Token::Word("second"), - Token::Pipe, - Token::Word("c"), - Token::BraceOpen, - Token::Word("second"), - Token::Colon, - Token::InputIdent("first"), - Token::BraceClose - ] -} - -lexer_test! { - test_lex_map_io_named, - "a @| c", - [ - Token::Word("a"), - Token::MappingPipe, - Token::Word("c") - ] -} - -lexer_test! { - test_lex_int_literal, - "42", - [ - Token::Int("42") - ] -} - -lexer_test! { - test_lex_float_literal_0, - "1.5", - [ - Token::Float("1.5") - ] -} - -lexer_test! { - test_lex_float_literal_1, - "42.", - [ - Token::Float("42.") - ] -} - -lexer_test! { - test_lex_float_literal_2, - ".42", - [ - Token::Float(".42") - ] -} diff --git a/testfiles/test.owo b/testfiles/test.owo index dc95a8d..ab4456d 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,7 +1 @@ -hello world test - 42 - (another command 3.14 "meow") - "uwu" - { - some: attrs 42 (meow gay 1) - } +hello world test 1.5 42 69 "gay" -- 2.46.0 From 06c9094227d477ea18556074ef37e6339e4c51c6 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 24 Apr 2024 11:09:48 +0200 Subject: [PATCH 39/67] lang: fix main --- crates/lang/src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 6b1caa0..515cbe0 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -1,10 +1,7 @@ use clap::Parser; use std::{fs, path::PathBuf}; -use lang::parser::{ - parser::{self, grammar, input, output::Output}, - syntax_kind, -}; +use lang::parser::{self, grammar, input, output::Output, syntax_kind}; #[derive(Parser)] struct Args { -- 2.46.0 From 86b1481943d8a0bb28374669a9e48e30d5eae1c7 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 24 Apr 2024 11:15:07 +0200 Subject: [PATCH 40/67] lang: remove empty recursive parser module --- crates/lang/src/parser/parser.rs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 crates/lang/src/parser/parser.rs diff --git a/crates/lang/src/parser/parser.rs b/crates/lang/src/parser/parser.rs deleted file mode 100644 index 9ee53da..0000000 --- a/crates/lang/src/parser/parser.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! The parser architecture is *heavily* inspired (and partially copied and adapted) from the amazing rust-analyzer -use drop_bomb::DropBomb; - -use self::{error::SyntaxError, events::Event, input::Input}; - -use super::syntax_kind::SyntaxKind; -- 2.46.0 From 2bea3994c29c9802eef50b166963f4aad15a46a0 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 24 Apr 2024 19:37:52 +0200 Subject: [PATCH 41/67] lang: matrix parser! --- crates/lang/src/parser.rs | 6 ++ crates/lang/src/parser/grammar/expression.rs | 58 ++++++++++++++-- .../grammar/expression/collection/matrix.rs | 69 +++++++++++++++++++ .../parser/grammar/expression/instruction.rs | 10 ++- crates/lang/src/parser/syntax_kind.rs | 8 ++- testfiles/test.owo | 5 +- 6 files changed, 144 insertions(+), 12 deletions(-) create mode 100644 crates/lang/src/parser/grammar/expression/collection/matrix.rs diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index e850ab0..996f15a 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -94,6 +94,7 @@ impl Marker { bomb: DropBomb::new("Marker must be completed or abandoned"), } } + pub(crate) fn complete(mut self, p: &mut Parser<'_, '_>, kind: SyntaxKind) -> CompletedMarker { self.bomb.defuse(); match &mut p.events[self.pos] { @@ -108,6 +109,11 @@ impl Marker { } } + pub(crate) fn complete_err(mut self, p: &mut Parser, kind: SyntaxError) -> CompletedMarker { + p.errors.push(kind); + self.complete(p, SyntaxKind::PARSE_ERR) + } + pub(crate) fn abandon(mut self, p: &mut Parser<'_, '_>) { self.bomb.defuse(); if self.pos == p.events.len() - 1 { diff --git a/crates/lang/src/parser/grammar/expression.rs b/crates/lang/src/parser/grammar/expression.rs index 18323a5..cb1f410 100644 --- a/crates/lang/src/parser/grammar/expression.rs +++ b/crates/lang/src/parser/grammar/expression.rs @@ -1,14 +1,62 @@ -use crate::parser::{syntax_kind::SyntaxKind::*, Parser}; +use crate::parser::{syntax_kind::SyntaxKind::*, CompletedMarker, Parser}; -use self::{instruction::instr, lit::literal}; +use self::{collection::collection, instruction::instr, lit::literal}; mod instruction; mod lit; +mod collection { + use enumset::enum_set; -pub fn expression(p: &mut Parser) { + use crate::parser::{ + syntax_kind::{SyntaxKind::*, TokenSet}, + CompletedMarker, Parser, + }; + + use self::{attr_set::attr_set, matrix::matrix, vec::vec}; + + const COLLECTION_START: TokenSet = enum_set!(MAT_KW | L_BRACK | L_BRACE); + + pub fn collection(p: &mut Parser) -> Option { + if !COLLECTION_START.contains(p.current()) { + return None; + } + + Some(match p.current() { + MAT_KW => matrix(p), + L_BRACK => vec(p), + L_BRACE => attr_set(p), + _ => unreachable!(), + }) + } + + mod matrix; + mod vec { + use crate::parser::{CompletedMarker, Parser}; + + pub fn vec(p: &mut Parser) -> CompletedMarker { + todo!() + } + } + mod attr_set { + use crate::parser::{CompletedMarker, Parser}; + + pub fn attr_set(p: &mut Parser) -> CompletedMarker { + todo!() + } + } +} + +pub fn expression(p: &mut Parser) -> Option { let expr = p.start(); - instr(p); + if atom(p).or_else(|| instr(p)).is_none() { + expr.abandon(p); + return None; + } - expr.complete(p, EXPR); + Some(expr.complete(p, EXPR)) +} + +pub fn atom(p: &mut Parser) -> Option { + literal(p).or_else(|| collection(p)) } diff --git a/crates/lang/src/parser/grammar/expression/collection/matrix.rs b/crates/lang/src/parser/grammar/expression/collection/matrix.rs new file mode 100644 index 0000000..58ba9e1 --- /dev/null +++ b/crates/lang/src/parser/grammar/expression/collection/matrix.rs @@ -0,0 +1,69 @@ +use crate::parser::{ + error::SyntaxError, + grammar::expression::{self, expression}, + syntax_kind::SyntaxKind::*, + CompletedMarker, Marker, Parser, +}; + +pub fn matrix(p: &mut Parser) -> CompletedMarker { + let matrix = p.start(); + p.eat(MAT_KW); + + if !p.eat(PAT_DIMENSIONS) { + eprintln!("TODO (as eprintln so i dont forget): improve recovery algorithms"); + return matrix.complete_err(p, SyntaxError::Expected(vec![PAT_DIMENSIONS])); + } + + matrix_body(p); + + matrix.complete(p, MATRIX) +} + +fn matrix_body(p: &mut Parser) { + let mat_body = p.start(); + if !p.eat(L_BRACK) { + mat_body.complete_err(p, SyntaxError::Expected(vec![MAT_BODY])); + return (); + } + + let mut going = true; + + let mut mat_row = p.start(); + let mut row_items = 0; + while going { + let mat_item = p.start(); + if expression(p).is_some() { + mat_item.complete(p, MAT_ITEM); + row_items += 1; + match p.current() { + COMMA => p.do_bump(), + SEMICOLON => { + mat_row.complete(p, MAT_ROW); + mat_row = p.start(); + p.do_bump(); + row_items = 0; + } + R_BRACK => going = false, + _ => { + let err = p.start(); + p.do_bump(); + + err.complete_err(p, SyntaxError::Expected(vec![COMMA, SEMICOLON, R_BRACK])); + } + }; + } else if p.at(R_BRACK) { + going = false; + } else { + let err = p.start(); + p.do_bump(); + err.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK])); + } + } + if row_items != 0 { + mat_row.complete(p, MAT_ROW); + } else { + mat_row.abandon(p); + } + p.eat(R_BRACK); + mat_body.complete(p, MAT_BODY); +} diff --git a/crates/lang/src/parser/grammar/expression/instruction.rs b/crates/lang/src/parser/grammar/expression/instruction.rs index 136f4ed..5da0f43 100644 --- a/crates/lang/src/parser/grammar/expression/instruction.rs +++ b/crates/lang/src/parser/grammar/expression/instruction.rs @@ -1,14 +1,18 @@ -use crate::parser::{syntax_kind::SyntaxKind::*, Parser}; +use crate::parser::{syntax_kind::SyntaxKind::*, CompletedMarker, Parser}; use super::lit::literal; -pub fn instr(p: &mut Parser) { +pub fn instr(p: &mut Parser) -> Option { + if !p.at(IDENT) { + return None; + } + let instr = p.start(); instr_name(p); instr_params(p); - instr.complete(p, INSTR); + Some(instr.complete(p, INSTR)) } fn instr_name(p: &mut Parser) { diff --git a/crates/lang/src/parser/syntax_kind.rs b/crates/lang/src/parser/syntax_kind.rs index e37254f..cc1754c 100644 --- a/crates/lang/src/parser/syntax_kind.rs +++ b/crates/lang/src/parser/syntax_kind.rs @@ -34,9 +34,11 @@ pub enum SyntaxKind { #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#)] STRING, MATRIX, + MAT_BODY, + MAT_ROW, + MAT_ITEM, DECL, LIST, - MAT_BODY, PARENTHESIZED_EXPR, EXPR, LITERAL, @@ -45,9 +47,9 @@ pub enum SyntaxKind { #[token(")")] R_PAREN, #[token("{")] - L_CURLY, + L_BRACE, #[token("}")] - R_CURLY, + R_BRACE, #[token("[")] L_BRACK, #[token("]")] diff --git a/testfiles/test.owo b/testfiles/test.owo index ab4456d..87a5bc8 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1 +1,4 @@ -hello world test 1.5 42 69 "gay" +mat 2x2 [ + 1, 2; + 3, 4 +] -- 2.46.0 From e62b50a51ae0d2b2e68a0a42d7ad8f3180c0b3e0 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 24 Apr 2024 19:55:16 +0200 Subject: [PATCH 42/67] lang: make `Marker`s debuggable --- crates/lang/src/parser.rs | 10 +++++----- crates/lang/src/parser/grammar.rs | 2 +- crates/lang/src/parser/grammar/expression.rs | 2 +- .../parser/grammar/expression/collection/matrix.rs | 14 +++++++------- .../src/parser/grammar/expression/instruction.rs | 4 ++-- crates/lang/src/parser/grammar/expression/lit.rs | 2 +- testfiles/test.owo | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 996f15a..dfd80c5 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -47,10 +47,10 @@ impl<'src, 'toks> Parser<'src, 'toks> { self.input.kind(self.pos) } - pub(crate) fn start(&mut self) -> Marker { + pub(crate) fn start(&mut self, name: &str) -> Marker { let pos = self.events.len(); self.push_ev(Event::tombstone()); - Marker::new(pos) + Marker::new(pos, name) } pub(crate) fn at(&self, kind: SyntaxKind) -> bool { @@ -88,10 +88,10 @@ pub(crate) struct Marker { } impl Marker { - pub(crate) fn new(pos: usize) -> Self { + pub(crate) fn new(pos: usize, name: &str) -> Self { Self { pos, - bomb: DropBomb::new("Marker must be completed or abandoned"), + bomb: DropBomb::new(format!("Marker {name} must be completed or abandoned")), } } @@ -135,7 +135,7 @@ pub(crate) struct CompletedMarker { impl CompletedMarker { pub(crate) fn precede(self, p: &mut Parser<'_, '_>) -> Marker { - let new_pos = p.start(); + let new_pos = p.start("new_pos"); match &mut p.events[self.pos] { Event::Start { forward_parent, .. } => { diff --git a/crates/lang/src/parser/grammar.rs b/crates/lang/src/parser/grammar.rs index f301d4e..75ed999 100644 --- a/crates/lang/src/parser/grammar.rs +++ b/crates/lang/src/parser/grammar.rs @@ -5,7 +5,7 @@ use super::Parser; mod expression; pub fn source_file(p: &mut Parser) { - let root = p.start(); + let root = p.start("root"); expression::expression(p); p.eat_succeeding_ws(); diff --git a/crates/lang/src/parser/grammar/expression.rs b/crates/lang/src/parser/grammar/expression.rs index cb1f410..3f3a43e 100644 --- a/crates/lang/src/parser/grammar/expression.rs +++ b/crates/lang/src/parser/grammar/expression.rs @@ -47,7 +47,7 @@ mod collection { } pub fn expression(p: &mut Parser) -> Option { - let expr = p.start(); + let expr = p.start("expr"); if atom(p).or_else(|| instr(p)).is_none() { expr.abandon(p); diff --git a/crates/lang/src/parser/grammar/expression/collection/matrix.rs b/crates/lang/src/parser/grammar/expression/collection/matrix.rs index 58ba9e1..2c572f5 100644 --- a/crates/lang/src/parser/grammar/expression/collection/matrix.rs +++ b/crates/lang/src/parser/grammar/expression/collection/matrix.rs @@ -6,7 +6,7 @@ use crate::parser::{ }; pub fn matrix(p: &mut Parser) -> CompletedMarker { - let matrix = p.start(); + let matrix = p.start("matrix"); p.eat(MAT_KW); if !p.eat(PAT_DIMENSIONS) { @@ -20,7 +20,7 @@ pub fn matrix(p: &mut Parser) -> CompletedMarker { } fn matrix_body(p: &mut Parser) { - let mat_body = p.start(); + let mat_body = p.start("mat_body"); if !p.eat(L_BRACK) { mat_body.complete_err(p, SyntaxError::Expected(vec![MAT_BODY])); return (); @@ -28,10 +28,10 @@ fn matrix_body(p: &mut Parser) { let mut going = true; - let mut mat_row = p.start(); + let mut mat_row = p.start("mat_row"); let mut row_items = 0; while going { - let mat_item = p.start(); + let mat_item = p.start("mat_item"); if expression(p).is_some() { mat_item.complete(p, MAT_ITEM); row_items += 1; @@ -39,13 +39,13 @@ fn matrix_body(p: &mut Parser) { COMMA => p.do_bump(), SEMICOLON => { mat_row.complete(p, MAT_ROW); - mat_row = p.start(); + mat_row = p.start("mat_row"); p.do_bump(); row_items = 0; } R_BRACK => going = false, _ => { - let err = p.start(); + let err = p.start("err"); p.do_bump(); err.complete_err(p, SyntaxError::Expected(vec![COMMA, SEMICOLON, R_BRACK])); @@ -54,7 +54,7 @@ fn matrix_body(p: &mut Parser) { } else if p.at(R_BRACK) { going = false; } else { - let err = p.start(); + let err = p.start("err"); p.do_bump(); err.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK])); } diff --git a/crates/lang/src/parser/grammar/expression/instruction.rs b/crates/lang/src/parser/grammar/expression/instruction.rs index 5da0f43..5a12f4c 100644 --- a/crates/lang/src/parser/grammar/expression/instruction.rs +++ b/crates/lang/src/parser/grammar/expression/instruction.rs @@ -7,7 +7,7 @@ pub fn instr(p: &mut Parser) -> Option { return None; } - let instr = p.start(); + let instr = p.start("instr"); instr_name(p); instr_params(p); @@ -16,7 +16,7 @@ pub fn instr(p: &mut Parser) -> Option { } fn instr_name(p: &mut Parser) { - let instr_name = p.start(); + let instr_name = p.start("instr_name"); while p.at(IDENT) { p.do_bump(); diff --git a/crates/lang/src/parser/grammar/expression/lit.rs b/crates/lang/src/parser/grammar/expression/lit.rs index bb48360..afc1408 100644 --- a/crates/lang/src/parser/grammar/expression/lit.rs +++ b/crates/lang/src/parser/grammar/expression/lit.rs @@ -12,7 +12,7 @@ pub fn literal(p: &mut Parser) -> Option { return None; } - let lit = p.start(); + let lit = p.start("lit"); p.do_bump(); diff --git a/testfiles/test.owo b/testfiles/test.owo index 87a5bc8..4a3beda 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,4 +1,4 @@ mat 2x2 [ - 1, 2; + 1 2, 2; 3, 4 ] -- 2.46.0 From 9510d9254cf4b9a4d57a2a12fa15dfdbee641bc7 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 24 Apr 2024 19:55:25 +0200 Subject: [PATCH 43/67] lang: fix matrix recovery --- crates/lang/src/parser/grammar/expression/collection/matrix.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/lang/src/parser/grammar/expression/collection/matrix.rs b/crates/lang/src/parser/grammar/expression/collection/matrix.rs index 2c572f5..a8e21a1 100644 --- a/crates/lang/src/parser/grammar/expression/collection/matrix.rs +++ b/crates/lang/src/parser/grammar/expression/collection/matrix.rs @@ -52,8 +52,10 @@ fn matrix_body(p: &mut Parser) { } }; } else if p.at(R_BRACK) { + mat_item.abandon(p); going = false; } else { + mat_item.abandon(p); let err = p.start("err"); p.do_bump(); err.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK])); -- 2.46.0 From ba0da33509cc2e02b1685cf742e4da8cb9d02f88 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 24 Apr 2024 20:00:17 +0200 Subject: [PATCH 44/67] split up files a bit --- crates/lang/src/parser/grammar/expression.rs | 42 +------------------ .../parser/grammar/expression/collection.rs | 27 ++++++++++++ .../grammar/expression/collection/attr_set.rs | 5 +++ .../grammar/expression/collection/vec.rs | 5 +++ 4 files changed, 38 insertions(+), 41 deletions(-) create mode 100644 crates/lang/src/parser/grammar/expression/collection.rs create mode 100644 crates/lang/src/parser/grammar/expression/collection/attr_set.rs create mode 100644 crates/lang/src/parser/grammar/expression/collection/vec.rs diff --git a/crates/lang/src/parser/grammar/expression.rs b/crates/lang/src/parser/grammar/expression.rs index 3f3a43e..2d3460c 100644 --- a/crates/lang/src/parser/grammar/expression.rs +++ b/crates/lang/src/parser/grammar/expression.rs @@ -2,49 +2,9 @@ use crate::parser::{syntax_kind::SyntaxKind::*, CompletedMarker, Parser}; use self::{collection::collection, instruction::instr, lit::literal}; +mod collection; mod instruction; mod lit; -mod collection { - use enumset::enum_set; - - use crate::parser::{ - syntax_kind::{SyntaxKind::*, TokenSet}, - CompletedMarker, Parser, - }; - - use self::{attr_set::attr_set, matrix::matrix, vec::vec}; - - const COLLECTION_START: TokenSet = enum_set!(MAT_KW | L_BRACK | L_BRACE); - - pub fn collection(p: &mut Parser) -> Option { - if !COLLECTION_START.contains(p.current()) { - return None; - } - - Some(match p.current() { - MAT_KW => matrix(p), - L_BRACK => vec(p), - L_BRACE => attr_set(p), - _ => unreachable!(), - }) - } - - mod matrix; - mod vec { - use crate::parser::{CompletedMarker, Parser}; - - pub fn vec(p: &mut Parser) -> CompletedMarker { - todo!() - } - } - mod attr_set { - use crate::parser::{CompletedMarker, Parser}; - - pub fn attr_set(p: &mut Parser) -> CompletedMarker { - todo!() - } - } -} pub fn expression(p: &mut Parser) -> Option { let expr = p.start("expr"); diff --git a/crates/lang/src/parser/grammar/expression/collection.rs b/crates/lang/src/parser/grammar/expression/collection.rs new file mode 100644 index 0000000..0b525ad --- /dev/null +++ b/crates/lang/src/parser/grammar/expression/collection.rs @@ -0,0 +1,27 @@ +use enumset::enum_set; + +use crate::parser::{ + syntax_kind::{SyntaxKind::*, TokenSet}, + CompletedMarker, Parser, +}; + +use self::{attr_set::attr_set, matrix::matrix, vec::vec}; + +mod attr_set; +mod matrix; +mod vec; + +const COLLECTION_START: TokenSet = enum_set!(MAT_KW | L_BRACK | L_BRACE); + +pub fn collection(p: &mut Parser) -> Option { + if !COLLECTION_START.contains(p.current()) { + return None; + } + + Some(match p.current() { + MAT_KW => matrix(p), + L_BRACK => vec(p), + L_BRACE => attr_set(p), + _ => unreachable!(), + }) +} diff --git a/crates/lang/src/parser/grammar/expression/collection/attr_set.rs b/crates/lang/src/parser/grammar/expression/collection/attr_set.rs new file mode 100644 index 0000000..a71ee7f --- /dev/null +++ b/crates/lang/src/parser/grammar/expression/collection/attr_set.rs @@ -0,0 +1,5 @@ +use crate::parser::{CompletedMarker, Parser}; + +pub fn attr_set(p: &mut Parser) -> CompletedMarker { + todo!() +} diff --git a/crates/lang/src/parser/grammar/expression/collection/vec.rs b/crates/lang/src/parser/grammar/expression/collection/vec.rs new file mode 100644 index 0000000..76f3684 --- /dev/null +++ b/crates/lang/src/parser/grammar/expression/collection/vec.rs @@ -0,0 +1,5 @@ +use crate::parser::{CompletedMarker, Parser}; + +pub fn vec(p: &mut Parser) -> CompletedMarker { + todo!() +} -- 2.46.0 From 4df0118aa4f7f78b935755649ce2fb4ec988e26b Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 24 Apr 2024 21:09:55 +0200 Subject: [PATCH 45/67] lang: first test and stuff --- Cargo.lock | 7 ++++ crates/lang/Cargo.toml | 1 + crates/lang/src/parser/grammar.rs | 22 ++++++++++- .../lang/src/parser/grammar/expression/lit.rs | 39 +++++++++++++++++++ testfiles/test.owo | 5 +-- 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d759c0b..0252665 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,6 +504,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + [[package]] name = "ir" version = "0.1.0" @@ -538,6 +544,7 @@ dependencies = [ "ego-tree", "enumset", "indexmap", + "indoc", "logos", "petgraph", "rowan", diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index fba2bcf..c64720c 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -15,6 +15,7 @@ ego-tree = "0.6.2" rowan = "0.15.15" drop_bomb = "0.1.5" enumset = "1.1.3" +indoc = "2" [lints] workspace = true diff --git a/crates/lang/src/parser/grammar.rs b/crates/lang/src/parser/grammar.rs index 75ed999..afcee25 100644 --- a/crates/lang/src/parser/grammar.rs +++ b/crates/lang/src/parser/grammar.rs @@ -1,6 +1,13 @@ +use std::fmt::Debug; + use crate::parser::syntax_kind::SyntaxKind::*; -use super::Parser; +use super::{ + input::Input, + output::Output, + syntax_kind::{self, lex}, + Parser, +}; mod expression; @@ -12,3 +19,16 @@ pub fn source_file(p: &mut Parser) { root.complete(p, ROOT); } + +fn check_parser(input: &str, parser_fn: fn(&mut Parser), output: &str) { + let toks = lex(input); + let mut parser = Parser::new(Input::new(&toks)); + + parser_fn(&mut parser); + + let p_out = dbg!(parser.finish()); + let o = Output::from_parser_output(toks, p_out); + + let s = format!("{o:?}"); + assert_eq!(&s, output); +} diff --git a/crates/lang/src/parser/grammar/expression/lit.rs b/crates/lang/src/parser/grammar/expression/lit.rs index afc1408..4917c88 100644 --- a/crates/lang/src/parser/grammar/expression/lit.rs +++ b/crates/lang/src/parser/grammar/expression/lit.rs @@ -1,6 +1,8 @@ use enumset::enum_set; +use indoc::indoc; use crate::parser::{ + grammar::check_parser, syntax_kind::{SyntaxKind::*, TokenSet}, CompletedMarker, Parser, }; @@ -18,3 +20,40 @@ pub fn literal(p: &mut Parser) -> Option { Some(lit.complete(p, LITERAL)) } + +#[test] +fn test_parse_lst_lit() { + check_parser( + "42", + |p| { + literal(p); + }, + indoc! {r#" + LITERAL { + INT_NUM "42"; + } + "#}, + ); + check_parser( + "3.14", + |p| { + literal(p); + }, + indoc! {r#" + LITERAL { + FLOAT_NUM "3.14"; + } + "#}, + ); + check_parser( + r#""Meow""#, + |p| { + literal(p); + }, + indoc! {r#" + LITERAL { + STRING "\"Meow\""; + } + "#}, + ); +} diff --git a/testfiles/test.owo b/testfiles/test.owo index 4a3beda..d81cc07 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,4 +1 @@ -mat 2x2 [ - 1 2, 2; - 3, 4 -] +42 -- 2.46.0 From 8a541546d9ddac4fa95aca958d58a2835deb69e5 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 28 Apr 2024 13:22:04 +0200 Subject: [PATCH 46/67] app: fix error_reporting not being used --- crates/app/src/config/config_file.rs | 18 +++++++++++++----- crates/app/src/error_reporting.rs | 2 +- testfiles/config.json | 4 ++++ testfiles/config.ron | 4 ++++ testfiles/test.owo | 5 ++++- 5 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 testfiles/config.json create mode 100644 testfiles/config.ron diff --git a/crates/app/src/config/config_file.rs b/crates/app/src/config/config_file.rs index 15f660c..7078d32 100644 --- a/crates/app/src/config/config_file.rs +++ b/crates/app/src/config/config_file.rs @@ -5,7 +5,9 @@ use std::{ use serde::{Deserialize, Serialize}; -use super::error::Config; +use crate::error_reporting::{report_serde_json_err, report_serde_ron_err}; + +use super::error::{self, Config}; #[derive(Debug, Serialize, Deserialize)] pub struct Configs { @@ -40,14 +42,20 @@ pub(super) fn find_config_file() -> Result { } impl Configs { - pub fn read(p: PathBuf) -> Result { + pub fn read(p: PathBuf) -> Result { match p .extension() .map(|v| v.to_str().expect("config path to be UTF-8")) { - Some("ron") => Ok(serde_json::from_str(&fs::read_to_string(p)?)?), - Some("json") => Ok(ron::from_str(&fs::read_to_string(p)?)?), - e => Err(Config::UnknownExtension(e.map(str::to_owned))), + Some("ron") => { + let f = fs::read_to_string(p)?; + ron::from_str(&f).or_else(|e| report_serde_ron_err(&f, &e)) + } + Some("json") => { + let f = fs::read_to_string(p)?; + serde_json::from_str(&f).or_else(|e| report_serde_json_err(&f, &e)) + } + e => Err(error::Config::UnknownExtension(e.map(str::to_owned))), } } } diff --git a/crates/app/src/error_reporting.rs b/crates/app/src/error_reporting.rs index bc4c44b..3981b17 100644 --- a/crates/app/src/error_reporting.rs +++ b/crates/app/src/error_reporting.rs @@ -25,7 +25,7 @@ fn report_serde_err(src: &str, line: usize, col: usize, msg: String) -> ! { .finish() .eprint(("test", Source::from(src))) .expect("writing error to stderr failed"); - process::exit(1); + process::exit(1) } /// Reconstruct a byte offset from the line + column numbers typical from serde crates diff --git a/testfiles/config.json b/testfiles/config.json new file mode 100644 index 0000000..3456c5c --- /dev/null +++ b/testfiles/config.json @@ -0,0 +1,4 @@ +{ + "example_value": "42", + "no_startup_message": true +} \ No newline at end of file diff --git a/testfiles/config.ron b/testfiles/config.ron new file mode 100644 index 0000000..7b47189 --- /dev/null +++ b/testfiles/config.ron @@ -0,0 +1,4 @@ +( + example_value: 42 + no_startup_message: false, +) diff --git a/testfiles/test.owo b/testfiles/test.owo index d81cc07..51f07d1 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1 +1,4 @@ -42 +generator | { + foo, + bar +} | -- 2.46.0 From 9af71ed3f4ad04cf5d31bc195b7abd12b5a61872 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 30 Apr 2024 09:45:36 +0200 Subject: [PATCH 47/67] lang: implement vec, list and matrix --- .../parser/grammar/expression/collection.rs | 8 +-- .../grammar/expression/collection/matrix.rs | 71 ------------------- .../grammar/expression/collection/vec.rs | 71 ++++++++++++++++++- .../parser/grammar/expression/instruction.rs | 6 +- crates/lang/src/parser/syntax_kind.rs | 11 ++- 5 files changed, 78 insertions(+), 89 deletions(-) delete mode 100644 crates/lang/src/parser/grammar/expression/collection/matrix.rs diff --git a/crates/lang/src/parser/grammar/expression/collection.rs b/crates/lang/src/parser/grammar/expression/collection.rs index 0b525ad..e72cb6c 100644 --- a/crates/lang/src/parser/grammar/expression/collection.rs +++ b/crates/lang/src/parser/grammar/expression/collection.rs @@ -5,13 +5,12 @@ use crate::parser::{ CompletedMarker, Parser, }; -use self::{attr_set::attr_set, matrix::matrix, vec::vec}; +use self::{attr_set::attr_set, vec::vec_matrix_list}; mod attr_set; -mod matrix; mod vec; -const COLLECTION_START: TokenSet = enum_set!(MAT_KW | L_BRACK | L_BRACE); +const COLLECTION_START: TokenSet = enum_set!(L_BRACK | L_BRACE); pub fn collection(p: &mut Parser) -> Option { if !COLLECTION_START.contains(p.current()) { @@ -19,8 +18,7 @@ pub fn collection(p: &mut Parser) -> Option { } Some(match p.current() { - MAT_KW => matrix(p), - L_BRACK => vec(p), + L_BRACK => vec_matrix_list(p), L_BRACE => attr_set(p), _ => unreachable!(), }) diff --git a/crates/lang/src/parser/grammar/expression/collection/matrix.rs b/crates/lang/src/parser/grammar/expression/collection/matrix.rs deleted file mode 100644 index a8e21a1..0000000 --- a/crates/lang/src/parser/grammar/expression/collection/matrix.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::parser::{ - error::SyntaxError, - grammar::expression::{self, expression}, - syntax_kind::SyntaxKind::*, - CompletedMarker, Marker, Parser, -}; - -pub fn matrix(p: &mut Parser) -> CompletedMarker { - let matrix = p.start("matrix"); - p.eat(MAT_KW); - - if !p.eat(PAT_DIMENSIONS) { - eprintln!("TODO (as eprintln so i dont forget): improve recovery algorithms"); - return matrix.complete_err(p, SyntaxError::Expected(vec![PAT_DIMENSIONS])); - } - - matrix_body(p); - - matrix.complete(p, MATRIX) -} - -fn matrix_body(p: &mut Parser) { - let mat_body = p.start("mat_body"); - if !p.eat(L_BRACK) { - mat_body.complete_err(p, SyntaxError::Expected(vec![MAT_BODY])); - return (); - } - - let mut going = true; - - let mut mat_row = p.start("mat_row"); - let mut row_items = 0; - while going { - let mat_item = p.start("mat_item"); - if expression(p).is_some() { - mat_item.complete(p, MAT_ITEM); - row_items += 1; - match p.current() { - COMMA => p.do_bump(), - SEMICOLON => { - mat_row.complete(p, MAT_ROW); - mat_row = p.start("mat_row"); - p.do_bump(); - row_items = 0; - } - R_BRACK => going = false, - _ => { - let err = p.start("err"); - p.do_bump(); - - err.complete_err(p, SyntaxError::Expected(vec![COMMA, SEMICOLON, R_BRACK])); - } - }; - } else if p.at(R_BRACK) { - mat_item.abandon(p); - going = false; - } else { - mat_item.abandon(p); - let err = p.start("err"); - p.do_bump(); - err.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK])); - } - } - if row_items != 0 { - mat_row.complete(p, MAT_ROW); - } else { - mat_row.abandon(p); - } - p.eat(R_BRACK); - mat_body.complete(p, MAT_BODY); -} diff --git a/crates/lang/src/parser/grammar/expression/collection/vec.rs b/crates/lang/src/parser/grammar/expression/collection/vec.rs index 76f3684..eff741a 100644 --- a/crates/lang/src/parser/grammar/expression/collection/vec.rs +++ b/crates/lang/src/parser/grammar/expression/collection/vec.rs @@ -1,5 +1,70 @@ -use crate::parser::{CompletedMarker, Parser}; +use crate::parser::{ + error::SyntaxError, grammar::expression::atom, CompletedMarker, Marker, Parser, SyntaxKind::*, +}; -pub fn vec(p: &mut Parser) -> CompletedMarker { - todo!() +pub fn vec_matrix_list(p: &mut Parser) -> CompletedMarker { + let start = p.start("vec_matrix_list_start"); + assert!(p.eat(L_BRACK)); + let row_start = p.start("matrix_row_start"); + if let Some(item) = atom(p) { + item.precede(p).complete(p, COLLECTION_ITEM); + + if p.at(COMMA) { + row_start.abandon(p); + return finish_list(p, start); + } + + finish_mat_or_vec(p, start, row_start) + } else if p.eat(R_BRACK) { + start.complete(p, LIST) + } else { + start.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK])) + } +} + +// TODO: handle semicolons, other wrong toks +fn finish_list(p: &mut Parser, list_start: Marker) -> CompletedMarker { + loop { + if p.eat(COMMA) { + if let Some(item) = atom(p) { + item.precede(p).complete(p, COLLECTION_ITEM); + } else if p.eat(R_BRACK) { + return list_start.complete(p, LIST); + } + } else if p.eat(R_BRACK) { + return list_start.complete(p, LIST); + } + } +} + +// TODO: handle commas, general other wrong toks +fn finish_mat_or_vec(p: &mut Parser, coll_start: Marker, mut row_start: Marker) -> CompletedMarker { + let mut is_matrix = false; + let mut row_item_count = 1; + loop { + if let Some(item) = atom(p) { + item.precede(p).complete(p, COLLECTION_ITEM); + row_item_count += 1; + } else if p.at(SEMICOLON) { + is_matrix = true; + row_start.complete(p, MAT_ROW); + p.eat(SEMICOLON); + row_start = p.start("matrix_row_start"); + row_item_count = 0; + } else if p.at(R_BRACK) { + if is_matrix && row_item_count == 0 { + row_start.abandon(p); + p.eat(R_BRACK); + return coll_start.complete(p, MATRIX); + } else if is_matrix { + row_start.complete(p, MAT_ROW); + p.eat(R_BRACK); + return coll_start.complete(p, MATRIX); + } else { + row_start.abandon(p); + p.eat(R_BRACK); + return coll_start.complete(p, VEC); + } + } + } } diff --git a/crates/lang/src/parser/grammar/expression/instruction.rs b/crates/lang/src/parser/grammar/expression/instruction.rs index 5a12f4c..832d2ab 100644 --- a/crates/lang/src/parser/grammar/expression/instruction.rs +++ b/crates/lang/src/parser/grammar/expression/instruction.rs @@ -1,6 +1,6 @@ use crate::parser::{syntax_kind::SyntaxKind::*, CompletedMarker, Parser}; -use super::lit::literal; +use super::{atom, lit::literal}; pub fn instr(p: &mut Parser) -> Option { if !p.at(IDENT) { @@ -26,8 +26,8 @@ fn instr_name(p: &mut Parser) { } fn instr_params(p: &mut Parser) { - if let Some(start) = literal(p) { - while literal(p).is_some() {} + if let Some(start) = atom(p) { + while atom(p).is_some() {} start.precede(p).complete(p, INSTR_PARAMS); } diff --git a/crates/lang/src/parser/syntax_kind.rs b/crates/lang/src/parser/syntax_kind.rs index cc1754c..0447a90 100644 --- a/crates/lang/src/parser/syntax_kind.rs +++ b/crates/lang/src/parser/syntax_kind.rs @@ -23,10 +23,6 @@ pub enum SyntaxKind { LET_KW, #[token("in")] IN_KW, - #[token("mat")] - MAT_KW, - #[regex("[\\d]+x[\\d]+")] - PAT_DIMENSIONS, #[regex("[\\d]+")] INT_NUM, #[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)")] @@ -34,11 +30,12 @@ pub enum SyntaxKind { #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#)] STRING, MATRIX, - MAT_BODY, MAT_ROW, - MAT_ITEM, - DECL, + VEC, LIST, + // either of a vec, a matrix or a list + COLLECTION_ITEM, + DECL, PARENTHESIZED_EXPR, EXPR, LITERAL, -- 2.46.0 From db2643359c950614602cc85286550b925597f319 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 30 Apr 2024 10:18:59 +0200 Subject: [PATCH 48/67] lang: basic attrset parser --- .../grammar/expression/collection/attr_set.rs | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/lang/src/parser/grammar/expression/collection/attr_set.rs b/crates/lang/src/parser/grammar/expression/collection/attr_set.rs index a71ee7f..2ef90b5 100644 --- a/crates/lang/src/parser/grammar/expression/collection/attr_set.rs +++ b/crates/lang/src/parser/grammar/expression/collection/attr_set.rs @@ -1,5 +1,45 @@ -use crate::parser::{CompletedMarker, Parser}; +use crate::parser::{ + error::SyntaxError, + grammar::expression::{atom, expression}, + CompletedMarker, Marker, Parser, + SyntaxKind::*, +}; pub fn attr_set(p: &mut Parser) -> CompletedMarker { - todo!() + let start = p.start("attr_set_start"); + assert!(p.eat(L_BRACE)); + + loop { + if attr(p).is_some() { + // TODO: handle others + if p.eat(COMMA) { + continue; + } else if p.eat(R_BRACE) { + return start.complete(p, ATTR_SET); + } + // TODO: check for newline and stuff following that for recov of others + } else if p.eat(R_BRACE) { + return start.complete(p, ATTR_SET); + } + } +} + +fn attr(p: &mut Parser) -> Option { + if p.at(IDENT) { + let attr_start = p.start("attr"); + let attr_name_start = p.start("attr_name"); + p.do_bump(); + attr_name_start.complete(p, ATTR_NAME); + + // TODO: handle comma, expr/atom, other + p.eat(COLON); + + // TODO: handle failed expr parser too + let attr_value = p.start("attr_value"); + let _ = expression(p); + attr_value.complete(p, ATTR_VALUE); + Some(attr_start.complete(p, ATTR)) + } else { + None + } } -- 2.46.0 From 30f17773a84d0e1202329721e52c82a629cc0e54 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 30 Apr 2024 12:21:06 +0200 Subject: [PATCH 49/67] lang: add pipelines and rename parser to lst_parser --- crates/lang/src/lib.rs | 2 +- crates/lang/src/{parser.rs => lst_parser.rs} | 4 +- .../lang/src/{parser => lst_parser}/error.rs | 3 +- .../lang/src/{parser => lst_parser}/events.rs | 2 +- .../src/{parser => lst_parser}/grammar.rs | 4 +- .../lang/src/lst_parser/grammar/expression.rs | 81 +++++++++++++++++++ .../grammar/expression/collection.rs | 2 +- .../grammar/expression/collection/attr_set.rs | 4 +- .../grammar/expression/collection/vec.rs | 11 ++- .../grammar/expression/instruction.rs | 4 +- .../grammar/expression/lit.rs | 2 +- .../lang/src/{parser => lst_parser}/input.rs | 2 +- .../lang/src/{parser => lst_parser}/output.rs | 2 +- .../src/{parser => lst_parser}/syntax_kind.rs | 1 + .../lang/src/{parser => lst_parser}/tests.rs | 0 crates/lang/src/main.rs | 4 +- crates/lang/src/parser/grammar/expression.rs | 22 ----- testfiles/test.owo | 5 +- 18 files changed, 108 insertions(+), 47 deletions(-) rename crates/lang/src/{parser.rs => lst_parser.rs} (96%) rename crates/lang/src/{parser => lst_parser}/error.rs (51%) rename crates/lang/src/{parser => lst_parser}/events.rs (88%) rename crates/lang/src/{parser => lst_parser}/grammar.rs (87%) create mode 100644 crates/lang/src/lst_parser/grammar/expression.rs rename crates/lang/src/{parser => lst_parser}/grammar/expression/collection.rs (95%) rename crates/lang/src/{parser => lst_parser}/grammar/expression/collection/attr_set.rs (95%) rename crates/lang/src/{parser => lst_parser}/grammar/expression/collection/vec.rs (87%) rename crates/lang/src/{parser => lst_parser}/grammar/expression/instruction.rs (78%) rename crates/lang/src/{parser => lst_parser}/grammar/expression/lit.rs (97%) rename crates/lang/src/{parser => lst_parser}/input.rs (97%) rename crates/lang/src/{parser => lst_parser}/output.rs (98%) rename crates/lang/src/{parser => lst_parser}/syntax_kind.rs (99%) rename crates/lang/src/{parser => lst_parser}/tests.rs (100%) delete mode 100644 crates/lang/src/parser/grammar/expression.rs diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index 1b08789..e552496 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -1,2 +1,2 @@ #![feature(type_alias_impl_trait, lint_reasons)] -pub mod parser; +pub mod lst_parser; diff --git a/crates/lang/src/parser.rs b/crates/lang/src/lst_parser.rs similarity index 96% rename from crates/lang/src/parser.rs rename to crates/lang/src/lst_parser.rs index dfd80c5..9b93010 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/lst_parser.rs @@ -134,8 +134,8 @@ pub(crate) struct CompletedMarker { } impl CompletedMarker { - pub(crate) fn precede(self, p: &mut Parser<'_, '_>) -> Marker { - let new_pos = p.start("new_pos"); + pub(crate) fn precede(self, p: &mut Parser<'_, '_>, name: &str) -> Marker { + let new_pos = p.start(name); match &mut p.events[self.pos] { Event::Start { forward_parent, .. } => { diff --git a/crates/lang/src/parser/error.rs b/crates/lang/src/lst_parser/error.rs similarity index 51% rename from crates/lang/src/parser/error.rs rename to crates/lang/src/lst_parser/error.rs index 698ecaf..d13306c 100644 --- a/crates/lang/src/parser/error.rs +++ b/crates/lang/src/lst_parser/error.rs @@ -1,6 +1,7 @@ -use crate::parser::syntax_kind::SyntaxKind; +use crate::lst_parser::syntax_kind::SyntaxKind; #[derive(Debug)] pub enum SyntaxError { Expected(Vec), + PipelineNeedsSink, } diff --git a/crates/lang/src/parser/events.rs b/crates/lang/src/lst_parser/events.rs similarity index 88% rename from crates/lang/src/parser/events.rs rename to crates/lang/src/lst_parser/events.rs index 6fc5b2c..00d7524 100644 --- a/crates/lang/src/parser/events.rs +++ b/crates/lang/src/lst_parser/events.rs @@ -1,4 +1,4 @@ -use crate::parser::syntax_kind::SyntaxKind; +use crate::lst_parser::syntax_kind::SyntaxKind; #[derive(Debug)] pub enum Event { diff --git a/crates/lang/src/parser/grammar.rs b/crates/lang/src/lst_parser/grammar.rs similarity index 87% rename from crates/lang/src/parser/grammar.rs rename to crates/lang/src/lst_parser/grammar.rs index afcee25..9d081bb 100644 --- a/crates/lang/src/parser/grammar.rs +++ b/crates/lang/src/lst_parser/grammar.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use crate::parser::syntax_kind::SyntaxKind::*; +use crate::lst_parser::syntax_kind::SyntaxKind::*; use super::{ input::Input, @@ -14,7 +14,7 @@ mod expression; pub fn source_file(p: &mut Parser) { let root = p.start("root"); - expression::expression(p); + expression::expression(p, false); p.eat_succeeding_ws(); root.complete(p, ROOT); diff --git a/crates/lang/src/lst_parser/grammar/expression.rs b/crates/lang/src/lst_parser/grammar/expression.rs new file mode 100644 index 0000000..6ad7235 --- /dev/null +++ b/crates/lang/src/lst_parser/grammar/expression.rs @@ -0,0 +1,81 @@ +use crate::lst_parser::{error::SyntaxError, syntax_kind::SyntaxKind::*, CompletedMarker, Parser}; + +use self::{collection::collection, instruction::instr, lit::literal, pipeline::PIPES}; + +mod collection; +mod instruction; +mod lit; +mod pipeline { + use enumset::enum_set; + + use crate::lst_parser::{ + error::SyntaxError, + syntax_kind::{SyntaxKind::*, TokenSet}, + CompletedMarker, Parser, + }; + + use super::expression; + + pub fn pipeline(p: &mut Parser, start_expr: CompletedMarker) -> Option { + if !pipe(p) { + return Some(start_expr); + } + let pipeline_marker = start_expr.precede(p, "pipeline_start"); + + loop { + if expression(p, true).is_none() { + return Some(pipeline_marker.complete_err(p, SyntaxError::PipelineNeedsSink)); + } + if !pipe(p) { + return Some(pipeline_marker.complete(p, PIPELINE)); + } + } + } + + pub const PIPES: TokenSet = enum_set!(PIPE | MAPPING_PIPE | NULL_PIPE); + + fn pipe(p: &mut Parser) -> bool { + if PIPES.contains(p.current()) { + p.do_bump(); + true + } else { + false + } + } +} + +pub fn expression(p: &mut Parser, in_pipe: bool) -> Option { + let expr = p.start("expr"); + + if atom(p).or_else(|| instr(p)).is_none() { + expr.abandon(p); + return None; + } + + let r = expr.complete(p, EXPR); + + if PIPES.contains(p.current()) && !in_pipe { + pipeline::pipeline(p, r) + } else { + Some(r) + } +} + +pub fn atom(p: &mut Parser) -> Option { + literal(p) + .or_else(|| collection(p)) + .or_else(|| parenthesized_expr(p)) +} + +pub fn parenthesized_expr(p: &mut Parser) -> Option { + if p.eat(L_PAREN) { + let par_expr = p.start("parenthesized"); + expression(p, false); + if !p.eat(R_PAREN) { + return Some(par_expr.complete_err(p, SyntaxError::Expected(vec![R_PAREN]))); + } + + return Some(par_expr.complete(p, PARENTHESIZED_EXPR)); + } + None +} diff --git a/crates/lang/src/parser/grammar/expression/collection.rs b/crates/lang/src/lst_parser/grammar/expression/collection.rs similarity index 95% rename from crates/lang/src/parser/grammar/expression/collection.rs rename to crates/lang/src/lst_parser/grammar/expression/collection.rs index e72cb6c..8535291 100644 --- a/crates/lang/src/parser/grammar/expression/collection.rs +++ b/crates/lang/src/lst_parser/grammar/expression/collection.rs @@ -1,6 +1,6 @@ use enumset::enum_set; -use crate::parser::{ +use crate::lst_parser::{ syntax_kind::{SyntaxKind::*, TokenSet}, CompletedMarker, Parser, }; diff --git a/crates/lang/src/parser/grammar/expression/collection/attr_set.rs b/crates/lang/src/lst_parser/grammar/expression/collection/attr_set.rs similarity index 95% rename from crates/lang/src/parser/grammar/expression/collection/attr_set.rs rename to crates/lang/src/lst_parser/grammar/expression/collection/attr_set.rs index 2ef90b5..b5a73fa 100644 --- a/crates/lang/src/parser/grammar/expression/collection/attr_set.rs +++ b/crates/lang/src/lst_parser/grammar/expression/collection/attr_set.rs @@ -1,4 +1,4 @@ -use crate::parser::{ +use crate::lst_parser::{ error::SyntaxError, grammar::expression::{atom, expression}, CompletedMarker, Marker, Parser, @@ -36,7 +36,7 @@ fn attr(p: &mut Parser) -> Option { // TODO: handle failed expr parser too let attr_value = p.start("attr_value"); - let _ = expression(p); + let _ = expression(p, false); attr_value.complete(p, ATTR_VALUE); Some(attr_start.complete(p, ATTR)) } else { diff --git a/crates/lang/src/parser/grammar/expression/collection/vec.rs b/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs similarity index 87% rename from crates/lang/src/parser/grammar/expression/collection/vec.rs rename to crates/lang/src/lst_parser/grammar/expression/collection/vec.rs index eff741a..fa76058 100644 --- a/crates/lang/src/parser/grammar/expression/collection/vec.rs +++ b/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs @@ -1,4 +1,4 @@ -use crate::parser::{ +use crate::lst_parser::{ error::SyntaxError, grammar::expression::atom, CompletedMarker, Marker, Parser, SyntaxKind::*, }; @@ -7,7 +7,8 @@ pub fn vec_matrix_list(p: &mut Parser) -> CompletedMarker { assert!(p.eat(L_BRACK)); let row_start = p.start("matrix_row_start"); if let Some(item) = atom(p) { - item.precede(p).complete(p, COLLECTION_ITEM); + item.precede(p, "coll_item_start") + .complete(p, COLLECTION_ITEM); if p.at(COMMA) { row_start.abandon(p); @@ -27,7 +28,8 @@ fn finish_list(p: &mut Parser, list_start: Marker) -> CompletedMarker { loop { if p.eat(COMMA) { if let Some(item) = atom(p) { - item.precede(p).complete(p, COLLECTION_ITEM); + item.precede(p, "coll_item_start") + .complete(p, COLLECTION_ITEM); } else if p.eat(R_BRACK) { return list_start.complete(p, LIST); } @@ -43,7 +45,8 @@ fn finish_mat_or_vec(p: &mut Parser, coll_start: Marker, mut row_start: Marker) let mut row_item_count = 1; loop { if let Some(item) = atom(p) { - item.precede(p).complete(p, COLLECTION_ITEM); + item.precede(p, "coll_item_start") + .complete(p, COLLECTION_ITEM); row_item_count += 1; } else if p.at(SEMICOLON) { is_matrix = true; diff --git a/crates/lang/src/parser/grammar/expression/instruction.rs b/crates/lang/src/lst_parser/grammar/expression/instruction.rs similarity index 78% rename from crates/lang/src/parser/grammar/expression/instruction.rs rename to crates/lang/src/lst_parser/grammar/expression/instruction.rs index 832d2ab..bbe0ed3 100644 --- a/crates/lang/src/parser/grammar/expression/instruction.rs +++ b/crates/lang/src/lst_parser/grammar/expression/instruction.rs @@ -1,4 +1,4 @@ -use crate::parser::{syntax_kind::SyntaxKind::*, CompletedMarker, Parser}; +use crate::lst_parser::{syntax_kind::SyntaxKind::*, CompletedMarker, Parser}; use super::{atom, lit::literal}; @@ -29,6 +29,6 @@ fn instr_params(p: &mut Parser) { if let Some(start) = atom(p) { while atom(p).is_some() {} - start.precede(p).complete(p, INSTR_PARAMS); + start.precede(p, "params_start").complete(p, INSTR_PARAMS); } } diff --git a/crates/lang/src/parser/grammar/expression/lit.rs b/crates/lang/src/lst_parser/grammar/expression/lit.rs similarity index 97% rename from crates/lang/src/parser/grammar/expression/lit.rs rename to crates/lang/src/lst_parser/grammar/expression/lit.rs index 4917c88..8a18a04 100644 --- a/crates/lang/src/parser/grammar/expression/lit.rs +++ b/crates/lang/src/lst_parser/grammar/expression/lit.rs @@ -1,7 +1,7 @@ use enumset::enum_set; use indoc::indoc; -use crate::parser::{ +use crate::lst_parser::{ grammar::check_parser, syntax_kind::{SyntaxKind::*, TokenSet}, CompletedMarker, Parser, diff --git a/crates/lang/src/parser/input.rs b/crates/lang/src/lst_parser/input.rs similarity index 97% rename from crates/lang/src/parser/input.rs rename to crates/lang/src/lst_parser/input.rs index c61fc87..e9a5e2e 100644 --- a/crates/lang/src/parser/input.rs +++ b/crates/lang/src/lst_parser/input.rs @@ -1,4 +1,4 @@ -use crate::parser::syntax_kind::SyntaxKind; +use crate::lst_parser::syntax_kind::SyntaxKind; pub struct Input<'src, 'toks> { raw: &'toks Vec<(SyntaxKind, &'src str)>, diff --git a/crates/lang/src/parser/output.rs b/crates/lang/src/lst_parser/output.rs similarity index 98% rename from crates/lang/src/parser/output.rs rename to crates/lang/src/lst_parser/output.rs index 75019bb..b530ac3 100644 --- a/crates/lang/src/parser/output.rs +++ b/crates/lang/src/lst_parser/output.rs @@ -1,7 +1,7 @@ use rowan::{GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken}; use std::mem; -use crate::parser::syntax_kind::{Lang, SyntaxKind}; +use crate::lst_parser::syntax_kind::{Lang, SyntaxKind}; use super::{error::SyntaxError, events::Event}; diff --git a/crates/lang/src/parser/syntax_kind.rs b/crates/lang/src/lst_parser/syntax_kind.rs similarity index 99% rename from crates/lang/src/parser/syntax_kind.rs rename to crates/lang/src/lst_parser/syntax_kind.rs index 0447a90..3d817e3 100644 --- a/crates/lang/src/parser/syntax_kind.rs +++ b/crates/lang/src/lst_parser/syntax_kind.rs @@ -92,6 +92,7 @@ pub enum SyntaxKind { MAPPING_PIPE, #[token("!|")] NULL_PIPE, + PIPELINE, #[token("=")] EQ, #[token(":")] diff --git a/crates/lang/src/parser/tests.rs b/crates/lang/src/lst_parser/tests.rs similarity index 100% rename from crates/lang/src/parser/tests.rs rename to crates/lang/src/lst_parser/tests.rs diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 515cbe0..517094d 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::{fs, path::PathBuf}; -use lang::parser::{self, grammar, input, output::Output, syntax_kind}; +use lang::lst_parser::{self, grammar, input, output::Output, syntax_kind}; #[derive(Parser)] struct Args { @@ -16,7 +16,7 @@ fn main() { let toks = dbg!(syntax_kind::lex(&f)); let input = input::Input::new(&toks); - let mut parser = parser::Parser::new(input); + let mut parser = lst_parser::Parser::new(input); grammar::source_file(&mut parser); diff --git a/crates/lang/src/parser/grammar/expression.rs b/crates/lang/src/parser/grammar/expression.rs deleted file mode 100644 index 2d3460c..0000000 --- a/crates/lang/src/parser/grammar/expression.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::parser::{syntax_kind::SyntaxKind::*, CompletedMarker, Parser}; - -use self::{collection::collection, instruction::instr, lit::literal}; - -mod collection; -mod instruction; -mod lit; - -pub fn expression(p: &mut Parser) -> Option { - let expr = p.start("expr"); - - if atom(p).or_else(|| instr(p)).is_none() { - expr.abandon(p); - return None; - } - - Some(expr.complete(p, EXPR)) -} - -pub fn atom(p: &mut Parser) -> Option { - literal(p).or_else(|| collection(p)) -} diff --git a/testfiles/test.owo b/testfiles/test.owo index 51f07d1..bbcf720 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,4 +1 @@ -generator | { - foo, - bar -} | +meow 1 (3 | add 1) -- 2.46.0 From afd493be16771b4e8fa056860073d96674a9b502 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 4 May 2024 21:44:02 +0200 Subject: [PATCH 50/67] lang: parse pipelines --- .../lang/src/lst_parser/grammar/expression.rs | 39 +------------------ .../lst_parser/grammar/expression/pipeline.rs | 36 +++++++++++++++++ crates/lang/src/lst_parser/syntax_kind.rs | 11 ++++++ 3 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 crates/lang/src/lst_parser/grammar/expression/pipeline.rs diff --git a/crates/lang/src/lst_parser/grammar/expression.rs b/crates/lang/src/lst_parser/grammar/expression.rs index 6ad7235..33ef3fc 100644 --- a/crates/lang/src/lst_parser/grammar/expression.rs +++ b/crates/lang/src/lst_parser/grammar/expression.rs @@ -5,44 +5,7 @@ use self::{collection::collection, instruction::instr, lit::literal, pipeline::P mod collection; mod instruction; mod lit; -mod pipeline { - use enumset::enum_set; - - use crate::lst_parser::{ - error::SyntaxError, - syntax_kind::{SyntaxKind::*, TokenSet}, - CompletedMarker, Parser, - }; - - use super::expression; - - pub fn pipeline(p: &mut Parser, start_expr: CompletedMarker) -> Option { - if !pipe(p) { - return Some(start_expr); - } - let pipeline_marker = start_expr.precede(p, "pipeline_start"); - - loop { - if expression(p, true).is_none() { - return Some(pipeline_marker.complete_err(p, SyntaxError::PipelineNeedsSink)); - } - if !pipe(p) { - return Some(pipeline_marker.complete(p, PIPELINE)); - } - } - } - - pub const PIPES: TokenSet = enum_set!(PIPE | MAPPING_PIPE | NULL_PIPE); - - fn pipe(p: &mut Parser) -> bool { - if PIPES.contains(p.current()) { - p.do_bump(); - true - } else { - false - } - } -} +mod pipeline; pub fn expression(p: &mut Parser, in_pipe: bool) -> Option { let expr = p.start("expr"); diff --git a/crates/lang/src/lst_parser/grammar/expression/pipeline.rs b/crates/lang/src/lst_parser/grammar/expression/pipeline.rs new file mode 100644 index 0000000..f2f090d --- /dev/null +++ b/crates/lang/src/lst_parser/grammar/expression/pipeline.rs @@ -0,0 +1,36 @@ +use enumset::enum_set; + +use crate::lst_parser::{ + error::SyntaxError, + syntax_kind::{SyntaxKind::*, TokenSet}, + CompletedMarker, Parser, +}; + +use super::expression; + +pub fn pipeline(p: &mut Parser, start_expr: CompletedMarker) -> Option { + if !pipe(p) { + return Some(start_expr); + } + let pipeline_marker = start_expr.precede(p, "pipeline_start"); + + loop { + if expression(p, true).is_none() { + return Some(pipeline_marker.complete_err(p, SyntaxError::PipelineNeedsSink)); + } + if !pipe(p) { + return Some(pipeline_marker.complete(p, PIPELINE)); + } + } +} + +pub const PIPES: TokenSet = enum_set!(PIPE | MAPPING_PIPE | NULL_PIPE); + +fn pipe(p: &mut Parser) -> bool { + if PIPES.contains(p.current()) { + p.do_bump(); + true + } else { + false + } +} diff --git a/crates/lang/src/lst_parser/syntax_kind.rs b/crates/lang/src/lst_parser/syntax_kind.rs index 3d817e3..4914aeb 100644 --- a/crates/lang/src/lst_parser/syntax_kind.rs +++ b/crates/lang/src/lst_parser/syntax_kind.rs @@ -19,10 +19,21 @@ pub fn lex(src: &str) -> Vec<(SyntaxKind, &str)> { pub enum SyntaxKind { #[token("def")] DEF_KW = 0, + DEF, + DEF_NAME, + DEF_BODY, #[token("let")] LET_KW, #[token("in")] IN_KW, + LET_IN, + #[token("mod")] + MOD_KW, + MODULE, + MODULE_BODY, + #[token("use")] + USE_KW, + USE_PAT, #[regex("[\\d]+")] INT_NUM, #[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)")] -- 2.46.0 From 29cdcfbe0ca7fa2bbcca463275b3e80331ef83b7 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 4 May 2024 21:56:12 +0200 Subject: [PATCH 51/67] lang: make output errors debuggable --- crates/lang/src/lst_parser/output.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/lang/src/lst_parser/output.rs b/crates/lang/src/lst_parser/output.rs index b530ac3..141d7d9 100644 --- a/crates/lang/src/lst_parser/output.rs +++ b/crates/lang/src/lst_parser/output.rs @@ -9,9 +9,12 @@ pub struct Output { pub green_node: GreenNode, pub errors: Vec, } + impl std::fmt::Debug for Output { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - debug_print_green_node(NodeOrToken::Node(&self.green_node), f, 0) + let mut errs: Vec<&SyntaxError> = self.errors.iter().collect(); + errs.reverse(); + debug_print_green_node(NodeOrToken::Node(&self.green_node), f, 0, &mut errs) } } @@ -19,6 +22,7 @@ fn debug_print_green_node( node: NodeOrToken<&GreenNodeData, &GreenTokenData>, f: &mut std::fmt::Formatter<'_>, lvl: i32, + errs: &mut Vec<&SyntaxError>, ) -> std::fmt::Result { for _ in 0..lvl { f.write_str(" ")?; @@ -26,9 +30,18 @@ fn debug_print_green_node( match node { NodeOrToken::Node(n) => { - writeln!(f, "{:?} {{", Lang::kind_from_raw(node.kind()))?; + let kind = Lang::kind_from_raw(node.kind()); + if kind != SyntaxKind::PARSE_ERR { + writeln!(f, "{:?} {{", Lang::kind_from_raw(node.kind()))?; + } else { + let err = errs + .pop() + .expect("all error syntax nodes should correspond to an error"); + + writeln!(f, "{:?}: {err:?} {{", kind)?; + } for c in n.children() { - debug_print_green_node(c, f, lvl + 1)?; + debug_print_green_node(c, f, lvl + 1, errs)?; } for _ in 0..lvl { f.write_str(" ")?; -- 2.46.0 From 4bcaf945d739fec76f2ba6ad6718f78af713c351 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 4 May 2024 22:35:18 +0200 Subject: [PATCH 52/67] lang: add highlighting to errors --- Cargo.lock | 37 ++++++++++++ crates/lang/Cargo.toml | 1 + crates/lang/src/lst_parser/input.rs | 15 ++++- crates/lang/src/lst_parser/output.rs | 86 ++++++++++++++++++++++++---- crates/lang/src/main.rs | 2 +- testfiles/test.owo | 2 +- 6 files changed, 128 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0252665..cc9818c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,6 +469,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "ident_case" version = "1.0.1" @@ -519,6 +525,23 @@ dependencies = [ "serde", ] +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_ci" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" + [[package]] name = "itoa" version = "1.0.10" @@ -546,6 +569,7 @@ dependencies = [ "indexmap", "indoc", "logos", + "owo-colors", "petgraph", "rowan", ] @@ -697,6 +721,9 @@ name = "owo-colors" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" +dependencies = [ + "supports-color", +] [[package]] name = "petgraph" @@ -974,6 +1001,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "supports-color" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" +dependencies = [ + "is-terminal", + "is_ci", +] + [[package]] name = "svg-filters" version = "0.1.0" diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index c64720c..8a9c80b 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -16,6 +16,7 @@ rowan = "0.15.15" drop_bomb = "0.1.5" enumset = "1.1.3" indoc = "2" +owo-colors = {version = "4", features = ["supports-colors"]} [lints] workspace = true diff --git a/crates/lang/src/lst_parser/input.rs b/crates/lang/src/lst_parser/input.rs index e9a5e2e..a00d862 100644 --- a/crates/lang/src/lst_parser/input.rs +++ b/crates/lang/src/lst_parser/input.rs @@ -1,5 +1,9 @@ +use enumset::enum_set; + use crate::lst_parser::syntax_kind::SyntaxKind; +use super::syntax_kind::TokenSet; + pub struct Input<'src, 'toks> { raw: &'toks Vec<(SyntaxKind, &'src str)>, /// indices of the "meaningful" tokens (not whitespace etc) @@ -10,14 +14,19 @@ pub struct Input<'src, 'toks> { newlines: Vec, } +pub const MEANINGLESS_TOKS: TokenSet = enum_set!(SyntaxKind::WHITESPACE | SyntaxKind::NEWLINE); + impl<'src, 'toks> Input<'src, 'toks> { pub fn new(raw_toks: &'toks Vec<(SyntaxKind, &'src str)>) -> Self { let meaningful = raw_toks .iter() .enumerate() - .filter_map(|(i, tok)| match tok.0 { - SyntaxKind::WHITESPACE | SyntaxKind::NEWLINE => None, - _ => Some(i), + .filter_map(|(i, tok)| { + if MEANINGLESS_TOKS.contains(tok.0) { + None + } else { + Some(i) + } }) .collect(); let newlines = raw_toks diff --git a/crates/lang/src/lst_parser/output.rs b/crates/lang/src/lst_parser/output.rs index 141d7d9..f17d97e 100644 --- a/crates/lang/src/lst_parser/output.rs +++ b/crates/lang/src/lst_parser/output.rs @@ -1,7 +1,11 @@ +use owo_colors::{unset_override, OwoColorize}; use rowan::{GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken}; use std::mem; -use crate::lst_parser::syntax_kind::{Lang, SyntaxKind}; +use crate::lst_parser::{ + input::MEANINGLESS_TOKS, + syntax_kind::{Lang, SyntaxKind}, +}; use super::{error::SyntaxError, events::Event}; @@ -14,47 +18,109 @@ impl std::fmt::Debug for Output { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut errs: Vec<&SyntaxError> = self.errors.iter().collect(); errs.reverse(); - debug_print_green_node(NodeOrToken::Node(&self.green_node), f, 0, &mut errs) + + debug_print_green_node(NodeOrToken::Node(&self.green_node), f, 0, &mut errs, false) } } fn debug_print_green_node( node: NodeOrToken<&GreenNodeData, &GreenTokenData>, - f: &mut std::fmt::Formatter<'_>, + f: &mut dyn std::fmt::Write, lvl: i32, errs: &mut Vec<&SyntaxError>, + colored: bool, ) -> std::fmt::Result { for _ in 0..lvl { f.write_str(" ")?; } - match node { + if !colored { + owo_colors::set_override(false); + } else { + owo_colors::set_override(true); + } + + let r = match node { NodeOrToken::Node(n) => { let kind = Lang::kind_from_raw(node.kind()); if kind != SyntaxKind::PARSE_ERR { - writeln!(f, "{:?} {{", Lang::kind_from_raw(node.kind()))?; + writeln!( + f, + "{:?} {}", + Lang::kind_from_raw(node.kind()).bright_yellow().bold(), + "{".yellow() + )?; } else { let err = errs .pop() - .expect("all error syntax nodes should correspond to an error"); + .expect("all error syntax nodes should correspond to an error") + .bright_red(); - writeln!(f, "{:?}: {err:?} {{", kind)?; + writeln!( + f, + "{:?}{} {err:?} {}", + kind.bright_red().bold(), + ":".red(), + "{".bright_red().bold() + )?; } for c in n.children() { - debug_print_green_node(c, f, lvl + 1, errs)?; + debug_print_green_node(c, f, lvl + 1, errs, colored)?; } for _ in 0..lvl { f.write_str(" ")?; } - f.write_str("}\n") + if kind != SyntaxKind::PARSE_ERR { + write!(f, "{}", "}\n".yellow()) + } else { + write!(f, "{}", "}\n".bright_red().bold()) + } } NodeOrToken::Token(t) => { - writeln!(f, "{:?} {:?};", Lang::kind_from_raw(t.kind()), t.text()) + let tok = Lang::kind_from_raw(t.kind()); + if MEANINGLESS_TOKS.contains(tok) { + writeln!( + f, + "{:?} {:?}{}", + Lang::kind_from_raw(t.kind()).white(), + t.text().white(), + ";".white() + ) + } else { + writeln!( + f, + "{:?} {:?}{}", + Lang::kind_from_raw(t.kind()).bright_cyan().bold(), + t.text().green(), + ";".yellow() + ) + } } + }; + + if !colored { + owo_colors::unset_override(); } + + r } impl Output { + pub fn debug_colored(&self) -> String { + let mut out = String::new(); + let mut errs: Vec<&SyntaxError> = self.errors.iter().collect(); + errs.reverse(); + + let _ = debug_print_green_node( + NodeOrToken::Node(&self.green_node), + &mut out, + 0, + &mut errs, + true, + ); + + out + } pub fn from_parser_output( mut raw_toks: Vec<(SyntaxKind, &str)>, (mut events, errs): (Vec, Vec), diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 517094d..2531709 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -23,7 +23,7 @@ fn main() { let p_out = dbg!(parser.finish()); let o = Output::from_parser_output(toks, p_out); - println!("Out: {:?}", o); + println!("{}", o.debug_colored()); // let parse_res = parser::parse(&f); // println!("parse: {:?}", parse_res); diff --git a/testfiles/test.owo b/testfiles/test.owo index bbcf720..ac1af18 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1 +1 @@ -meow 1 (3 | add 1) +meow | gay | -- 2.46.0 From ed151c2e3c9d732739c8b03acbe42a9a5c70bd0b Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 4 May 2024 23:12:47 +0200 Subject: [PATCH 53/67] lang: handle and recover some errors in lists --- crates/lang/src/lst_parser/error.rs | 3 +++ .../grammar/expression/collection/vec.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/crates/lang/src/lst_parser/error.rs b/crates/lang/src/lst_parser/error.rs index d13306c..1269acc 100644 --- a/crates/lang/src/lst_parser/error.rs +++ b/crates/lang/src/lst_parser/error.rs @@ -4,4 +4,7 @@ use crate::lst_parser::syntax_kind::SyntaxKind; pub enum SyntaxError { Expected(Vec), PipelineNeedsSink, + // if there was two space seperated items in a list + SpaceSepInList, + SemicolonInList, } diff --git a/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs b/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs index fa76058..6e5884d 100644 --- a/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs +++ b/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs @@ -17,8 +17,10 @@ pub fn vec_matrix_list(p: &mut Parser) -> CompletedMarker { finish_mat_or_vec(p, start, row_start) } else if p.eat(R_BRACK) { + row_start.abandon(p); start.complete(p, LIST) } else { + row_start.abandon(p); start.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK])) } } @@ -35,6 +37,21 @@ fn finish_list(p: &mut Parser, list_start: Marker) -> CompletedMarker { } } else if p.eat(R_BRACK) { return list_start.complete(p, LIST); + } else if let Some(item) = atom(p) { + item.precede(p, "next_item") + .complete(p, COLLECTION_ITEM) + .precede(p, "err_space_sep") + .complete_err(p, SyntaxError::SpaceSepInList); + } else if p.at(SEMICOLON) { + let semi_err = p.start("semicolon_err"); + p.eat(SEMICOLON); + semi_err.complete_err(p, SyntaxError::SemicolonInList); + if let Some(item) = atom(p) { + item.precede(p, "coll_item_start") + .complete(p, COLLECTION_ITEM); + } else if p.eat(R_BRACK) { + return list_start.complete(p, LIST); + } } } } -- 2.46.0 From f6da90a354d5f1bbba51e57177f7a8270869c0f0 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 3 Jun 2024 10:53:59 +0200 Subject: [PATCH 54/67] lang: improve and simplify error handling and storage fixes wrong error ordering with errors using `forward_parents`. --- crates/lang/src/lst_parser.rs | 44 ++++++++++----- crates/lang/src/lst_parser/error.rs | 2 +- crates/lang/src/lst_parser/events.rs | 53 +++++++++++++++++-- .../lang/src/lst_parser/grammar/expression.rs | 2 +- .../grammar/expression/collection/vec.rs | 2 +- .../lst_parser/grammar/expression/pipeline.rs | 2 +- crates/lang/src/lst_parser/output.rs | 28 +++++++--- flake.nix | 19 ++----- 8 files changed, 110 insertions(+), 42 deletions(-) diff --git a/crates/lang/src/lst_parser.rs b/crates/lang/src/lst_parser.rs index 9b93010..a6ec4d7 100644 --- a/crates/lang/src/lst_parser.rs +++ b/crates/lang/src/lst_parser.rs @@ -1,6 +1,12 @@ use drop_bomb::DropBomb; -use self::{error::SyntaxError, events::Event, input::Input, syntax_kind::SyntaxKind}; +use self::{ + error::SyntaxError, + events::{Event, NodeKind}, + input::Input, + syntax_kind::SyntaxKind, +}; +use std::cell::Cell; pub mod syntax_kind; #[cfg(test)] @@ -12,11 +18,13 @@ pub mod grammar; pub mod input; pub mod output; +const PARSER_STEP_LIMIT: u32 = 4096; + pub struct Parser<'src, 'toks> { input: Input<'src, 'toks>, pos: usize, events: Vec, - errors: Vec, + steps: Cell, } impl<'src, 'toks> Parser<'src, 'toks> { @@ -25,15 +33,16 @@ impl<'src, 'toks> Parser<'src, 'toks> { input, pos: 0, events: Vec::new(), - errors: Vec::new(), + steps: Cell::new(0), } } - pub fn finish(self) -> (Vec, Vec) { - (self.events, self.errors) + pub fn finish(self) -> Vec { + self.events } pub(crate) fn nth(&self, n: usize) -> SyntaxKind { + self.step(); self.input.kind(self.pos + n) } @@ -44,6 +53,7 @@ impl<'src, 'toks> Parser<'src, 'toks> { } pub(crate) fn current(&self) -> SyntaxKind { + self.step(); self.input.kind(self.pos) } @@ -80,6 +90,12 @@ impl<'src, 'toks> Parser<'src, 'toks> { fn push_ev(&mut self, event: Event) { self.events.push(event) } + + fn step(&self) { + let steps = self.steps.get(); + assert!(steps <= PARSER_STEP_LIMIT, "the parser seems stuck..."); + self.steps.set(steps + 1); + } } pub(crate) struct Marker { @@ -95,12 +111,13 @@ impl Marker { } } - pub(crate) fn complete(mut self, p: &mut Parser<'_, '_>, kind: SyntaxKind) -> CompletedMarker { + fn complete_node(mut self, p: &mut Parser, kind: NodeKind) -> CompletedMarker { self.bomb.defuse(); match &mut p.events[self.pos] { - Event::Start { kind: slot, .. } => *slot = kind, + Event::Start { kind: slot, .. } => *slot = kind.clone(), _ => unreachable!(), } + p.push_ev(Event::Finish); CompletedMarker { @@ -109,9 +126,12 @@ impl Marker { } } - pub(crate) fn complete_err(mut self, p: &mut Parser, kind: SyntaxError) -> CompletedMarker { - p.errors.push(kind); - self.complete(p, SyntaxKind::PARSE_ERR) + pub(crate) fn complete(self, p: &mut Parser<'_, '_>, kind: SyntaxKind) -> CompletedMarker { + self.complete_node(p, NodeKind::Syntax(kind)) + } + + pub(crate) fn error(self, p: &mut Parser, kind: SyntaxError) -> CompletedMarker { + self.complete_node(p, NodeKind::Error(kind)) } pub(crate) fn abandon(mut self, p: &mut Parser<'_, '_>) { @@ -119,7 +139,7 @@ impl Marker { if self.pos == p.events.len() - 1 { match p.events.pop() { Some(Event::Start { - kind: SyntaxKind::TOMBSTONE, + kind: NodeKind::Syntax(SyntaxKind::TOMBSTONE), forward_parent: None, }) => (), _ => unreachable!(), @@ -130,7 +150,7 @@ impl Marker { pub(crate) struct CompletedMarker { pos: usize, - kind: SyntaxKind, + kind: NodeKind, } impl CompletedMarker { diff --git a/crates/lang/src/lst_parser/error.rs b/crates/lang/src/lst_parser/error.rs index 1269acc..7fa4431 100644 --- a/crates/lang/src/lst_parser/error.rs +++ b/crates/lang/src/lst_parser/error.rs @@ -1,6 +1,6 @@ use crate::lst_parser::syntax_kind::SyntaxKind; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum SyntaxError { Expected(Vec), PipelineNeedsSink, diff --git a/crates/lang/src/lst_parser/events.rs b/crates/lang/src/lst_parser/events.rs index 00d7524..3ed630a 100644 --- a/crates/lang/src/lst_parser/events.rs +++ b/crates/lang/src/lst_parser/events.rs @@ -1,22 +1,69 @@ use crate::lst_parser::syntax_kind::SyntaxKind; +use super::error::SyntaxError; + #[derive(Debug)] pub enum Event { Start { - kind: SyntaxKind, + kind: NodeKind, forward_parent: Option, }, Finish, Eat { count: usize, }, - Error, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum NodeKind { + Syntax(SyntaxKind), + Error(SyntaxError), +} + +impl NodeKind { + pub fn is_syntax(&self) -> bool { + matches!(self, Self::Syntax(_)) + } + + pub fn is_error(&self) -> bool { + matches!(self, Self::Error(_)) + } +} + +impl From for NodeKind { + fn from(value: SyntaxKind) -> Self { + NodeKind::Syntax(value) + } +} + +impl From for NodeKind { + fn from(value: SyntaxError) -> Self { + NodeKind::Error(value) + } +} + +impl PartialEq for NodeKind { + fn eq(&self, other: &SyntaxKind) -> bool { + match self { + NodeKind::Syntax(s) => s == other, + NodeKind::Error(_) => false, + } + } +} + +impl PartialEq for NodeKind { + fn eq(&self, other: &SyntaxError) -> bool { + match self { + NodeKind::Syntax(_) => false, + NodeKind::Error(e) => e == other, + } + } } impl Event { pub(crate) fn tombstone() -> Self { Self::Start { - kind: SyntaxKind::TOMBSTONE, + kind: SyntaxKind::TOMBSTONE.into(), forward_parent: None, } } diff --git a/crates/lang/src/lst_parser/grammar/expression.rs b/crates/lang/src/lst_parser/grammar/expression.rs index 33ef3fc..03c0d10 100644 --- a/crates/lang/src/lst_parser/grammar/expression.rs +++ b/crates/lang/src/lst_parser/grammar/expression.rs @@ -35,7 +35,7 @@ pub fn parenthesized_expr(p: &mut Parser) -> Option { let par_expr = p.start("parenthesized"); expression(p, false); if !p.eat(R_PAREN) { - return Some(par_expr.complete_err(p, SyntaxError::Expected(vec![R_PAREN]))); + return Some(par_expr.error(p, SyntaxError::Expected(vec![R_PAREN]))); } return Some(par_expr.complete(p, PARENTHESIZED_EXPR)); diff --git a/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs b/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs index 6e5884d..385723b 100644 --- a/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs +++ b/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs @@ -21,7 +21,7 @@ pub fn vec_matrix_list(p: &mut Parser) -> CompletedMarker { start.complete(p, LIST) } else { row_start.abandon(p); - start.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK])) + start.error(p, SyntaxError::Expected(vec![EXPR, R_BRACK])) } } diff --git a/crates/lang/src/lst_parser/grammar/expression/pipeline.rs b/crates/lang/src/lst_parser/grammar/expression/pipeline.rs index f2f090d..2c1c678 100644 --- a/crates/lang/src/lst_parser/grammar/expression/pipeline.rs +++ b/crates/lang/src/lst_parser/grammar/expression/pipeline.rs @@ -16,7 +16,7 @@ pub fn pipeline(p: &mut Parser, start_expr: CompletedMarker) -> Option, f: &mut dyn std::fmt::Write, @@ -31,7 +36,7 @@ fn debug_print_green_node( colored: bool, ) -> std::fmt::Result { for _ in 0..lvl { - f.write_str(" ")?; + f.write_str(INDENT_STR)?; } if !colored { @@ -68,7 +73,7 @@ fn debug_print_green_node( debug_print_green_node(c, f, lvl + 1, errs, colored)?; } for _ in 0..lvl { - f.write_str(" ")?; + f.write_str(INDENT_STR)?; } if kind != SyntaxKind::PARSE_ERR { write!(f, "{}", "}\n".yellow()) @@ -123,10 +128,11 @@ impl Output { } pub fn from_parser_output( mut raw_toks: Vec<(SyntaxKind, &str)>, - (mut events, errs): (Vec, Vec), + mut events: Vec, ) -> Self { let mut builder = GreenNodeBuilder::new(); let mut fw_parents = Vec::new(); + let mut errors = Vec::new(); raw_toks.reverse(); for i in 0..events.len() { @@ -170,8 +176,15 @@ impl Output { } for kind in fw_parents.drain(..).rev() { - if kind != SyntaxKind::TOMBSTONE { - builder.start_node(kind.into()); + match kind { + NodeKind::Syntax(kind) if kind != SyntaxKind::TOMBSTONE => { + builder.start_node(kind.into()) + } + NodeKind::Error(err) => { + errors.push(err); + builder.start_node(SyntaxKind::PARSE_ERR.into()) + } + _ => {} } } } @@ -180,13 +193,12 @@ impl Output { let (tok, text): (SyntaxKind, &str) = raw_toks.pop().unwrap(); builder.token(tok.into(), text); }), - Event::Error => todo!(), } } Self { green_node: builder.finish(), - errors: errs, + errors, } } } diff --git a/flake.nix b/flake.nix index caad7d6..8dd48aa 100644 --- a/flake.nix +++ b/flake.nix @@ -27,8 +27,7 @@ pkgs = nixpkgs.legacyPackages.${system}; toolchain = with fenix.packages.${system}; combine [ - default.toolchain - rust-analyzer + complete.toolchain ]; in { default = devenv.lib.mkShell { @@ -39,24 +38,14 @@ config, ... }: { - # languages.rust = { - # enable = true; - # channel = "nightly"; - # components = [ - # "rustc" - # "cargo" - # "clippy" - # "rustfmt" - # "rust-src" - # "rust-analyzer" - # ]; - # }; - pre-commit.hooks = { clippy.enable = false; rustfmt.enable = true; }; + env = { + RUST_BACKTRACE = 1; + }; packages = with pkgs; [ just nushell -- 2.46.0 From 946ac879a7fa9135bdbdc5ba73b0738216955244 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 3 Jun 2024 11:22:36 +0200 Subject: [PATCH 55/67] lang: basic module syntax grammar --- crates/lang/src/lst_parser.rs | 6 +- crates/lang/src/lst_parser/error.rs | 3 + crates/lang/src/lst_parser/grammar.rs | 6 +- .../grammar/expression/collection/vec.rs | 13 ++- crates/lang/src/lst_parser/grammar/module.rs | 106 ++++++++++++++++++ crates/lang/src/lst_parser/syntax_kind.rs | 1 + testfiles/test.owo | 9 +- 7 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 crates/lang/src/lst_parser/grammar/module.rs diff --git a/crates/lang/src/lst_parser.rs b/crates/lang/src/lst_parser.rs index a6ec4d7..5c90bb0 100644 --- a/crates/lang/src/lst_parser.rs +++ b/crates/lang/src/lst_parser.rs @@ -111,7 +111,7 @@ impl Marker { } } - fn complete_node(mut self, p: &mut Parser, kind: NodeKind) -> CompletedMarker { + fn close_node(mut self, p: &mut Parser, kind: NodeKind) -> CompletedMarker { self.bomb.defuse(); match &mut p.events[self.pos] { Event::Start { kind: slot, .. } => *slot = kind.clone(), @@ -127,11 +127,11 @@ impl Marker { } pub(crate) fn complete(self, p: &mut Parser<'_, '_>, kind: SyntaxKind) -> CompletedMarker { - self.complete_node(p, NodeKind::Syntax(kind)) + self.close_node(p, NodeKind::Syntax(kind)) } pub(crate) fn error(self, p: &mut Parser, kind: SyntaxError) -> CompletedMarker { - self.complete_node(p, NodeKind::Error(kind)) + self.close_node(p, NodeKind::Error(kind)) } pub(crate) fn abandon(mut self, p: &mut Parser<'_, '_>) { diff --git a/crates/lang/src/lst_parser/error.rs b/crates/lang/src/lst_parser/error.rs index 7fa4431..04f5e7b 100644 --- a/crates/lang/src/lst_parser/error.rs +++ b/crates/lang/src/lst_parser/error.rs @@ -7,4 +7,7 @@ pub enum SyntaxError { // if there was two space seperated items in a list SpaceSepInList, SemicolonInList, + CommaInMatOrVec, + UnterminatedTopLevelItem, + UnclosedModuleBody, } diff --git a/crates/lang/src/lst_parser/grammar.rs b/crates/lang/src/lst_parser/grammar.rs index 9d081bb..2db0824 100644 --- a/crates/lang/src/lst_parser/grammar.rs +++ b/crates/lang/src/lst_parser/grammar.rs @@ -2,6 +2,8 @@ use std::fmt::Debug; use crate::lst_parser::syntax_kind::SyntaxKind::*; +use self::module::{mod_body, top_level_item}; + use super::{ input::Input, output::Output, @@ -10,11 +12,13 @@ use super::{ }; mod expression; +mod module; pub fn source_file(p: &mut Parser) { let root = p.start("root"); - expression::expression(p, false); + mod_body(p); + // expression::expression(p, false); p.eat_succeeding_ws(); root.complete(p, ROOT); diff --git a/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs b/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs index 385723b..4dfd299 100644 --- a/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs +++ b/crates/lang/src/lst_parser/grammar/expression/collection/vec.rs @@ -25,7 +25,6 @@ pub fn vec_matrix_list(p: &mut Parser) -> CompletedMarker { } } -// TODO: handle semicolons, other wrong toks fn finish_list(p: &mut Parser, list_start: Marker) -> CompletedMarker { loop { if p.eat(COMMA) { @@ -41,11 +40,11 @@ fn finish_list(p: &mut Parser, list_start: Marker) -> CompletedMarker { item.precede(p, "next_item") .complete(p, COLLECTION_ITEM) .precede(p, "err_space_sep") - .complete_err(p, SyntaxError::SpaceSepInList); + .error(p, SyntaxError::SpaceSepInList); } else if p.at(SEMICOLON) { let semi_err = p.start("semicolon_err"); p.eat(SEMICOLON); - semi_err.complete_err(p, SyntaxError::SemicolonInList); + semi_err.error(p, SyntaxError::SemicolonInList); if let Some(item) = atom(p) { item.precede(p, "coll_item_start") .complete(p, COLLECTION_ITEM); @@ -85,6 +84,14 @@ fn finish_mat_or_vec(p: &mut Parser, coll_start: Marker, mut row_start: Marker) p.eat(R_BRACK); return coll_start.complete(p, VEC); } + } else if p.at(COMMA) { + let err_unexpected_comma = p.start("err_unexpected_comma"); + p.do_bump(); + err_unexpected_comma.error(p, SyntaxError::CommaInMatOrVec); + } else { + let err_unexpected = p.start("err_unexpected_tok"); + p.do_bump(); + err_unexpected.error(p, SyntaxError::Expected(vec![EXPR, SEMICOLON, R_BRACK])); } } } diff --git a/crates/lang/src/lst_parser/grammar/module.rs b/crates/lang/src/lst_parser/grammar/module.rs new file mode 100644 index 0000000..fe51d2c --- /dev/null +++ b/crates/lang/src/lst_parser/grammar/module.rs @@ -0,0 +1,106 @@ +use enumset::enum_set; + +use crate::lst_parser::{ + error::SyntaxError, + grammar::expression::expression, + syntax_kind::{SyntaxKind::*, TokenSet}, + CompletedMarker, Parser, +}; + +const TOP_LEVEL_ITEM_START: TokenSet = enum_set!(DEF_KW | MOD_KW | USE_KW); + +pub fn mod_body(p: &mut Parser) { + loop { + if top_level_item(p).is_none() { + break; + } + } +} + +fn mod_decl(p: &mut Parser) -> Option { + let mod_start = p.start("module"); + if !p.eat(MOD_KW) { + return None; + } + + let mod_name = p.start("module_name"); + if p.eat(IDENT) { + mod_name.complete(p, MODULE_NAME); + } else { + mod_name.error(p, SyntaxError::Expected(vec![IDENT])); + } + + let mod_body_marker = p.start("mod_body"); + if p.eat(SEMICOLON) { + mod_body_marker.abandon(p); + Some(mod_start.complete(p, MODULE)) + } else if p.eat(L_BRACE) { + mod_body(p); + if !p.eat(R_BRACE) { + mod_body_marker + .complete(p, MODULE_BODY) + .precede(p, "unclosed_mod_body_err") + .error(p, SyntaxError::UnclosedModuleBody); + } else { + mod_body_marker.complete(p, MODULE_BODY); + } + Some(mod_start.complete(p, MODULE)) + } else { + Some(mod_start.error(p, SyntaxError::Expected(vec![MODULE_BODY]))) + } +} + +pub fn top_level_item(p: &mut Parser) -> Option { + if !TOP_LEVEL_ITEM_START.contains(p.current()) { + return None; + } + def(p).or_else(|| mod_decl(p)) +} + +fn def(p: &mut Parser) -> Option { + let def_start = p.start("top_level_def"); + if !p.eat(DEF_KW) { + def_start.abandon(p); + return None; + } + + let def_name = p.start("def_name"); + if p.eat(IDENT) { + def_name.complete(p, DEF_NAME); + } else { + def_name.error(p, SyntaxError::Expected(vec![IDENT])); + } + + let maybe_expected_eq = p.start("maybe_expect_eq"); + if !p.eat(EQ) { + maybe_expected_eq.error(p, SyntaxError::Expected(vec![EQ])); + } else { + maybe_expected_eq.abandon(p); + } + + let body = p.start("def_body"); + if expression(p, false).is_some() { + body.complete(p, DEF_BODY); + } else { + body.error(p, SyntaxError::Expected(vec![DEF_BODY])); + } + + let def = def_start.complete(p, DEF); + Some(if p.eat(SEMICOLON) { + def + } else if TOP_LEVEL_ITEM_START.contains(p.current()) || p.at(EOF) { + def.precede(p, "unterminated_tl_item") + .error(p, SyntaxError::UnterminatedTopLevelItem) + } else { + def.precede(p, "err_unexpected") + .error(p, SyntaxError::Expected(vec![SEMICOLON])) + }) +} + +fn r#use(p: &mut Parser) -> Option { + if !p.eat(USE_KW) { + return None; + } + + todo!() +} diff --git a/crates/lang/src/lst_parser/syntax_kind.rs b/crates/lang/src/lst_parser/syntax_kind.rs index 4914aeb..7b0fe41 100644 --- a/crates/lang/src/lst_parser/syntax_kind.rs +++ b/crates/lang/src/lst_parser/syntax_kind.rs @@ -30,6 +30,7 @@ pub enum SyntaxKind { #[token("mod")] MOD_KW, MODULE, + MODULE_NAME, MODULE_BODY, #[token("use")] USE_KW, diff --git a/testfiles/test.owo b/testfiles/test.owo index ac1af18..b50e5c6 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1 +1,8 @@ -meow | gay | +def hello_world = meow [ 1 2 ] + +def test + +mod hello { + def meow = uwu; +} + -- 2.46.0 From 0de076ace166de49372580c0a8980ab356b6559b Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 3 Jun 2024 12:05:38 +0200 Subject: [PATCH 56/67] lang: finish module/top level syntax --- crates/lang/src/lst_parser/error.rs | 2 + crates/lang/src/lst_parser/grammar/module.rs | 77 +++++++++++++++++++- crates/lang/src/lst_parser/syntax_kind.rs | 6 ++ testfiles/test.owo | 4 +- 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/crates/lang/src/lst_parser/error.rs b/crates/lang/src/lst_parser/error.rs index 04f5e7b..73f290f 100644 --- a/crates/lang/src/lst_parser/error.rs +++ b/crates/lang/src/lst_parser/error.rs @@ -10,4 +10,6 @@ pub enum SyntaxError { CommaInMatOrVec, UnterminatedTopLevelItem, UnclosedModuleBody, + UnfinishedPath, + PathSepContainsSemicolon, } diff --git a/crates/lang/src/lst_parser/grammar/module.rs b/crates/lang/src/lst_parser/grammar/module.rs index fe51d2c..1bce2a4 100644 --- a/crates/lang/src/lst_parser/grammar/module.rs +++ b/crates/lang/src/lst_parser/grammar/module.rs @@ -20,6 +20,7 @@ pub fn mod_body(p: &mut Parser) { fn mod_decl(p: &mut Parser) -> Option { let mod_start = p.start("module"); if !p.eat(MOD_KW) { + mod_start.abandon(p); return None; } @@ -54,7 +55,7 @@ pub fn top_level_item(p: &mut Parser) -> Option { if !TOP_LEVEL_ITEM_START.contains(p.current()) { return None; } - def(p).or_else(|| mod_decl(p)) + def(p).or_else(|| mod_decl(p)).or_else(|| r#use(p)) } fn def(p: &mut Parser) -> Option { @@ -98,9 +99,81 @@ fn def(p: &mut Parser) -> Option { } fn r#use(p: &mut Parser) -> Option { + let use_start = p.start("use_start"); if !p.eat(USE_KW) { + use_start.abandon(p); return None; } - todo!() + if use_pat(p).is_none() { + p.start("expected_use_pat") + .error(p, SyntaxError::Expected(vec![USE_PAT])); + } + + let use_item = use_start.complete(p, DEF); + Some(if p.eat(SEMICOLON) { + use_item + } else if TOP_LEVEL_ITEM_START.contains(p.current()) || p.at(EOF) { + use_item + .precede(p, "unterminated_tl_item") + .error(p, SyntaxError::UnterminatedTopLevelItem) + } else { + use_item + .precede(p, "err_unexpected") + .error(p, SyntaxError::Expected(vec![SEMICOLON])) + }) +} + +fn use_pat(p: &mut Parser) -> Option { + let use_pat_marker = p.start("use_pat"); + if !p.eat(IDENT) { + return None; + } + + loop { + if p.eat(PATH_SEP) { + if pat_item(p).is_none() { + break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath)); + } + } else if p.at(SEMICOLON) && p.nth_at(1, COLON) { + let broken_sep = p.start("broken_path_sep"); + let wrong_semi = p.start("semi_typo"); + p.eat(SEMICOLON); + wrong_semi.error(p, SyntaxError::PathSepContainsSemicolon); + p.eat(COLON); + broken_sep.complete(p, PATH_SEP); + } else if p.at(COLON) && p.nth_at(1, SEMICOLON) { + let broken_sep = p.start("broken_path_sep"); + p.eat(COLON); + let wrong_semi = p.start("semi_typo"); + p.eat(SEMICOLON); + wrong_semi.error(p, SyntaxError::PathSepContainsSemicolon); + broken_sep.complete(p, PATH_SEP); + } else if p.at(SEMICOLON) && p.nth_at(1, SEMICOLON) { + let broken_sep = p.start("broken_path_sep"); + p.eat(SEMICOLON); + p.eat(SEMICOLON); + broken_sep + .complete(p, PATH_SEP) + .precede(p, "semi_typo_err") + .error(p, SyntaxError::PathSepContainsSemicolon); + } else if p.at(SEMICOLON) { + break Some(use_pat_marker.complete(p, USE_PAT)); + } else { + break Some(use_pat_marker.error(p, SyntaxError::Expected(vec![PATH_SEP, SEMICOLON]))); + } + } +} + +fn pat_item(p: &mut Parser) -> Option { + let item_start = p.start("pat_item_start"); + if p.eat(IDENT) { + Some(item_start.complete(p, PAT_ITEM)) + } else if p.eat(STAR) { + Some(item_start.complete(p, PAT_GLOB)) + } else if p.eat(L_BRACE) { + todo!("write PAT_GROUPs") + } else { + None + } } diff --git a/crates/lang/src/lst_parser/syntax_kind.rs b/crates/lang/src/lst_parser/syntax_kind.rs index 7b0fe41..87230e7 100644 --- a/crates/lang/src/lst_parser/syntax_kind.rs +++ b/crates/lang/src/lst_parser/syntax_kind.rs @@ -27,14 +27,20 @@ pub enum SyntaxKind { #[token("in")] IN_KW, LET_IN, + #[token("::")] + PATH_SEP, #[token("mod")] MOD_KW, MODULE, MODULE_NAME, MODULE_BODY, + USE, #[token("use")] USE_KW, USE_PAT, + PAT_ITEM, + PAT_GLOB, + PAT_GROUP, #[regex("[\\d]+")] INT_NUM, #[regex("[+-]?([\\d]+\\.[\\d]*|[\\d]*\\.[\\d]+)")] diff --git a/testfiles/test.owo b/testfiles/test.owo index b50e5c6..f94256a 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,4 +1,6 @@ -def hello_world = meow [ 1 2 ] +use hello::meow; + +def hello_world = meow [ 1 2 ]; def test -- 2.46.0 From cfefab9fd05fa8ecff99f99901a1cb77485f83e8 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 5 Jun 2024 09:57:08 +0200 Subject: [PATCH 57/67] lang: fix some details in the parser --- crates/lang/src/lst_parser/grammar/module.rs | 24 +++++++++++++++----- testfiles/test.owo | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/lang/src/lst_parser/grammar/module.rs b/crates/lang/src/lst_parser/grammar/module.rs index 1bce2a4..008627c 100644 --- a/crates/lang/src/lst_parser/grammar/module.rs +++ b/crates/lang/src/lst_parser/grammar/module.rs @@ -86,14 +86,17 @@ fn def(p: &mut Parser) -> Option { body.error(p, SyntaxError::Expected(vec![DEF_BODY])); } - let def = def_start.complete(p, DEF); Some(if p.eat(SEMICOLON) { - def + def_start.complete(p, DEF) } else if TOP_LEVEL_ITEM_START.contains(p.current()) || p.at(EOF) { - def.precede(p, "unterminated_tl_item") + def_start + .complete(p, DEF) + .precede(p, "unterminated_tl_item") .error(p, SyntaxError::UnterminatedTopLevelItem) } else { - def.precede(p, "err_unexpected") + def_start + .complete(p, DEF) + .precede(p, "err_unexpected") .error(p, SyntaxError::Expected(vec![SEMICOLON])) }) } @@ -110,7 +113,7 @@ fn r#use(p: &mut Parser) -> Option { .error(p, SyntaxError::Expected(vec![USE_PAT])); } - let use_item = use_start.complete(p, DEF); + let use_item = use_start.complete(p, USE); Some(if p.eat(SEMICOLON) { use_item } else if TOP_LEVEL_ITEM_START.contains(p.current()) || p.at(EOF) { @@ -142,6 +145,9 @@ fn use_pat(p: &mut Parser) -> Option { wrong_semi.error(p, SyntaxError::PathSepContainsSemicolon); p.eat(COLON); broken_sep.complete(p, PATH_SEP); + if pat_item(p).is_none() { + break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath)); + } } else if p.at(COLON) && p.nth_at(1, SEMICOLON) { let broken_sep = p.start("broken_path_sep"); p.eat(COLON); @@ -149,6 +155,9 @@ fn use_pat(p: &mut Parser) -> Option { p.eat(SEMICOLON); wrong_semi.error(p, SyntaxError::PathSepContainsSemicolon); broken_sep.complete(p, PATH_SEP); + if pat_item(p).is_none() { + break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath)); + } } else if p.at(SEMICOLON) && p.nth_at(1, SEMICOLON) { let broken_sep = p.start("broken_path_sep"); p.eat(SEMICOLON); @@ -157,7 +166,10 @@ fn use_pat(p: &mut Parser) -> Option { .complete(p, PATH_SEP) .precede(p, "semi_typo_err") .error(p, SyntaxError::PathSepContainsSemicolon); - } else if p.at(SEMICOLON) { + if pat_item(p).is_none() { + break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath)); + } + } else if p.eat(SEMICOLON) { break Some(use_pat_marker.complete(p, USE_PAT)); } else { break Some(use_pat_marker.error(p, SyntaxError::Expected(vec![PATH_SEP, SEMICOLON]))); diff --git a/testfiles/test.owo b/testfiles/test.owo index f94256a..4f06140 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -5,6 +5,7 @@ def hello_world = meow [ 1 2 ]; def test mod hello { + use gay:;uwu_meow::*; def meow = uwu; } -- 2.46.0 From d6bc644fb6fd6340c731045286ecb09258f9a3b9 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 5 Jun 2024 18:00:14 +0200 Subject: [PATCH 58/67] lang: basic ast work --- crates/lang/src/ast.rs | 68 ++++++ crates/lang/src/ast/error.rs | 30 +++ crates/lang/src/ast/mod_tree.rs | 248 ++++++++++++++++++++++ crates/lang/src/ast/nodes.rs | 77 +++++++ crates/lang/src/ast/source_file.rs | 80 +++++++ crates/lang/src/lib.rs | 22 ++ crates/lang/src/lst_parser/output.rs | 14 +- crates/lang/src/lst_parser/syntax_kind.rs | 16 -- crates/lang/src/main.rs | 42 ++-- testfiles/hello.owo | 1 + testfiles/hello/meow.owo | 2 + testfiles/test.owo | 17 +- testfiles/uwu.owo | 1 + 13 files changed, 560 insertions(+), 58 deletions(-) create mode 100644 crates/lang/src/ast.rs create mode 100644 crates/lang/src/ast/error.rs create mode 100644 crates/lang/src/ast/mod_tree.rs create mode 100644 crates/lang/src/ast/nodes.rs create mode 100644 crates/lang/src/ast/source_file.rs create mode 100644 testfiles/hello.owo create mode 100644 testfiles/hello/meow.owo create mode 100644 testfiles/uwu.owo diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs new file mode 100644 index 0000000..1375694 --- /dev/null +++ b/crates/lang/src/ast.rs @@ -0,0 +1,68 @@ +use std::{collections::HashMap, path::PathBuf, sync::Arc}; + +use rowan::ast::{AstNode, AstPtr}; + +use self::{ + error::{Error, WorldCreationError}, + mod_tree::{Module, ModuleTree}, + source_file::SourceFile, +}; + +mod error; +mod mod_tree; +mod nodes; +mod source_file; + +struct Loc { + file: PathBuf, + syntax_el: AstPtr, +} + +impl Loc { + pub fn new(file: PathBuf, syntax_el: &T) -> Self { + Self { + file, + syntax_el: AstPtr::new(syntax_el), + } + } +} + +pub struct World { + entry_point: PathBuf, + files: HashMap, + errors: Vec, + module_tree: ModuleTree, +} + +impl World { + pub fn new(entry_point: PathBuf) -> Result { + let entry_point = entry_point.canonicalize().unwrap(); + let source = match std::fs::read_to_string(&entry_point) { + Ok(f) => f, + Err(e) => return Err(WorldCreationError::FailedToOpenEntryPoint(entry_point, e)), + }; + + let (src, mut errors) = SourceFile::parse_from(entry_point.clone(), source); + + let (module_tree, mut files, new_errors) = ModuleTree::parse_from_main(&entry_point, &src); + errors.extend(new_errors); + module_tree.print_tree(&src.tree()); + dbg!(&errors); + + let _ = files.insert(entry_point.clone(), src); + + Ok(Self { + files, + entry_point, + errors, + module_tree, + }) + } + + // fn parse_mod_tree(files: &mut HashMap) +} + +// struct SourceFile { +// tree: SyntaxNode, +// items: Vec, +// } diff --git a/crates/lang/src/ast/error.rs b/crates/lang/src/ast/error.rs new file mode 100644 index 0000000..352e831 --- /dev/null +++ b/crates/lang/src/ast/error.rs @@ -0,0 +1,30 @@ +use std::{fmt::Display, path::PathBuf}; + +use crate::lst_parser::error::SyntaxError; + +#[derive(Debug)] +pub enum Error { + Syntax(PathBuf, SyntaxError), + FailedToOpenFileMod(PathBuf, std::io::Error), +} + +impl Error { + pub fn from_syntax(file: PathBuf, e: SyntaxError) -> Self { + Self::Syntax(file, e) + } +} + +#[derive(Debug)] +pub enum WorldCreationError { + FailedToOpenEntryPoint(PathBuf, std::io::Error), +} + +impl Display for WorldCreationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WorldCreationError::FailedToOpenEntryPoint(entry_path, e) => { + write!(f, "failed to open entry_point '{entry_path:?}': {e}") + } + } + } +} diff --git a/crates/lang/src/ast/mod_tree.rs b/crates/lang/src/ast/mod_tree.rs new file mode 100644 index 0000000..035faec --- /dev/null +++ b/crates/lang/src/ast/mod_tree.rs @@ -0,0 +1,248 @@ +use std::{ + collections::{HashMap, HashSet}, + path::PathBuf, + sync::Arc, +}; + +use rowan::ast::{AstNode, AstPtr}; + +use crate::{lst_parser::syntax_kind::SyntaxKind, SyntaxNode}; + +use super::{ + error::Error, + nodes::{self, Mod, ModBody, ModName, Root}, + source_file::SourceFile, + Loc, +}; + +pub struct ModuleTree { + modules: HashMap>, +} + +impl ModuleTree { + pub fn parse_from_main( + path: &PathBuf, + main_file: &SourceFile, + ) -> (Self, HashMap, Vec) { + let tree = main_file.tree(); + let mut files = HashMap::new(); + let mut errors = Vec::new(); + + let entry_path = path.parent().unwrap().to_owned(); + let modules = main_file + .top_level_modules() + .into_iter() + .filter_map(|m| { + let module = + Module::parse_mod(m.to_node(tree.syntax()), Vec::new(), &path, &entry_path); + match module { + Ok(module) => { + files.extend(module.1); + errors.extend(module.2); + Some((module.0.name(), Arc::new(module.0))) + } + Err(err) => { + errors.push(err); + None + } + } + }) + .collect::>>(); + + (Self { modules }, files, errors) + } + + pub fn print_tree(&self, lst: &Root) { + let name = "main"; + print_tree(&name, &self.modules, 0) + } +} + +pub struct Module { + path: Vec, + name: String, + kind: ModuleKind, + children: HashMap>, + parent: Option>, +} + +impl Module { + fn parse_mod( + module: Mod, + cur_path: Vec, + cur_file: &PathBuf, + entry_path: &PathBuf, + ) -> Result<(Self, HashMap, Vec), Error> { + dbg!(cur_file); + dbg!(entry_path); + let children = module + .syntax() + .children() + // .map(|n| n.kind()) + .collect::>(); + + if children.len() == 1 { + let name = &children[0]; + assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); + return Self::parse_file_mod( + name.text().to_string(), + Loc::new(cur_file.clone(), &module), + cur_path, + entry_path, + ); + } else if children.len() == 2 { + let name = &children[0]; + assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); + let body = &children[1]; + assert_eq!(body.kind(), SyntaxKind::MODULE_BODY); + return Ok(Self::parse_inline_mod( + module, cur_path, cur_file, entry_path, + )); + } + todo!() + } + + fn parse_file_mod( + name: String, + decl: Loc, + mut cur_path: Vec, + entry_path: &PathBuf, + ) -> Result<(Self, HashMap, Vec), Error> { + let mut mod_file_path = entry_path.to_owned(); + + for el in &cur_path { + mod_file_path.push(format!("{el}/")); + } + mod_file_path.push(format!("{name}.owo")); + + let mut files = HashMap::new(); + let mut errors = Vec::new(); + + let source = match std::fs::read_to_string(dbg!(&mod_file_path)) { + Ok(f) => f, + Err(e) => return Err(Error::FailedToOpenFileMod(mod_file_path, e)), + }; + + let (source_file, file_errors) = SourceFile::parse_from(mod_file_path.clone(), source); + errors.extend(file_errors); + + let tree = source_file.tree(); + let old_path = cur_path.clone(); + cur_path.push(name.clone()); + let children = source_file + .top_level_modules() + .into_iter() + .filter_map(|m| { + let module = Module::parse_mod( + m.to_node(tree.syntax()), + cur_path.clone(), + &mod_file_path, + &entry_path, + ); + match module { + Ok(module) => { + files.extend(module.1); + errors.extend(module.2); + Some((module.0.name(), Arc::new(module.0))) + } + Err(err) => { + errors.push(err); + None + } + } + }) + .collect::>>(); + + files.insert(mod_file_path.clone(), source_file); + + Ok(( + Self { + path: old_path, + name, + kind: ModuleKind::File { + declaration: decl, + file_id: mod_file_path, + }, + children, + parent: None, + }, + files, + errors, + )) + } + + fn parse_inline_mod( + module: Mod, + mut cur_path: Vec, + cur_file: &PathBuf, + entry_path: &PathBuf, + ) -> (Self, HashMap, Vec) { + let mut children = module.syntax().children().collect::>(); + + let body = ModBody::cast(children.pop().unwrap()).unwrap(); + let name = ModName::cast(children.pop().unwrap()).unwrap(); + + let mut files = HashMap::new(); + let mut errors = Vec::new(); + + let old_path = cur_path.clone(); + cur_path.push(name.syntax().to_string()); + let children = body + .syntax() + .children() + .filter_map(|node| Mod::cast(node)) + .filter_map(|m| { + let m = Self::parse_mod(m, cur_path.clone(), cur_file, entry_path); + match m { + Ok(module) => { + files.extend(module.1); + errors.extend(module.2); + Some((module.0.name(), Arc::new(module.0))) + } + Err(err) => { + errors.push(err); + None + } + } + }) + .collect::>>(); + + ( + Self { + name: name.syntax().text().to_string(), + kind: ModuleKind::Inline(Loc::new(cur_file.to_owned(), &module)), + children, + parent: None, + path: old_path, + }, + files, + errors, + ) + } + + pub fn name(&self) -> String { + // self.name.to_node(lst.syntax()).syntax().text().to_string() + self.name.clone() + } +} + +fn print_tree(name: &str, children: &HashMap>, level: u32) { + const INDENT_STR: &str = " "; + + for _ in 0..level { + print!("{}", INDENT_STR); + } + print!("{name}\n"); + + for (name, module) in children { + print_tree(name, &module.children, level + 1); + } +} + +enum ModuleKind { + Inline(Loc), + File { + declaration: Loc, + file_id: PathBuf, + }, +} diff --git a/crates/lang/src/ast/nodes.rs b/crates/lang/src/ast/nodes.rs new file mode 100644 index 0000000..e59c0f3 --- /dev/null +++ b/crates/lang/src/ast/nodes.rs @@ -0,0 +1,77 @@ +use crate::lst_parser::syntax_kind::SyntaxKind::*; +use crate::SyntaxNode; +use rowan::Language; +macro_rules! ast_nodes { + ($($ast:ident, $kind:ident);+) => { + $( + #[derive(PartialEq, Eq, Hash)] + #[repr(transparent)] + pub struct $ast(SyntaxNode); + impl rowan::ast::AstNode for $ast { + type Language = crate::Lang; + + fn can_cast(kind: ::Kind) -> bool { + kind == $kind + } + + fn cast(node: SyntaxNode) -> Option { + if node.kind() == $kind { + Some(Self(node)) + } else { + None + } + } + + fn syntax(&self) -> &SyntaxNode { + &self.0 + } + } + )+ + }; +} +ast_nodes!( + Def, DEF; + DefName, DEF_NAME; + DefBody, DEF_BODY; + + Mod, MODULE; + ModName, MODULE_NAME; + ModBody, MODULE_BODY; + + Use, USE; + UsePat, USE_PAT; + PatItem, PAT_ITEM; + PatGlob, PAT_GLOB; + PatGroup, PAT_GROUP; + + Literal, LITERAL; + IntLit, INT_NUM; + FloatLit, FLOAT_NUM; + StringLit, STRING; + + Matrix, MATRIX; + MatrixRow, MAT_ROW; + Vector, VEC; + List, LIST; + CollectionItem, COLLECTION_ITEM; + + ParenthesizedExpr, PARENTHESIZED_EXPR; + Expression, EXPR; + + Pipeline, PIPELINE; + + Instruction, INSTR; + InstructionName, INSTR_NAME; + InstructionParams, INSTR_PARAMS; + + AttributeSet, ATTR_SET; + Attribute, ATTR; + AttributeName, ATTR_NAME; + AttributeValue, ATTR_VALUE; + + ParseError, PARSE_ERR; + LexError, LEX_ERR; + + Root, ROOT; + Eof, EOF +); diff --git a/crates/lang/src/ast/source_file.rs b/crates/lang/src/ast/source_file.rs new file mode 100644 index 0000000..e6e33be --- /dev/null +++ b/crates/lang/src/ast/source_file.rs @@ -0,0 +1,80 @@ +use std::path::PathBuf; + +use rowan::ast::{AstNode, AstPtr}; + +use crate::lst_parser::{ + grammar::source_file, + input::Input, + output::Output, + syntax_kind::{self, SyntaxKind}, + Parser, +}; + +use super::{ + error::Error, + nodes::{Def, Mod, Root, Use}, +}; + +pub struct SourceFile { + lst: Root, + modules: Vec>, + defs: Vec>, + uses: Vec>, +} + +impl SourceFile { + pub fn parse_from(path: PathBuf, source_text: String) -> (Self, Vec) { + let toks = syntax_kind::lex(&source_text); + let input = Input::new(&toks); + let mut parser = Parser::new(input); + + source_file(&mut parser); + + let parser_out = parser.finish(); + let out = Output::from_parser_output(toks, parser_out); + + let lst = out.syntax(); + + let mut modules = Vec::new(); + let mut defs = Vec::new(); + let mut uses = Vec::new(); + + for c in lst.children() { + match c.kind() { + SyntaxKind::MODULE => modules.push(AstPtr::new(&Mod::cast(c).unwrap())), + SyntaxKind::DEF => defs.push(AstPtr::new(&Def::cast(c).unwrap())), + SyntaxKind::USE => uses.push(AstPtr::new(&Use::cast(c).unwrap())), + _ => {} + } + } + + ( + Self { + lst: Root::cast(lst).unwrap(), + modules, + defs, + uses, + }, + out.errors() + .into_iter() + .map(|err| Error::from_syntax(path.clone(), err)) + .collect(), + ) + } + + pub fn tree(&self) -> &Root { + &self.lst + } + + pub fn top_level_modules(&self) -> Vec> { + self.modules.clone() + } + + pub fn top_level_defs(&self) -> Vec> { + self.defs.clone() + } + + pub fn top_level_uses(&self) -> Vec> { + self.uses.clone() + } +} diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index e552496..d700d45 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -1,2 +1,24 @@ #![feature(type_alias_impl_trait, lint_reasons)] + +use crate::lst_parser::syntax_kind::SyntaxKind; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Lang {} +impl rowan::Language for Lang { + type Kind = SyntaxKind; + #[allow(unsafe_code)] + fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { + assert!(raw.0 <= SyntaxKind::ROOT as u16); + unsafe { std::mem::transmute::(raw.0) } + } + fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { + kind.into() + } +} + +pub type SyntaxNode = rowan::SyntaxNode; +pub type SyntaxToken = rowan::SyntaxNode; +pub type SyntaxElement = rowan::NodeOrToken; + +pub mod ast; pub mod lst_parser; diff --git a/crates/lang/src/lst_parser/output.rs b/crates/lang/src/lst_parser/output.rs index 49d29c6..af483ab 100644 --- a/crates/lang/src/lst_parser/output.rs +++ b/crates/lang/src/lst_parser/output.rs @@ -3,9 +3,9 @@ use owo_colors::{unset_override, OwoColorize}; use rowan::{GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken}; use std::mem; -use crate::lst_parser::{ - input::MEANINGLESS_TOKS, - syntax_kind::{Lang, SyntaxKind}, +use crate::{ + lst_parser::{input::MEANINGLESS_TOKS, syntax_kind::SyntaxKind}, + Lang, SyntaxNode, }; use super::{ @@ -201,4 +201,12 @@ impl Output { errors, } } + + pub fn syntax(&self) -> SyntaxNode { + SyntaxNode::new_root(self.green_node.clone()) + } + + pub fn errors(&self) -> Vec { + self.errors.clone() + } } diff --git a/crates/lang/src/lst_parser/syntax_kind.rs b/crates/lang/src/lst_parser/syntax_kind.rs index 87230e7..5cb7fb1 100644 --- a/crates/lang/src/lst_parser/syntax_kind.rs +++ b/crates/lang/src/lst_parser/syntax_kind.rs @@ -53,7 +53,6 @@ pub enum SyntaxKind { LIST, // either of a vec, a matrix or a list COLLECTION_ITEM, - DECL, PARENTHESIZED_EXPR, EXPR, LITERAL, @@ -130,7 +129,6 @@ pub enum SyntaxKind { ROOT, EOF, TOMBSTONE, - ERROR, } pub type TokenSet = EnumSet; @@ -140,17 +138,3 @@ impl From for rowan::SyntaxKind { Self(kind as u16) } } - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Lang {} -impl rowan::Language for Lang { - type Kind = SyntaxKind; - #[allow(unsafe_code)] - fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { - assert!(raw.0 <= SyntaxKind::ROOT as u16); - unsafe { std::mem::transmute::(raw.0) } - } - fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { - kind.into() - } -} diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 2531709..1dbd527 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -1,7 +1,10 @@ use clap::Parser; use std::{fs, path::PathBuf}; -use lang::lst_parser::{self, grammar, input, output::Output, syntax_kind}; +use lang::{ + ast::World, + lst_parser::{self, grammar, input, output::Output, syntax_kind}, +}; #[derive(Parser)] struct Args { @@ -12,37 +15,18 @@ struct Args { fn main() { let args = Args::parse(); let n = args.file.clone(); - let f = fs::read_to_string(n.clone()).expect("failed to read file"); + // let f = fs::read_to_string(n.clone()).expect("failed to read file"); - let toks = dbg!(syntax_kind::lex(&f)); - let input = input::Input::new(&toks); - let mut parser = lst_parser::Parser::new(input); + // let toks = dbg!(syntax_kind::lex(&f)); + // let input = input::Input::new(&toks); + // let mut parser = lst_parser::Parser::new(input); - grammar::source_file(&mut parser); + // grammar::source_file(&mut parser); - let p_out = dbg!(parser.finish()); - let o = Output::from_parser_output(toks, p_out); + // let p_out = dbg!(parser.finish()); + // let o = Output::from_parser_output(toks, p_out); - println!("{}", o.debug_colored()); + // println!("{}", o.debug_colored()); - // let parse_res = parser::parse(&f); - // println!("parse: {:?}", parse_res); - - // dbg!(lex::lex(&f)); - // let mut err_collector = ErrorCollector::new(vec![(n.to_str().unwrap(), &f)]); - - // println!("file: {f}\n"); - // let parse_res = parse(&f); - // err_collector.insert_many( - // args.file.to_str().unwrap(), - // lang::err_reporting::Stage::Parse, - // parse_res - // .errors() - // .into_iter() - // .map(|e| e.to_owned()) - // .collect::>(), - // ); - - // err_collector.report_raw(); - // println!("res: {:?}", parse_res); + World::new(n); } diff --git a/testfiles/hello.owo b/testfiles/hello.owo new file mode 100644 index 0000000..d72460b --- /dev/null +++ b/testfiles/hello.owo @@ -0,0 +1 @@ +mod meow; diff --git a/testfiles/hello/meow.owo b/testfiles/hello/meow.owo new file mode 100644 index 0000000..643c13a --- /dev/null +++ b/testfiles/hello/meow.owo @@ -0,0 +1,2 @@ +mod mrawr {} +mod mrow {} diff --git a/testfiles/test.owo b/testfiles/test.owo index 4f06140..13bb5e7 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,11 +1,8 @@ -use hello::meow; - -def hello_world = meow [ 1 2 ]; - -def test - -mod hello { - use gay:;uwu_meow::*; - def meow = uwu; +mod hello; +mod world { + mod meow {} + mod uwu { + mod test {} + mod meow {} + } } - diff --git a/testfiles/uwu.owo b/testfiles/uwu.owo new file mode 100644 index 0000000..1a6b5d1 --- /dev/null +++ b/testfiles/uwu.owo @@ -0,0 +1 @@ +mod meow {} -- 2.46.0 From 7bc603f7e70fc81a1519a7f5aad2c451bbd0b0b3 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 5 Jun 2024 21:10:52 +0200 Subject: [PATCH 59/67] lang: module resolvin --- Cargo.lock | 8 +- crates/lang/src/ast.rs | 6 +- crates/lang/src/ast/mod_tree.rs | 281 ++++++++++++++--------------- crates/lang/src/ast/nodes.rs | 2 +- crates/lang/src/ast/source_file.rs | 7 +- 5 files changed, 148 insertions(+), 156 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc9818c..eaa8be7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -850,9 +850,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -860,9 +860,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index 1375694..6ff6b03 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -1,10 +1,10 @@ -use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use std::{collections::HashMap, path::PathBuf}; use rowan::ast::{AstNode, AstPtr}; use self::{ error::{Error, WorldCreationError}, - mod_tree::{Module, ModuleTree}, + mod_tree::ModuleTree, source_file::SourceFile, }; @@ -42,7 +42,7 @@ impl World { Err(e) => return Err(WorldCreationError::FailedToOpenEntryPoint(entry_point, e)), }; - let (src, mut errors) = SourceFile::parse_from(entry_point.clone(), source); + let (src, mut errors) = SourceFile::parse_from(&entry_point, source); let (module_tree, mut files, new_errors) = ModuleTree::parse_from_main(&entry_point, &src); errors.extend(new_errors); diff --git a/crates/lang/src/ast/mod_tree.rs b/crates/lang/src/ast/mod_tree.rs index 035faec..35df053 100644 --- a/crates/lang/src/ast/mod_tree.rs +++ b/crates/lang/src/ast/mod_tree.rs @@ -1,10 +1,12 @@ use std::{ - collections::{HashMap, HashSet}, - path::PathBuf, + cell::RefCell, + collections::HashMap, + path::{Path, PathBuf}, + rc::Rc, sync::Arc, }; -use rowan::ast::{AstNode, AstPtr}; +use rowan::ast::AstNode; use crate::{lst_parser::syntax_kind::SyntaxKind, SyntaxNode}; @@ -16,7 +18,80 @@ use super::{ }; pub struct ModuleTree { - modules: HashMap>, + modules: Rc>, +} + +#[derive(Clone)] +struct ModuleParsingContext { + files: Rc>>, + errors: Rc>>, + proj_dir: Rc, + cur_path: Vec, + cur_file: PathBuf, +} + +impl ModuleParsingContext { + fn new(entry_path: &Path) -> Self { + let proj_dir = entry_path.parent().unwrap().to_owned(); + Self { + files: Rc::new(RefCell::new(HashMap::new())), + errors: Rc::new(RefCell::new(Vec::new())), + proj_dir: Rc::new(entry_path.to_owned()), + cur_path: Vec::new(), + cur_file: entry_path.to_owned(), + } + } + + fn entry_path(&self) -> PathBuf { + self.proj_dir.to_path_buf() + } + + fn make_mod_file_path(&self, mod_path: &[String], mod_name: &str) -> PathBuf { + let mut entry_path = self.entry_path(); + for el in mod_path { + entry_path.push(format!("{el}/")); + } + entry_path.push(format!("{mod_name}.owo")); + entry_path + } + + fn push_cur_path(&mut self, name_to_add: String) { + self.cur_path.push(name_to_add); + } + + fn set_cur_file(&mut self, new: PathBuf) { + self.cur_file = new; + } + + fn push_errs(&self, errs: Vec) { + self.errors.borrow_mut().extend(errs); + } + + fn parse_child_modules( + &self, + node: &SyntaxNode, + name: Option, + ) -> HashMap { + let mut ctx = self.clone(); + if let Some(name) = name { + ctx.push_cur_path(name) + } + node.children() + .filter_map(Mod::cast) + .filter_map(|m| match Module::parse_mod(m, self.clone()) { + Ok(module) => Some((module.name(), module)), + Err(error) => { + self.errors.borrow_mut().push(error); + None + } + }) + .collect::>() + } + + fn dissolve(self) -> (HashMap, Vec) { + let Self { files, errors, .. } = self; + (files.take(), errors.take()) + } } impl ModuleTree { @@ -24,37 +99,18 @@ impl ModuleTree { path: &PathBuf, main_file: &SourceFile, ) -> (Self, HashMap, Vec) { - let tree = main_file.tree(); - let mut files = HashMap::new(); - let mut errors = Vec::new(); - let entry_path = path.parent().unwrap().to_owned(); - let modules = main_file - .top_level_modules() - .into_iter() - .filter_map(|m| { - let module = - Module::parse_mod(m.to_node(tree.syntax()), Vec::new(), &path, &entry_path); - match module { - Ok(module) => { - files.extend(module.1); - errors.extend(module.2); - Some((module.0.name(), Arc::new(module.0))) - } - Err(err) => { - errors.push(err); - None - } - } - }) - .collect::>>(); + let ctx = ModuleParsingContext::new(&entry_path); + let modules = Rc::new(ctx.parse_child_modules(main_file.tree().syntax(), None)); + + let (files, errors) = ctx.dissolve(); (Self { modules }, files, errors) } pub fn print_tree(&self, lst: &Root) { let name = "main"; - print_tree(&name, &self.modules, 0) + print_tree(&name, self.modules.clone(), 0) } } @@ -62,19 +118,12 @@ pub struct Module { path: Vec, name: String, kind: ModuleKind, - children: HashMap>, + children: Rc>, parent: Option>, } impl Module { - fn parse_mod( - module: Mod, - cur_path: Vec, - cur_file: &PathBuf, - entry_path: &PathBuf, - ) -> Result<(Self, HashMap, Vec), Error> { - dbg!(cur_file); - dbg!(entry_path); + fn parse_mod(module: Mod, ctx: ModuleParsingContext) -> Result { let children = module .syntax() .children() @@ -86,138 +135,77 @@ impl Module { assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); return Self::parse_file_mod( name.text().to_string(), - Loc::new(cur_file.clone(), &module), - cur_path, - entry_path, + ctx.clone(), + Loc::new(ctx.cur_file.clone(), &module), ); } else if children.len() == 2 { let name = &children[0]; assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); let body = &children[1]; assert_eq!(body.kind(), SyntaxKind::MODULE_BODY); - return Ok(Self::parse_inline_mod( - module, cur_path, cur_file, entry_path, - )); + return Ok(Self::parse_inline_mod(module, ctx.clone())); } todo!() } fn parse_file_mod( name: String, + mut ctx: ModuleParsingContext, decl: Loc, - mut cur_path: Vec, - entry_path: &PathBuf, - ) -> Result<(Self, HashMap, Vec), Error> { - let mut mod_file_path = entry_path.to_owned(); + ) -> Result { + ctx.set_cur_file(ctx.make_mod_file_path(&ctx.cur_path, &name)); - for el in &cur_path { - mod_file_path.push(format!("{el}/")); - } - mod_file_path.push(format!("{name}.owo")); - - let mut files = HashMap::new(); - let mut errors = Vec::new(); - - let source = match std::fs::read_to_string(dbg!(&mod_file_path)) { + let source = match std::fs::read_to_string(&ctx.cur_file) { Ok(f) => f, - Err(e) => return Err(Error::FailedToOpenFileMod(mod_file_path, e)), + Err(e) => return Err(Error::FailedToOpenFileMod(ctx.cur_file, e)), }; - let (source_file, file_errors) = SourceFile::parse_from(mod_file_path.clone(), source); - errors.extend(file_errors); + let (source_file, file_errors) = SourceFile::parse_from(&ctx.cur_file, source); - let tree = source_file.tree(); - let old_path = cur_path.clone(); - cur_path.push(name.clone()); - let children = source_file - .top_level_modules() - .into_iter() - .filter_map(|m| { - let module = Module::parse_mod( - m.to_node(tree.syntax()), - cur_path.clone(), - &mod_file_path, - &entry_path, - ); - match module { - Ok(module) => { - files.extend(module.1); - errors.extend(module.2); - Some((module.0.name(), Arc::new(module.0))) - } - Err(err) => { - errors.push(err); - None - } - } - }) - .collect::>>(); + let old_path = ctx.cur_path.clone(); + ctx.push_cur_path(name.clone()); + ctx.push_errs(file_errors); - files.insert(mod_file_path.clone(), source_file); + let children = + Rc::new(ctx.parse_child_modules(source_file.tree().syntax(), Some(name.clone()))); - Ok(( - Self { - path: old_path, - name, - kind: ModuleKind::File { - declaration: decl, - file_id: mod_file_path, - }, - children, - parent: None, + ctx.files + .borrow_mut() + .insert(ctx.cur_file.clone(), source_file); + + Ok(Self { + path: old_path, + name, + kind: ModuleKind::File { + declaration: decl, + file_id: ctx.cur_file.clone(), }, - files, - errors, - )) + children, + parent: None, + }) } - fn parse_inline_mod( - module: Mod, - mut cur_path: Vec, - cur_file: &PathBuf, - entry_path: &PathBuf, - ) -> (Self, HashMap, Vec) { + fn parse_inline_mod(module: Mod, mut ctx: ModuleParsingContext) -> Self { let mut children = module.syntax().children().collect::>(); let body = ModBody::cast(children.pop().unwrap()).unwrap(); - let name = ModName::cast(children.pop().unwrap()).unwrap(); - - let mut files = HashMap::new(); - let mut errors = Vec::new(); - - let old_path = cur_path.clone(); - cur_path.push(name.syntax().to_string()); - let children = body + let name = ModName::cast(children.pop().unwrap()) + .unwrap() .syntax() - .children() - .filter_map(|node| Mod::cast(node)) - .filter_map(|m| { - let m = Self::parse_mod(m, cur_path.clone(), cur_file, entry_path); - match m { - Ok(module) => { - files.extend(module.1); - errors.extend(module.2); - Some((module.0.name(), Arc::new(module.0))) - } - Err(err) => { - errors.push(err); - None - } - } - }) - .collect::>>(); + .text() + .to_string(); - ( - Self { - name: name.syntax().text().to_string(), - kind: ModuleKind::Inline(Loc::new(cur_file.to_owned(), &module)), - children, - parent: None, - path: old_path, - }, - files, - errors, - ) + let old_path = ctx.cur_path.clone(); + ctx.push_cur_path(name.clone()); + let children = Rc::new(ctx.parse_child_modules(body.syntax(), Some(name.clone()))); + + Self { + name, + kind: ModuleKind::Inline(Loc::new(ctx.cur_file.to_owned(), &module)), + children, + parent: None, + path: old_path, + } } pub fn name(&self) -> String { @@ -226,7 +214,7 @@ impl Module { } } -fn print_tree(name: &str, children: &HashMap>, level: u32) { +fn print_tree(name: &str, children: Rc>, level: u32) { const INDENT_STR: &str = " "; for _ in 0..level { @@ -234,9 +222,12 @@ fn print_tree(name: &str, children: &HashMap>, level: u32) { } print!("{name}\n"); - for (name, module) in children { - print_tree(name, &module.children, level + 1); - } + // for (name, module) in children.iter() { + // ; + // } + children + .iter() + .for_each(|(name, m)| print_tree(name, m.children.clone(), level + 1)) } enum ModuleKind { diff --git a/crates/lang/src/ast/nodes.rs b/crates/lang/src/ast/nodes.rs index e59c0f3..744596e 100644 --- a/crates/lang/src/ast/nodes.rs +++ b/crates/lang/src/ast/nodes.rs @@ -4,7 +4,7 @@ use rowan::Language; macro_rules! ast_nodes { ($($ast:ident, $kind:ident);+) => { $( - #[derive(PartialEq, Eq, Hash)] + #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] pub struct $ast(SyntaxNode); impl rowan::ast::AstNode for $ast { diff --git a/crates/lang/src/ast/source_file.rs b/crates/lang/src/ast/source_file.rs index e6e33be..912decd 100644 --- a/crates/lang/src/ast/source_file.rs +++ b/crates/lang/src/ast/source_file.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use rowan::ast::{AstNode, AstPtr}; @@ -15,6 +15,7 @@ use super::{ nodes::{Def, Mod, Root, Use}, }; +#[derive(Debug)] pub struct SourceFile { lst: Root, modules: Vec>, @@ -23,7 +24,7 @@ pub struct SourceFile { } impl SourceFile { - pub fn parse_from(path: PathBuf, source_text: String) -> (Self, Vec) { + pub fn parse_from(path: &Path, source_text: String) -> (Self, Vec) { let toks = syntax_kind::lex(&source_text); let input = Input::new(&toks); let mut parser = Parser::new(input); @@ -57,7 +58,7 @@ impl SourceFile { }, out.errors() .into_iter() - .map(|err| Error::from_syntax(path.clone(), err)) + .map(|err| Error::from_syntax(path.to_owned(), err)) .collect(), ) } -- 2.46.0 From 1a533eb788c914edb97eae9a7af972207f2876c7 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Thu, 6 Jun 2024 09:53:28 +0200 Subject: [PATCH 60/67] lang: smol module tree things and details --- crates/lang/src/ast.rs | 7 ------- crates/lang/src/ast/mod_tree.rs | 14 +++++++------- testfiles/hello.owo | 2 ++ testfiles/hello/meow.owo | 4 +++- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index 6ff6b03..3bbb2f1 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -58,11 +58,4 @@ impl World { module_tree, }) } - - // fn parse_mod_tree(files: &mut HashMap) } - -// struct SourceFile { -// tree: SyntaxNode, -// items: Vec, -// } diff --git a/crates/lang/src/ast/mod_tree.rs b/crates/lang/src/ast/mod_tree.rs index 35df053..8cc373f 100644 --- a/crates/lang/src/ast/mod_tree.rs +++ b/crates/lang/src/ast/mod_tree.rs @@ -18,7 +18,7 @@ use super::{ }; pub struct ModuleTree { - modules: Rc>, + modules: Rc>>, } #[derive(Clone)] @@ -71,7 +71,7 @@ impl ModuleParsingContext { &self, node: &SyntaxNode, name: Option, - ) -> HashMap { + ) -> HashMap> { let mut ctx = self.clone(); if let Some(name) = name { ctx.push_cur_path(name) @@ -79,13 +79,13 @@ impl ModuleParsingContext { node.children() .filter_map(Mod::cast) .filter_map(|m| match Module::parse_mod(m, self.clone()) { - Ok(module) => Some((module.name(), module)), + Ok(module) => Some((module.name(), Rc::new(module))), Err(error) => { self.errors.borrow_mut().push(error); None } }) - .collect::>() + .collect::>>() } fn dissolve(self) -> (HashMap, Vec) { @@ -118,8 +118,8 @@ pub struct Module { path: Vec, name: String, kind: ModuleKind, - children: Rc>, - parent: Option>, + children: Rc>>, + parent: Option>, } impl Module { @@ -214,7 +214,7 @@ impl Module { } } -fn print_tree(name: &str, children: Rc>, level: u32) { +fn print_tree(name: &str, children: Rc>>, level: u32) { const INDENT_STR: &str = " "; for _ in 0..level { diff --git a/testfiles/hello.owo b/testfiles/hello.owo index d72460b..f2fe209 100644 --- a/testfiles/hello.owo +++ b/testfiles/hello.owo @@ -1 +1,3 @@ mod meow; + +def broken diff --git a/testfiles/hello/meow.owo b/testfiles/hello/meow.owo index 643c13a..3140a4a 100644 --- a/testfiles/hello/meow.owo +++ b/testfiles/hello/meow.owo @@ -1,2 +1,4 @@ mod mrawr {} -mod mrow {} +mod mrow { + def gay = ; +} -- 2.46.0 From 3e2c5946c8e03745ffb1d31d97ddeeb168cee442 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Thu, 6 Jun 2024 12:59:30 +0200 Subject: [PATCH 61/67] lang: add registry/namespace --- crates/lang/src/ast.rs | 13 ++- crates/lang/src/ast/mod_tree.rs | 68 +++++++++++++--- crates/lang/src/ast/namespace.rs | 103 ++++++++++++++++++++++++ crates/lang/src/ast/path.rs | 94 +++++++++++++++++++++ testfiles/{ => testproj}/hello.owo | 0 testfiles/{ => testproj}/hello/meow.owo | 0 testfiles/{ => testproj}/test.owo | 0 testfiles/{ => testproj}/uwu.owo | 0 8 files changed, 264 insertions(+), 14 deletions(-) create mode 100644 crates/lang/src/ast/namespace.rs create mode 100644 crates/lang/src/ast/path.rs rename testfiles/{ => testproj}/hello.owo (100%) rename testfiles/{ => testproj}/hello/meow.owo (100%) rename testfiles/{ => testproj}/test.owo (100%) rename testfiles/{ => testproj}/uwu.owo (100%) diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index 3bbb2f1..0c63620 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -5,14 +5,18 @@ use rowan::ast::{AstNode, AstPtr}; use self::{ error::{Error, WorldCreationError}, mod_tree::ModuleTree, + namespace::Registry, source_file::SourceFile, }; mod error; mod mod_tree; +mod namespace; mod nodes; +mod path; mod source_file; +#[derive(Debug)] struct Loc { file: PathBuf, syntax_el: AstPtr, @@ -25,6 +29,10 @@ impl Loc { syntax_el: AstPtr::new(syntax_el), } } + + pub fn file(&self) -> &PathBuf { + &self.file + } } pub struct World { @@ -32,6 +40,7 @@ pub struct World { files: HashMap, errors: Vec, module_tree: ModuleTree, + registry: Registry, } impl World { @@ -44,7 +53,8 @@ impl World { let (src, mut errors) = SourceFile::parse_from(&entry_point, source); - let (module_tree, mut files, new_errors) = ModuleTree::parse_from_main(&entry_point, &src); + let (module_tree, mut files, new_errors, registry) = + ModuleTree::parse_from_main(&entry_point, &src); errors.extend(new_errors); module_tree.print_tree(&src.tree()); dbg!(&errors); @@ -56,6 +66,7 @@ impl World { entry_point, errors, module_tree, + registry, }) } } diff --git a/crates/lang/src/ast/mod_tree.rs b/crates/lang/src/ast/mod_tree.rs index 8cc373f..2e5e269 100644 --- a/crates/lang/src/ast/mod_tree.rs +++ b/crates/lang/src/ast/mod_tree.rs @@ -6,13 +6,15 @@ use std::{ sync::Arc, }; -use rowan::ast::AstNode; +use rowan::ast::{AstNode, SyntaxNodePtr}; -use crate::{lst_parser::syntax_kind::SyntaxKind, SyntaxNode}; +use crate::{lst_parser::syntax_kind::SyntaxKind, Lang, SyntaxNode}; use super::{ error::Error, + namespace::Registry, nodes::{self, Mod, ModBody, ModName, Root}, + path::{ItemPath, OwnedItemPath}, source_file::SourceFile, Loc, }; @@ -25,6 +27,7 @@ pub struct ModuleTree { struct ModuleParsingContext { files: Rc>>, errors: Rc>>, + reg: Rc>, proj_dir: Rc, cur_path: Vec, cur_file: PathBuf, @@ -36,6 +39,7 @@ impl ModuleParsingContext { Self { files: Rc::new(RefCell::new(HashMap::new())), errors: Rc::new(RefCell::new(Vec::new())), + reg: Rc::new(RefCell::new(Registry::new())), proj_dir: Rc::new(entry_path.to_owned()), cur_path: Vec::new(), cur_file: entry_path.to_owned(), @@ -79,7 +83,7 @@ impl ModuleParsingContext { node.children() .filter_map(Mod::cast) .filter_map(|m| match Module::parse_mod(m, self.clone()) { - Ok(module) => Some((module.name(), Rc::new(module))), + Ok(module) => Some((module.name(), module)), Err(error) => { self.errors.borrow_mut().push(error); None @@ -88,9 +92,14 @@ impl ModuleParsingContext { .collect::>>() } - fn dissolve(self) -> (HashMap, Vec) { - let Self { files, errors, .. } = self; - (files.take(), errors.take()) + fn dissolve(self) -> (HashMap, Vec, Registry) { + let Self { + files, + errors, + mut reg, + .. + } = self; + (files.take(), errors.take(), (&*reg).take()) } } @@ -98,14 +107,14 @@ impl ModuleTree { pub fn parse_from_main( path: &PathBuf, main_file: &SourceFile, - ) -> (Self, HashMap, Vec) { + ) -> (Self, HashMap, Vec, Registry) { let entry_path = path.parent().unwrap().to_owned(); let ctx = ModuleParsingContext::new(&entry_path); let modules = Rc::new(ctx.parse_child_modules(main_file.tree().syntax(), None)); - let (files, errors) = ctx.dissolve(); - (Self { modules }, files, errors) + let (files, errors, reg) = ctx.dissolve(); + (Self { modules }, files, errors, reg) } pub fn print_tree(&self, lst: &Root) { @@ -114,26 +123,29 @@ impl ModuleTree { } } +#[derive(Debug)] pub struct Module { path: Vec, name: String, kind: ModuleKind, children: Rc>>, parent: Option>, + body: SyntaxNodePtr, } impl Module { - fn parse_mod(module: Mod, ctx: ModuleParsingContext) -> Result { + fn parse_mod(module: Mod, mut ctx: ModuleParsingContext) -> Result, Error> { let children = module .syntax() .children() // .map(|n| n.kind()) .collect::>(); + let r; if children.len() == 1 { let name = &children[0]; assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); - return Self::parse_file_mod( + r = Self::parse_file_mod( name.text().to_string(), ctx.clone(), Loc::new(ctx.cur_file.clone(), &module), @@ -143,9 +155,15 @@ impl Module { assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); let body = &children[1]; assert_eq!(body.kind(), SyntaxKind::MODULE_BODY); - return Ok(Self::parse_inline_mod(module, ctx.clone())); + r = Ok(Self::parse_inline_mod(module, ctx.clone())); + } else { + unreachable!() } - todo!() + r.map(|module| { + let rc = Rc::new(module); + ctx.reg.borrow_mut().insert_mod(rc.path(), rc.clone()); + rc + }) } fn parse_file_mod( @@ -168,6 +186,7 @@ impl Module { let children = Rc::new(ctx.parse_child_modules(source_file.tree().syntax(), Some(name.clone()))); + let body = SyntaxNodePtr::new(source_file.tree().syntax()); ctx.files .borrow_mut() @@ -182,6 +201,7 @@ impl Module { }, children, parent: None, + body, }) } @@ -205,13 +225,34 @@ impl Module { children, parent: None, path: old_path, + body: SyntaxNodePtr::new(body.syntax()), } } + pub fn path(&self) -> ItemPath { + let mut p = self.path.clone(); + p.push(self.name()); + let mut r_p = ItemPath::new(); + for i in p { + r_p = r_p.push(i); + } + r_p + } + pub fn name(&self) -> String { // self.name.to_node(lst.syntax()).syntax().text().to_string() self.name.clone() } + + pub fn body(&self, files: &HashMap) -> SyntaxNode { + match &self.kind { + ModuleKind::Inline(l) => { + let file = files.get(l.file()).unwrap(); + self.body.to_node(file.tree().syntax()) + } + ModuleKind::File { file_id, .. } => files.get(file_id).unwrap().tree().syntax().clone(), + } + } } fn print_tree(name: &str, children: Rc>>, level: u32) { @@ -230,6 +271,7 @@ fn print_tree(name: &str, children: Rc>>, level: u32) .for_each(|(name, m)| print_tree(name, m.children.clone(), level + 1)) } +#[derive(Debug)] enum ModuleKind { Inline(Loc), File { diff --git a/crates/lang/src/ast/namespace.rs b/crates/lang/src/ast/namespace.rs new file mode 100644 index 0000000..014a6c8 --- /dev/null +++ b/crates/lang/src/ast/namespace.rs @@ -0,0 +1,103 @@ +use std::{ + cell::RefCell, + collections::{HashMap, HashSet}, + rc::Rc, +}; + +use super::{ + mod_tree::Module, + nodes, + path::{ItemPath, OwnedItemPath}, +}; + +// requires mappings: +// names -> sets of references to individual objects +// paths -> individual objects +// glob paths -> sets of object refs +// (OPT/LATER) names (fuzzy) ~> sets of objects +#[derive(Debug)] +pub struct Registry { + defs: Vec>, + mods: Vec>, + names: HashMap>, + paths: HashMap, +} + +impl Registry { + pub fn new() -> Self { + Self { + defs: Vec::new(), + mods: Vec::new(), + names: HashMap::new(), + paths: HashMap::new(), + } + } + + // TODO: rewrite. has lots of flaws + pub fn insert_mod(&mut self, path: ItemPath<'_>, module: Rc) -> Option { + let idx = self.mods.len(); + let Some(name) = path.name() else { return None }; + self.mods.push(StoreObj::new(&name, Mod { module })); + + if let Some(set) = self.names.get_mut(&name) { + set.insert(RegistryIdx::Mod(idx)); + } else { + self.names + .insert(name, HashSet::from([RegistryIdx::Mod(idx)])); + } + + if self + .paths + .get(&path.clone().into()) + .is_some_and(|other_idx| *other_idx != RegistryIdx::Mod(idx)) + { + return None; + } else { + self.paths.insert(path.into(), RegistryIdx::Mod(idx)) + } + } +} + +impl Default for Registry { + fn default() -> Self { + Self::new() + } +} + +#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)] +pub enum RegistryIdx { + Def(usize), + Mod(usize), +} + +#[derive(Debug)] +struct StoreObj { + inner: Rc>, +} + +impl StoreObj { + pub fn new(name: impl ToString, item: T) -> Self { + Self { + inner: Rc::new(InnerStoreObj { + name: name.to_string(), + obj: item, + }), + } + } +} + +#[derive(Debug)] +struct InnerStoreObj { + name: String, + obj: T, +} + +#[derive(Debug)] +struct Mod { + module: Rc, +} + +#[derive(Debug)] +struct Def { + node: nodes::Def, +} diff --git a/crates/lang/src/ast/path.rs b/crates/lang/src/ast/path.rs new file mode 100644 index 0000000..a15f141 --- /dev/null +++ b/crates/lang/src/ast/path.rs @@ -0,0 +1,94 @@ +use std::borrow::Cow; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ItemPath<'a> { + items: Vec>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct OwnedItemPath { + items: Vec, +} + +impl OwnedItemPath { + pub fn name(&self) -> Option { + self.items.last().cloned() + } +} + +impl From> for OwnedItemPath { + fn from(value: ItemPath<'_>) -> Self { + Self { + items: value + .items + .into_iter() + .map(|v| v.into_owned()) + .collect::>(), + } + } +} + +impl<'a> ItemPath<'a> { + pub fn new() -> Self { + Self { items: Vec::new() } + } + + pub fn from_slices(slice: &[&'a str]) -> Self { + Self { + items: slice.into_iter().map(|i| (*i).into()).collect::>(), + } + } + + pub fn push(mut self, item: impl Into>) -> Self { + self.items.push(item.into()); + self + } + + pub fn name(&self) -> Option { + self.items.last().cloned().map(|item| item.into_owned()) + } +} + +impl<'a> TryFrom> for ItemPath<'a> { + type Error = (); + + fn try_from(value: Pattern<'a>) -> Result { + let mut items = Vec::new(); + + for i in value.items { + match i { + PatternEl::Name(n) => items.push(n), + PatternEl::Glob => return Err(()), + } + } + + Ok(Self { items }) + } +} + +#[derive(Clone, Debug)] +pub struct Pattern<'a> { + items: Vec>, +} + +impl<'a> Pattern<'a> { + pub fn new() -> Self { + Self { items: Vec::new() } + } + + pub fn add_name(mut self, name: &'a str) -> Self { + self.items.push(PatternEl::Name(name.into())); + self + } + + pub fn add_glob(mut self) -> Self { + self.items.push(PatternEl::Glob); + self + } +} + +#[derive(Clone, Debug)] +enum PatternEl<'a> { + Name(Cow<'a, str>), + Glob, +} diff --git a/testfiles/hello.owo b/testfiles/testproj/hello.owo similarity index 100% rename from testfiles/hello.owo rename to testfiles/testproj/hello.owo diff --git a/testfiles/hello/meow.owo b/testfiles/testproj/hello/meow.owo similarity index 100% rename from testfiles/hello/meow.owo rename to testfiles/testproj/hello/meow.owo diff --git a/testfiles/test.owo b/testfiles/testproj/test.owo similarity index 100% rename from testfiles/test.owo rename to testfiles/testproj/test.owo diff --git a/testfiles/uwu.owo b/testfiles/testproj/uwu.owo similarity index 100% rename from testfiles/uwu.owo rename to testfiles/testproj/uwu.owo -- 2.46.0 From 37651a83bc7db9ec5cebf98be1d8c45ee8bab9f0 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 10 Jun 2024 09:47:48 +0200 Subject: [PATCH 62/67] lang: current state idk try to parallelize --- Cargo.lock | 91 ++++++++++++++- crates/lang/Cargo.toml | 3 + crates/lang/src/lib.rs | 7 +- crates/lang/src/main.rs | 18 +-- crates/lang/src/world.rs | 47 ++++++++ crates/lang/src/world/error.rs | 17 +++ crates/lang/src/world/files.rs | 116 +++++++++++++++++++ crates/lang/src/world/modules.rs | 38 ++++++ crates/lang/src/world/nodes.rs | 77 ++++++++++++ crates/lang/src/world/registry.rs | 41 +++++++ crates/lang/src/world/world_creation_pool.rs | 82 +++++++++++++ 11 files changed, 522 insertions(+), 15 deletions(-) create mode 100644 crates/lang/src/world.rs create mode 100644 crates/lang/src/world/error.rs create mode 100644 crates/lang/src/world/files.rs create mode 100644 crates/lang/src/world/modules.rs create mode 100644 crates/lang/src/world/nodes.rs create mode 100644 crates/lang/src/world/registry.rs create mode 100644 crates/lang/src/world/world_creation_pool.rs diff --git a/Cargo.lock b/Cargo.lock index eaa8be7..cd7e5fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,6 +206,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.4" @@ -228,6 +250,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.18" @@ -286,6 +317,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "deranged" version = "0.3.11" @@ -563,12 +607,15 @@ version = "0.1.0" dependencies = [ "ariadne", "clap", + "crossbeam", + "dashmap", "drop_bomb", "ego-tree", "enumset", "indexmap", "indoc", "logos", + "num_cpus", "owo-colors", "petgraph", "rowan", @@ -588,9 +635,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libredox" @@ -600,7 +647,7 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.4.1", "libc", - "redox_syscall", + "redox_syscall 0.4.1", ] [[package]] @@ -701,6 +748,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -710,6 +767,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "option-ext" version = "0.2.0" @@ -725,6 +788,19 @@ dependencies = [ "supports-color", ] +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.0", +] + [[package]] name = "petgraph" version = "0.6.4" @@ -877,6 +953,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.4.1", +] + [[package]] name = "redox_users" version = "0.4.4" diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 8a9c80b..0323c98 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -16,6 +16,9 @@ rowan = "0.15.15" drop_bomb = "0.1.5" enumset = "1.1.3" indoc = "2" +num_cpus = "1.16" +dashmap = "5.5.3" +crossbeam = "0.8.4" owo-colors = {version = "4", features = ["supports-colors"]} [lints] diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index d700d45..c8b48f6 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -2,6 +2,10 @@ use crate::lst_parser::syntax_kind::SyntaxKind; +pub mod ast; +pub mod lst_parser; +pub mod world; + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Lang {} impl rowan::Language for Lang { @@ -19,6 +23,3 @@ impl rowan::Language for Lang { pub type SyntaxNode = rowan::SyntaxNode; pub type SyntaxToken = rowan::SyntaxNode; pub type SyntaxElement = rowan::NodeOrToken; - -pub mod ast; -pub mod lst_parser; diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 1dbd527..c0c04d5 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -15,18 +15,18 @@ struct Args { fn main() { let args = Args::parse(); let n = args.file.clone(); - // let f = fs::read_to_string(n.clone()).expect("failed to read file"); + let f = fs::read_to_string(n.clone()).expect("failed to read file"); - // let toks = dbg!(syntax_kind::lex(&f)); - // let input = input::Input::new(&toks); - // let mut parser = lst_parser::Parser::new(input); + let toks = dbg!(syntax_kind::lex(&f)); + let input = input::Input::new(&toks); + let mut parser = lst_parser::Parser::new(input); - // grammar::source_file(&mut parser); + grammar::source_file(&mut parser); - // let p_out = dbg!(parser.finish()); - // let o = Output::from_parser_output(toks, p_out); + let p_out = dbg!(parser.finish()); + let o = Output::from_parser_output(toks, p_out); - // println!("{}", o.debug_colored()); + println!("{}", o.debug_colored()); - World::new(n); + // World::new(n); } diff --git a/crates/lang/src/world.rs b/crates/lang/src/world.rs new file mode 100644 index 0000000..8a2506b --- /dev/null +++ b/crates/lang/src/world.rs @@ -0,0 +1,47 @@ +use std::{ + fmt::Display, + path::{Path, PathBuf}, +}; + +use crate::world::world_creation_pool::WorldCreationPool; + +use self::{files::Files, registry::Registry}; + +pub mod error; +pub mod files; +pub mod modules; +pub mod nodes; +pub mod registry; +mod world_creation_pool; + +pub struct World {} + +impl World { + pub fn new(entry_point: &Path) -> Result { + let files = Files::new(); + let reg = Registry::new(); + + let (entry_point_id, errors) = files.add_and_parse(entry_point).map_err(|e| { + WorldCreationError::FailedToOpenEntryPoint(entry_point.to_path_buf(), e) + })?; + + let pool = WorldCreationPool::new(files, reg); + + todo!() + } +} + +#[derive(Debug)] +pub enum WorldCreationError { + FailedToOpenEntryPoint(PathBuf, std::io::Error), +} + +impl Display for WorldCreationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WorldCreationError::FailedToOpenEntryPoint(entry_path, e) => { + write!(f, "failed to open entry_point '{entry_path:?}': {e}") + } + } + } +} diff --git a/crates/lang/src/world/error.rs b/crates/lang/src/world/error.rs new file mode 100644 index 0000000..6232a68 --- /dev/null +++ b/crates/lang/src/world/error.rs @@ -0,0 +1,17 @@ +use std::path::PathBuf; + +use crate::lst_parser::error::SyntaxError; + +use super::files::FileId; + +#[derive(Debug)] +pub enum Error { + Syntax(FileId, SyntaxError), + FailedToOpenFileMod(PathBuf, std::io::Error), +} + +impl Error { + pub fn from_syntax(file: FileId, e: SyntaxError) -> Self { + Self::Syntax(file, e) + } +} diff --git a/crates/lang/src/world/files.rs b/crates/lang/src/world/files.rs new file mode 100644 index 0000000..ca61d83 --- /dev/null +++ b/crates/lang/src/world/files.rs @@ -0,0 +1,116 @@ +use std::{ + clone, fs, + path::{Path, PathBuf}, + sync::{Arc, Mutex, RwLock}, +}; + +use dashmap::DashMap; +use rowan::ast::{AstNode, AstPtr}; + +use crate::lst_parser::{ + error::SyntaxError, grammar::source_file, input::Input, output::Output, syntax_kind, Parser, +}; + +use super::{error::Error, modules::Module, nodes}; + +pub struct Loc { + file: FileId, + syntax: AstPtr, +} + +#[derive(Clone)] +pub struct Files(Arc); + +impl Files { + pub fn new() -> Self { + Self(Arc::new(FilesInner::new())) + } + + pub fn add_and_parse(&self, file: &Path) -> Result<(FileId, Vec), std::io::Error> { + self.0.add_and_parse(file) + } + + pub fn get(&self, id: FileId) -> Arc { + self.0.get(id) + } +} + +/// global file store +/// contains all known files etc. +struct FilesInner { + paths: Mutex>, + store: DashMap>, +} + +impl FilesInner { + fn new() -> Self { + Self { + paths: Mutex::new(Vec::new()), + store: DashMap::new(), + } + } + + fn add_and_parse(&self, path: &Path) -> Result<(FileId, Vec), std::io::Error> { + let (file, errors) = SourceFile::read_and_parse(&path)?; + + // add file to paths and unlock again + let id = { + let path = path.clone(); + let mut paths = self.paths.lock().unwrap(); + let r = paths.len(); + paths.push(path.to_path_buf()); + FileId(r) + }; + + let _ = self.store.insert(path.to_path_buf(), Arc::new(file)); + + Ok(( + id, + errors + .into_iter() + .map(|e| Error::from_syntax(id, e)) + .collect(), + )) + } + + fn get(&self, id: FileId) -> Arc { + let path = { + let paths = self.paths.lock().unwrap(); + paths[id.0].clone() + }; + + self.store.get(&path).unwrap().clone() + } +} + +pub struct SourceFile { + pub lst: Mutex, + root_module: Option>, +} + +impl SourceFile { + fn read_and_parse(path: &Path) -> Result<(Self, Vec), std::io::Error> { + let source_text = fs::read_to_string(&path)?; + + let toks = syntax_kind::lex(&source_text); + let input = Input::new(&toks); + let mut parser = Parser::new(input); + + source_file(&mut parser); + + let events = parser.finish(); + let out = Output::from_parser_output(toks, events); + let lst = out.syntax(); + + Ok(( + Self { + lst: Mutex::new(nodes::Root::cast(lst).unwrap()), + root_module: None, + }, + out.errors(), + )) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct FileId(pub usize); diff --git a/crates/lang/src/world/modules.rs b/crates/lang/src/world/modules.rs new file mode 100644 index 0000000..0f6d060 --- /dev/null +++ b/crates/lang/src/world/modules.rs @@ -0,0 +1,38 @@ +use std::sync::Arc; + +use dashmap::{DashMap, DashSet}; +use rowan::ast::AstNode; + +use super::{ + files::{FileId, Loc}, + nodes, + registry::ItemPath, + world_creation_pool::WorkerCtx, + // world_creation_pool::WorkerCtx, +}; + +pub struct Module { + /// is none if this is the root module of the entry point file + decl: Option>, + body: ModuleBody, + own_path: ItemPath, + child_modules: Arc>>, + child_defs: Arc>, +} + +impl Module { + pub fn parse_file(ctx: WorkerCtx, file: FileId, decl: Option>) { + let f = ctx.files.get(file); + + let tree = f.lst.lock().unwrap(); + + let children = (&*tree).syntax().children(); + } +} + +struct Def; + +enum ModuleBody { + InLine(Loc), + File(FileId), +} diff --git a/crates/lang/src/world/nodes.rs b/crates/lang/src/world/nodes.rs new file mode 100644 index 0000000..c77399a --- /dev/null +++ b/crates/lang/src/world/nodes.rs @@ -0,0 +1,77 @@ +use crate::lst_parser::syntax_kind::SyntaxKind::*; +use crate::SyntaxNode; +use rowan::Language; +macro_rules! ast_nodes { +($($ast:ident, $kind:ident);+) => { + $( + #[derive(Debug, PartialEq, Eq, Hash)] + #[repr(transparent)] + pub struct $ast(SyntaxNode); + impl rowan::ast::AstNode for $ast { + type Language = crate::Lang; + + fn can_cast(kind: ::Kind) -> bool { + kind == $kind + } + + fn cast(node: SyntaxNode) -> Option { + if node.kind() == $kind { + Some(Self(node)) + } else { + None + } + } + + fn syntax(&self) -> &SyntaxNode { + &self.0 + } + } + )+ +}; +} +ast_nodes!( + Def, DEF; + DefName, DEF_NAME; + DefBody, DEF_BODY; + + Mod, MODULE; + ModName, MODULE_NAME; + ModBody, MODULE_BODY; + + Use, USE; + UsePat, USE_PAT; + PatItem, PAT_ITEM; + PatGlob, PAT_GLOB; + PatGroup, PAT_GROUP; + + Literal, LITERAL; + IntLit, INT_NUM; + FloatLit, FLOAT_NUM; + StringLit, STRING; + + Matrix, MATRIX; + MatrixRow, MAT_ROW; + Vector, VEC; + List, LIST; + CollectionItem, COLLECTION_ITEM; + + ParenthesizedExpr, PARENTHESIZED_EXPR; + Expression, EXPR; + + Pipeline, PIPELINE; + + Instruction, INSTR; + InstructionName, INSTR_NAME; + InstructionParams, INSTR_PARAMS; + + AttributeSet, ATTR_SET; + Attribute, ATTR; + AttributeName, ATTR_NAME; + AttributeValue, ATTR_VALUE; + + ParseError, PARSE_ERR; + LexError, LEX_ERR; + + Root, ROOT; + Eof, EOF +); diff --git a/crates/lang/src/world/registry.rs b/crates/lang/src/world/registry.rs new file mode 100644 index 0000000..791be15 --- /dev/null +++ b/crates/lang/src/world/registry.rs @@ -0,0 +1,41 @@ +use std::sync::{Arc, Mutex, RwLock}; + +use dashmap::{DashMap, DashSet}; + +use super::modules::Module; + +pub type ItemPath = Vec; + +#[derive(Clone)] +pub struct Registry(Arc); + +impl Registry { + pub fn new() -> Self { + Self(Arc::new(RegistryInner::new())) + } +} + +struct RegistryInner { + /// ids will refer to paths of modules + paths: RwLock>, + modules: DashMap>, + names: DashMap>, +} + +impl RegistryInner { + fn new() -> Self { + Self { + paths: RwLock::new(Vec::new()), + modules: DashMap::new(), + names: DashMap::new(), + } + } +} + +pub enum RegistryId { + /// refer to the modlue item itself, the name must be the same as the last item of the path + Module(usize), + /// refer to a def in a module + /// name must refer to name of child of module + Def(usize), +} diff --git a/crates/lang/src/world/world_creation_pool.rs b/crates/lang/src/world/world_creation_pool.rs new file mode 100644 index 0000000..fd04e65 --- /dev/null +++ b/crates/lang/src/world/world_creation_pool.rs @@ -0,0 +1,82 @@ +use std::{path::PathBuf, thread}; + +use crossbeam::channel::{Receiver, Sender}; + +use super::{ + error::Error, + files::{FileId, Files, Loc}, + nodes, + registry::Registry, +}; + +pub(super) struct WorldCreationPool { + workers: Vec, + tx: Sender, +} + +impl WorldCreationPool { + pub fn new(files: Files, reg: Registry) -> Self { + let mut workers = Vec::new(); + let (tx, rx) = crossbeam::channel::unbounded(); + + for i in 0..num_cpus::get() { + workers.push(Worker::new( + i, + files.clone(), + reg.clone(), + JobSender(tx.clone()), + rx.clone(), + )) + } + + Self { workers, tx } + } +} + +pub struct JobSender(Sender); + +enum Job { + ParseFileMod { + file: FileId, + decl: Option>, + }, + OpenFile(PathBuf), +} + +struct Worker { + id: usize, + // thead: thread::JoinHandle>, + thread: thread::JoinHandle<()>, +} + +pub struct WorkerCtx { + errors: Vec, + pub files: Files, + pub reg: Registry, + pub tx: JobSender, +} + +impl Worker { + fn new(id: usize, files: Files, reg: Registry, sender: JobSender, rx: Receiver) -> Self { + let ctx = WorkerCtx { + errors: Vec::new(), + files, + reg, + tx: sender, + }; + + let thread_handle = thread::spawn(move || { + for msg in rx { + match msg { + Job::ParseFileMod { file, decl } => todo!(), + Job::OpenFile(path) => todo!(), + } + } + }); + + Self { + id, + thread: thread_handle, + } + } +} -- 2.46.0 From 1c6180aabc767332d196bc4ab78624421d94eea9 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 23 Jun 2024 20:32:10 +0200 Subject: [PATCH 63/67] lang: current state for archival purposes --- Cargo.lock | 328 ++++++++++++++++++ crates/lang/Cargo.toml | 3 + crates/lang/src/lib.rs | 2 +- crates/lang/src/lst_parser/output.rs | 5 + crates/lang/src/world.rs | 10 +- crates/lang/src/world/files.rs | 67 ++-- crates/lang/src/world/modules.rs | 157 ++++++++- crates/lang/src/world/nodes.rs | 2 +- crates/lang/src/world/world_creation_pool.rs | 173 +++++++-- .../world/world_creation_pool/wait_blocker.rs | 87 +++++ 10 files changed, 775 insertions(+), 59 deletions(-) create mode 100644 crates/lang/src/world/world_creation_pool/wait_blocker.rs diff --git a/Cargo.lock b/Cargo.lock index cd7e5fc..92c691e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,133 @@ dependencies = [ "yansi", ] +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-signal" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.1.0" @@ -121,6 +248,19 @@ dependencies = [ "serde", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bytemuck" version = "1.14.0" @@ -191,6 +331,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "countme" version = "3.0.1" @@ -366,6 +515,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" +[[package]] +name = "easy-parallel" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2afbb9b0aef60e4f0d2b18129b6c0dff035a6f7dbbd17c2f38c1432102ee223c" + [[package]] name = "ego-tree" version = "0.6.2" @@ -405,6 +560,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "eval" version = "0.1.0" @@ -415,6 +580,27 @@ dependencies = [ "serde", ] +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "exr" version = "1.71.0" @@ -431,6 +617,12 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "fdeflate" version = "0.3.3" @@ -471,6 +663,31 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "getrandom" version = "0.2.12" @@ -610,8 +827,10 @@ dependencies = [ "crossbeam", "dashmap", "drop_bomb", + "easy-parallel", "ego-tree", "enumset", + "futures-lite", "indexmap", "indoc", "logos", @@ -619,6 +838,7 @@ dependencies = [ "owo-colors", "petgraph", "rowan", + "smol", ] [[package]] @@ -650,6 +870,12 @@ dependencies = [ "redox_syscall 0.4.1", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.11" @@ -788,6 +1014,12 @@ dependencies = [ "supports-color", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot_core" version = "0.9.10" @@ -853,6 +1085,23 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "piper" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "png" version = "0.17.10" @@ -866,6 +1115,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1010,6 +1274,19 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.16" @@ -1053,6 +1330,15 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -1065,12 +1351,38 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "smol" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e635339259e51ef85ac7aa29a1cd991b957047507288697a690e80ab97d07cad" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + [[package]] name = "spin" version = "0.9.8" @@ -1174,6 +1486,22 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 0323c98..f50073a 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -20,6 +20,9 @@ num_cpus = "1.16" dashmap = "5.5.3" crossbeam = "0.8.4" owo-colors = {version = "4", features = ["supports-colors"]} +smol = "2" +futures-lite = "2.3" +easy-parallel = "3.3.1" [lints] workspace = true diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index c8b48f6..f48385e 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(type_alias_impl_trait, lint_reasons)] +#![feature(type_alias_impl_trait, lint_reasons, box_into_inner)] use crate::lst_parser::syntax_kind::SyntaxKind; diff --git a/crates/lang/src/lst_parser/output.rs b/crates/lang/src/lst_parser/output.rs index af483ab..ce96fb5 100644 --- a/crates/lang/src/lst_parser/output.rs +++ b/crates/lang/src/lst_parser/output.rs @@ -209,4 +209,9 @@ impl Output { pub fn errors(&self) -> Vec { self.errors.clone() } + + pub fn dissolve(self) -> (GreenNode, Vec) { + let Self { green_node, errors } = self; + (green_node, errors) + } } diff --git a/crates/lang/src/world.rs b/crates/lang/src/world.rs index 8a2506b..2f32cab 100644 --- a/crates/lang/src/world.rs +++ b/crates/lang/src/world.rs @@ -1,11 +1,19 @@ use std::{ fmt::Display, path::{Path, PathBuf}, + sync::{Arc, Mutex}, }; use crate::world::world_creation_pool::WorldCreationPool; -use self::{files::Files, registry::Registry}; +use easy_parallel::Parallel; +use futures_lite::future; +use smol::{ + channel::{unbounded, Sender}, + Executor, +}; + +use self::{error::Error, files::Files, registry::Registry}; pub mod error; pub mod files; diff --git a/crates/lang/src/world/files.rs b/crates/lang/src/world/files.rs index ca61d83..b4a1309 100644 --- a/crates/lang/src/world/files.rs +++ b/crates/lang/src/world/files.rs @@ -7,50 +7,55 @@ use std::{ use dashmap::DashMap; use rowan::ast::{AstNode, AstPtr}; -use crate::lst_parser::{ - error::SyntaxError, grammar::source_file, input::Input, output::Output, syntax_kind, Parser, +use crate::{ + lst_parser::{ + error::SyntaxError, grammar::source_file, input::Input, output::Output, syntax_kind, Parser, + }, + Lang, }; use super::{error::Error, modules::Module, nodes}; -pub struct Loc { +#[derive(Clone)] +pub struct Loc> { file: FileId, syntax: AstPtr, } -#[derive(Clone)] -pub struct Files(Arc); - -impl Files { - pub fn new() -> Self { - Self(Arc::new(FilesInner::new())) +impl> Loc { + pub fn new(node: N, file: FileId) -> Self { + Self { + file, + syntax: AstPtr::new(&node), + } } - pub fn add_and_parse(&self, file: &Path) -> Result<(FileId, Vec), std::io::Error> { - self.0.add_and_parse(file) + pub fn file(&self) -> FileId { + self.file } - pub fn get(&self, id: FileId) -> Arc { - self.0.get(id) + pub fn syntax(&self) -> AstPtr { + self.syntax.clone() } } /// global file store /// contains all known files etc. -struct FilesInner { - paths: Mutex>, - store: DashMap>, +#[derive(Clone)] +pub struct Files { + paths: Arc>>, + store: Arc>>, } -impl FilesInner { - fn new() -> Self { +impl Files { + pub fn new() -> Self { Self { - paths: Mutex::new(Vec::new()), - store: DashMap::new(), + paths: Arc::new(Mutex::new(Vec::new())), + store: Arc::new(DashMap::new()), } } - fn add_and_parse(&self, path: &Path) -> Result<(FileId, Vec), std::io::Error> { + pub fn add_and_parse(&self, path: &Path) -> Result<(FileId, Vec), std::io::Error> { let (file, errors) = SourceFile::read_and_parse(&path)?; // add file to paths and unlock again @@ -73,7 +78,7 @@ impl FilesInner { )) } - fn get(&self, id: FileId) -> Arc { + pub fn get(&self, id: FileId) -> Arc { let path = { let paths = self.paths.lock().unwrap(); paths[id.0].clone() @@ -81,10 +86,19 @@ impl FilesInner { self.store.get(&path).unwrap().clone() } + + pub fn get_path(&self, id: FileId) -> PathBuf { + let paths = self.paths.lock().unwrap(); + paths[id.0].clone() + } + + pub fn path_store(&self) -> Arc>> { + self.paths.clone() + } } pub struct SourceFile { - pub lst: Mutex, + pub lst: RwLock, root_module: Option>, } @@ -100,14 +114,15 @@ impl SourceFile { let events = parser.finish(); let out = Output::from_parser_output(toks, events); - let lst = out.syntax(); + // let lst = out.syntax(); + let (lst, errors) = out.dissolve(); Ok(( Self { - lst: Mutex::new(nodes::Root::cast(lst).unwrap()), + lst: RwLock::new(lst), root_module: None, }, - out.errors(), + errors, )) } } diff --git a/crates/lang/src/world/modules.rs b/crates/lang/src/world/modules.rs index 0f6d060..676553b 100644 --- a/crates/lang/src/world/modules.rs +++ b/crates/lang/src/world/modules.rs @@ -1,14 +1,18 @@ -use std::sync::Arc; +use std::{ + string::String, + sync::{Arc, Mutex}, +}; use dashmap::{DashMap, DashSet}; -use rowan::ast::AstNode; +use rowan::ast::{AstNode, AstPtr}; + +use crate::SyntaxNode; use super::{ files::{FileId, Loc}, nodes, registry::ItemPath, - world_creation_pool::WorkerCtx, - // world_creation_pool::WorkerCtx, + world_creation_pool::{wait_blocker::WaitBlocker, Task, WorkerCtx}, }; pub struct Module { @@ -21,18 +25,151 @@ pub struct Module { } impl Module { - pub fn parse_file(ctx: WorkerCtx, file: FileId, decl: Option>) { - let f = ctx.files.get(file); + pub fn parse_mod_body( + ctx: &WorkerCtx, + tree: SyntaxNode, + path: ItemPath, + file: FileId, + ) -> (DashMap>>>>, Vec) { + let children: Vec<(String, Arc>>>, nodes::Mod)> = tree + .children() + .filter_map(|c| nodes::Mod::cast(c)) + .map(|m| { + let name = nodes::ModName::cast(m.syntax().first_child().unwrap()) + .expect("ModName should always be first child of Mod"); + ( + name.syntax().text().to_string(), + Arc::new(Mutex::new(None)), + m, + ) + }) + .collect(); + let retv: DashMap>>>> = children + .iter() + .map(|(name, mod_r, _)| (name.to_owned(), mod_r.clone())) + .collect(); + let tasks = children + .into_iter() + .map(|(name, mod_r, loc)| { + let mut path = path.clone(); + path.push(name); + Task::ParseMod(Loc::new(loc, file), mod_r, path) + }) + .collect::>(); - let tree = f.lst.lock().unwrap(); + (retv, tasks) + } + pub fn parse_file0( + ctx: &mut WorkerCtx, + file: FileId, + path: ItemPath, + decl: Option>, + ) { + let tree = ctx.get_tree(file); - let children = (&*tree).syntax().children(); + let (retv, tasks) = Self::parse_mod_body(ctx, tree, path.clone(), file); + + ctx.send_tasks( + Task::ParseFileMod1 { + file, + decl, + ret: retv.into(), + path, + }, + tasks, + ) + } + + pub fn parse_mod( + ctx: &mut WorkerCtx, + loc: Loc, + path: ItemPath, + ret: Arc>>, + blocker: WaitBlocker, + ) { + let mod_decl = ctx.resolve_loc(loc); + let children = mod_decl.syntax().children().collect::>(); + + if children.len() == 1 { + // TODO: file mod + todo!() + } else if children.len() == 2 { + // inline mod + } + } + + pub fn parse_inline_mod( + ctx: &mut WorkerCtx, + decl: Loc, + path: ItemPath, + ret: Arc>>>, + blocker: WaitBlocker, + ) { + let mod_decl_children = ctx + .resolve_loc(decl.clone()) + .syntax() + .children() + .collect::>(); + + assert_eq!(mod_decl_children.len(), 2); + + let name = nodes::ModName::cast(mod_decl_children[0].clone()).unwrap(); + let body = nodes::ModBody::cast(mod_decl_children[1].clone()).unwrap(); + + let (retv, tasks) = + Self::parse_mod_body(ctx, body.syntax().clone(), path.clone(), decl.file()); + + ctx.send_tasks( + Task::CompleteMod { + ret_self: ret, + body: ModuleBody::Inline(Loc::new(body, decl.file())), + decl: Some(decl), + path, + child_mods: retv.into(), + blocker, + }, + tasks, + ) + } + + pub fn complete_mod( + ctx: &mut WorkerCtx, + decl: Option>, + ret_self: Arc>>>, + body: ModuleBody, + path: ItemPath, + child_mods: Arc>>>>>, + blocker: WaitBlocker, + ) { + assert!(child_mods + .iter() + .all(|item| item.value().lock().unwrap().is_some())); + + let module = Arc::new(Module { + decl, + body, + own_path: path, + child_modules: Arc::new( + Arc::<_>::unwrap_or_clone(child_mods) + .into_iter() + .map(|(name, module)| { + let module = module.lock().unwrap().take().unwrap(); + (name, module) + }) + .collect(), + ), + child_defs: Arc::default(), + }); + + let mut ret_self = ret_self.lock().unwrap(); + *ret_self = Some(module); + drop(blocker); } } struct Def; -enum ModuleBody { - InLine(Loc), +pub enum ModuleBody { + Inline(Loc), File(FileId), } diff --git a/crates/lang/src/world/nodes.rs b/crates/lang/src/world/nodes.rs index c77399a..303a3a1 100644 --- a/crates/lang/src/world/nodes.rs +++ b/crates/lang/src/world/nodes.rs @@ -4,7 +4,7 @@ use rowan::Language; macro_rules! ast_nodes { ($($ast:ident, $kind:ident);+) => { $( - #[derive(Debug, PartialEq, Eq, Hash)] + #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[repr(transparent)] pub struct $ast(SyntaxNode); impl rowan::ast::AstNode for $ast { diff --git a/crates/lang/src/world/world_creation_pool.rs b/crates/lang/src/world/world_creation_pool.rs index fd04e65..651d7eb 100644 --- a/crates/lang/src/world/world_creation_pool.rs +++ b/crates/lang/src/world/world_creation_pool.rs @@ -1,14 +1,28 @@ -use std::{path::PathBuf, thread}; +use std::{ + collections::HashMap, + path::PathBuf, + sync::{Arc, Mutex}, + thread, +}; use crossbeam::channel::{Receiver, Sender}; +use dashmap::DashMap; +use rowan::ast::{AstNode, AstPtr}; + +use crate::{Lang, SyntaxNode}; + +use self::wait_blocker::WaitBlocker; use super::{ error::Error, files::{FileId, Files, Loc}, + modules::{Module, ModuleBody}, nodes, - registry::Registry, + registry::{ItemPath, Registry}, }; +pub mod wait_blocker; + pub(super) struct WorldCreationPool { workers: Vec, tx: Sender, @@ -24,7 +38,7 @@ impl WorldCreationPool { i, files.clone(), reg.clone(), - JobSender(tx.clone()), + tx.clone(), rx.clone(), )) } @@ -33,12 +47,31 @@ impl WorldCreationPool { } } -pub struct JobSender(Sender); - enum Job { - ParseFileMod { + WaitingFor(WaitBlocker, Task), + Awaited(WaitBlocker, Task), +} + +pub enum Task { + ParseMod(Loc, Arc>>>, ItemPath), + ParseFileMod0 { file: FileId, decl: Option>, + path: ItemPath, + }, + ParseFileMod1 { + file: FileId, + decl: Option>, + path: ItemPath, + ret: Arc>>>>>, + }, + CompleteMod { + ret_self: Arc>>>, + decl: Option>, + body: ModuleBody, + path: ItemPath, + child_mods: Arc>>>>>, + blocker: WaitBlocker, }, OpenFile(PathBuf), } @@ -51,26 +84,104 @@ struct Worker { pub struct WorkerCtx { errors: Vec, - pub files: Files, + files: Files, + local_files: HashMap, pub reg: Registry, - pub tx: JobSender, + tx: Sender, +} + +impl WorkerCtx { + pub fn get_tree(&mut self, id: FileId) -> SyntaxNode { + let p = self.files.get_path(id); + if self.local_files.contains_key(&p) { + self.local_files[&p].clone() + } else { + let f = self.files.get(id); + let lst = SyntaxNode::new_root(f.lst.read().unwrap().clone()); + self.local_files.insert(p, lst.clone()); + lst + } + } + + pub fn resolve_loc>(&mut self, loc: Loc) -> N { + let f = self.get_tree(loc.file()); + + loc.syntax().to_node(&f) + } + + pub fn send_tasks(&self, task: Task, dependencies: Vec) { + let blocker = WaitBlocker::new(); + for dep_task in dependencies { + self.tx + .send(Job::Awaited(blocker.clone(), dep_task)) + .unwrap(); + } + self.tx.send(Job::WaitingFor(blocker, task)).unwrap(); + } } impl Worker { - fn new(id: usize, files: Files, reg: Registry, sender: JobSender, rx: Receiver) -> Self { - let ctx = WorkerCtx { - errors: Vec::new(), - files, - reg, - tx: sender, - }; - + fn new(id: usize, files: Files, reg: Registry, sender: Sender, rx: Receiver) -> Self { let thread_handle = thread::spawn(move || { - for msg in rx { - match msg { - Job::ParseFileMod { file, decl } => todo!(), - Job::OpenFile(path) => todo!(), + let ctx = WorkerCtx { + errors: Vec::new(), + local_files: HashMap::new(), + files, + reg, + tx: sender, + }; + + for job in &rx { + // if matches!(job, Job::WithCond(_, _)) { + + // } + match job { + Job::WaitingFor(blocker, task) => { + if blocker.is_ready() { + Self::do_task(&ctx, task, None) + } else if rx.is_empty() { + if let Some(blocker) = + blocker.wait_for(std::time::Duration::from_millis(50)) + { + ctx.tx.send(Job::WaitingFor(blocker, task)).unwrap(); + } else { + Self::do_task(&ctx, task, None) + } + } else { + ctx.tx.send(Job::WaitingFor(blocker, task)).unwrap(); + } + } + Job::Awaited(blocker, task) => { + Self::do_task(&ctx, task, Some(blocker.clone())); + drop(blocker) + } } + + // if let Job::WithCond(blocker, job_inner) = job_msg { + // if blocker.is_ready() { + // job = Box::::into_inner(job_inner); + // } else if rx.is_empty() { + // if let Some(blocker) = + // blocker.wait_for(std::time::Duration::from_millis(50)) + // { + // job = Job::WithCond(blocker, job_inner); + // } else { + // job = Box::::into_inner(job_inner); + // } + // } else { + // job = Job::WithCond(blocker, job_inner); + // } + // } else { + // job = job_msg; + // } + + // match job { + // Job::ParseFileMod { file, decl } => todo!(), + // Job::OpenFile(path) => todo!(), + // Job::WithCond(blocker, job) => { + // ctx.tx.send(Job::WithCond(blocker, job)).unwrap() + // } + // } } }); @@ -79,4 +190,26 @@ impl Worker { thread: thread_handle, } } + + fn do_task(ctx: &WorkerCtx, task: Task, blocker: Option) { + match task { + Task::ParseMod(_, _, _) => todo!(), + Task::ParseFileMod0 { file, decl, path } => todo!(), + Task::ParseFileMod1 { + file, + decl, + path, + ret, + } => todo!(), + Task::CompleteMod { + ret_self, + decl, + body, + path, + child_mods, + blocker, + } => todo!(), + Task::OpenFile(_) => todo!(), + } + } } diff --git a/crates/lang/src/world/world_creation_pool/wait_blocker.rs b/crates/lang/src/world/world_creation_pool/wait_blocker.rs new file mode 100644 index 0000000..2d23d8a --- /dev/null +++ b/crates/lang/src/world/world_creation_pool/wait_blocker.rs @@ -0,0 +1,87 @@ +use std::{ + sync::{Arc, Condvar, Mutex}, + time::Duration, +}; + +/// like a WaitGroup from crossbeam, but can also just check if it's the last one +pub struct WaitBlocker { + inner: Arc, +} + +struct Inner { + waiting: Mutex, + cvar: Condvar, +} + +impl WaitBlocker { + pub fn new() -> Self { + Self { + inner: Arc::new(Inner { + waiting: Mutex::new(1), + cvar: Condvar::new(), + }), + } + } + + pub fn wait(self) { + if *self.inner.waiting.lock().unwrap() == 1 { + return; + } + + let inner = self.inner.clone(); + drop(self); + + inner + .cvar + .wait_while(inner.waiting.lock().unwrap(), |w| *w > 0); + } + + pub fn wait_for(self, dur: Duration) -> Option { + if *self.inner.waiting.lock().unwrap() == 1 { + return None; + } + + let inner = self.inner.clone(); + drop(self); + + let (_, timeout_res) = inner + .cvar + .wait_timeout_while(inner.waiting.lock().unwrap(), dur, |w| *w > 0) + .unwrap(); + + if timeout_res.timed_out() { + None + } else { + { + let mut w = inner.waiting.lock().unwrap(); + *w += 1; + } + Some(Self { inner }) + } + } + + pub fn is_ready(&self) -> bool { + *self.inner.waiting.lock().unwrap() == 1 + } +} + +impl Clone for WaitBlocker { + fn clone(&self) -> Self { + let mut w = self.inner.waiting.lock().unwrap(); + *w += 1; + drop(w); + Self { + inner: self.inner.clone(), + } + } +} + +impl Drop for WaitBlocker { + fn drop(&mut self) { + let mut w = self.inner.waiting.lock().unwrap(); + *w -= 1; + if *w == 0 { + self.inner.cvar.notify_all() + } + } +} -- 2.46.0 From eb7806572b0522a0e51bc19793f9656f80c24f13 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sun, 23 Jun 2024 20:53:05 +0200 Subject: [PATCH 64/67] lang: remove this attempt --- Cargo.lock | 339 ------------------ crates/lang/Cargo.toml | 4 - crates/lang/src/ast.rs | 72 ---- crates/lang/src/ast/error.rs | 30 -- crates/lang/src/ast/mod_tree.rs | 281 --------------- crates/lang/src/ast/namespace.rs | 103 ------ crates/lang/src/ast/nodes.rs | 77 ---- crates/lang/src/ast/path.rs | 94 ----- crates/lang/src/ast/source_file.rs | 81 ----- crates/lang/src/lib.rs | 2 - crates/lang/src/main.rs | 5 +- crates/lang/src/world.rs | 55 --- crates/lang/src/world/error.rs | 17 - crates/lang/src/world/files.rs | 131 ------- crates/lang/src/world/modules.rs | 175 --------- crates/lang/src/world/nodes.rs | 77 ---- crates/lang/src/world/registry.rs | 41 --- crates/lang/src/world/world_creation_pool.rs | 215 ----------- .../world/world_creation_pool/wait_blocker.rs | 87 ----- 19 files changed, 1 insertion(+), 1885 deletions(-) delete mode 100644 crates/lang/src/ast.rs delete mode 100644 crates/lang/src/ast/error.rs delete mode 100644 crates/lang/src/ast/mod_tree.rs delete mode 100644 crates/lang/src/ast/namespace.rs delete mode 100644 crates/lang/src/ast/nodes.rs delete mode 100644 crates/lang/src/ast/path.rs delete mode 100644 crates/lang/src/ast/source_file.rs delete mode 100644 crates/lang/src/world.rs delete mode 100644 crates/lang/src/world/error.rs delete mode 100644 crates/lang/src/world/files.rs delete mode 100644 crates/lang/src/world/modules.rs delete mode 100644 crates/lang/src/world/nodes.rs delete mode 100644 crates/lang/src/world/registry.rs delete mode 100644 crates/lang/src/world/world_creation_pool.rs delete mode 100644 crates/lang/src/world/world_creation_pool/wait_blocker.rs diff --git a/Cargo.lock b/Cargo.lock index 92c691e..fff075e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,133 +82,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "async-channel" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" -dependencies = [ - "async-lock", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-signal" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" version = "1.1.0" @@ -248,19 +121,6 @@ dependencies = [ "serde", ] -[[package]] -name = "blocking" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "bytemuck" version = "1.14.0" @@ -331,15 +191,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "countme" version = "3.0.1" @@ -515,12 +366,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" -[[package]] -name = "easy-parallel" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2afbb9b0aef60e4f0d2b18129b6c0dff035a6f7dbbd17c2f38c1432102ee223c" - [[package]] name = "ego-tree" version = "0.6.2" @@ -560,16 +405,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "eval" version = "0.1.0" @@ -580,27 +415,6 @@ dependencies = [ "serde", ] -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "exr" version = "1.71.0" @@ -617,12 +431,6 @@ dependencies = [ "zune-inflate", ] -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - [[package]] name = "fdeflate" version = "0.3.3" @@ -663,31 +471,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "getrandom" version = "0.2.12" @@ -827,18 +610,14 @@ dependencies = [ "crossbeam", "dashmap", "drop_bomb", - "easy-parallel", "ego-tree", "enumset", - "futures-lite", "indexmap", "indoc", "logos", - "num_cpus", "owo-colors", "petgraph", "rowan", - "smol", ] [[package]] @@ -870,12 +649,6 @@ dependencies = [ "redox_syscall 0.4.1", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - [[package]] name = "lock_api" version = "0.4.11" @@ -974,16 +747,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_threads" version = "0.1.6" @@ -1014,12 +777,6 @@ dependencies = [ "supports-color", ] -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - [[package]] name = "parking_lot_core" version = "0.9.10" @@ -1085,23 +842,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "piper" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - [[package]] name = "png" version = "0.17.10" @@ -1115,21 +855,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "tracing", - "windows-sys 0.52.0", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1274,19 +999,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "ryu" version = "1.0.16" @@ -1330,15 +1042,6 @@ dependencies = [ "serde", ] -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - [[package]] name = "simd-adler32" version = "0.3.7" @@ -1351,38 +1054,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" -[[package]] -name = "smol" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e635339259e51ef85ac7aa29a1cd991b957047507288697a690e80ab97d07cad" -dependencies = [ - "async-channel", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", - "async-process", - "blocking", - "futures-lite", -] - [[package]] name = "spin" version = "0.9.8" @@ -1486,22 +1163,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" - [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index f50073a..de54f2b 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -16,13 +16,9 @@ rowan = "0.15.15" drop_bomb = "0.1.5" enumset = "1.1.3" indoc = "2" -num_cpus = "1.16" dashmap = "5.5.3" crossbeam = "0.8.4" owo-colors = {version = "4", features = ["supports-colors"]} -smol = "2" -futures-lite = "2.3" -easy-parallel = "3.3.1" [lints] workspace = true diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs deleted file mode 100644 index 0c63620..0000000 --- a/crates/lang/src/ast.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::{collections::HashMap, path::PathBuf}; - -use rowan::ast::{AstNode, AstPtr}; - -use self::{ - error::{Error, WorldCreationError}, - mod_tree::ModuleTree, - namespace::Registry, - source_file::SourceFile, -}; - -mod error; -mod mod_tree; -mod namespace; -mod nodes; -mod path; -mod source_file; - -#[derive(Debug)] -struct Loc { - file: PathBuf, - syntax_el: AstPtr, -} - -impl Loc { - pub fn new(file: PathBuf, syntax_el: &T) -> Self { - Self { - file, - syntax_el: AstPtr::new(syntax_el), - } - } - - pub fn file(&self) -> &PathBuf { - &self.file - } -} - -pub struct World { - entry_point: PathBuf, - files: HashMap, - errors: Vec, - module_tree: ModuleTree, - registry: Registry, -} - -impl World { - pub fn new(entry_point: PathBuf) -> Result { - let entry_point = entry_point.canonicalize().unwrap(); - let source = match std::fs::read_to_string(&entry_point) { - Ok(f) => f, - Err(e) => return Err(WorldCreationError::FailedToOpenEntryPoint(entry_point, e)), - }; - - let (src, mut errors) = SourceFile::parse_from(&entry_point, source); - - let (module_tree, mut files, new_errors, registry) = - ModuleTree::parse_from_main(&entry_point, &src); - errors.extend(new_errors); - module_tree.print_tree(&src.tree()); - dbg!(&errors); - - let _ = files.insert(entry_point.clone(), src); - - Ok(Self { - files, - entry_point, - errors, - module_tree, - registry, - }) - } -} diff --git a/crates/lang/src/ast/error.rs b/crates/lang/src/ast/error.rs deleted file mode 100644 index 352e831..0000000 --- a/crates/lang/src/ast/error.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::{fmt::Display, path::PathBuf}; - -use crate::lst_parser::error::SyntaxError; - -#[derive(Debug)] -pub enum Error { - Syntax(PathBuf, SyntaxError), - FailedToOpenFileMod(PathBuf, std::io::Error), -} - -impl Error { - pub fn from_syntax(file: PathBuf, e: SyntaxError) -> Self { - Self::Syntax(file, e) - } -} - -#[derive(Debug)] -pub enum WorldCreationError { - FailedToOpenEntryPoint(PathBuf, std::io::Error), -} - -impl Display for WorldCreationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - WorldCreationError::FailedToOpenEntryPoint(entry_path, e) => { - write!(f, "failed to open entry_point '{entry_path:?}': {e}") - } - } - } -} diff --git a/crates/lang/src/ast/mod_tree.rs b/crates/lang/src/ast/mod_tree.rs deleted file mode 100644 index 2e5e269..0000000 --- a/crates/lang/src/ast/mod_tree.rs +++ /dev/null @@ -1,281 +0,0 @@ -use std::{ - cell::RefCell, - collections::HashMap, - path::{Path, PathBuf}, - rc::Rc, - sync::Arc, -}; - -use rowan::ast::{AstNode, SyntaxNodePtr}; - -use crate::{lst_parser::syntax_kind::SyntaxKind, Lang, SyntaxNode}; - -use super::{ - error::Error, - namespace::Registry, - nodes::{self, Mod, ModBody, ModName, Root}, - path::{ItemPath, OwnedItemPath}, - source_file::SourceFile, - Loc, -}; - -pub struct ModuleTree { - modules: Rc>>, -} - -#[derive(Clone)] -struct ModuleParsingContext { - files: Rc>>, - errors: Rc>>, - reg: Rc>, - proj_dir: Rc, - cur_path: Vec, - cur_file: PathBuf, -} - -impl ModuleParsingContext { - fn new(entry_path: &Path) -> Self { - let proj_dir = entry_path.parent().unwrap().to_owned(); - Self { - files: Rc::new(RefCell::new(HashMap::new())), - errors: Rc::new(RefCell::new(Vec::new())), - reg: Rc::new(RefCell::new(Registry::new())), - proj_dir: Rc::new(entry_path.to_owned()), - cur_path: Vec::new(), - cur_file: entry_path.to_owned(), - } - } - - fn entry_path(&self) -> PathBuf { - self.proj_dir.to_path_buf() - } - - fn make_mod_file_path(&self, mod_path: &[String], mod_name: &str) -> PathBuf { - let mut entry_path = self.entry_path(); - for el in mod_path { - entry_path.push(format!("{el}/")); - } - entry_path.push(format!("{mod_name}.owo")); - entry_path - } - - fn push_cur_path(&mut self, name_to_add: String) { - self.cur_path.push(name_to_add); - } - - fn set_cur_file(&mut self, new: PathBuf) { - self.cur_file = new; - } - - fn push_errs(&self, errs: Vec) { - self.errors.borrow_mut().extend(errs); - } - - fn parse_child_modules( - &self, - node: &SyntaxNode, - name: Option, - ) -> HashMap> { - let mut ctx = self.clone(); - if let Some(name) = name { - ctx.push_cur_path(name) - } - node.children() - .filter_map(Mod::cast) - .filter_map(|m| match Module::parse_mod(m, self.clone()) { - Ok(module) => Some((module.name(), module)), - Err(error) => { - self.errors.borrow_mut().push(error); - None - } - }) - .collect::>>() - } - - fn dissolve(self) -> (HashMap, Vec, Registry) { - let Self { - files, - errors, - mut reg, - .. - } = self; - (files.take(), errors.take(), (&*reg).take()) - } -} - -impl ModuleTree { - pub fn parse_from_main( - path: &PathBuf, - main_file: &SourceFile, - ) -> (Self, HashMap, Vec, Registry) { - let entry_path = path.parent().unwrap().to_owned(); - let ctx = ModuleParsingContext::new(&entry_path); - - let modules = Rc::new(ctx.parse_child_modules(main_file.tree().syntax(), None)); - - let (files, errors, reg) = ctx.dissolve(); - (Self { modules }, files, errors, reg) - } - - pub fn print_tree(&self, lst: &Root) { - let name = "main"; - print_tree(&name, self.modules.clone(), 0) - } -} - -#[derive(Debug)] -pub struct Module { - path: Vec, - name: String, - kind: ModuleKind, - children: Rc>>, - parent: Option>, - body: SyntaxNodePtr, -} - -impl Module { - fn parse_mod(module: Mod, mut ctx: ModuleParsingContext) -> Result, Error> { - let children = module - .syntax() - .children() - // .map(|n| n.kind()) - .collect::>(); - - let r; - if children.len() == 1 { - let name = &children[0]; - assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); - r = Self::parse_file_mod( - name.text().to_string(), - ctx.clone(), - Loc::new(ctx.cur_file.clone(), &module), - ); - } else if children.len() == 2 { - let name = &children[0]; - assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); - let body = &children[1]; - assert_eq!(body.kind(), SyntaxKind::MODULE_BODY); - r = Ok(Self::parse_inline_mod(module, ctx.clone())); - } else { - unreachable!() - } - r.map(|module| { - let rc = Rc::new(module); - ctx.reg.borrow_mut().insert_mod(rc.path(), rc.clone()); - rc - }) - } - - fn parse_file_mod( - name: String, - mut ctx: ModuleParsingContext, - decl: Loc, - ) -> Result { - ctx.set_cur_file(ctx.make_mod_file_path(&ctx.cur_path, &name)); - - let source = match std::fs::read_to_string(&ctx.cur_file) { - Ok(f) => f, - Err(e) => return Err(Error::FailedToOpenFileMod(ctx.cur_file, e)), - }; - - let (source_file, file_errors) = SourceFile::parse_from(&ctx.cur_file, source); - - let old_path = ctx.cur_path.clone(); - ctx.push_cur_path(name.clone()); - ctx.push_errs(file_errors); - - let children = - Rc::new(ctx.parse_child_modules(source_file.tree().syntax(), Some(name.clone()))); - let body = SyntaxNodePtr::new(source_file.tree().syntax()); - - ctx.files - .borrow_mut() - .insert(ctx.cur_file.clone(), source_file); - - Ok(Self { - path: old_path, - name, - kind: ModuleKind::File { - declaration: decl, - file_id: ctx.cur_file.clone(), - }, - children, - parent: None, - body, - }) - } - - fn parse_inline_mod(module: Mod, mut ctx: ModuleParsingContext) -> Self { - let mut children = module.syntax().children().collect::>(); - - let body = ModBody::cast(children.pop().unwrap()).unwrap(); - let name = ModName::cast(children.pop().unwrap()) - .unwrap() - .syntax() - .text() - .to_string(); - - let old_path = ctx.cur_path.clone(); - ctx.push_cur_path(name.clone()); - let children = Rc::new(ctx.parse_child_modules(body.syntax(), Some(name.clone()))); - - Self { - name, - kind: ModuleKind::Inline(Loc::new(ctx.cur_file.to_owned(), &module)), - children, - parent: None, - path: old_path, - body: SyntaxNodePtr::new(body.syntax()), - } - } - - pub fn path(&self) -> ItemPath { - let mut p = self.path.clone(); - p.push(self.name()); - let mut r_p = ItemPath::new(); - for i in p { - r_p = r_p.push(i); - } - r_p - } - - pub fn name(&self) -> String { - // self.name.to_node(lst.syntax()).syntax().text().to_string() - self.name.clone() - } - - pub fn body(&self, files: &HashMap) -> SyntaxNode { - match &self.kind { - ModuleKind::Inline(l) => { - let file = files.get(l.file()).unwrap(); - self.body.to_node(file.tree().syntax()) - } - ModuleKind::File { file_id, .. } => files.get(file_id).unwrap().tree().syntax().clone(), - } - } -} - -fn print_tree(name: &str, children: Rc>>, level: u32) { - const INDENT_STR: &str = " "; - - for _ in 0..level { - print!("{}", INDENT_STR); - } - print!("{name}\n"); - - // for (name, module) in children.iter() { - // ; - // } - children - .iter() - .for_each(|(name, m)| print_tree(name, m.children.clone(), level + 1)) -} - -#[derive(Debug)] -enum ModuleKind { - Inline(Loc), - File { - declaration: Loc, - file_id: PathBuf, - }, -} diff --git a/crates/lang/src/ast/namespace.rs b/crates/lang/src/ast/namespace.rs deleted file mode 100644 index 014a6c8..0000000 --- a/crates/lang/src/ast/namespace.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::{ - cell::RefCell, - collections::{HashMap, HashSet}, - rc::Rc, -}; - -use super::{ - mod_tree::Module, - nodes, - path::{ItemPath, OwnedItemPath}, -}; - -// requires mappings: -// names -> sets of references to individual objects -// paths -> individual objects -// glob paths -> sets of object refs -// (OPT/LATER) names (fuzzy) ~> sets of objects -#[derive(Debug)] -pub struct Registry { - defs: Vec>, - mods: Vec>, - names: HashMap>, - paths: HashMap, -} - -impl Registry { - pub fn new() -> Self { - Self { - defs: Vec::new(), - mods: Vec::new(), - names: HashMap::new(), - paths: HashMap::new(), - } - } - - // TODO: rewrite. has lots of flaws - pub fn insert_mod(&mut self, path: ItemPath<'_>, module: Rc) -> Option { - let idx = self.mods.len(); - let Some(name) = path.name() else { return None }; - self.mods.push(StoreObj::new(&name, Mod { module })); - - if let Some(set) = self.names.get_mut(&name) { - set.insert(RegistryIdx::Mod(idx)); - } else { - self.names - .insert(name, HashSet::from([RegistryIdx::Mod(idx)])); - } - - if self - .paths - .get(&path.clone().into()) - .is_some_and(|other_idx| *other_idx != RegistryIdx::Mod(idx)) - { - return None; - } else { - self.paths.insert(path.into(), RegistryIdx::Mod(idx)) - } - } -} - -impl Default for Registry { - fn default() -> Self { - Self::new() - } -} - -#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)] -pub enum RegistryIdx { - Def(usize), - Mod(usize), -} - -#[derive(Debug)] -struct StoreObj { - inner: Rc>, -} - -impl StoreObj { - pub fn new(name: impl ToString, item: T) -> Self { - Self { - inner: Rc::new(InnerStoreObj { - name: name.to_string(), - obj: item, - }), - } - } -} - -#[derive(Debug)] -struct InnerStoreObj { - name: String, - obj: T, -} - -#[derive(Debug)] -struct Mod { - module: Rc, -} - -#[derive(Debug)] -struct Def { - node: nodes::Def, -} diff --git a/crates/lang/src/ast/nodes.rs b/crates/lang/src/ast/nodes.rs deleted file mode 100644 index 744596e..0000000 --- a/crates/lang/src/ast/nodes.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::lst_parser::syntax_kind::SyntaxKind::*; -use crate::SyntaxNode; -use rowan::Language; -macro_rules! ast_nodes { - ($($ast:ident, $kind:ident);+) => { - $( - #[derive(Debug, PartialEq, Eq, Hash)] - #[repr(transparent)] - pub struct $ast(SyntaxNode); - impl rowan::ast::AstNode for $ast { - type Language = crate::Lang; - - fn can_cast(kind: ::Kind) -> bool { - kind == $kind - } - - fn cast(node: SyntaxNode) -> Option { - if node.kind() == $kind { - Some(Self(node)) - } else { - None - } - } - - fn syntax(&self) -> &SyntaxNode { - &self.0 - } - } - )+ - }; -} -ast_nodes!( - Def, DEF; - DefName, DEF_NAME; - DefBody, DEF_BODY; - - Mod, MODULE; - ModName, MODULE_NAME; - ModBody, MODULE_BODY; - - Use, USE; - UsePat, USE_PAT; - PatItem, PAT_ITEM; - PatGlob, PAT_GLOB; - PatGroup, PAT_GROUP; - - Literal, LITERAL; - IntLit, INT_NUM; - FloatLit, FLOAT_NUM; - StringLit, STRING; - - Matrix, MATRIX; - MatrixRow, MAT_ROW; - Vector, VEC; - List, LIST; - CollectionItem, COLLECTION_ITEM; - - ParenthesizedExpr, PARENTHESIZED_EXPR; - Expression, EXPR; - - Pipeline, PIPELINE; - - Instruction, INSTR; - InstructionName, INSTR_NAME; - InstructionParams, INSTR_PARAMS; - - AttributeSet, ATTR_SET; - Attribute, ATTR; - AttributeName, ATTR_NAME; - AttributeValue, ATTR_VALUE; - - ParseError, PARSE_ERR; - LexError, LEX_ERR; - - Root, ROOT; - Eof, EOF -); diff --git a/crates/lang/src/ast/path.rs b/crates/lang/src/ast/path.rs deleted file mode 100644 index a15f141..0000000 --- a/crates/lang/src/ast/path.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::borrow::Cow; - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct ItemPath<'a> { - items: Vec>, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct OwnedItemPath { - items: Vec, -} - -impl OwnedItemPath { - pub fn name(&self) -> Option { - self.items.last().cloned() - } -} - -impl From> for OwnedItemPath { - fn from(value: ItemPath<'_>) -> Self { - Self { - items: value - .items - .into_iter() - .map(|v| v.into_owned()) - .collect::>(), - } - } -} - -impl<'a> ItemPath<'a> { - pub fn new() -> Self { - Self { items: Vec::new() } - } - - pub fn from_slices(slice: &[&'a str]) -> Self { - Self { - items: slice.into_iter().map(|i| (*i).into()).collect::>(), - } - } - - pub fn push(mut self, item: impl Into>) -> Self { - self.items.push(item.into()); - self - } - - pub fn name(&self) -> Option { - self.items.last().cloned().map(|item| item.into_owned()) - } -} - -impl<'a> TryFrom> for ItemPath<'a> { - type Error = (); - - fn try_from(value: Pattern<'a>) -> Result { - let mut items = Vec::new(); - - for i in value.items { - match i { - PatternEl::Name(n) => items.push(n), - PatternEl::Glob => return Err(()), - } - } - - Ok(Self { items }) - } -} - -#[derive(Clone, Debug)] -pub struct Pattern<'a> { - items: Vec>, -} - -impl<'a> Pattern<'a> { - pub fn new() -> Self { - Self { items: Vec::new() } - } - - pub fn add_name(mut self, name: &'a str) -> Self { - self.items.push(PatternEl::Name(name.into())); - self - } - - pub fn add_glob(mut self) -> Self { - self.items.push(PatternEl::Glob); - self - } -} - -#[derive(Clone, Debug)] -enum PatternEl<'a> { - Name(Cow<'a, str>), - Glob, -} diff --git a/crates/lang/src/ast/source_file.rs b/crates/lang/src/ast/source_file.rs deleted file mode 100644 index 912decd..0000000 --- a/crates/lang/src/ast/source_file.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::path::{Path, PathBuf}; - -use rowan::ast::{AstNode, AstPtr}; - -use crate::lst_parser::{ - grammar::source_file, - input::Input, - output::Output, - syntax_kind::{self, SyntaxKind}, - Parser, -}; - -use super::{ - error::Error, - nodes::{Def, Mod, Root, Use}, -}; - -#[derive(Debug)] -pub struct SourceFile { - lst: Root, - modules: Vec>, - defs: Vec>, - uses: Vec>, -} - -impl SourceFile { - pub fn parse_from(path: &Path, source_text: String) -> (Self, Vec) { - let toks = syntax_kind::lex(&source_text); - let input = Input::new(&toks); - let mut parser = Parser::new(input); - - source_file(&mut parser); - - let parser_out = parser.finish(); - let out = Output::from_parser_output(toks, parser_out); - - let lst = out.syntax(); - - let mut modules = Vec::new(); - let mut defs = Vec::new(); - let mut uses = Vec::new(); - - for c in lst.children() { - match c.kind() { - SyntaxKind::MODULE => modules.push(AstPtr::new(&Mod::cast(c).unwrap())), - SyntaxKind::DEF => defs.push(AstPtr::new(&Def::cast(c).unwrap())), - SyntaxKind::USE => uses.push(AstPtr::new(&Use::cast(c).unwrap())), - _ => {} - } - } - - ( - Self { - lst: Root::cast(lst).unwrap(), - modules, - defs, - uses, - }, - out.errors() - .into_iter() - .map(|err| Error::from_syntax(path.to_owned(), err)) - .collect(), - ) - } - - pub fn tree(&self) -> &Root { - &self.lst - } - - pub fn top_level_modules(&self) -> Vec> { - self.modules.clone() - } - - pub fn top_level_defs(&self) -> Vec> { - self.defs.clone() - } - - pub fn top_level_uses(&self) -> Vec> { - self.uses.clone() - } -} diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index f48385e..a45e202 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -2,9 +2,7 @@ use crate::lst_parser::syntax_kind::SyntaxKind; -pub mod ast; pub mod lst_parser; -pub mod world; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Lang {} diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index c0c04d5..e9c0257 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -1,10 +1,7 @@ use clap::Parser; use std::{fs, path::PathBuf}; -use lang::{ - ast::World, - lst_parser::{self, grammar, input, output::Output, syntax_kind}, -}; +use lang::lst_parser::{self, grammar, input, output::Output, syntax_kind}; #[derive(Parser)] struct Args { diff --git a/crates/lang/src/world.rs b/crates/lang/src/world.rs deleted file mode 100644 index 2f32cab..0000000 --- a/crates/lang/src/world.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::{ - fmt::Display, - path::{Path, PathBuf}, - sync::{Arc, Mutex}, -}; - -use crate::world::world_creation_pool::WorldCreationPool; - -use easy_parallel::Parallel; -use futures_lite::future; -use smol::{ - channel::{unbounded, Sender}, - Executor, -}; - -use self::{error::Error, files::Files, registry::Registry}; - -pub mod error; -pub mod files; -pub mod modules; -pub mod nodes; -pub mod registry; -mod world_creation_pool; - -pub struct World {} - -impl World { - pub fn new(entry_point: &Path) -> Result { - let files = Files::new(); - let reg = Registry::new(); - - let (entry_point_id, errors) = files.add_and_parse(entry_point).map_err(|e| { - WorldCreationError::FailedToOpenEntryPoint(entry_point.to_path_buf(), e) - })?; - - let pool = WorldCreationPool::new(files, reg); - - todo!() - } -} - -#[derive(Debug)] -pub enum WorldCreationError { - FailedToOpenEntryPoint(PathBuf, std::io::Error), -} - -impl Display for WorldCreationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - WorldCreationError::FailedToOpenEntryPoint(entry_path, e) => { - write!(f, "failed to open entry_point '{entry_path:?}': {e}") - } - } - } -} diff --git a/crates/lang/src/world/error.rs b/crates/lang/src/world/error.rs deleted file mode 100644 index 6232a68..0000000 --- a/crates/lang/src/world/error.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::path::PathBuf; - -use crate::lst_parser::error::SyntaxError; - -use super::files::FileId; - -#[derive(Debug)] -pub enum Error { - Syntax(FileId, SyntaxError), - FailedToOpenFileMod(PathBuf, std::io::Error), -} - -impl Error { - pub fn from_syntax(file: FileId, e: SyntaxError) -> Self { - Self::Syntax(file, e) - } -} diff --git a/crates/lang/src/world/files.rs b/crates/lang/src/world/files.rs deleted file mode 100644 index b4a1309..0000000 --- a/crates/lang/src/world/files.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::{ - clone, fs, - path::{Path, PathBuf}, - sync::{Arc, Mutex, RwLock}, -}; - -use dashmap::DashMap; -use rowan::ast::{AstNode, AstPtr}; - -use crate::{ - lst_parser::{ - error::SyntaxError, grammar::source_file, input::Input, output::Output, syntax_kind, Parser, - }, - Lang, -}; - -use super::{error::Error, modules::Module, nodes}; - -#[derive(Clone)] -pub struct Loc> { - file: FileId, - syntax: AstPtr, -} - -impl> Loc { - pub fn new(node: N, file: FileId) -> Self { - Self { - file, - syntax: AstPtr::new(&node), - } - } - - pub fn file(&self) -> FileId { - self.file - } - - pub fn syntax(&self) -> AstPtr { - self.syntax.clone() - } -} - -/// global file store -/// contains all known files etc. -#[derive(Clone)] -pub struct Files { - paths: Arc>>, - store: Arc>>, -} - -impl Files { - pub fn new() -> Self { - Self { - paths: Arc::new(Mutex::new(Vec::new())), - store: Arc::new(DashMap::new()), - } - } - - pub fn add_and_parse(&self, path: &Path) -> Result<(FileId, Vec), std::io::Error> { - let (file, errors) = SourceFile::read_and_parse(&path)?; - - // add file to paths and unlock again - let id = { - let path = path.clone(); - let mut paths = self.paths.lock().unwrap(); - let r = paths.len(); - paths.push(path.to_path_buf()); - FileId(r) - }; - - let _ = self.store.insert(path.to_path_buf(), Arc::new(file)); - - Ok(( - id, - errors - .into_iter() - .map(|e| Error::from_syntax(id, e)) - .collect(), - )) - } - - pub fn get(&self, id: FileId) -> Arc { - let path = { - let paths = self.paths.lock().unwrap(); - paths[id.0].clone() - }; - - self.store.get(&path).unwrap().clone() - } - - pub fn get_path(&self, id: FileId) -> PathBuf { - let paths = self.paths.lock().unwrap(); - paths[id.0].clone() - } - - pub fn path_store(&self) -> Arc>> { - self.paths.clone() - } -} - -pub struct SourceFile { - pub lst: RwLock, - root_module: Option>, -} - -impl SourceFile { - fn read_and_parse(path: &Path) -> Result<(Self, Vec), std::io::Error> { - let source_text = fs::read_to_string(&path)?; - - let toks = syntax_kind::lex(&source_text); - let input = Input::new(&toks); - let mut parser = Parser::new(input); - - source_file(&mut parser); - - let events = parser.finish(); - let out = Output::from_parser_output(toks, events); - // let lst = out.syntax(); - let (lst, errors) = out.dissolve(); - - Ok(( - Self { - lst: RwLock::new(lst), - root_module: None, - }, - errors, - )) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct FileId(pub usize); diff --git a/crates/lang/src/world/modules.rs b/crates/lang/src/world/modules.rs deleted file mode 100644 index 676553b..0000000 --- a/crates/lang/src/world/modules.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{ - string::String, - sync::{Arc, Mutex}, -}; - -use dashmap::{DashMap, DashSet}; -use rowan::ast::{AstNode, AstPtr}; - -use crate::SyntaxNode; - -use super::{ - files::{FileId, Loc}, - nodes, - registry::ItemPath, - world_creation_pool::{wait_blocker::WaitBlocker, Task, WorkerCtx}, -}; - -pub struct Module { - /// is none if this is the root module of the entry point file - decl: Option>, - body: ModuleBody, - own_path: ItemPath, - child_modules: Arc>>, - child_defs: Arc>, -} - -impl Module { - pub fn parse_mod_body( - ctx: &WorkerCtx, - tree: SyntaxNode, - path: ItemPath, - file: FileId, - ) -> (DashMap>>>>, Vec) { - let children: Vec<(String, Arc>>>, nodes::Mod)> = tree - .children() - .filter_map(|c| nodes::Mod::cast(c)) - .map(|m| { - let name = nodes::ModName::cast(m.syntax().first_child().unwrap()) - .expect("ModName should always be first child of Mod"); - ( - name.syntax().text().to_string(), - Arc::new(Mutex::new(None)), - m, - ) - }) - .collect(); - let retv: DashMap>>>> = children - .iter() - .map(|(name, mod_r, _)| (name.to_owned(), mod_r.clone())) - .collect(); - let tasks = children - .into_iter() - .map(|(name, mod_r, loc)| { - let mut path = path.clone(); - path.push(name); - Task::ParseMod(Loc::new(loc, file), mod_r, path) - }) - .collect::>(); - - (retv, tasks) - } - pub fn parse_file0( - ctx: &mut WorkerCtx, - file: FileId, - path: ItemPath, - decl: Option>, - ) { - let tree = ctx.get_tree(file); - - let (retv, tasks) = Self::parse_mod_body(ctx, tree, path.clone(), file); - - ctx.send_tasks( - Task::ParseFileMod1 { - file, - decl, - ret: retv.into(), - path, - }, - tasks, - ) - } - - pub fn parse_mod( - ctx: &mut WorkerCtx, - loc: Loc, - path: ItemPath, - ret: Arc>>, - blocker: WaitBlocker, - ) { - let mod_decl = ctx.resolve_loc(loc); - let children = mod_decl.syntax().children().collect::>(); - - if children.len() == 1 { - // TODO: file mod - todo!() - } else if children.len() == 2 { - // inline mod - } - } - - pub fn parse_inline_mod( - ctx: &mut WorkerCtx, - decl: Loc, - path: ItemPath, - ret: Arc>>>, - blocker: WaitBlocker, - ) { - let mod_decl_children = ctx - .resolve_loc(decl.clone()) - .syntax() - .children() - .collect::>(); - - assert_eq!(mod_decl_children.len(), 2); - - let name = nodes::ModName::cast(mod_decl_children[0].clone()).unwrap(); - let body = nodes::ModBody::cast(mod_decl_children[1].clone()).unwrap(); - - let (retv, tasks) = - Self::parse_mod_body(ctx, body.syntax().clone(), path.clone(), decl.file()); - - ctx.send_tasks( - Task::CompleteMod { - ret_self: ret, - body: ModuleBody::Inline(Loc::new(body, decl.file())), - decl: Some(decl), - path, - child_mods: retv.into(), - blocker, - }, - tasks, - ) - } - - pub fn complete_mod( - ctx: &mut WorkerCtx, - decl: Option>, - ret_self: Arc>>>, - body: ModuleBody, - path: ItemPath, - child_mods: Arc>>>>>, - blocker: WaitBlocker, - ) { - assert!(child_mods - .iter() - .all(|item| item.value().lock().unwrap().is_some())); - - let module = Arc::new(Module { - decl, - body, - own_path: path, - child_modules: Arc::new( - Arc::<_>::unwrap_or_clone(child_mods) - .into_iter() - .map(|(name, module)| { - let module = module.lock().unwrap().take().unwrap(); - (name, module) - }) - .collect(), - ), - child_defs: Arc::default(), - }); - - let mut ret_self = ret_self.lock().unwrap(); - *ret_self = Some(module); - drop(blocker); - } -} - -struct Def; - -pub enum ModuleBody { - Inline(Loc), - File(FileId), -} diff --git a/crates/lang/src/world/nodes.rs b/crates/lang/src/world/nodes.rs deleted file mode 100644 index 303a3a1..0000000 --- a/crates/lang/src/world/nodes.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::lst_parser::syntax_kind::SyntaxKind::*; -use crate::SyntaxNode; -use rowan::Language; -macro_rules! ast_nodes { -($($ast:ident, $kind:ident);+) => { - $( - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - #[repr(transparent)] - pub struct $ast(SyntaxNode); - impl rowan::ast::AstNode for $ast { - type Language = crate::Lang; - - fn can_cast(kind: ::Kind) -> bool { - kind == $kind - } - - fn cast(node: SyntaxNode) -> Option { - if node.kind() == $kind { - Some(Self(node)) - } else { - None - } - } - - fn syntax(&self) -> &SyntaxNode { - &self.0 - } - } - )+ -}; -} -ast_nodes!( - Def, DEF; - DefName, DEF_NAME; - DefBody, DEF_BODY; - - Mod, MODULE; - ModName, MODULE_NAME; - ModBody, MODULE_BODY; - - Use, USE; - UsePat, USE_PAT; - PatItem, PAT_ITEM; - PatGlob, PAT_GLOB; - PatGroup, PAT_GROUP; - - Literal, LITERAL; - IntLit, INT_NUM; - FloatLit, FLOAT_NUM; - StringLit, STRING; - - Matrix, MATRIX; - MatrixRow, MAT_ROW; - Vector, VEC; - List, LIST; - CollectionItem, COLLECTION_ITEM; - - ParenthesizedExpr, PARENTHESIZED_EXPR; - Expression, EXPR; - - Pipeline, PIPELINE; - - Instruction, INSTR; - InstructionName, INSTR_NAME; - InstructionParams, INSTR_PARAMS; - - AttributeSet, ATTR_SET; - Attribute, ATTR; - AttributeName, ATTR_NAME; - AttributeValue, ATTR_VALUE; - - ParseError, PARSE_ERR; - LexError, LEX_ERR; - - Root, ROOT; - Eof, EOF -); diff --git a/crates/lang/src/world/registry.rs b/crates/lang/src/world/registry.rs deleted file mode 100644 index 791be15..0000000 --- a/crates/lang/src/world/registry.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::sync::{Arc, Mutex, RwLock}; - -use dashmap::{DashMap, DashSet}; - -use super::modules::Module; - -pub type ItemPath = Vec; - -#[derive(Clone)] -pub struct Registry(Arc); - -impl Registry { - pub fn new() -> Self { - Self(Arc::new(RegistryInner::new())) - } -} - -struct RegistryInner { - /// ids will refer to paths of modules - paths: RwLock>, - modules: DashMap>, - names: DashMap>, -} - -impl RegistryInner { - fn new() -> Self { - Self { - paths: RwLock::new(Vec::new()), - modules: DashMap::new(), - names: DashMap::new(), - } - } -} - -pub enum RegistryId { - /// refer to the modlue item itself, the name must be the same as the last item of the path - Module(usize), - /// refer to a def in a module - /// name must refer to name of child of module - Def(usize), -} diff --git a/crates/lang/src/world/world_creation_pool.rs b/crates/lang/src/world/world_creation_pool.rs deleted file mode 100644 index 651d7eb..0000000 --- a/crates/lang/src/world/world_creation_pool.rs +++ /dev/null @@ -1,215 +0,0 @@ -use std::{ - collections::HashMap, - path::PathBuf, - sync::{Arc, Mutex}, - thread, -}; - -use crossbeam::channel::{Receiver, Sender}; -use dashmap::DashMap; -use rowan::ast::{AstNode, AstPtr}; - -use crate::{Lang, SyntaxNode}; - -use self::wait_blocker::WaitBlocker; - -use super::{ - error::Error, - files::{FileId, Files, Loc}, - modules::{Module, ModuleBody}, - nodes, - registry::{ItemPath, Registry}, -}; - -pub mod wait_blocker; - -pub(super) struct WorldCreationPool { - workers: Vec, - tx: Sender, -} - -impl WorldCreationPool { - pub fn new(files: Files, reg: Registry) -> Self { - let mut workers = Vec::new(); - let (tx, rx) = crossbeam::channel::unbounded(); - - for i in 0..num_cpus::get() { - workers.push(Worker::new( - i, - files.clone(), - reg.clone(), - tx.clone(), - rx.clone(), - )) - } - - Self { workers, tx } - } -} - -enum Job { - WaitingFor(WaitBlocker, Task), - Awaited(WaitBlocker, Task), -} - -pub enum Task { - ParseMod(Loc, Arc>>>, ItemPath), - ParseFileMod0 { - file: FileId, - decl: Option>, - path: ItemPath, - }, - ParseFileMod1 { - file: FileId, - decl: Option>, - path: ItemPath, - ret: Arc>>>>>, - }, - CompleteMod { - ret_self: Arc>>>, - decl: Option>, - body: ModuleBody, - path: ItemPath, - child_mods: Arc>>>>>, - blocker: WaitBlocker, - }, - OpenFile(PathBuf), -} - -struct Worker { - id: usize, - // thead: thread::JoinHandle>, - thread: thread::JoinHandle<()>, -} - -pub struct WorkerCtx { - errors: Vec, - files: Files, - local_files: HashMap, - pub reg: Registry, - tx: Sender, -} - -impl WorkerCtx { - pub fn get_tree(&mut self, id: FileId) -> SyntaxNode { - let p = self.files.get_path(id); - if self.local_files.contains_key(&p) { - self.local_files[&p].clone() - } else { - let f = self.files.get(id); - let lst = SyntaxNode::new_root(f.lst.read().unwrap().clone()); - self.local_files.insert(p, lst.clone()); - lst - } - } - - pub fn resolve_loc>(&mut self, loc: Loc) -> N { - let f = self.get_tree(loc.file()); - - loc.syntax().to_node(&f) - } - - pub fn send_tasks(&self, task: Task, dependencies: Vec) { - let blocker = WaitBlocker::new(); - for dep_task in dependencies { - self.tx - .send(Job::Awaited(blocker.clone(), dep_task)) - .unwrap(); - } - self.tx.send(Job::WaitingFor(blocker, task)).unwrap(); - } -} - -impl Worker { - fn new(id: usize, files: Files, reg: Registry, sender: Sender, rx: Receiver) -> Self { - let thread_handle = thread::spawn(move || { - let ctx = WorkerCtx { - errors: Vec::new(), - local_files: HashMap::new(), - files, - reg, - tx: sender, - }; - - for job in &rx { - // if matches!(job, Job::WithCond(_, _)) { - - // } - match job { - Job::WaitingFor(blocker, task) => { - if blocker.is_ready() { - Self::do_task(&ctx, task, None) - } else if rx.is_empty() { - if let Some(blocker) = - blocker.wait_for(std::time::Duration::from_millis(50)) - { - ctx.tx.send(Job::WaitingFor(blocker, task)).unwrap(); - } else { - Self::do_task(&ctx, task, None) - } - } else { - ctx.tx.send(Job::WaitingFor(blocker, task)).unwrap(); - } - } - Job::Awaited(blocker, task) => { - Self::do_task(&ctx, task, Some(blocker.clone())); - drop(blocker) - } - } - - // if let Job::WithCond(blocker, job_inner) = job_msg { - // if blocker.is_ready() { - // job = Box::::into_inner(job_inner); - // } else if rx.is_empty() { - // if let Some(blocker) = - // blocker.wait_for(std::time::Duration::from_millis(50)) - // { - // job = Job::WithCond(blocker, job_inner); - // } else { - // job = Box::::into_inner(job_inner); - // } - // } else { - // job = Job::WithCond(blocker, job_inner); - // } - // } else { - // job = job_msg; - // } - - // match job { - // Job::ParseFileMod { file, decl } => todo!(), - // Job::OpenFile(path) => todo!(), - // Job::WithCond(blocker, job) => { - // ctx.tx.send(Job::WithCond(blocker, job)).unwrap() - // } - // } - } - }); - - Self { - id, - thread: thread_handle, - } - } - - fn do_task(ctx: &WorkerCtx, task: Task, blocker: Option) { - match task { - Task::ParseMod(_, _, _) => todo!(), - Task::ParseFileMod0 { file, decl, path } => todo!(), - Task::ParseFileMod1 { - file, - decl, - path, - ret, - } => todo!(), - Task::CompleteMod { - ret_self, - decl, - body, - path, - child_mods, - blocker, - } => todo!(), - Task::OpenFile(_) => todo!(), - } - } -} diff --git a/crates/lang/src/world/world_creation_pool/wait_blocker.rs b/crates/lang/src/world/world_creation_pool/wait_blocker.rs deleted file mode 100644 index 2d23d8a..0000000 --- a/crates/lang/src/world/world_creation_pool/wait_blocker.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::{ - sync::{Arc, Condvar, Mutex}, - time::Duration, -}; - -/// like a WaitGroup from crossbeam, but can also just check if it's the last one -pub struct WaitBlocker { - inner: Arc, -} - -struct Inner { - waiting: Mutex, - cvar: Condvar, -} - -impl WaitBlocker { - pub fn new() -> Self { - Self { - inner: Arc::new(Inner { - waiting: Mutex::new(1), - cvar: Condvar::new(), - }), - } - } - - pub fn wait(self) { - if *self.inner.waiting.lock().unwrap() == 1 { - return; - } - - let inner = self.inner.clone(); - drop(self); - - inner - .cvar - .wait_while(inner.waiting.lock().unwrap(), |w| *w > 0); - } - - pub fn wait_for(self, dur: Duration) -> Option { - if *self.inner.waiting.lock().unwrap() == 1 { - return None; - } - - let inner = self.inner.clone(); - drop(self); - - let (_, timeout_res) = inner - .cvar - .wait_timeout_while(inner.waiting.lock().unwrap(), dur, |w| *w > 0) - .unwrap(); - - if timeout_res.timed_out() { - None - } else { - { - let mut w = inner.waiting.lock().unwrap(); - *w += 1; - } - Some(Self { inner }) - } - } - - pub fn is_ready(&self) -> bool { - *self.inner.waiting.lock().unwrap() == 1 - } -} - -impl Clone for WaitBlocker { - fn clone(&self) -> Self { - let mut w = self.inner.waiting.lock().unwrap(); - *w += 1; - drop(w); - Self { - inner: self.inner.clone(), - } - } -} - -impl Drop for WaitBlocker { - fn drop(&mut self) { - let mut w = self.inner.waiting.lock().unwrap(); - *w -= 1; - if *w == 0 { - self.inner.cvar.notify_all() - } - } -} -- 2.46.0 From 3eee768ce1cd92a4f02c9105faef718b47bc9ba9 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Sat, 6 Jul 2024 21:57:42 +0200 Subject: [PATCH 65/67] lang: work on various things - work on new world - add file db - source_file parsing - locs - fix some test stuff --- Cargo.lock | 30 ++++++ crates/lang/Cargo.toml | 1 + crates/lang/src/ast.rs | 78 ++++++++++++++ crates/lang/src/lib.rs | 2 + crates/lang/src/lst_parser/grammar.rs | 2 +- crates/lang/src/lst_parser/output.rs | 11 +- crates/lang/src/world.rs | 14 +++ crates/lang/src/world/error.rs | 10 ++ crates/lang/src/world/files.rs | 56 ++++++++++ crates/lang/src/world/files/loc.rs | 29 ++++++ crates/lang/src/world/files/source_file.rs | 113 +++++++++++++++++++++ crates/svg-filters/src/main.rs | 63 +++++++++++- 12 files changed, 395 insertions(+), 14 deletions(-) create mode 100644 crates/lang/src/ast.rs create mode 100644 crates/lang/src/world.rs create mode 100644 crates/lang/src/world/error.rs create mode 100644 crates/lang/src/world/files.rs create mode 100644 crates/lang/src/world/files/loc.rs create mode 100644 crates/lang/src/world/files/source_file.rs diff --git a/Cargo.lock b/Cargo.lock index fff075e..4ff50ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -618,6 +618,7 @@ dependencies = [ "owo-colors", "petgraph", "rowan", + "strip-ansi-escapes", ] [[package]] @@ -1069,6 +1070,15 @@ dependencies = [ "lock_api", ] +[[package]] +name = "strip-ansi-escapes" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +dependencies = [ + "vte", +] + [[package]] name = "strsim" version = "0.10.0" @@ -1181,6 +1191,26 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index de54f2b..c7a2659 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -19,6 +19,7 @@ indoc = "2" dashmap = "5.5.3" crossbeam = "0.8.4" owo-colors = {version = "4", features = ["supports-colors"]} +strip-ansi-escapes = "0.2.0" [lints] workspace = true diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs new file mode 100644 index 0000000..42d160c --- /dev/null +++ b/crates/lang/src/ast.rs @@ -0,0 +1,78 @@ +use crate::lst_parser::syntax_kind::SyntaxKind::*; +use crate::SyntaxNode; +use rowan::Language; +macro_rules! ast_nodes { + ($($ast:ident, $kind:ident);+) => { + $( + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + #[repr(transparent)] + pub struct $ast(SyntaxNode); + impl rowan::ast::AstNode for $ast { + type Language = crate::Lang; + + fn can_cast(kind: ::Kind) -> bool { + kind == $kind + } + + fn cast(node: SyntaxNode) -> Option { + if node.kind() == $kind { + Some(Self(node)) + } else { + None + } + } + + fn syntax(&self) -> &SyntaxNode { + &self.0 + } + } + )+ + }; +} + +ast_nodes!( + Def, DEF; + DefName, DEF_NAME; + DefBody, DEF_BODY; + + Mod, MODULE; + ModName, MODULE_NAME; + ModBody, MODULE_BODY; + + Use, USE; + UsePat, USE_PAT; + PatItem, PAT_ITEM; + PatGlob, PAT_GLOB; + PatGroup, PAT_GROUP; + + Literal, LITERAL; + IntLit, INT_NUM; + FloatLit, FLOAT_NUM; + StringLit, STRING; + + Matrix, MATRIX; + MatrixRow, MAT_ROW; + Vector, VEC; + List, LIST; + CollectionItem, COLLECTION_ITEM; + + ParenthesizedExpr, PARENTHESIZED_EXPR; + Expression, EXPR; + + Pipeline, PIPELINE; + + Instruction, INSTR; + InstructionName, INSTR_NAME; + InstructionParams, INSTR_PARAMS; + + AttributeSet, ATTR_SET; + Attribute, ATTR; + AttributeName, ATTR_NAME; + AttributeValue, ATTR_VALUE; + + ParseError, PARSE_ERR; + LexError, LEX_ERR; + + Root, ROOT; + Eof, EOF +); diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index a45e202..f48385e 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -2,7 +2,9 @@ use crate::lst_parser::syntax_kind::SyntaxKind; +pub mod ast; pub mod lst_parser; +pub mod world; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Lang {} diff --git a/crates/lang/src/lst_parser/grammar.rs b/crates/lang/src/lst_parser/grammar.rs index 2db0824..001050e 100644 --- a/crates/lang/src/lst_parser/grammar.rs +++ b/crates/lang/src/lst_parser/grammar.rs @@ -33,6 +33,6 @@ fn check_parser(input: &str, parser_fn: fn(&mut Parser), output: &str) { let p_out = dbg!(parser.finish()); let o = Output::from_parser_output(toks, p_out); - let s = format!("{o:?}"); + let s = strip_ansi_escapes::strip_str(format!("{o:?}")); assert_eq!(&s, output); } diff --git a/crates/lang/src/lst_parser/output.rs b/crates/lang/src/lst_parser/output.rs index ce96fb5..78433b1 100644 --- a/crates/lang/src/lst_parser/output.rs +++ b/crates/lang/src/lst_parser/output.rs @@ -28,6 +28,7 @@ impl std::fmt::Debug for Output { } const INDENT_STR: &str = " "; +/// colored argument currently broken fn debug_print_green_node( node: NodeOrToken<&GreenNodeData, &GreenTokenData>, f: &mut dyn std::fmt::Write, @@ -39,12 +40,6 @@ fn debug_print_green_node( f.write_str(INDENT_STR)?; } - if !colored { - owo_colors::set_override(false); - } else { - owo_colors::set_override(true); - } - let r = match node { NodeOrToken::Node(n) => { let kind = Lang::kind_from_raw(node.kind()); @@ -103,10 +98,6 @@ fn debug_print_green_node( } }; - if !colored { - owo_colors::unset_override(); - } - r } diff --git a/crates/lang/src/world.rs b/crates/lang/src/world.rs new file mode 100644 index 0000000..2ce9997 --- /dev/null +++ b/crates/lang/src/world.rs @@ -0,0 +1,14 @@ +use std::path::Path; + +mod error; +mod files; + +struct World; + +impl World { + pub fn new(entry_point: &Path) -> Result { + todo!() + } +} + +enum WorldCreationError {} diff --git a/crates/lang/src/world/error.rs b/crates/lang/src/world/error.rs new file mode 100644 index 0000000..1cebf8d --- /dev/null +++ b/crates/lang/src/world/error.rs @@ -0,0 +1,10 @@ +use std::path::PathBuf; + +use crate::{ast::ParseError, lst_parser::error::SyntaxError}; + +use super::files::{FileId, Loc, OpenFileError}; + +pub enum Error { + Syntax(Loc, SyntaxError), + OpenFileError(OpenFileError), +} diff --git a/crates/lang/src/world/files.rs b/crates/lang/src/world/files.rs new file mode 100644 index 0000000..0c112ad --- /dev/null +++ b/crates/lang/src/world/files.rs @@ -0,0 +1,56 @@ +use std::{ + collections::HashMap, + io, + path::{Path, PathBuf}, +}; + +mod loc; + +pub use loc::Loc; +use rowan::ast::AstNode; + +use crate::{ + ast::ParseError, + lst_parser::{self, error::SyntaxError, input, output::Output}, + world::{error::Error, files::source_file::SourceFile}, +}; + +struct Files { + inner: Vec, + path_to_id_map: HashMap, +} + +impl Files { + pub fn add_file(&mut self, path: &Path) -> Result<(FileId, Vec), OpenFileError> { + if !path.exists() { + return Err(OpenFileError::NotFound(path.to_owned())); + } + + let file_id = FileId(self.inner.len()); + let (source_file, errs) = match SourceFile::open(path) { + Ok((source_file, errs)) => { + let errs = errs + .into_iter() + .map(|(ptr, err)| Error::Syntax(Loc::from_ptr(ptr, file_id), err)) + .collect::>(); + (source_file, errs) + } + Err(e) => return Err(OpenFileError::IoError(path.to_path_buf(), e)), + }; + + self.inner.push(source_file); + self.path_to_id_map.insert(path.to_path_buf(), file_id); + + Ok((file_id, errs)) + } +} + +pub enum OpenFileError { + NotFound(PathBuf), + IoError(PathBuf, std::io::Error), +} + +#[derive(Copy, Clone, Debug)] +pub struct FileId(usize); + +mod source_file; diff --git a/crates/lang/src/world/files/loc.rs b/crates/lang/src/world/files/loc.rs new file mode 100644 index 0000000..fa865d8 --- /dev/null +++ b/crates/lang/src/world/files/loc.rs @@ -0,0 +1,29 @@ +use rowan::ast::{AstNode, AstPtr}; + +use crate::Lang; + +use super::FileId; + +#[derive(Clone)] +pub struct Loc> { + file: FileId, + syntax: AstPtr, +} + +impl> Loc { + pub fn new(node: N, file: FileId) -> Self { + Self::from_ptr(AstPtr::new(&node), file) + } + + pub fn from_ptr(ptr: AstPtr, file: FileId) -> Self { + Self { file, syntax: ptr } + } + + pub fn file(&self) -> FileId { + self.file + } + + pub fn syntax(&self) -> AstPtr { + self.syntax.clone() + } +} diff --git a/crates/lang/src/world/files/source_file.rs b/crates/lang/src/world/files/source_file.rs new file mode 100644 index 0000000..8ed8043 --- /dev/null +++ b/crates/lang/src/world/files/source_file.rs @@ -0,0 +1,113 @@ +use crate::lst_parser::{self, grammar, input, syntax_kind}; +use crate::SyntaxNode; + +use crate::lst_parser::output::Output; + +use crate::lst_parser::error::SyntaxError; + +use crate::ast::ParseError; + +use rowan::ast::{AstNode, AstPtr}; + +use std::path::Path; +use std::{fs, io}; + +use rowan::GreenNode; + +use std::path::PathBuf; + +pub(crate) struct SourceFile { + pub(crate) path: PathBuf, + pub(crate) lst: rowan::GreenNode, +} + +impl SourceFile { + pub(crate) fn open(p: &Path) -> io::Result<(Self, Vec<(AstPtr, SyntaxError)>)> { + assert!(p.exists()); + + let f = fs::read_to_string(p)?; + let (lst, errs) = Self::parse(f); + + Ok(( + Self { + path: p.to_path_buf(), + lst, + }, + errs, + )) + } + + pub(crate) fn parse(f: String) -> (GreenNode, Vec<(AstPtr, SyntaxError)>) { + let toks = syntax_kind::lex(&f); + let input = input::Input::new(&toks); + let mut parser = lst_parser::Parser::new(input); + + grammar::source_file(&mut parser); + + let p_out = parser.finish(); + let (lst, errs) = Output::from_parser_output(toks, p_out).dissolve(); + + (lst.clone(), Self::find_errs(lst, errs)) + } + + pub(crate) fn find_errs( + lst: GreenNode, + mut errs: Vec, + ) -> Vec<(AstPtr, SyntaxError)> { + let mut out = Vec::new(); + errs.reverse(); + + let lst = SyntaxNode::new_root(lst); + Self::find_errs_recursive(&mut out, lst, &mut errs); + + out + } + + pub(crate) fn find_errs_recursive( + mut out: &mut Vec<(AstPtr, SyntaxError)>, + lst: SyntaxNode, + mut errs: &mut Vec, + ) { + lst.children() + .filter_map(|c| ParseError::cast(c)) + .for_each(|e| out.push((AstPtr::new(&e), errs.pop().unwrap()))); + + lst.children() + .for_each(|c| Self::find_errs_recursive(out, c, errs)); + } +} + +#[cfg(test)] +mod tests { + use crate::world::files::source_file::SourceFile; + + fn check_find_errs(input: &str, expected: &[&str]) { + let (_, errs) = SourceFile::parse(input.to_string()); + + let errs = errs + .into_iter() + .map(|(loc, err)| format!("{:?}@{:?}", err, loc.syntax_node_ptr().text_range())) + .collect::>(); + + assert_eq!( + errs, + expected + .into_iter() + .map(|s| s.to_string()) + .collect::>() + ) + } + + #[test] + fn test_find_errs() { + check_find_errs( + "def meow = ;\n mod ;", + &["Expected([DEF_BODY])@11..11", "Expected([IDENT])@18..18"], + ); + + check_find_errs( + "def awawa = a |", + &["UnterminatedTopLevelItem@0..15", "PipelineNeedsSink@12..15"], + ) + } +} diff --git a/crates/svg-filters/src/main.rs b/crates/svg-filters/src/main.rs index 5ce0102..a063791 100644 --- a/crates/svg-filters/src/main.rs +++ b/crates/svg-filters/src/main.rs @@ -1,8 +1,65 @@ -use svg_filters::codegen::SvgDocument; +use svg_filters::{ + codegen::SvgDocument, + types::nodes::{ + primitives::{ + blend::BlendMode, + color_matrix::ColorMatrixType, + component_transfer::TransferFn, + displacement_map::Channel, + turbulence::{NoiseType, StitchTiles}, + }, + standard_input::StandardInput, + }, +}; fn main() { let mut doc = SvgDocument::new(); - eprintln!("{}", doc.generate_svg_pretty()); - println!("{}", doc.generate_svg()); + let f = doc.create_filter("cmyk-chromabb"); + + let noise = f.turbulence(0., 0.1, 2, 0, StitchTiles::Stitch, NoiseType::FractalNoise); + let noise = f.component_transfer_rgba( + noise, + TransferFn::Discrete { + table_values: vec![0., 0.2, 0.4, 0.6, 0.8, 1.], + }, + TransferFn::Discrete { + table_values: vec![0., 0.2, 0.4, 0.6, 0.8, 1.], + }, + TransferFn::Discrete { + table_values: vec![0., 0.2, 0.4, 0.6, 0.8, 1.], + }, + TransferFn::Linear { + slope: 0., + intercept: 0.5, + }, + ); + + let cyan = f.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 0., 0., 0., 0., 0., // + 0., 1., 0., 0., 0., // + 0., 0., 1., 0., 0., // + 0., 0., 0., 1., 0., + ])), + ); + let cyan = f.offset(cyan, 25., 0.); + let cyan = f.displacement_map(cyan, noise, 50., Channel::R, Channel::A); + + let magenta = f.color_matrix( + StandardInput::SourceGraphic, + ColorMatrixType::Matrix(Box::new([ + 1., 0., 0., 0., 0., // + 0., 0., 0., 0., 0., // + 0., 0., 1., 0., 0., // + 0., 0., 0., 1., 0., + ])), + ); + let magenta = f.displacement_map(magenta, noise, 50., Channel::R, Channel::A); + let magenta = f.offset(magenta, -25., 0.); + + f.blend(cyan, magenta, BlendMode::Screen); + + println!("{}", doc.generate_svg_pretty()); } -- 2.46.0 From 1e0741e6004309d8cd49c2f5d4bbf8c0eae2b8c2 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 8 Jul 2024 20:20:45 +0200 Subject: [PATCH 66/67] lang: add credit to macro --- crates/lang/src/ast.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index 42d160c..d5838a2 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -1,6 +1,8 @@ use crate::lst_parser::syntax_kind::SyntaxKind::*; use crate::SyntaxNode; use rowan::Language; + +// Heavily modified version of https://github.com/rust-analyzer/rowan/blob/e2d2e93e16c5104b136d0bc738a0d48346922200/examples/s_expressions.rs#L250-L266 macro_rules! ast_nodes { ($($ast:ident, $kind:ident);+) => { $( -- 2.46.0 From dc44244e7b88fa86ca471d2659c4e772d6c9a817 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 8 Jul 2024 20:23:29 +0200 Subject: [PATCH 67/67] lang: work on some basics --- crates/lang/src/world.rs | 15 ++++++++++++++- crates/lang/src/world/files.rs | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/lang/src/world.rs b/crates/lang/src/world.rs index 2ce9997..7713422 100644 --- a/crates/lang/src/world.rs +++ b/crates/lang/src/world.rs @@ -1,5 +1,7 @@ use std::path::Path; +use self::files::{Files, OpenFileError}; + mod error; mod files; @@ -7,8 +9,19 @@ struct World; impl World { pub fn new(entry_point: &Path) -> Result { + let mut files = Files::default(); + let (entry_point_id, errors) = files.add_file(entry_point)?; + todo!() } } -enum WorldCreationError {} +enum WorldCreationError { + FailedToOpenEntryPoint(OpenFileError), +} + +impl From for WorldCreationError { + fn from(value: OpenFileError) -> Self { + Self::FailedToOpenEntryPoint(value) + } +} diff --git a/crates/lang/src/world/files.rs b/crates/lang/src/world/files.rs index 0c112ad..24053d7 100644 --- a/crates/lang/src/world/files.rs +++ b/crates/lang/src/world/files.rs @@ -15,7 +15,8 @@ use crate::{ world::{error::Error, files::source_file::SourceFile}, }; -struct Files { +#[derive(Default)] +pub struct Files { inner: Vec, path_to_id_map: HashMap, } -- 2.46.0