svg-filters & basic parser #15

Merged
schrottkatze merged 67 commits from schrottkatze/iowo:svg-filters into main 2024-07-08 18:29:05 +00:00
12 changed files with 395 additions and 14 deletions
Showing only changes of commit 3eee768ce1 - Show all commits

30
Cargo.lock generated
View file

@ -618,6 +618,7 @@ dependencies = [
"owo-colors", "owo-colors",
"petgraph", "petgraph",
"rowan", "rowan",
"strip-ansi-escapes",
] ]
[[package]] [[package]]
@ -1069,6 +1070,15 @@ dependencies = [
"lock_api", "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]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"
@ -1181,6 +1191,26 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 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]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"

View file

@ -19,6 +19,7 @@ indoc = "2"
dashmap = "5.5.3" dashmap = "5.5.3"
crossbeam = "0.8.4" crossbeam = "0.8.4"
owo-colors = {version = "4", features = ["supports-colors"]} owo-colors = {version = "4", features = ["supports-colors"]}
strip-ansi-escapes = "0.2.0"
[lints] [lints]
workspace = true workspace = true

78
crates/lang/src/ast.rs Normal file
View file

@ -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: <Self::Language as Language>::Kind) -> bool {
kind == $kind
}
fn cast(node: SyntaxNode) -> Option<Self> {
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
);

View file

@ -2,7 +2,9 @@
use crate::lst_parser::syntax_kind::SyntaxKind; use crate::lst_parser::syntax_kind::SyntaxKind;
pub mod ast;
pub mod lst_parser; pub mod lst_parser;
pub mod world;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Lang {} pub enum Lang {}

View file

@ -33,6 +33,6 @@ fn check_parser(input: &str, parser_fn: fn(&mut Parser), output: &str) {
let p_out = dbg!(parser.finish()); let p_out = dbg!(parser.finish());
let o = Output::from_parser_output(toks, p_out); 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); assert_eq!(&s, output);
} }

View file

@ -28,6 +28,7 @@ impl std::fmt::Debug for Output {
} }
const INDENT_STR: &str = " "; const INDENT_STR: &str = " ";
/// colored argument currently broken
fn debug_print_green_node( fn debug_print_green_node(
node: NodeOrToken<&GreenNodeData, &GreenTokenData>, node: NodeOrToken<&GreenNodeData, &GreenTokenData>,
f: &mut dyn std::fmt::Write, f: &mut dyn std::fmt::Write,
@ -39,12 +40,6 @@ fn debug_print_green_node(
f.write_str(INDENT_STR)?; f.write_str(INDENT_STR)?;
} }
if !colored {
owo_colors::set_override(false);
} else {
owo_colors::set_override(true);
}
let r = match node { let r = match node {
NodeOrToken::Node(n) => { NodeOrToken::Node(n) => {
let kind = Lang::kind_from_raw(node.kind()); let kind = Lang::kind_from_raw(node.kind());
@ -103,10 +98,6 @@ fn debug_print_green_node(
} }
}; };
if !colored {
owo_colors::unset_override();
}
r r
} }

14
crates/lang/src/world.rs Normal file
View file

@ -0,0 +1,14 @@
use std::path::Path;
mod error;
mod files;
struct World;
impl World {
pub fn new(entry_point: &Path) -> Result<Self, WorldCreationError> {
todo!()
}
}
enum WorldCreationError {}

View file

@ -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<ParseError>, SyntaxError),
OpenFileError(OpenFileError),
}

View file

@ -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<source_file::SourceFile>,
path_to_id_map: HashMap<PathBuf, FileId>,
}
impl Files {
pub fn add_file(&mut self, path: &Path) -> Result<(FileId, Vec<Error>), 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::<Vec<_>>();
(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;

View file

@ -0,0 +1,29 @@
use rowan::ast::{AstNode, AstPtr};
use crate::Lang;
use super::FileId;
#[derive(Clone)]
pub struct Loc<N: AstNode<Language = Lang>> {
file: FileId,
syntax: AstPtr<N>,
}
impl<N: AstNode<Language = Lang>> Loc<N> {
pub fn new(node: N, file: FileId) -> Self {
Self::from_ptr(AstPtr::new(&node), file)
}
pub fn from_ptr(ptr: AstPtr<N>, file: FileId) -> Self {
Self { file, syntax: ptr }
}
pub fn file(&self) -> FileId {
self.file
}
pub fn syntax(&self) -> AstPtr<N> {
self.syntax.clone()
}
}

View file

@ -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<ParseError>, 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<ParseError>, 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<SyntaxError>,
) -> Vec<(AstPtr<ParseError>, 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<ParseError>, SyntaxError)>,
lst: SyntaxNode,
mut errs: &mut Vec<SyntaxError>,
) {
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::<Vec<String>>();
assert_eq!(
errs,
expected
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
)
}
#[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"],
)
}
}

View file

@ -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() { fn main() {
let mut doc = SvgDocument::new(); let mut doc = SvgDocument::new();
eprintln!("{}", doc.generate_svg_pretty()); let f = doc.create_filter("cmyk-chromabb");
println!("{}", doc.generate_svg());
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());
} }