Skip to main content

besl/
lib.rs

1//! Use this crate to parse, link, and execute Byte Engine Shader Language (BESL) source.
2//!
3//! Call [`compile_to_besl`] for the normal parse-and-link path. Next, pass the
4//! linked [`NodeReference`] to the resource-management shader generator or use
5//! [`vm`] when tests need to execute BESL semantics directly.
6//!
7//! See the [BESL language reference](https://byte-engine.0x44491229.dev/docs/reference/besl)
8//! for syntax, interfaces, stages, sidecar settings, and supported operations.
9
10pub mod lexer;
11pub mod parser;
12mod tokenizer;
13pub mod vm;
14
15pub use besl_derive::BeslStruct;
16pub use lexer::Expressions;
17pub use lexer::Node;
18pub use lexer::Nodes;
19pub use lexer::Operators;
20
21pub use crate::lexer::BindingTypes;
22pub use crate::lexer::NodeReference;
23
24/// A shared parser node used by BESL syntax trees.
25pub type ParserNode<'a> = parser::Node<'a>;
26
27/// The `BeslStructDefinition` trait exposes a Rust struct as a BESL parser struct definition.
28pub trait BeslStructDefinition {
29	fn besl_struct_node() -> ParserNode<'static>;
30
31	fn besl_definition(&self) -> ParserNode<'static> {
32		Self::besl_struct_node()
33	}
34}
35
36/// Builds a BESL parser struct node from Rust-style struct syntax.
37#[macro_export]
38macro_rules! besl_struct_node {
39	(struct $name:ident { $($body:tt)* }) => {{
40		let mut fields = Vec::new();
41		$crate::besl_struct_node!(@fields fields [] $($body)*);
42
43		$crate::ParserNode::r#struct(
44			stringify!($name),
45			fields,
46		)
47	}};
48	(@fields $fields:ident [] ) => {};
49	(@fields $fields:ident [$($field:tt)+] ) => {
50		$crate::besl_struct_node!(@emit $fields [$($field)+]);
51	};
52	(@fields $fields:ident [$($field:tt)*] , $($rest:tt)*) => {
53		$crate::besl_struct_node!(@emit $fields [$($field)*]);
54		$crate::besl_struct_node!(@fields $fields [] $($rest)*);
55	};
56	(@fields $fields:ident [$($field:tt)*] $next:tt $($rest:tt)*) => {
57		$crate::besl_struct_node!(@fields $fields [$($field)* $next] $($rest)*);
58	};
59	(@emit $fields:ident []) => {};
60	(@emit $fields:ident [$field:ident : $($field_type:tt)+]) => {
61		{
62			let field_type = stringify!($($field_type)+).replace(' ', "");
63			$fields.push($crate::ParserNode::member(stringify!($field), &field_type));
64		}
65	};
66}
67
68/// Parses BESL source and returns the root syntax node.
69///
70/// This function tokenizes the source and builds a syntax tree. Call [`lex`] to
71/// resolve the tree's named references before compilation.
72pub fn parse<'a>(source: &'a str) -> Result<parser::Node<'a>, CompilationError> {
73	let tokens = tokenizer::tokenize(source).map_err(|_e| CompilationError::Tokenization)?;
74	let parser_root_node = parser::parse(&tokens).map_err(CompilationError::Parsing)?;
75
76	Ok(parser_root_node)
77}
78
79/// Resolves a parsed syntax tree and returns its linked root node.
80///
81/// The linked tree contains the resolved relationships needed by later
82/// compilation stages. Next, give the returned [`NodeReference`] to a shader
83/// generator or to [`vm`] for semantic execution.
84pub fn lex(node: parser::Node) -> Result<NodeReference, CompilationError> {
85	let besl = lexer::lex(node).map_err(CompilationError::Lex)?;
86
87	Ok(besl)
88}
89
90/// Parses and links BESL source into a JSPD.
91///
92/// When `parent` is present, the compiled source can resolve names from that
93/// parent scope. Next, pass the returned [`NodeReference`] to the active shader
94/// generator, or use [`vm`] to validate behavior in a test.
95pub fn compile_to_besl(source: &str, parent: Option<Node>) -> Result<NodeReference, CompilationError> {
96	if source.split_whitespace().next().is_none() {
97		return Ok(lexer::Node::scope("".to_string()).into());
98	}
99
100	let parser_root_node = parse(source)?;
101
102	let besl = if let Some(parent) = parent {
103		lexer::lex_with_root(parent, parser_root_node).map_err(CompilationError::Lex)?
104	} else {
105		lexer::lex(parser_root_node).map_err(CompilationError::Lex)?
106	};
107
108	Ok(besl)
109}
110
111#[derive(Debug)]
112pub enum CompilationError {
113	Undefined,
114	Tokenization,
115	Parsing(parser::ParsingFailReasons),
116	Lex(lexer::LexError),
117}
118
119#[cfg(test)]
120mod tests {
121	use crate::parser::Nodes;
122
123	#[test]
124	fn besl_struct_node_macro_builds_a_struct_node() {
125		let mut node = crate::besl_struct_node!(struct Light {
126			position: vec3f,
127			color: vec3f,
128			indices: u32[3],
129		});
130
131		match node.node_mut() {
132			Nodes::Struct { name, fields } => {
133				assert_eq!(*name, "Light");
134				assert_eq!(fields.len(), 3);
135
136				match fields[0].node_mut() {
137					Nodes::Member { name, r#type } => {
138						assert_eq!(*name, "position");
139						assert_eq!(r#type, "vec3f");
140					}
141					_ => panic!("Expected member node."),
142				}
143
144				match fields[2].node_mut() {
145					Nodes::Member { name, r#type } => {
146						assert_eq!(*name, "indices");
147						assert_eq!(r#type, "u32[3]");
148					}
149					_ => panic!("Expected member node."),
150				}
151			}
152			_ => panic!("Expected struct node."),
153		}
154	}
155}