svg-filters & basic parser #15
12 changed files with 395 additions and 14 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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
78
crates/lang/src/ast.rs
Normal 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
|
||||||
|
);
|
|
@ -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 {}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
14
crates/lang/src/world.rs
Normal 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 {}
|
10
crates/lang/src/world/error.rs
Normal file
10
crates/lang/src/world/error.rs
Normal 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),
|
||||||
|
}
|
56
crates/lang/src/world/files.rs
Normal file
56
crates/lang/src/world/files.rs
Normal 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;
|
29
crates/lang/src/world/files/loc.rs
Normal file
29
crates/lang/src/world/files/loc.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
113
crates/lang/src/world/files/source_file.rs
Normal file
113
crates/lang/src/world/files/source_file.rs
Normal 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"],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue