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",
|
||||
"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"
|
||||
|
|
|
@ -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
|
||||
|
|
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;
|
||||
|
||||
pub mod ast;
|
||||
pub mod lst_parser;
|
||||
pub mod world;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
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 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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
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() {
|
||||
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());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue