1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::sync::Arc;
4
5use crate::models::error::Error;
6use crate::models::file::File;
7use crate::models::nlcst::Node;
8use crate::models::position::Position;
9use crate::models::tree::{Tree, TreeKind};
10use crate::{Allocator, ArenaVec};
11
12pub trait Plugin: Send + Sync {
14 fn attach(&self, context: &mut Attach) -> Result<(), Error>;
15}
16
17pub trait Parser: Send + Sync {
19 fn output(&self) -> TreeKind;
20
21 fn source<'a>(&self, input: &'a str) -> &'a str {
23 input
24 }
25
26 fn parse<'a>(
27 &self,
28 input: &'a str,
29 file: &mut File,
30 alloc: &'a Allocator,
31 ) -> Result<Tree<'a>, Error>;
32}
33
34pub trait Transform: Send + Sync {
36 fn input(&self) -> TreeKind;
37 fn output(&self) -> TreeKind;
38 fn run<'a>(
39 &self,
40 tree: Tree<'a>,
41 file: &mut File,
42 alloc: &'a Allocator,
43 ) -> Result<Tree<'a>, Error>;
44
45 fn start<'a>(
47 &self,
48 tree: Tree<'a>,
49 file: &mut File,
50 alloc: &'a Allocator,
51 ) -> Result<Step<'a>, Error> {
52 self.run(tree, file, alloc).map(Step::Done)
53 }
54
55 fn host(&self) -> Option<u32> {
58 None
59 }
60
61 fn html_writer(&self, _target: HtmlTarget) -> Option<Box<dyn HtmlWriter>> {
64 None
65 }
66}
67
68#[derive(Clone, Copy, Debug, PartialEq)]
70pub struct HtmlTarget {
71 pub allow_dangerous_html: bool,
73}
74
75pub trait HtmlWriter: Send + Sync {
77 fn write(&self, tree: &Tree<'_>) -> Option<String>;
78}
79
80pub type Job<'a> = Box<dyn FnOnce(&mut File) -> Result<Tree<'a>, Error> + 'a>;
82
83pub enum Step<'a> {
85 Done(Tree<'a>),
86 Job(Job<'a>),
87}
88
89pub trait Compiler: Send + Sync {
91 fn input(&self) -> TreeKind;
92 fn extension(&self) -> &'static str;
93 fn compile(&self, tree: &Tree<'_>, file: &mut File) -> Result<String, Error>;
94
95 fn html_target(&self) -> Option<HtmlTarget> {
97 None
98 }
99}
100
101pub trait NaturalLanguageParser: Send + Sync {
104 fn tokenize<'a>(
107 &self,
108 value: &'a str,
109 offset: usize,
110 alloc: &'a Allocator,
111 ) -> ArenaVec<'a, Node<'a>>;
112
113 fn tokenize_paragraph<'a>(
117 &self,
118 content: ArenaVec<'a, Node<'a>>,
119 position: Option<Position<'a>>,
120 alloc: &'a Allocator,
121 ) -> Node<'a>;
122
123 fn tokenize_root<'a>(
126 &self,
127 paragraph: Node<'a>,
128 position: Option<Position<'a>>,
129 alloc: &'a Allocator,
130 ) -> Node<'a>;
131}
132
133#[derive(Clone, Default)]
136pub struct NaturalLanguageParserSlot {
137 pub parser: Option<Arc<dyn NaturalLanguageParser>>,
138}
139
140pub trait ScriptParser: Send + Sync {
143 fn parse_expression(&self, source: &str) -> Result<ScriptExpression, ScriptError>;
146
147 fn parse_module(&self, source: &str) -> Result<Vec<ScriptStatement>, ScriptError>;
149}
150
151#[derive(Clone, Debug, PartialEq, Eq)]
155pub struct ScriptExpression {
156 pub end: usize,
157 pub object: Option<Vec<(String, usize)>>,
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
163pub struct ScriptStatement {
164 pub kind: String,
165 pub offset: usize,
166 pub imports: Vec<String>,
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
172pub struct ScriptError {
173 pub start: usize,
174 pub end: usize,
175 pub reason: String,
176}
177
178#[derive(Clone, Default)]
182pub struct ScriptParserSlot {
183 pub parser: Option<Arc<dyn ScriptParser>>,
184}
185
186type ParserFactory = Box<dyn FnOnce(&Slots) -> Result<Box<dyn Parser>, Error>>;
187type CompilerFactory = Box<dyn FnOnce(&Slots) -> Result<Box<dyn Compiler>, Error>>;
188
189#[derive(Default)]
191pub struct Attach {
192 parser: Option<ParserFactory>,
193 compiler: Option<CompilerFactory>,
194 transforms: Vec<Box<dyn Transform>>,
195 slots: Slots,
196}
197
198#[derive(Default)]
200pub struct Slots {
201 values: HashMap<TypeId, Box<dyn Any>>,
202}
203
204impl Attach {
205 pub fn set_parser(
207 &mut self,
208 factory: impl FnOnce(&Slots) -> Result<Box<dyn Parser>, Error> + 'static,
209 ) {
210 self.parser = Some(Box::new(factory));
211 }
212
213 pub fn set_compiler(
215 &mut self,
216 factory: impl FnOnce(&Slots) -> Result<Box<dyn Compiler>, Error> + 'static,
217 ) {
218 self.compiler = Some(Box::new(factory));
219 }
220
221 pub fn add_transform(&mut self, transform: Box<dyn Transform>) {
222 self.transforms.push(transform);
223 }
224
225 pub fn slots_mut(&mut self) -> &mut Slots {
226 &mut self.slots
227 }
228
229 #[allow(clippy::type_complexity)]
231 pub fn finish(
232 self,
233 ) -> Result<
234 (
235 Option<Box<dyn Parser>>,
236 Vec<Box<dyn Transform>>,
237 Option<Box<dyn Compiler>>,
238 ),
239 Error,
240 > {
241 let parser = self
242 .parser
243 .map(|factory| factory(&self.slots))
244 .transpose()?;
245 let compiler = self
246 .compiler
247 .map(|factory| factory(&self.slots))
248 .transpose()?;
249 Ok((parser, self.transforms, compiler))
250 }
251}
252
253impl Slots {
254 pub fn get<T: 'static>(&self) -> Option<&T> {
255 self.values
256 .get(&TypeId::of::<T>())
257 .and_then(|value| value.downcast_ref())
258 }
259
260 pub fn get_or_default<T: Default + 'static>(&mut self) -> &mut T {
261 let value = self
262 .values
263 .entry(TypeId::of::<T>())
264 .or_insert_with(|| Box::new(T::default()));
265 match value.downcast_mut() {
266 Some(value) => value,
267 None => unreachable!("a slot holds the type it is keyed by"),
268 }
269 }
270}