orchidlang/parse/
parse_plugin.rs

1//! Abstractions for dynamic extensions to the parser that act across entries.
2//! Macros are the primary syntax extension  mechanism, but they only operate
3//! within a constant and can't interfere with name reproject.
4
5use std::ops::Range;
6
7use dyn_clone::DynClone;
8use intern_all::Tok;
9
10use super::context::ParseCtx;
11use super::errors::{expect, expect_block, expect_name};
12use super::facade::parse_entries;
13use super::frag::Frag;
14use super::lexer::{Entry, Lexeme};
15use super::parsed::{Constant, Expr, ModuleBlock, PType, Rule, SourceLine, SourceLineKind};
16use super::sourcefile::{
17  exprv_to_single, parse_const, parse_exprv, parse_line, parse_module, parse_module_body,
18  parse_nsname, parse_rule, split_lines,
19};
20use crate::error::{ProjectErrorObj, ProjectResult};
21use crate::location::SourceRange;
22use crate::name::VName;
23use crate::utils::boxed_iter::BoxedIter;
24
25/// Information and actions exposed to [ParseLinePlugin]. A plugin should never
26/// import and call the parser directly because it might be executed in a
27/// different version of the parser.
28pub trait ParsePluginReq<'t> {
29  // ################ Frag and ParseCtx ################
30
31  /// The token sequence this parser must parse
32  fn frag(&self) -> Frag;
33  /// Get the location of a fragment
34  fn frag_loc(&self, f: Frag) -> SourceRange;
35  /// Convert a numeric byte range into a location
36  fn range_loc(&self, r: Range<usize>) -> SourceRange;
37  /// Remove the first token of the fragment
38  fn pop<'a>(&self, f: Frag<'a>) -> ProjectResult<(&'a Entry, Frag<'a>)>;
39  /// Remove the last element of the fragment
40  fn pop_back<'a>(&self, f: Frag<'a>) -> ProjectResult<(&'a Entry, Frag<'a>)>;
41
42  // ################ Parser states ################
43
44  /// Split up the lines in a fragment. The fragment must outlive the iterator
45  /// and the request itself must outlive both
46  fn split_lines<'a: 'b, 'b>(&'b self, f: Frag<'a>) -> BoxedIter<'b, Frag<'a>>
47  where 't: 'b + 'a;
48  /// Parse a sequence of source lines separated by line breaks
49  fn parse_module_body(&self, frag: Frag) -> ProjectResult<Vec<SourceLine>>;
50  /// Parse a single source line. This returns a vector because plugins can
51  /// convert a single line into multiple entries
52  fn parse_line(&self, frag: Frag) -> ProjectResult<Vec<SourceLineKind>>;
53  /// Parse a macro rule `<exprv> =prio=> <exprv>`
54  fn parse_rule(&self, frag: Frag) -> ProjectResult<Rule>;
55  /// Parse a constant declaration `<name> := <exprv>`
56  fn parse_const(&self, frag: Frag) -> ProjectResult<Constant>;
57  /// Parse a namespaced name `name::name`
58  fn parse_nsname<'a>(&self, f: Frag<'a>) -> ProjectResult<(VName, Frag<'a>)>;
59  /// Parse a module declaration. `<name> ( <module_body> )`
60  fn parse_module(&self, frag: Frag) -> ProjectResult<ModuleBlock>;
61  /// Parse a sequence of expressions. In principle, it never makes sense to
62  /// parse a single expression because it could always be a macro invocation.
63  fn parse_exprv<'a>(&self, f: Frag<'a>, p: Option<PType>) -> ProjectResult<(Vec<Expr>, Frag<'a>)>;
64  /// Parse a prepared string of code
65  fn parse_entries(&self, t: &'static str, r: SourceRange) -> Vec<SourceLine>;
66  /// Convert a sequence of expressions to a single one by parenthesization if
67  /// necessary
68  fn vec_to_single(&self, fallback: &Entry, v: Vec<Expr>) -> ProjectResult<Expr>;
69
70  // ################ Assertions ################
71
72  /// Unwrap a single name token or raise an error
73  fn expect_name(&self, entry: &Entry) -> ProjectResult<Tok<String>>;
74  /// Assert that the entry contains exactly the specified lexeme
75  fn expect(&self, l: Lexeme, e: &Entry) -> ProjectResult<()>;
76  /// Remove two parentheses from the ends of the cursor
77  fn expect_block<'a>(&self, f: Frag<'a>, p: PType) -> ProjectResult<Frag<'a>>;
78  /// Ensure that the fragment is empty
79  fn expect_empty(&self, f: Frag) -> ProjectResult<()>;
80  /// Report a fatal error while also producing output to be consumed by later
81  /// stages for improved error reporting
82  fn report_err(&self, e: ProjectErrorObj);
83}
84
85/// External plugin that parses an unrecognized source line into lines of
86/// recognized types
87pub trait ParseLinePlugin: Sync + Send + DynClone {
88  /// Attempt to parse a line. Returns [None] if the line isn't recognized,
89  /// [Some][Err] if it's recognized but incorrect.
90  fn parse(&self, req: &dyn ParsePluginReq) -> Option<ProjectResult<Vec<SourceLineKind>>>;
91}
92
93/// Implementation of [ParsePluginReq] exposing sub-parsers and data to the
94/// plugin via dynamic dispatch
95pub struct ParsePlugReqImpl<'a, TCtx: ParseCtx + ?Sized> {
96  /// Fragment of text to be parsed by the plugin
97  pub frag: Frag<'a>,
98  /// Context for recursive commands and to expose to the plugin
99  pub ctx: &'a TCtx,
100}
101impl<'ty, TCtx: ParseCtx + ?Sized> ParsePluginReq<'ty> for ParsePlugReqImpl<'ty, TCtx> {
102  fn frag(&self) -> Frag { self.frag }
103  fn frag_loc(&self, f: Frag) -> SourceRange { self.range_loc(f.range()) }
104  fn range_loc(&self, r: Range<usize>) -> SourceRange { self.ctx.range_loc(&r) }
105  fn pop<'a>(&self, f: Frag<'a>) -> ProjectResult<(&'a Entry, Frag<'a>)> { f.pop(self.ctx) }
106  fn pop_back<'a>(&self, f: Frag<'a>) -> ProjectResult<(&'a Entry, Frag<'a>)> {
107    f.pop_back(self.ctx)
108  }
109  fn split_lines<'a: 'b, 'b>(&'b self, f: Frag<'a>) -> BoxedIter<'b, Frag<'a>>
110  where
111    'ty: 'b,
112    'ty: 'a,
113  {
114    Box::new(split_lines(f, self.ctx))
115  }
116  fn parse_module_body(&self, f: Frag) -> ProjectResult<Vec<SourceLine>> {
117    Ok(parse_module_body(f, self.ctx))
118  }
119  fn parse_line(&self, f: Frag) -> ProjectResult<Vec<SourceLineKind>> { parse_line(f, self.ctx) }
120  fn parse_rule(&self, f: Frag) -> ProjectResult<Rule> { parse_rule(f, self.ctx) }
121  fn parse_const(&self, f: Frag) -> ProjectResult<Constant> { parse_const(f, self.ctx) }
122  fn parse_nsname<'a>(&self, f: Frag<'a>) -> ProjectResult<(VName, Frag<'a>)> {
123    parse_nsname(f, self.ctx)
124  }
125  fn parse_module(&self, f: Frag) -> ProjectResult<ModuleBlock> { parse_module(f, self.ctx) }
126  fn parse_exprv<'a>(&self, f: Frag<'a>, p: Option<PType>) -> ProjectResult<(Vec<Expr>, Frag<'a>)> {
127    parse_exprv(f, p, self.ctx)
128  }
129  fn parse_entries(&self, s: &'static str, r: SourceRange) -> Vec<SourceLine> {
130    parse_entries(&self.ctx, s, r)
131  }
132  fn vec_to_single(&self, fb: &Entry, v: Vec<Expr>) -> ProjectResult<Expr> {
133    exprv_to_single(fb, v, self.ctx)
134  }
135  fn expect_name(&self, e: &Entry) -> ProjectResult<Tok<String>> { expect_name(e, self.ctx) }
136  fn expect(&self, l: Lexeme, e: &Entry) -> ProjectResult<()> { expect(l, e, self.ctx) }
137  fn expect_block<'a>(&self, f: Frag<'a>, t: PType) -> ProjectResult<Frag<'a>> {
138    expect_block(f, t, self.ctx)
139  }
140  fn expect_empty(&self, f: Frag) -> ProjectResult<()> { f.expect_empty(self.ctx) }
141  fn report_err(&self, e: ProjectErrorObj) { self.ctx.reporter().report(e) }
142}