Macro system done in theory
too afraid to begin debugging, resting for a moment
This commit is contained in:
@@ -9,3 +9,6 @@ ORCHID_EXTENSIONS = "target/debug/orchid-std"
|
|||||||
ORCHID_DEFAULT_SYSTEMS = "orchid::std"
|
ORCHID_DEFAULT_SYSTEMS = "orchid::std"
|
||||||
ORCHID_LOG_BUFFERS = "true"
|
ORCHID_LOG_BUFFERS = "true"
|
||||||
RUSTBACKTRACE = "1"
|
RUSTBACKTRACE = "1"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# rustflags = ["-Znext-solver"]
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ Reference loops are resource leaks. There are two primary ways to avoid referenc
|
|||||||
- Constants reference their constituent Expressions
|
- Constants reference their constituent Expressions
|
||||||
- Expressions reference Atoms
|
- Expressions reference Atoms
|
||||||
- During evaluation, Constants replace their unbound names with Constants
|
- During evaluation, Constants replace their unbound names with Constants
|
||||||
- There is a reference cycle here, but it always goes through a Constant.
|
- There is a reference cycle here, but it always goes through a Constant.
|
||||||
> **todo** A potential fix may be to update all Constants to point to a dummy value before freeing Trees
|
> **todo** A potential fix may be to update all Constants to point to a dummy value before freeing Trees
|
||||||
- Atoms reference the Systems that implement them
|
- Atoms reference the Systems that implement them
|
||||||
- Atoms may reference Expressions that are not younger than them
|
- Atoms may reference Expressions that are not younger than them
|
||||||
- This link is managed by the System but tied to Atom and not System lifecycle
|
- This link is managed by the System but tied to Atom and not System lifecycle
|
||||||
- Atoms can technically be applied to themselves, but it's a copying apply so it probably isn't a risk factor
|
- Atoms can technically be applied to themselves, but it's a copying apply so it probably isn't a risk factor
|
||||||
- Systems reference the Extension that contains them
|
- Systems reference the Extension that contains them
|
||||||
- Extensions reference the Port that connects them
|
- Extensions reference the Port that connects them
|
||||||
- The Extension signals the remote peer to disconnect on drop
|
- The Extension signals the remote peer to disconnect on drop
|
||||||
- The port is also referenced in a loose receiver thread, which always eventually tries to find the Extension or polls for ingress so it always eventually exits after the Extension's drop handler is called
|
- The port is also referenced in a loose receiver thread, which always eventually tries to find the Extension or polls for ingress so it always eventually exits after the Extension's drop handler is called
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ Priority numbers are written in hexadecimal normal form to avoid precision bugs,
|
|||||||
- **32-39**: Binary operators, in inverse priority order
|
- **32-39**: Binary operators, in inverse priority order
|
||||||
- **80-87**: Expression-like structures such as if/then/else
|
- **80-87**: Expression-like structures such as if/then/else
|
||||||
- **128-135**: Anything that creates lambdas
|
- **128-135**: Anything that creates lambdas
|
||||||
Programs triggered by a lower priority pattern than this can assume that all names are correctly bound
|
Programs triggered by a lower priority pattern than this can assume that all names are correctly bound
|
||||||
- **200**: Aliases extracted for readability
|
- **200**: Aliases extracted for readability
|
||||||
The user-accessible entry points of all macro programs must be lower priority than this, so any arbitrary syntax can be extracted into an alias with no side effects
|
The user-accessible entry points of all macro programs must be lower priority than this, so any arbitrary syntax can be extracted into an alias with no side effects
|
||||||
- **224-231**: Integration; documented hooks exposed by a macro package to allow third party packages to extend its functionality
|
- **224-231**: Integration; documented hooks exposed by a macro package to allow third party packages to extend its functionality
|
||||||
The `statement` pattern produced by `do{}` blocks and matched by `let` and `cps` is a good example of this. When any of these are triggered, all macro programs are in a documented state.
|
The `statement` pattern produced by `do{}` blocks and matched by `let` and `cps` is a good example of this. When any of these are triggered, all macro programs are in a documented state.
|
||||||
- **248-255**: Transitional states within macro programs get the highest priority
|
- **248-255**: Transitional states within macro programs get the highest priority
|
||||||
|
|
||||||
The numbers are arbitrary and up for debate. These are just the ones I came up with when writing the examples.
|
The numbers are arbitrary and up for debate. These are just the ones I came up with when writing the examples.
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ Prioritised macro patterns must start and end with a vectorial placeholder. They
|
|||||||
Macros are checked from the outermost block inwards.
|
Macros are checked from the outermost block inwards.
|
||||||
|
|
||||||
1. For every name token, test all named macros starting with that name
|
1. For every name token, test all named macros starting with that name
|
||||||
1. If the tail is implicit, continue iterating
|
1. If the tail is implicit, continue iterating
|
||||||
2. Test all prioritized macros
|
2. Test all prioritized macros
|
||||||
1. Take the first rule that matches in the highest prioritized block
|
1. Take the first rule that matches in the highest prioritized block
|
||||||
|
|
||||||
Test all in a set of macros
|
Test all in a set of macros
|
||||||
1. Take the first rule that matches in each block
|
1. Take the first rule that matches in each block
|
||||||
@@ -75,26 +75,26 @@ Recursion has to happen through the interpreter itself, so the macro system is d
|
|||||||
- line parser `macro` parses a macro with the existing logic
|
- line parser `macro` parses a macro with the existing logic
|
||||||
- atom `MacRecurState` holds the recursion state
|
- atom `MacRecurState` holds the recursion state
|
||||||
- function `resolve_recur` finds all matches on a MacTree
|
- function `resolve_recur` finds all matches on a MacTree
|
||||||
- type: `MacRecurState -> MacTree -> MacTree`
|
- type: `MacRecurState -> MacTree -> MacTree`
|
||||||
- use all relevant macros to find all matches in the tree
|
- use all relevant macros to find all matches in the tree
|
||||||
- since macros must contain a locally defined token, it can be assumed that at the point that a constant is evaluated and all imports in the parent module have been resolved, necessarily all relevant macro rules must have been loaded
|
- since macros must contain a locally defined token, it can be assumed that at the point that a constant is evaluated and all imports in the parent module have been resolved, necessarily all relevant macro rules must have been loaded
|
||||||
- for each match
|
- for each match
|
||||||
- check for recursion violations
|
- check for recursion violations
|
||||||
- wrap the body in iife-s corresponding to the named values in the match state
|
- wrap the body in iife-s corresponding to the named values in the match state
|
||||||
- emit a recursive call to process and run the body, and pass the same recursive call as argument for the macro to use
|
- emit a recursive call to process and run the body, and pass the same recursive call as argument for the macro to use
|
||||||
```
|
```
|
||||||
(\recur. lower (recur $body) recur)
|
(\recur. lower (recur $body) recur)
|
||||||
(resolve_recur $mac_recur_state)
|
(resolve_recur $mac_recur_state)
|
||||||
```
|
```
|
||||||
- emit a single call to `instantiate_tpl` which receives all of these
|
- emit a single call to `instantiate_tpl` which receives all of these
|
||||||
- function `instantiate_tpl` inserts `MacTree` values into a `MacTree(tpl)`
|
- function `instantiate_tpl` inserts `MacTree` values into a `MacTree(tpl)`
|
||||||
- type: `MacTree(tpl) [-> MacTree] -> MacTree`
|
- type: `MacTree(tpl) [-> MacTree] -> MacTree`
|
||||||
_this function deduces the number of arguments from the first argument. This combines poorly with autocurry, but it's an easy way to avoid representing standalone tree lists_
|
_this function deduces the number of arguments from the first argument. This combines poorly with autocurry, but it's an easy way to avoid representing standalone tree lists_
|
||||||
- walks the tree to find max template slot number, reads and type checks as many template values
|
- walks the tree to find max template slot number, reads and type checks as many template values
|
||||||
- returns the populated tree
|
- returns the populated tree
|
||||||
- function `resolve` is the main entry point of the code
|
- function `resolve` is the main entry point of the code
|
||||||
- type: `MacTree -> MacTree`
|
- type: `MacTree -> MacTree`
|
||||||
- invokes `resolve_recur` with an empty `MacRecurState`
|
- invokes `resolve_recur` with an empty `MacRecurState`
|
||||||
- function `lower` is the main exit point of the code
|
- function `lower` is the main exit point of the code
|
||||||
- type: `MacTree -> any`
|
- type: `MacTree -> any`
|
||||||
- Lowers `MacTree` into the equivalent `Expr`.
|
- Lowers `MacTree` into the equivalent `Expr`.
|
||||||
@@ -85,8 +85,8 @@ pub struct Comment {
|
|||||||
/// called during a [FetchParsedConst] call, but it can be called for a
|
/// called during a [FetchParsedConst] call, but it can be called for a
|
||||||
/// different [ParsedConstId] from the one in [FetchParsedConst].
|
/// different [ParsedConstId] from the one in [FetchParsedConst].
|
||||||
///
|
///
|
||||||
/// Each name is either resolved to an alias or existing constant `Some(TStrv)`
|
/// Each name is either resolved to a valid name or a potential error error.
|
||||||
/// or not resolved `None`. An error is never raised, as names may have a
|
/// The error is not raised by the interpreter itself, as names may have a
|
||||||
/// primary meaning such as a local binding which can be overridden by specific
|
/// primary meaning such as a local binding which can be overridden by specific
|
||||||
/// true names such as those triggering macro keywords. It is not recommended to
|
/// true names such as those triggering macro keywords. It is not recommended to
|
||||||
/// define syntax that can break by defining arbitrary constants, as line
|
/// define syntax that can break by defining arbitrary constants, as line
|
||||||
@@ -100,5 +100,5 @@ pub struct ResolveNames {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Request for ResolveNames {
|
impl Request for ResolveNames {
|
||||||
type Response = Vec<Option<TStrv>>;
|
type Response = Vec<OrcResult<TStrv>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ macro_rules! clone {
|
|||||||
$body
|
$body
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
($($n:ident),+) => {
|
($($n:ident $($mut:ident)?),+) => {
|
||||||
$( let $n = $n.clone(); )+
|
$( let $($mut)? $n = $n.clone(); )+
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,9 +71,8 @@ impl OrcErr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Eq for OrcErr {}
|
impl PartialEq<Tok<String>> for OrcErr {
|
||||||
impl PartialEq for OrcErr {
|
fn eq(&self, other: &Tok<String>) -> bool { self.description == *other }
|
||||||
fn eq(&self, other: &Self) -> bool { self.description == other.description }
|
|
||||||
}
|
}
|
||||||
impl From<OrcErr> for Vec<OrcErr> {
|
impl From<OrcErr> for Vec<OrcErr> {
|
||||||
fn from(value: OrcErr) -> Self { vec![value] }
|
fn from(value: OrcErr) -> Self { vec![value] }
|
||||||
@@ -192,16 +191,8 @@ macro_rules! join_ok {
|
|||||||
(@VALUES) => { Ok(()) };
|
(@VALUES) => { Ok(()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mk_err(
|
pub fn mk_errv_floating(description: Tok<String>, message: impl AsRef<str>) -> OrcErrv {
|
||||||
description: Tok<String>,
|
mk_errv::<Pos>(description, message, [])
|
||||||
message: impl AsRef<str>,
|
|
||||||
posv: impl IntoIterator<Item = ErrPos>,
|
|
||||||
) -> OrcErr {
|
|
||||||
OrcErr {
|
|
||||||
description,
|
|
||||||
message: Arc::new(message.as_ref().to_string()),
|
|
||||||
positions: posv.into_iter().collect(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mk_errv<I: Into<ErrPos>>(
|
pub fn mk_errv<I: Into<ErrPos>>(
|
||||||
@@ -209,7 +200,12 @@ pub fn mk_errv<I: Into<ErrPos>>(
|
|||||||
message: impl AsRef<str>,
|
message: impl AsRef<str>,
|
||||||
posv: impl IntoIterator<Item = I>,
|
posv: impl IntoIterator<Item = I>,
|
||||||
) -> OrcErrv {
|
) -> OrcErrv {
|
||||||
mk_err(description, message, posv.into_iter().map_into()).into()
|
OrcErr {
|
||||||
|
description,
|
||||||
|
message: Arc::new(message.as_ref().to_string()),
|
||||||
|
positions: posv.into_iter().map_into().collect(),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn async_io_err<I: Into<ErrPos>>(
|
pub async fn async_io_err<I: Into<ErrPos>>(
|
||||||
|
|||||||
24
orchid-base/src/iter_utils.rs
Normal file
24
orchid-base/src/iter_utils.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use itertools::{Itertools, Position};
|
||||||
|
|
||||||
|
pub struct PrintList<'a, I: Iterator<Item = E> + Clone, E: fmt::Display>(pub I, pub &'a str);
|
||||||
|
impl<'a, I: Iterator<Item = E> + Clone, E: fmt::Display> fmt::Display for PrintList<'a, I, E> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
for (pos, item) in self.0.clone().with_position() {
|
||||||
|
match pos {
|
||||||
|
Position::First | Position::Only => write!(f, "{item}")?,
|
||||||
|
Position::Middle => write!(f, ", {item}")?,
|
||||||
|
Position::Last => write!(f, ", {} {item}", self.1)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IteratorPrint: Iterator<Item: fmt::Display> + Clone {
|
||||||
|
fn display<'a>(self, operator: &'a str) -> PrintList<'a, Self, Self::Item> {
|
||||||
|
PrintList(self, operator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Iterator<Item: fmt::Display> + Clone> IteratorPrint for T {}
|
||||||
@@ -12,6 +12,7 @@ pub mod event;
|
|||||||
pub mod format;
|
pub mod format;
|
||||||
pub mod id_store;
|
pub mod id_store;
|
||||||
pub mod interner;
|
pub mod interner;
|
||||||
|
pub mod iter_utils;
|
||||||
pub mod join;
|
pub mod join;
|
||||||
pub mod location;
|
pub mod location;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/// A shorthand for mapping over enums with identical structure. Used for
|
/// A shorthand for mapping over enums with similar structure. Used for
|
||||||
/// converting between owned enums and the corresponding API enums that only
|
/// converting between owned enums and the corresponding API enums that only
|
||||||
/// differ in the type of their fields.
|
/// differ in the type of their fields.
|
||||||
///
|
///
|
||||||
@@ -7,7 +7,11 @@
|
|||||||
/// match_mapping!(self, ThisType => OtherType {
|
/// match_mapping!(self, ThisType => OtherType {
|
||||||
/// EmptyVariant,
|
/// EmptyVariant,
|
||||||
/// TupleVariant(foo => intern(foo), bar.clone()),
|
/// TupleVariant(foo => intern(foo), bar.clone()),
|
||||||
/// StructVariant{ a.to_api(), b }
|
/// StructVariant{ a.to_api(), b },
|
||||||
|
/// DedicatedConverter(value () convert)
|
||||||
|
/// } {
|
||||||
|
/// ThisType::DimorphicVariant(c) => OtherType::CorrespondingVariant(c.left(), c.right()),
|
||||||
|
/// ThisType::UnexpectedVariant => panic!(),
|
||||||
/// })
|
/// })
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|||||||
@@ -19,10 +19,9 @@ trait_set! {
|
|||||||
pub trait NameIter = Iterator<Item = Tok<String>> + DoubleEndedIterator + ExactSizeIterator;
|
pub trait NameIter = Iterator<Item = Tok<String>> + DoubleEndedIterator + ExactSizeIterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A token path which may be empty. [VName] is the non-empty,
|
/// A token path which may be empty. [VName] is the non-empty version
|
||||||
/// [PathSlice] is the borrowed version
|
|
||||||
#[derive(Clone, Default, Hash, PartialEq, Eq)]
|
#[derive(Clone, Default, Hash, PartialEq, Eq)]
|
||||||
pub struct VPath(pub Vec<Tok<String>>);
|
pub struct VPath(Vec<Tok<String>>);
|
||||||
impl VPath {
|
impl VPath {
|
||||||
/// Collect segments into a vector
|
/// Collect segments into a vector
|
||||||
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::ops::Range;
|
|||||||
|
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
use crate::error::{OrcErr, mk_err};
|
use crate::error::{OrcErrv, mk_errv};
|
||||||
use crate::interner::Interner;
|
use crate::interner::Interner;
|
||||||
use crate::location::SrcRange;
|
use crate::location::SrcRange;
|
||||||
use crate::name::Sym;
|
use crate::name::Sym;
|
||||||
@@ -55,20 +55,20 @@ pub struct NumError {
|
|||||||
pub kind: NumErrorKind,
|
pub kind: NumErrorKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn num_to_err(
|
pub async fn num_to_errv(
|
||||||
NumError { kind, range }: NumError,
|
NumError { kind, range }: NumError,
|
||||||
offset: u32,
|
offset: u32,
|
||||||
source: &Sym,
|
source: &Sym,
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
) -> OrcErr {
|
) -> OrcErrv {
|
||||||
mk_err(
|
mk_errv(
|
||||||
i.i("Failed to parse number").await,
|
i.i("Failed to parse number").await,
|
||||||
match kind {
|
match kind {
|
||||||
NumErrorKind::NaN => "NaN emerged during parsing",
|
NumErrorKind::NaN => "NaN emerged during parsing",
|
||||||
NumErrorKind::InvalidDigit => "non-digit character encountered",
|
NumErrorKind::InvalidDigit => "non-digit character encountered",
|
||||||
NumErrorKind::Overflow => "The number being described is too large or too accurate",
|
NumErrorKind::Overflow => "The number being described is too large or too accurate",
|
||||||
},
|
},
|
||||||
[SrcRange::new(offset + range.start as u32..offset + range.end as u32, source).pos().into()],
|
[SrcRange::new(offset + range.start as u32..offset + range.end as u32, source)],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,11 +92,11 @@ pub fn parse_num(string: &str) -> Result<Numeric, NumError> {
|
|||||||
match base_s.split_once('.') {
|
match base_s.split_once('.') {
|
||||||
None => {
|
None => {
|
||||||
let base = int_parse(base_s, radix, pos)?;
|
let base = int_parse(base_s, radix, pos)?;
|
||||||
if let Ok(pos_exp) = u32::try_from(exponent) {
|
if let Ok(pos_exp) = u32::try_from(exponent)
|
||||||
if let Some(radical) = u64::from(radix).checked_pow(pos_exp) {
|
&& let Some(radical) = u64::from(radix).checked_pow(pos_exp)
|
||||||
let num = base.checked_mul(radical).and_then(|m| m.try_into().ok()).ok_or(overflow_e)?;
|
{
|
||||||
return Ok(Numeric::Int(num));
|
let num = base.checked_mul(radical).and_then(|m| m.try_into().ok()).ok_or(overflow_e)?;
|
||||||
}
|
return Ok(Numeric::Int(num));
|
||||||
}
|
}
|
||||||
let f = (base as f64) * (radix as f64).powi(exponent);
|
let f = (base as f64) * (radix as f64).powi(exponent);
|
||||||
let err = NumError { range: 0..string.len(), kind: NumErrorKind::NaN };
|
let err = NumError { range: 0..string.len(), kind: NumErrorKind::NaN };
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use futures::future::join_all;
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::error::{OrcErrv, OrcRes, Reporter, mk_err, mk_errv};
|
use crate::error::{OrcErrv, OrcRes, Reporter, mk_errv};
|
||||||
use crate::format::{FmtCtx, FmtUnit, Format, fmt};
|
use crate::format::{FmtCtx, FmtUnit, Format, fmt};
|
||||||
use crate::interner::{Interner, Tok};
|
use crate::interner::{Interner, Tok};
|
||||||
use crate::location::SrcRange;
|
use crate::location::SrcRange;
|
||||||
use crate::name::{NameLike, Sym, VName, VPath};
|
use crate::name::{Sym, VName, VPath};
|
||||||
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token, ttv_fmt, ttv_range};
|
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token, ttv_fmt, ttv_range};
|
||||||
|
|
||||||
pub trait ParseCtx {
|
pub trait ParseCtx {
|
||||||
@@ -237,10 +237,10 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
|||||||
match &tt.tok {
|
match &tt.tok {
|
||||||
Token::NS(ns, body) => {
|
Token::NS(ns, body) => {
|
||||||
if !ns.starts_with(name_start) {
|
if !ns.starts_with(name_start) {
|
||||||
ctx.rep().report(mk_err(
|
ctx.rep().report(mk_errv(
|
||||||
ctx.i().i("Unexpected name prefix").await,
|
ctx.i().i("Unexpected name prefix").await,
|
||||||
"Only names can precede ::",
|
"Only names can precede ::",
|
||||||
[ttpos.into()],
|
[ttpos],
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
let out = Box::pin(rec(body, ctx)).await?;
|
let out = Box::pin(rec(body, ctx)).await?;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use futures::{FutureExt, StreamExt};
|
|||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec};
|
use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec};
|
||||||
use orchid_base::clone;
|
use orchid_base::clone;
|
||||||
use orchid_base::error::{OrcErr, OrcRes, mk_err};
|
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating};
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
||||||
use orchid_base::interner::Interner;
|
use orchid_base::interner::Interner;
|
||||||
use orchid_base::location::Pos;
|
use orchid_base::location::Pos;
|
||||||
@@ -24,6 +24,7 @@ use orchid_base::reqnot::Requester;
|
|||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
use crate::conv::ToExpr;
|
||||||
// use crate::error::{ProjectError, ProjectResult};
|
// use crate::error::{ProjectError, ProjectResult};
|
||||||
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
|
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
|
||||||
use crate::gen_expr::GExpr;
|
use crate::gen_expr::GExpr;
|
||||||
@@ -92,7 +93,7 @@ pub struct ForeignAtom {
|
|||||||
}
|
}
|
||||||
impl ForeignAtom {
|
impl ForeignAtom {
|
||||||
pub fn pos(&self) -> Pos { self.pos.clone() }
|
pub fn pos(&self) -> Pos { self.pos.clone() }
|
||||||
pub fn ctx(&self) -> SysCtx { self.expr.ctx.clone() }
|
pub fn ctx(&self) -> &SysCtx { &self.expr.ctx }
|
||||||
pub fn ex(self) -> Expr {
|
pub fn ex(self) -> Expr {
|
||||||
let (handle, pos) = (self.expr.clone(), self.pos.clone());
|
let (handle, pos) = (self.expr.clone(), self.pos.clone());
|
||||||
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { ..self }) };
|
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { ..self }) };
|
||||||
@@ -110,6 +111,9 @@ impl ForeignAtom {
|
|||||||
.await?;
|
.await?;
|
||||||
Some(M::Response::decode(Pin::new(&mut &rep[..])).await)
|
Some(M::Response::decode(Pin::new(&mut &rep[..])).await)
|
||||||
}
|
}
|
||||||
|
pub async fn downcast<T: AtomicFeatures>(self) -> Result<TypAtom<T>, NotTypAtom> {
|
||||||
|
TypAtom::downcast(self.ex().handle()).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for ForeignAtom {
|
impl fmt::Display for ForeignAtom {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Atom::{:?}", self.atom) }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Atom::{:?}", self.atom) }
|
||||||
@@ -122,6 +126,9 @@ impl Format for ForeignAtom {
|
|||||||
FmtUnit::from_api(&self.ctx().reqnot().request(api::ExtAtomPrint(self.atom.clone())).await)
|
FmtUnit::from_api(&self.ctx().reqnot().request(api::ExtAtomPrint(self.atom.clone())).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ToExpr for ForeignAtom {
|
||||||
|
async fn to_expr(self) -> GExpr { self.ex().to_expr().await }
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NotTypAtom {
|
pub struct NotTypAtom {
|
||||||
pub pos: Pos,
|
pub pos: Pos,
|
||||||
@@ -130,11 +137,11 @@ pub struct NotTypAtom {
|
|||||||
pub ctx: SysCtx,
|
pub ctx: SysCtx,
|
||||||
}
|
}
|
||||||
impl NotTypAtom {
|
impl NotTypAtom {
|
||||||
pub async fn mk_err(&self) -> OrcErr {
|
pub async fn mk_err(&self) -> OrcErrv {
|
||||||
mk_err(
|
mk_errv(
|
||||||
self.ctx.i().i("Not the expected type").await,
|
self.ctx.i().i("Not the expected type").await,
|
||||||
format!("This expression is not a {}", self.typ.name()),
|
format!("This expression is not a {}", self.typ.name()),
|
||||||
[self.pos.clone().into()],
|
[self.pos.clone()],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,10 +223,12 @@ impl<A: AtomCard> Default for MethodSetBuilder<A> {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TypAtom<A: AtomicFeatures> {
|
pub struct TypAtom<A: AtomicFeatures> {
|
||||||
pub data: ForeignAtom,
|
pub untyped: ForeignAtom,
|
||||||
pub value: A::Data,
|
pub value: A::Data,
|
||||||
}
|
}
|
||||||
impl<A: AtomicFeatures> TypAtom<A> {
|
impl<A: AtomicFeatures> TypAtom<A> {
|
||||||
|
pub fn ctx(&self) -> &SysCtx { self.untyped.ctx() }
|
||||||
|
pub fn i(&self) -> &Interner { self.ctx().i() }
|
||||||
pub async fn downcast(expr: Rc<ExprHandle>) -> Result<Self, NotTypAtom> {
|
pub async fn downcast(expr: Rc<ExprHandle>) -> Result<Self, NotTypAtom> {
|
||||||
match Expr::from_handle(expr).atom().await {
|
match Expr::from_handle(expr).atom().await {
|
||||||
Err(expr) => Err(NotTypAtom {
|
Err(expr) => Err(NotTypAtom {
|
||||||
@@ -242,9 +251,9 @@ impl<A: AtomicFeatures> TypAtom<A> {
|
|||||||
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
|
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
|
||||||
where A: Supports<M> {
|
where A: Supports<M> {
|
||||||
M::Response::decode(Pin::new(
|
M::Response::decode(Pin::new(
|
||||||
&mut &(self.data.ctx().reqnot().request(api::Fwd(
|
&mut &(self.untyped.ctx().reqnot().request(api::Fwd(
|
||||||
self.data.atom.clone(),
|
self.untyped.atom.clone(),
|
||||||
Sym::parse(M::NAME, self.data.ctx().i()).await.unwrap().tok().to_api(),
|
Sym::parse(M::NAME, self.untyped.ctx().i()).await.unwrap().tok().to_api(),
|
||||||
enc_vec(&req).await,
|
enc_vec(&req).await,
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
@@ -257,6 +266,9 @@ impl<A: AtomicFeatures> Deref for TypAtom<A> {
|
|||||||
type Target = A::Data;
|
type Target = A::Data;
|
||||||
fn deref(&self) -> &Self::Target { &self.value }
|
fn deref(&self) -> &Self::Target { &self.value }
|
||||||
}
|
}
|
||||||
|
impl<A: AtomicFeatures> ToExpr for TypAtom<A> {
|
||||||
|
async fn to_expr(self) -> GExpr { self.untyped.to_expr().await }
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>, pub SysCtx);
|
pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>, pub SysCtx);
|
||||||
impl FmtCtx for AtomCtx<'_> {
|
impl FmtCtx for AtomCtx<'_> {
|
||||||
@@ -317,10 +329,10 @@ impl Format for AtomFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn err_not_callable(i: &Interner) -> OrcErr {
|
pub async fn err_not_callable(i: &Interner) -> OrcErrv {
|
||||||
mk_err(i.i("This atom is not callable").await, "Attempted to apply value as function", [])
|
mk_errv_floating(i.i("This atom is not callable").await, "Attempted to apply value as function")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn err_not_command(i: &Interner) -> OrcErr {
|
pub async fn err_not_command(i: &Interner) -> OrcErrv {
|
||||||
mk_err(i.i("This atom is not a command").await, "Settled on an inactionable value", [])
|
mk_errv_floating(i.i("This atom is not a command").await, "Settled on an inactionable value")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
|||||||
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
|
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn call_ref(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
fn call_ref(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
||||||
async move { bot([err_not_callable(arg.ctx().i()).await]) }
|
async move { bot(err_not_callable(arg.ctx().i()).await) }
|
||||||
}
|
}
|
||||||
fn call(self, arg: Expr) -> impl Future<Output = GExpr> {
|
fn call(self, arg: Expr) -> impl Future<Output = GExpr> {
|
||||||
async {
|
async {
|
||||||
@@ -229,12 +229,12 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
|||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn command(self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
|
fn command(self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
|
||||||
async move { Err(err_not_command(ctx.i()).await.into()) }
|
async move { Err(err_not_command(ctx.i()).await) }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn free(self, ctx: SysCtx) -> impl Future<Output = ()> { async {} }
|
fn free(self, ctx: SysCtx) -> impl Future<Output = ()> { async {} }
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> {
|
fn print_atom<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> {
|
||||||
async { format!("OwnedAtom({})", type_name::<Self>()).into() }
|
async { format!("OwnedAtom({})", type_name::<Self>()).into() }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
@@ -294,7 +294,7 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
|
|||||||
self.free(ctx).boxed_local()
|
self.free(ctx).boxed_local()
|
||||||
}
|
}
|
||||||
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, FmtUnit> {
|
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, FmtUnit> {
|
||||||
async move { self.print(&FmtCtxImpl { i: ctx.i() }).await }.boxed_local()
|
async move { self.print_atom(&FmtCtxImpl { i: ctx.i() }).await }.boxed_local()
|
||||||
}
|
}
|
||||||
fn dyn_serialize<'a>(
|
fn dyn_serialize<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
@@ -315,10 +315,10 @@ struct ObjStore {
|
|||||||
}
|
}
|
||||||
impl SysCtxEntry for ObjStore {}
|
impl SysCtxEntry for ObjStore {}
|
||||||
|
|
||||||
pub async fn get_own_instance<A: OwnedAtom>(typ: TypAtom<A>) -> A {
|
pub async fn own<A: OwnedAtom>(typ: TypAtom<A>) -> A {
|
||||||
let ctx = typ.data.ctx();
|
let ctx = typ.untyped.ctx();
|
||||||
let g = ctx.get_or_default::<ObjStore>().objects.read().await;
|
let g = ctx.get_or_default::<ObjStore>().objects.read().await;
|
||||||
let dyn_atom = (g.get(&typ.data.atom.drop.expect("Owned atoms always have a drop ID")))
|
let dyn_atom = (g.get(&typ.untyped.atom.drop.expect("Owned atoms always have a drop ID")))
|
||||||
.expect("Atom ID invalid; atom type probably not owned by this crate");
|
.expect("Atom ID invalid; atom type probably not owned by this crate");
|
||||||
dyn_atom.as_any_ref().downcast_ref().cloned().expect("The ID should imply a type as well")
|
dyn_atom.as_any_ref().downcast_ref().cloned().expect("The ID should imply a type as well")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,11 +105,11 @@ pub trait ThinAtom:
|
|||||||
{
|
{
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn call(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
fn call(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
||||||
async move { bot([err_not_callable(arg.ctx().i()).await]) }
|
async move { bot(err_not_callable(arg.ctx().i()).await) }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn command(&self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
|
fn command(&self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
|
||||||
async move { Err(err_not_command(ctx.i()).await.into()) }
|
async move { Err(err_not_command(ctx.i()).await) }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn print(&self, ctx: SysCtx) -> impl Future<Output = FmtUnit> {
|
fn print(&self, ctx: SysCtx) -> impl Future<Output = FmtUnit> {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::error::{OrcErr, OrcRes, mk_err};
|
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
||||||
use orchid_base::interner::Interner;
|
use orchid_base::interner::Interner;
|
||||||
use orchid_base::location::Pos;
|
use orchid_base::location::Pos;
|
||||||
|
|
||||||
use crate::atom::{AtomicFeatures, ToAtom, TypAtom};
|
use crate::atom::{AtomicFeatures, ForeignAtom, ToAtom, TypAtom};
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::gen_expr::{GExpr, atom, bot};
|
use crate::gen_expr::{GExpr, atom, bot};
|
||||||
use crate::system::{SysCtx, downcast_atom};
|
use crate::system::{SysCtx, downcast_atom};
|
||||||
@@ -24,22 +24,29 @@ impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn err_not_atom(pos: Pos, i: &Interner) -> OrcErr {
|
async fn err_not_atom(pos: Pos, i: &Interner) -> OrcErrv {
|
||||||
mk_err(i.i("Expected an atom").await, "This expression is not an atom", [pos.into()])
|
mk_errv(i.i("Expected an atom").await, "This expression is not an atom", [pos])
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn err_type(pos: Pos, i: &Interner) -> OrcErr {
|
async fn err_type(pos: Pos, i: &Interner) -> OrcErrv {
|
||||||
mk_err(i.i("Type error").await, "The atom is a different type than expected", [pos.into()])
|
mk_errv(i.i("Type error").await, "The atom is a different type than expected", [pos])
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFromExpr for ForeignAtom {
|
||||||
|
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||||
|
match expr.atom().await {
|
||||||
|
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), ex.ctx().i()).await),
|
||||||
|
Ok(f) => Ok(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: AtomicFeatures> TryFromExpr for TypAtom<A> {
|
impl<A: AtomicFeatures> TryFromExpr for TypAtom<A> {
|
||||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||||
match expr.atom().await {
|
let f = ForeignAtom::try_from_expr(expr).await?;
|
||||||
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), ex.ctx().i()).await.into()),
|
match downcast_atom::<A>(f).await {
|
||||||
Ok(f) => match downcast_atom::<A>(f).await {
|
Ok(a) => Ok(a),
|
||||||
Ok(a) => Ok(a),
|
Err(f) => Err(err_type(f.pos(), f.ctx().i()).await),
|
||||||
Err(f) => Err(err_type(f.pos(), f.ctx().i()).await.into()),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,29 +56,29 @@ impl TryFromExpr for SysCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToExpr {
|
pub trait ToExpr {
|
||||||
fn to_expr(self) -> GExpr;
|
fn to_expr(self) -> impl Future<Output = GExpr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToExpr for GExpr {
|
impl ToExpr for GExpr {
|
||||||
fn to_expr(self) -> GExpr { self }
|
async fn to_expr(self) -> GExpr { self }
|
||||||
}
|
}
|
||||||
impl ToExpr for Expr {
|
impl ToExpr for Expr {
|
||||||
fn to_expr(self) -> GExpr { self.slot() }
|
async fn to_expr(self) -> GExpr { self.slot() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ToExpr> ToExpr for OrcRes<T> {
|
impl<T: ToExpr> ToExpr for OrcRes<T> {
|
||||||
fn to_expr(self) -> GExpr {
|
async fn to_expr(self) -> GExpr {
|
||||||
match self {
|
match self {
|
||||||
Err(e) => bot(e),
|
Err(e) => bot(e),
|
||||||
Ok(t) => t.to_expr(),
|
Ok(t) => t.to_expr().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: ToAtom> ToExpr for A {
|
impl<A: ToAtom> ToExpr for A {
|
||||||
fn to_expr(self) -> GExpr { atom(self) }
|
async fn to_expr(self) -> GExpr { atom(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToExpr for Never {
|
impl ToExpr for Never {
|
||||||
fn to_expr(self) -> GExpr { match self {} }
|
async fn to_expr(self) -> GExpr { match self {} }
|
||||||
}
|
}
|
||||||
|
|||||||
96
orchid-extension/src/coroutine_exec.rs
Normal file
96
orchid-extension/src/coroutine_exec.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use async_std::channel::{Receiver, RecvError, Sender, bounded};
|
||||||
|
use async_std::sync::Mutex;
|
||||||
|
use futures::future::Fuse;
|
||||||
|
use futures::{FutureExt, select};
|
||||||
|
use never::Never;
|
||||||
|
use orchid_base::error::OrcRes;
|
||||||
|
|
||||||
|
use crate::atom::Atomic;
|
||||||
|
use crate::atom_owned::{OwnedAtom, OwnedVariant};
|
||||||
|
use crate::conv::{ToExpr, TryFromExpr};
|
||||||
|
use crate::expr::Expr;
|
||||||
|
use crate::gen_expr::{GExpr, arg, call, lambda, seq};
|
||||||
|
|
||||||
|
enum Command {
|
||||||
|
Execute(GExpr, Sender<Expr>),
|
||||||
|
Register(GExpr, Sender<Expr>),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BuilderCoroutineData {
|
||||||
|
work: Mutex<Fuse<Pin<Box<dyn Future<Output = GExpr>>>>>,
|
||||||
|
cmd_recv: Receiver<Command>,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct BuilderCoroutine(Rc<BuilderCoroutineData>);
|
||||||
|
impl BuilderCoroutine {
|
||||||
|
pub async fn run(self) -> GExpr {
|
||||||
|
let cmd = {
|
||||||
|
let mut work = self.0.work.lock().await;
|
||||||
|
select! {
|
||||||
|
ret_val = &mut *work => { return ret_val },
|
||||||
|
cmd_res = self.0.cmd_recv.recv().fuse() => match cmd_res {
|
||||||
|
Ok(cmd) => cmd,
|
||||||
|
Err(RecvError) => return (&mut *work).await
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match cmd {
|
||||||
|
Command::Execute(expr, reply) => call([
|
||||||
|
lambda(0, [seq([
|
||||||
|
arg(0),
|
||||||
|
call([Replier { reply, builder: self }.to_expr().await, arg(0)]),
|
||||||
|
])]),
|
||||||
|
expr,
|
||||||
|
]),
|
||||||
|
Command::Register(expr, reply) =>
|
||||||
|
call([Replier { reply, builder: self }.to_expr().await, expr]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Replier {
|
||||||
|
reply: Sender<Expr>,
|
||||||
|
builder: BuilderCoroutine,
|
||||||
|
}
|
||||||
|
impl Atomic for Replier {
|
||||||
|
type Data = ();
|
||||||
|
type Variant = OwnedVariant;
|
||||||
|
}
|
||||||
|
impl OwnedAtom for Replier {
|
||||||
|
type Refs = Never;
|
||||||
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
|
async fn call(self, arg: Expr) -> GExpr {
|
||||||
|
let _ = self.reply.send(arg).await;
|
||||||
|
self.builder.run().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static) -> GExpr {
|
||||||
|
let (cmd_snd, cmd_recv) = bounded(1);
|
||||||
|
let work =
|
||||||
|
async { f(ExecHandle(cmd_snd, PhantomData)).await.to_expr().await }.boxed_local().fuse();
|
||||||
|
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData { cmd_recv, work: Mutex::new(work) }));
|
||||||
|
coro.run().await
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEIRD_DROP_ERR: &str = "Coroutine dropped while we are being polled somehow";
|
||||||
|
|
||||||
|
pub struct ExecHandle<'a>(Sender<Command>, PhantomData<&'a ()>);
|
||||||
|
impl ExecHandle<'_> {
|
||||||
|
pub async fn exec<T: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<T> {
|
||||||
|
let (reply_snd, reply_recv) = bounded(1);
|
||||||
|
self.0.send(Command::Execute(val.to_expr().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
|
||||||
|
T::try_from_expr(reply_recv.recv().await.expect(WEIRD_DROP_ERR)).await
|
||||||
|
}
|
||||||
|
pub async fn register(&mut self, val: impl ToExpr) -> Expr {
|
||||||
|
let (reply_snd, reply_recv) = bounded(1);
|
||||||
|
self.0.send(Command::Register(val.to_expr().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
|
||||||
|
reply_recv.recv().await.expect(WEIRD_DROP_ERR)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ use crate::api;
|
|||||||
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId};
|
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId};
|
||||||
use crate::atom_owned::take_atom;
|
use crate::atom_owned::take_atom;
|
||||||
use crate::expr::{Expr, ExprHandle};
|
use crate::expr::{Expr, ExprHandle};
|
||||||
use crate::lexer::{LexContext, err_cascade, err_not_applicable};
|
use crate::lexer::{LexContext, ekey_cascade, ekey_not_applicable};
|
||||||
use crate::parser::{PTok, PTokTree, ParsCtx, get_const, linev_into_api};
|
use crate::parser::{PTok, PTokTree, ParsCtx, get_const, linev_into_api};
|
||||||
use crate::system::{SysCtx, atom_by_idx};
|
use crate::system::{SysCtx, atom_by_idx};
|
||||||
use crate::system_ctor::{CtedObj, DynSystemCtor};
|
use crate::system_ctor::{CtedObj, DynSystemCtor};
|
||||||
@@ -230,16 +230,17 @@ pub fn extension_init(
|
|||||||
let sys_ctx = get_ctx(sys).await;
|
let sys_ctx = get_ctx(sys).await;
|
||||||
let text = Tok::from_api(text, &i).await;
|
let text = Tok::from_api(text, &i).await;
|
||||||
let src = Sym::from_api(src, sys_ctx.i()).await;
|
let src = Sym::from_api(src, sys_ctx.i()).await;
|
||||||
let ctx = LexContext { id, pos, text: &text, src, ctx: sys_ctx.clone() };
|
let rep = Reporter::new();
|
||||||
|
let ctx = LexContext { id, pos, text: &text, src, ctx: sys_ctx.clone(), rep: &rep };
|
||||||
let trigger_char = text.chars().nth(pos as usize).unwrap();
|
let trigger_char = text.chars().nth(pos as usize).unwrap();
|
||||||
let err_na = err_not_applicable(&i).await;
|
let ekey_na = ekey_not_applicable(&i).await;
|
||||||
let err_cascade = err_cascade(&i).await;
|
let ekey_cascade = ekey_cascade(&i).await;
|
||||||
let lexers = sys_ctx.cted().inst().dyn_lexers();
|
let lexers = sys_ctx.cted().inst().dyn_lexers();
|
||||||
for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) {
|
for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) {
|
||||||
match lx.lex(&text[pos as usize..], &ctx).await {
|
match lx.lex(&text[pos as usize..], &ctx).await {
|
||||||
Err(e) if e.any(|e| *e == err_na) => continue,
|
Err(e) if e.any(|e| *e == ekey_na) => continue,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let eopt = e.keep_only(|e| *e != err_cascade).map(|e| Err(e.to_api()));
|
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
|
||||||
return hand.handle(&lex, &eopt).await;
|
return hand.handle(&lex, &eopt).await;
|
||||||
},
|
},
|
||||||
Ok((s, expr)) => {
|
Ok((s, expr)) => {
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
//! #TODO: redefine this in terms of [crate::coroutine_exec::exec]. Try
|
||||||
|
//! differentiating between eager and lazy arguments. [ExprFunc] can remain with
|
||||||
|
//! tweaks but other techniques probably must go.
|
||||||
|
//!
|
||||||
|
//! Notice also that Func must still be resumable
|
||||||
|
use std::any::TypeId;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
@@ -20,6 +26,7 @@ use trait_set::trait_set;
|
|||||||
use crate::atom::Atomic;
|
use crate::atom::Atomic;
|
||||||
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||||
use crate::conv::ToExpr;
|
use crate::conv::ToExpr;
|
||||||
|
use crate::coroutine_exec::{ExecHandle, exec};
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::gen_expr::GExpr;
|
use crate::gen_expr::GExpr;
|
||||||
use crate::system::{SysCtx, SysCtxEntry};
|
use crate::system::{SysCtx, SysCtxEntry};
|
||||||
@@ -29,13 +36,37 @@ trait_set! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ExprFunc<I, O>: Clone + 'static {
|
pub trait ExprFunc<I, O>: Clone + 'static {
|
||||||
const ARITY: u8;
|
fn argtyps() -> &'static [TypeId];
|
||||||
fn apply(&self, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
|
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct FunsCtx(Mutex<HashMap<Sym, (u8, Rc<dyn FunCB>)>>);
|
struct FunsCtx(Mutex<HashMap<Sym, FunRecord>>);
|
||||||
impl SysCtxEntry for FunsCtx {}
|
impl SysCtxEntry for FunsCtx {}
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct FunRecord {
|
||||||
|
argtyps: &'static [TypeId],
|
||||||
|
fun: Rc<dyn FunCB>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_args<I, O, F: ExprFunc<I, O>>(f: F) -> FunRecord {
|
||||||
|
let argtyps = F::argtyps();
|
||||||
|
let fun = Rc::new(move |v: Vec<Expr>| {
|
||||||
|
clone!(f, v mut);
|
||||||
|
exec(async move |mut hand| {
|
||||||
|
let mut norm_args = Vec::with_capacity(v.len());
|
||||||
|
for (expr, typ) in v.into_iter().zip(argtyps) {
|
||||||
|
if *typ != TypeId::of::<Expr>() {
|
||||||
|
norm_args.push(hand.exec(expr).await?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.apply(hand, norm_args).await
|
||||||
|
})
|
||||||
|
.map(Ok)
|
||||||
|
.boxed_local()
|
||||||
|
});
|
||||||
|
FunRecord { argtyps, fun }
|
||||||
|
}
|
||||||
|
|
||||||
/// An Atom representing a partially applied named native function. These
|
/// An Atom representing a partially applied named native function. These
|
||||||
/// partial calls are serialized into the name of the native function and the
|
/// partial calls are serialized into the name of the native function and the
|
||||||
@@ -46,23 +77,22 @@ impl SysCtxEntry for FunsCtx {}
|
|||||||
pub(crate) struct Fun {
|
pub(crate) struct Fun {
|
||||||
path: Sym,
|
path: Sym,
|
||||||
args: Vec<Expr>,
|
args: Vec<Expr>,
|
||||||
arity: u8,
|
record: FunRecord,
|
||||||
fun: Rc<dyn FunCB>,
|
|
||||||
}
|
}
|
||||||
impl Fun {
|
impl Fun {
|
||||||
pub async fn new<I, O, F: ExprFunc<I, O>>(path: Sym, ctx: SysCtx, f: F) -> Self {
|
pub async fn new<I, O, F: ExprFunc<I, O>>(path: Sym, ctx: SysCtx, f: F) -> Self {
|
||||||
let funs: &FunsCtx = ctx.get_or_default();
|
let funs: &FunsCtx = ctx.get_or_default();
|
||||||
let mut fung = funs.0.lock().await;
|
let mut fung = funs.0.lock().await;
|
||||||
let fun = if let Some(x) = fung.get(&path) {
|
let record = if let Some(record) = fung.get(&path) {
|
||||||
x.1.clone()
|
record.clone()
|
||||||
} else {
|
} else {
|
||||||
let fun = Rc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local()));
|
let record = process_args(f).await;
|
||||||
fung.insert(path.clone(), (F::ARITY, fun.clone()));
|
fung.insert(path.clone(), record.clone());
|
||||||
fun
|
record
|
||||||
};
|
};
|
||||||
Self { args: vec![], arity: F::ARITY, path, fun }
|
Self { args: vec![], path, record }
|
||||||
}
|
}
|
||||||
pub fn arity(&self) -> u8 { self.arity }
|
pub fn arity(&self) -> u8 { self.record.argtyps.len() as u8 }
|
||||||
}
|
}
|
||||||
impl Atomic for Fun {
|
impl Atomic for Fun {
|
||||||
type Data = ();
|
type Data = ();
|
||||||
@@ -74,11 +104,10 @@ impl OwnedAtom for Fun {
|
|||||||
async fn call_ref(&self, arg: Expr) -> GExpr {
|
async fn call_ref(&self, arg: Expr) -> GExpr {
|
||||||
std::io::Write::flush(&mut std::io::stderr()).unwrap();
|
std::io::Write::flush(&mut std::io::stderr()).unwrap();
|
||||||
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
|
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
|
||||||
if new_args.len() == self.arity.into() {
|
if new_args.len() == self.record.argtyps.len() {
|
||||||
(self.fun)(new_args).await.to_expr()
|
(self.record.fun)(new_args).await.to_expr().await
|
||||||
} else {
|
} else {
|
||||||
Self { args: new_args, arity: self.arity, fun: self.fun.clone(), path: self.path.clone() }
|
Self { args: new_args, record: self.record.clone(), path: self.path.clone() }.to_expr().await
|
||||||
.to_expr()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
||||||
@@ -89,11 +118,13 @@ impl OwnedAtom for Fun {
|
|||||||
async fn deserialize(mut ctx: impl DeserializeCtx, args: Self::Refs) -> Self {
|
async fn deserialize(mut ctx: impl DeserializeCtx, args: Self::Refs) -> Self {
|
||||||
let sys = ctx.sys();
|
let sys = ctx.sys();
|
||||||
let path = Sym::from_api(ctx.decode().await, sys.i()).await;
|
let path = Sym::from_api(ctx.decode().await, sys.i()).await;
|
||||||
let (arity, fun) = sys.get_or_default::<FunsCtx>().0.lock().await.get(&path).unwrap().clone();
|
let record = (sys.get::<FunsCtx>().0.lock().await.get(&path))
|
||||||
Self { args, arity, path, fun }
|
.expect("Function missing during deserialization")
|
||||||
|
.clone();
|
||||||
|
Self { args, path, record }
|
||||||
}
|
}
|
||||||
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
format!("{}:{}/{}", self.path, self.args.len(), self.arity).into()
|
format!("{}:{}/{}", self.path, self.args.len(), self.arity()).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,13 +135,11 @@ impl OwnedAtom for Fun {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Lambda {
|
pub struct Lambda {
|
||||||
args: Vec<Expr>,
|
args: Vec<Expr>,
|
||||||
arity: u8,
|
record: FunRecord,
|
||||||
fun: Rc<dyn FunCB>,
|
|
||||||
}
|
}
|
||||||
impl Lambda {
|
impl Lambda {
|
||||||
pub fn new<I, O, F: ExprFunc<I, O>>(f: F) -> Self {
|
pub async fn new<I, O, F: ExprFunc<I, O>>(f: F) -> Self {
|
||||||
let fun = Rc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local()));
|
Self { args: vec![], record: process_args(f).await }
|
||||||
Self { args: vec![], arity: F::ARITY, fun }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Atomic for Lambda {
|
impl Atomic for Lambda {
|
||||||
@@ -122,53 +151,59 @@ impl OwnedAtom for Lambda {
|
|||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
async fn call_ref(&self, arg: Expr) -> GExpr {
|
async fn call_ref(&self, arg: Expr) -> GExpr {
|
||||||
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
|
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
|
||||||
if new_args.len() == self.arity.into() {
|
if new_args.len() == self.record.argtyps.len() {
|
||||||
(self.fun)(new_args).await.to_expr()
|
(self.record.fun)(new_args).await.to_expr().await
|
||||||
} else {
|
} else {
|
||||||
Self { args: new_args, arity: self.arity, fun: self.fun.clone() }.to_expr()
|
Self { args: new_args, record: self.record.clone() }.to_expr().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
||||||
}
|
}
|
||||||
|
|
||||||
mod expr_func_derives {
|
mod expr_func_derives {
|
||||||
|
use std::any::TypeId;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::error::OrcRes;
|
||||||
|
|
||||||
use super::ExprFunc;
|
use super::ExprFunc;
|
||||||
use crate::conv::{ToExpr, TryFromExpr};
|
use crate::conv::{ToExpr, TryFromExpr};
|
||||||
use crate::func_atom::Expr;
|
use crate::func_atom::{ExecHandle, Expr};
|
||||||
use crate::gen_expr::GExpr;
|
use crate::gen_expr::GExpr;
|
||||||
|
|
||||||
macro_rules! expr_func_derive {
|
macro_rules! expr_func_derive {
|
||||||
($arity: tt, $($t:ident),*) => {
|
($($t:ident),*) => {
|
||||||
pastey::paste!{
|
pastey::paste!{
|
||||||
impl<
|
impl<
|
||||||
$($t: TryFromExpr, )*
|
$($t: TryFromExpr + 'static, )*
|
||||||
Out: ToExpr,
|
Out: ToExpr,
|
||||||
Func: AsyncFn($($t,)*) -> Out + Clone + Send + Sync + 'static
|
Func: AsyncFn($($t,)*) -> Out + Clone + Send + Sync + 'static
|
||||||
> ExprFunc<($($t,)*), Out> for Func {
|
> ExprFunc<($($t,)*), Out> for Func {
|
||||||
const ARITY: u8 = $arity;
|
fn argtyps() -> &'static [TypeId] {
|
||||||
async fn apply(&self, v: Vec<Expr>) -> OrcRes<GExpr> {
|
static STORE: OnceLock<Vec<TypeId>> = OnceLock::new();
|
||||||
assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch");
|
&*STORE.get_or_init(|| vec![$(TypeId::of::<$t>()),*])
|
||||||
|
}
|
||||||
|
async fn apply<'a>(&self, _: ExecHandle<'a>, v: Vec<Expr>) -> OrcRes<GExpr> {
|
||||||
|
assert_eq!(v.len(), Self::argtyps().len(), "Arity mismatch");
|
||||||
let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above"));
|
let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above"));
|
||||||
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).await.to_expr())
|
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).await.to_expr().await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
expr_func_derive!(1, A);
|
expr_func_derive!(A);
|
||||||
expr_func_derive!(2, A, B);
|
expr_func_derive!(A, B);
|
||||||
expr_func_derive!(3, A, B, C);
|
expr_func_derive!(A, B, C);
|
||||||
expr_func_derive!(4, A, B, C, D);
|
expr_func_derive!(A, B, C, D);
|
||||||
expr_func_derive!(5, A, B, C, D, E);
|
expr_func_derive!(A, B, C, D, E);
|
||||||
expr_func_derive!(6, A, B, C, D, E, F);
|
// expr_func_derive!(A, B, C, D, E, F);
|
||||||
expr_func_derive!(7, A, B, C, D, E, F, G);
|
// expr_func_derive!(A, B, C, D, E, F, G);
|
||||||
expr_func_derive!(8, A, B, C, D, E, F, G, H);
|
// expr_func_derive!(A, B, C, D, E, F, G, H);
|
||||||
expr_func_derive!(9, A, B, C, D, E, F, G, H, I);
|
// expr_func_derive!(A, B, C, D, E, F, G, H, I);
|
||||||
expr_func_derive!(10, A, B, C, D, E, F, G, H, I, J);
|
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J);
|
||||||
expr_func_derive!(11, A, B, C, D, E, F, G, H, I, J, K);
|
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J, K);
|
||||||
expr_func_derive!(12, A, B, C, D, E, F, G, H, I, J, K, L);
|
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||||
expr_func_derive!(13, A, B, C, D, E, F, G, H, I, J, K, L, M);
|
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||||
expr_func_derive!(14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ use orchid_base::{match_mapping, tl_cache};
|
|||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::atom::{AtomFactory, ToAtom};
|
use crate::atom::{AtomFactory, ToAtom};
|
||||||
use crate::conv::{ToExpr, TryFromExpr};
|
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::func_atom::Lambda;
|
|
||||||
use crate::system::SysCtx;
|
use crate::system::SysCtx;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -35,6 +33,7 @@ impl GExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn at(self, pos: Pos) -> Self { GExpr { pos, kind: self.kind } }
|
||||||
}
|
}
|
||||||
impl Format for GExpr {
|
impl Format for GExpr {
|
||||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
@@ -129,10 +128,3 @@ pub fn call(v: impl IntoIterator<Item = GExpr>) -> GExpr {
|
|||||||
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
|
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
|
||||||
inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap()))
|
inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with<I: TryFromExpr, O: ToExpr>(
|
|
||||||
expr: GExpr,
|
|
||||||
cont: impl AsyncFn(I) -> O + Clone + Send + Sync + 'static,
|
|
||||||
) -> GExpr {
|
|
||||||
call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr])
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,31 +4,33 @@ use std::ops::RangeInclusive;
|
|||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use orchid_base::error::{OrcErr, OrcRes, mk_err};
|
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv};
|
||||||
use orchid_base::interner::{Interner, Tok};
|
use orchid_base::interner::{Interner, Tok};
|
||||||
use orchid_base::location::{Pos, SrcRange};
|
use orchid_base::location::{Pos, SrcRange};
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
|
use orchid_base::parse::ParseCtx;
|
||||||
use orchid_base::reqnot::Requester;
|
use orchid_base::reqnot::Requester;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
use crate::parser::PTokTree;
|
||||||
use crate::system::SysCtx;
|
use crate::system::SysCtx;
|
||||||
use crate::tree::GenTokTree;
|
use crate::tree::GenTokTree;
|
||||||
|
|
||||||
pub async fn err_cascade(i: &Interner) -> OrcErr {
|
pub async fn ekey_cascade(i: &Interner) -> Tok<String> {
|
||||||
mk_err(
|
i.i("An error cascading from a recursive call").await
|
||||||
i.i("An error cascading from a recursive call").await,
|
}
|
||||||
"This error is a sentinel for the extension library.\
|
pub async fn ekey_not_applicable(i: &Interner) -> Tok<String> {
|
||||||
it should not be emitted by the extension.",
|
i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await
|
||||||
[Pos::None.into()],
|
}
|
||||||
)
|
const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\
|
||||||
|
it should not be emitted by the extension.";
|
||||||
|
|
||||||
|
pub async fn err_cascade(i: &Interner) -> OrcErrv {
|
||||||
|
mk_errv(ekey_cascade(i).await, MSG_INTERNAL_ERROR, [Pos::None])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn err_not_applicable(i: &Interner) -> OrcErr {
|
pub async fn err_not_applicable(i: &Interner) -> OrcErrv {
|
||||||
mk_err(
|
mk_errv(ekey_not_applicable(i).await, MSG_INTERNAL_ERROR, [Pos::None])
|
||||||
i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await,
|
|
||||||
&*err_cascade(i).await.message,
|
|
||||||
[Pos::None.into()],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LexContext<'a> {
|
pub struct LexContext<'a> {
|
||||||
@@ -36,16 +38,22 @@ pub struct LexContext<'a> {
|
|||||||
pub text: &'a Tok<String>,
|
pub text: &'a Tok<String>,
|
||||||
pub id: api::ParsId,
|
pub id: api::ParsId,
|
||||||
pub pos: u32,
|
pub pos: u32,
|
||||||
pub src: Sym,
|
pub(crate) src: Sym,
|
||||||
|
pub(crate) rep: &'a Reporter,
|
||||||
}
|
}
|
||||||
impl<'a> LexContext<'a> {
|
impl<'a> LexContext<'a> {
|
||||||
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, GenTokTree)> {
|
pub fn src(&self) -> &Sym { &self.src }
|
||||||
|
/// This function returns [PTokTree] because it can never return
|
||||||
|
/// [orchid_base::tree::Token::NewExpr]. You can use
|
||||||
|
/// [crate::parser::p_tree2gen] to convert this to [crate::tree::GenTokTree]
|
||||||
|
/// for embedding in the return value.
|
||||||
|
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, PTokTree)> {
|
||||||
let start = self.pos(tail);
|
let start = self.pos(tail);
|
||||||
let Some(lx) = self.ctx.reqnot().request(api::SubLex { pos: start, id: self.id }).await else {
|
let Some(lx) = self.ctx.reqnot().request(api::SubLex { pos: start, id: self.id }).await else {
|
||||||
return Err(err_cascade(self.ctx.i()).await.into());
|
return Err(err_cascade(self.ctx.i()).await);
|
||||||
};
|
};
|
||||||
let tree =
|
let tree =
|
||||||
GenTokTree::from_api(&lx.tree, &mut self.ctx.clone(), &mut (), &self.src, self.ctx.i()).await;
|
PTokTree::from_api(&lx.tree, &mut self.ctx.clone(), &mut (), &self.src, self.ctx.i()).await;
|
||||||
Ok((&self.text[lx.pos as usize..], tree))
|
Ok((&self.text[lx.pos as usize..], tree))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,8 +65,10 @@ impl<'a> LexContext<'a> {
|
|||||||
pub fn pos_lt(&self, len: impl TryInto<u32, Error: fmt::Debug>, tail: &'a str) -> SrcRange {
|
pub fn pos_lt(&self, len: impl TryInto<u32, Error: fmt::Debug>, tail: &'a str) -> SrcRange {
|
||||||
SrcRange::new(self.pos(tail) - len.try_into().unwrap()..self.pos(tail), &self.src)
|
SrcRange::new(self.pos(tail) - len.try_into().unwrap()..self.pos(tail), &self.src)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn i(&self) -> &Interner { self.ctx.i() }
|
impl ParseCtx for LexContext<'_> {
|
||||||
|
fn i(&self) -> &Interner { self.ctx.i() }
|
||||||
|
fn rep(&self) -> &Reporter { self.rep }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Lexer: Send + Sync + Sized + Default + 'static {
|
pub trait Lexer: Send + Sync + Sized + Default + 'static {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ pub mod atom;
|
|||||||
pub mod atom_owned;
|
pub mod atom_owned;
|
||||||
pub mod atom_thin;
|
pub mod atom_thin;
|
||||||
pub mod conv;
|
pub mod conv;
|
||||||
|
pub mod coroutine_exec;
|
||||||
pub mod entrypoint;
|
pub mod entrypoint;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
pub mod func_atom;
|
pub mod func_atom;
|
||||||
@@ -12,6 +13,7 @@ pub mod lexer;
|
|||||||
pub mod msg;
|
pub mod msg;
|
||||||
pub mod other_system;
|
pub mod other_system;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod reflection;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
pub mod system_ctor;
|
pub mod system_ctor;
|
||||||
pub mod tokio;
|
pub mod tokio;
|
||||||
|
|||||||
@@ -2,29 +2,48 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use futures::future::{LocalBoxFuture, join_all};
|
use futures::future::{LocalBoxFuture, join_all};
|
||||||
use futures::{FutureExt, Stream, StreamExt, pin_mut};
|
use futures::{FutureExt, Stream, StreamExt};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_api::ResolveNames;
|
use orchid_api::ResolveNames;
|
||||||
use orchid_base::error::{OrcRes, Reporter};
|
use orchid_base::error::{OrcErrv, OrcRes, Reporter};
|
||||||
use orchid_base::id_store::IdStore;
|
use orchid_base::id_store::IdStore;
|
||||||
use orchid_base::interner::{Interner, Tok};
|
use orchid_base::interner::{Interner, Tok};
|
||||||
use orchid_base::location::SrcRange;
|
use orchid_base::location::SrcRange;
|
||||||
|
use orchid_base::match_mapping;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
use orchid_base::parse::{Comment, ParseCtx, Snippet};
|
use orchid_base::parse::{Comment, ParseCtx, Snippet};
|
||||||
use orchid_base::reqnot::{ReqHandlish, Requester};
|
use orchid_base::reqnot::{ReqHandlish, Requester};
|
||||||
use orchid_base::tree::{TokTree, Token, ttv_into_api};
|
use orchid_base::tree::{TokTree, Token, ttv_into_api};
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
use crate::conv::ToExpr;
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::gen_expr::GExpr;
|
use crate::gen_expr::GExpr;
|
||||||
use crate::system::{SysCtx, SysCtxEntry};
|
use crate::system::{SysCtx, SysCtxEntry};
|
||||||
use crate::tree::GenTokTree;
|
use crate::tree::{GenTok, GenTokTree};
|
||||||
|
|
||||||
pub type PTok = Token<Expr, Never>;
|
pub type PTok = Token<Expr, Never>;
|
||||||
pub type PTokTree = TokTree<Expr, Never>;
|
pub type PTokTree = TokTree<Expr, Never>;
|
||||||
pub type PSnippet<'a> = Snippet<'a, Expr, Never>;
|
pub type PSnippet<'a> = Snippet<'a, Expr, Never>;
|
||||||
|
|
||||||
|
pub fn p_tok2gen(tok: PTok) -> GenTok {
|
||||||
|
match_mapping!(tok, PTok => GenTok {
|
||||||
|
Comment(s), Name(n), BR, Handle(ex), Bottom(err),
|
||||||
|
LambdaHead(arg => Box::new(p_tree2gen(*arg))),
|
||||||
|
NS(n, arg => Box::new(p_tree2gen(*arg))),
|
||||||
|
S(p, body () p_v2gen),
|
||||||
|
} {
|
||||||
|
PTok::NewExpr(never) => match never {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn p_tree2gen(tree: PTokTree) -> GenTokTree {
|
||||||
|
TokTree { tok: p_tok2gen(tree.tok), sr: tree.sr }
|
||||||
|
}
|
||||||
|
pub fn p_v2gen(v: impl IntoIterator<Item = PTokTree>) -> Vec<GenTokTree> {
|
||||||
|
v.into_iter().map(p_tree2gen).collect_vec()
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Parser: Send + Sync + Sized + Default + 'static {
|
pub trait Parser: Send + Sync + Sized + Default + 'static {
|
||||||
const LINE_HEAD: &'static str;
|
const LINE_HEAD: &'static str;
|
||||||
fn parse<'a>(
|
fn parse<'a>(
|
||||||
@@ -93,6 +112,31 @@ pub struct ParsedLine {
|
|||||||
pub kind: ParsedLineKind,
|
pub kind: ParsedLineKind,
|
||||||
}
|
}
|
||||||
impl ParsedLine {
|
impl ParsedLine {
|
||||||
|
pub fn cnst<'a, R: ToExpr + 'static, F: AsyncFnOnce(ConstCtx) -> R + 'static>(
|
||||||
|
sr: &SrcRange,
|
||||||
|
comments: impl IntoIterator<Item = &'a Comment>,
|
||||||
|
exported: bool,
|
||||||
|
name: Tok<String>,
|
||||||
|
f: F,
|
||||||
|
) -> Self {
|
||||||
|
let cb = Box::new(|ctx| async move { f(ctx).await.to_expr().await }.boxed_local());
|
||||||
|
let kind = ParsedLineKind::Mem(ParsedMem { name, exported, kind: ParsedMemKind::Const(cb) });
|
||||||
|
let comments = comments.into_iter().cloned().collect();
|
||||||
|
ParsedLine { comments, sr: sr.clone(), kind }
|
||||||
|
}
|
||||||
|
pub fn module<'a>(
|
||||||
|
sr: &SrcRange,
|
||||||
|
comments: impl IntoIterator<Item = &'a Comment>,
|
||||||
|
exported: bool,
|
||||||
|
name: &Tok<String>,
|
||||||
|
use_prelude: bool,
|
||||||
|
lines: impl IntoIterator<Item = ParsedLine>,
|
||||||
|
) -> Self {
|
||||||
|
let mem_kind = ParsedMemKind::Mod { lines: lines.into_iter().collect(), use_prelude };
|
||||||
|
let line_kind = ParsedLineKind::Mem(ParsedMem { name: name.clone(), exported, kind: mem_kind });
|
||||||
|
let comments = comments.into_iter().cloned().collect();
|
||||||
|
ParsedLine { comments, sr: sr.clone(), kind: line_kind }
|
||||||
|
}
|
||||||
pub async fn into_api(self, ctx: SysCtx, hand: &dyn ReqHandlish) -> api::ParsedLine {
|
pub async fn into_api(self, ctx: SysCtx, hand: &dyn ReqHandlish) -> api::ParsedLine {
|
||||||
api::ParsedLine {
|
api::ParsedLine {
|
||||||
comments: self.comments.into_iter().map(|c| c.to_api()).collect(),
|
comments: self.comments.into_iter().map(|c| c.to_api()).collect(),
|
||||||
@@ -142,18 +186,6 @@ pub enum ParsedMemKind {
|
|||||||
Mod { lines: Vec<ParsedLine>, use_prelude: bool },
|
Mod { lines: Vec<ParsedLine>, use_prelude: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParsedMemKind {
|
|
||||||
pub fn cnst<F: AsyncFnOnce(ConstCtx) -> GExpr + 'static>(f: F) -> Self {
|
|
||||||
Self::Const(Box::new(|ctx| Box::pin(f(ctx))))
|
|
||||||
}
|
|
||||||
pub fn module(lines: impl IntoIterator<Item = ParsedLine>) -> Self {
|
|
||||||
Self::Mod { lines: lines.into_iter().collect(), use_prelude: true }
|
|
||||||
}
|
|
||||||
pub fn clean_module(lines: impl IntoIterator<Item = ParsedLine>) -> Self {
|
|
||||||
Self::Mod { lines: lines.into_iter().collect(), use_prelude: false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: how the macro runner uses the multi-stage loader
|
/* TODO: how the macro runner uses the multi-stage loader
|
||||||
|
|
||||||
Since the macro runner actually has to invoke the interpreter,
|
Since the macro runner actually has to invoke the interpreter,
|
||||||
@@ -169,16 +201,18 @@ postparse / const load links up constants with every macro they can directly inv
|
|||||||
the constants representing the keywords resolve to panic
|
the constants representing the keywords resolve to panic
|
||||||
execute relies on these links detected in the extension to dispatch relevant macros
|
execute relies on these links detected in the extension to dispatch relevant macros
|
||||||
*/
|
*/
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ConstCtx {
|
pub struct ConstCtx {
|
||||||
ctx: SysCtx,
|
ctx: SysCtx,
|
||||||
constid: api::ParsedConstId,
|
constid: api::ParsedConstId,
|
||||||
}
|
}
|
||||||
impl ConstCtx {
|
impl ConstCtx {
|
||||||
pub fn names<'a>(
|
pub fn ctx(&self) -> &SysCtx { &self.ctx }
|
||||||
&'a self,
|
pub fn i(&self) -> &Interner { self.ctx.i() }
|
||||||
names: impl IntoIterator<Item = &'a Sym> + 'a,
|
pub fn names<'b>(
|
||||||
) -> impl Stream<Item = Option<Sym>> + 'a {
|
&'b self,
|
||||||
|
names: impl IntoIterator<Item = &'b Sym> + 'b,
|
||||||
|
) -> impl Stream<Item = OrcRes<Sym>> + 'b {
|
||||||
let resolve_names = ResolveNames {
|
let resolve_names = ResolveNames {
|
||||||
constid: self.constid,
|
constid: self.constid,
|
||||||
sys: self.ctx.sys_id(),
|
sys: self.ctx.sys_id(),
|
||||||
@@ -187,20 +221,14 @@ impl ConstCtx {
|
|||||||
stream! {
|
stream! {
|
||||||
for name_opt in self.ctx.reqnot().request(resolve_names).await {
|
for name_opt in self.ctx.reqnot().request(resolve_names).await {
|
||||||
yield match name_opt {
|
yield match name_opt {
|
||||||
None => None,
|
Err(e) => Err(OrcErrv::from_api(&e, self.ctx.i()).await),
|
||||||
Some(name) => Some(Sym::from_api(name, self.ctx.i()).await)
|
Ok(name) => Ok(Sym::from_api(name, self.ctx.i()).await)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [Option<Sym>; N] {
|
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] {
|
||||||
let mut results = [const { None }; N];
|
self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match")
|
||||||
let names = self.names(names).enumerate().filter_map(|(i, n)| async move { Some((i, n?)) });
|
|
||||||
pin_mut!(names);
|
|
||||||
while let Some((i, name)) = names.next().await {
|
|
||||||
results[i] = Some(name);
|
|
||||||
}
|
|
||||||
results
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
163
orchid-extension/src/reflection.rs
Normal file
163
orchid-extension/src/reflection.rs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
use std::cell::OnceCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use async_std::sync::Mutex;
|
||||||
|
use futures::FutureExt;
|
||||||
|
use memo_map::MemoMap;
|
||||||
|
use orchid_base::interner::Tok;
|
||||||
|
use orchid_base::name::{NameLike, VPath};
|
||||||
|
use orchid_base::reqnot::Requester;
|
||||||
|
|
||||||
|
use crate::api;
|
||||||
|
use crate::system::{SysCtx, SysCtxEntry, WeakSysCtx};
|
||||||
|
|
||||||
|
pub struct ReflMemData {
|
||||||
|
// None for inferred steps
|
||||||
|
public: OnceCell<bool>,
|
||||||
|
kind: ReflMemKind,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ReflMem(Rc<ReflMemData>);
|
||||||
|
impl ReflMem {
|
||||||
|
pub fn kind(&self) -> ReflMemKind { self.0.kind.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum ReflMemKind {
|
||||||
|
Const,
|
||||||
|
Mod(ReflMod),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReflModData {
|
||||||
|
inferred: Mutex<bool>,
|
||||||
|
path: VPath,
|
||||||
|
ctx: WeakSysCtx,
|
||||||
|
members: MemoMap<Tok<String>, ReflMem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ReflMod(Rc<ReflModData>);
|
||||||
|
impl ReflMod {
|
||||||
|
fn ctx(&self) -> SysCtx {
|
||||||
|
self.0.ctx.upgrade().expect("ReflectedModule accessed after context drop")
|
||||||
|
}
|
||||||
|
pub fn path(&self) -> &[Tok<String>] { &self.0.path[..] }
|
||||||
|
pub fn is_root(&self) -> bool { self.0.path.is_empty() }
|
||||||
|
async fn try_populate(&self) -> Result<(), api::LsModuleError> {
|
||||||
|
let ctx = self.ctx();
|
||||||
|
let path_tok = ctx.i().i(&self.0.path[..]).await;
|
||||||
|
let reply = match ctx.reqnot().request(api::LsModule(ctx.sys_id(), path_tok.to_api())).await {
|
||||||
|
Err(api::LsModuleError::TreeUnavailable) =>
|
||||||
|
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty."),
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
Ok(details) => details,
|
||||||
|
};
|
||||||
|
for (k, v) in reply.members {
|
||||||
|
let k = ctx.i().ex(k).await;
|
||||||
|
let mem = match self.0.members.get(&k) {
|
||||||
|
Some(mem) => mem,
|
||||||
|
None => {
|
||||||
|
let path = self.0.path.clone().name_with_suffix(k.clone()).to_sym(ctx.i()).await;
|
||||||
|
let kind = match v.kind {
|
||||||
|
api::MemberInfoKind::Constant => ReflMemKind::Const,
|
||||||
|
api::MemberInfoKind::Module =>
|
||||||
|
ReflMemKind::Mod(default_module(&ctx, VPath::new(path.segs()))),
|
||||||
|
};
|
||||||
|
self.0.members.get_or_insert(&k, || default_member(self.is_root(), kind))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let _ = mem.0.public.set(v.public);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub async fn get_child(&self, key: &Tok<String>) -> Option<ReflMem> {
|
||||||
|
let inferred_g = self.0.inferred.lock().await;
|
||||||
|
if let Some(mem) = self.0.members.get(key) {
|
||||||
|
return Some(mem.clone());
|
||||||
|
}
|
||||||
|
if !*inferred_g {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match self.try_populate().await {
|
||||||
|
Err(api::LsModuleError::InvalidPath) =>
|
||||||
|
panic!("Path became invalid since module was created"),
|
||||||
|
Err(api::LsModuleError::IsConstant) =>
|
||||||
|
panic!("Path previously contained a module but now contains a constant"),
|
||||||
|
Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
|
||||||
|
Ok(()) => (),
|
||||||
|
}
|
||||||
|
self.0.members.get(key).cloned()
|
||||||
|
}
|
||||||
|
pub async fn get_by_path(&self, path: &[Tok<String>]) -> Result<ReflMem, InvalidPathError> {
|
||||||
|
let ctx = self.ctx();
|
||||||
|
let (next, tail) = path.split_first().expect("Attempted to walk by empty path");
|
||||||
|
let inferred_g = self.0.inferred.lock().await;
|
||||||
|
if let Some(next) = self.0.members.get(next) {
|
||||||
|
return if tail.is_empty() {
|
||||||
|
Ok(next.clone())
|
||||||
|
} else {
|
||||||
|
match next.kind() {
|
||||||
|
ReflMemKind::Const => Err(InvalidPathError { keep_ancestry: true }),
|
||||||
|
ReflMemKind::Mod(m) => m.get_by_path(tail).boxed_local().await,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if !*inferred_g {
|
||||||
|
return Err(InvalidPathError { keep_ancestry: true });
|
||||||
|
}
|
||||||
|
let candidate = default_module(&ctx, self.0.path.clone().suffix([next.clone()]));
|
||||||
|
if tail.is_empty() {
|
||||||
|
return match candidate.try_populate().await {
|
||||||
|
Ok(()) => {
|
||||||
|
let tgt_mem = default_member(self.is_root(), ReflMemKind::Mod(candidate));
|
||||||
|
self.0.members.insert(next.clone(), tgt_mem.clone());
|
||||||
|
Ok(tgt_mem)
|
||||||
|
},
|
||||||
|
Err(api::LsModuleError::InvalidPath) => Err(InvalidPathError { keep_ancestry: false }),
|
||||||
|
Err(api::LsModuleError::IsConstant) => {
|
||||||
|
let const_mem = default_member(self.is_root(), ReflMemKind::Const);
|
||||||
|
self.0.members.insert(next.clone(), const_mem);
|
||||||
|
Err(InvalidPathError { keep_ancestry: true })
|
||||||
|
},
|
||||||
|
Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
match candidate.get_by_path(tail).boxed_local().await {
|
||||||
|
e @ Err(InvalidPathError { keep_ancestry: false }) => e,
|
||||||
|
res @ Err(InvalidPathError { keep_ancestry: true }) | res @ Ok(_) => {
|
||||||
|
let tgt_mem = default_member(self.is_root(), ReflMemKind::Mod(candidate));
|
||||||
|
self.0.members.insert(next.clone(), tgt_mem);
|
||||||
|
res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ReflRoot(ReflMod);
|
||||||
|
impl SysCtxEntry for ReflRoot {}
|
||||||
|
|
||||||
|
pub struct InvalidPathError {
|
||||||
|
keep_ancestry: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_module(ctx: &SysCtx, path: VPath) -> ReflMod {
|
||||||
|
ReflMod(Rc::new(ReflModData {
|
||||||
|
ctx: ctx.downgrade(),
|
||||||
|
inferred: Mutex::new(true),
|
||||||
|
path,
|
||||||
|
members: MemoMap::new(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_member(is_root: bool, kind: ReflMemKind) -> ReflMem {
|
||||||
|
ReflMem(Rc::new(ReflMemData {
|
||||||
|
public: if is_root { true.into() } else { OnceCell::new() },
|
||||||
|
kind,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_root(ctx: &SysCtx) -> &ReflRoot {
|
||||||
|
ctx.get_or_insert(|| ReflRoot(default_module(ctx, VPath::new([]))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refl(ctx: &SysCtx) -> ReflMod { get_root(ctx).0.clone() }
|
||||||
@@ -3,7 +3,7 @@ use std::fmt;
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::rc::Rc;
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use memo_map::MemoMap;
|
use memo_map::MemoMap;
|
||||||
@@ -36,7 +36,7 @@ pub trait DynSystemCard: Send + Sync + 'static {
|
|||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
/// Atoms explicitly defined by the system card. Do not rely on this for
|
/// Atoms explicitly defined by the system card. Do not rely on this for
|
||||||
/// querying atoms as it doesn't include the general atom types
|
/// querying atoms as it doesn't include the general atom types
|
||||||
fn atoms(&self) -> BoxedIter<Option<Box<dyn AtomDynfo>>>;
|
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomDynfo>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Atoms supported by this package which may appear in all extensions.
|
/// Atoms supported by this package which may appear in all extensions.
|
||||||
@@ -77,7 +77,9 @@ pub async fn resolv_atom(
|
|||||||
|
|
||||||
impl<T: SystemCard> DynSystemCard for T {
|
impl<T: SystemCard> DynSystemCard for T {
|
||||||
fn name(&self) -> &'static str { T::Ctor::NAME }
|
fn name(&self) -> &'static str { T::Ctor::NAME }
|
||||||
fn atoms(&self) -> BoxedIter<Option<Box<dyn AtomDynfo>>> { Box::new(Self::atoms().into_iter()) }
|
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomDynfo>>> {
|
||||||
|
Box::new(Self::atoms().into_iter())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// System as defined by author
|
/// System as defined by author
|
||||||
@@ -91,7 +93,7 @@ pub trait System: Send + Sync + SystemCard + 'static {
|
|||||||
|
|
||||||
pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
|
pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
|
||||||
fn dyn_prelude<'a>(&'a self, i: &'a Interner) -> LocalBoxFuture<'a, Vec<Sym>>;
|
fn dyn_prelude<'a>(&'a self, i: &'a Interner) -> LocalBoxFuture<'a, Vec<Sym>>;
|
||||||
fn dyn_env(&self) -> Vec<GenMember>;
|
fn dyn_env(&'_ self) -> Vec<GenMember>;
|
||||||
fn dyn_lexers(&self) -> Vec<LexerObj>;
|
fn dyn_lexers(&self) -> Vec<LexerObj>;
|
||||||
fn dyn_parsers(&self) -> Vec<ParserObj>;
|
fn dyn_parsers(&self) -> Vec<ParserObj>;
|
||||||
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>>;
|
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>>;
|
||||||
@@ -102,7 +104,7 @@ impl<T: System> DynSystem for T {
|
|||||||
fn dyn_prelude<'a>(&'a self, i: &'a Interner) -> LocalBoxFuture<'a, Vec<Sym>> {
|
fn dyn_prelude<'a>(&'a self, i: &'a Interner) -> LocalBoxFuture<'a, Vec<Sym>> {
|
||||||
Box::pin(Self::prelude(i))
|
Box::pin(Self::prelude(i))
|
||||||
}
|
}
|
||||||
fn dyn_env(&self) -> Vec<GenMember> { Self::env() }
|
fn dyn_env(&'_ self) -> Vec<GenMember> { Self::env() }
|
||||||
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
|
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
|
||||||
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
|
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
|
||||||
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>> {
|
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>> {
|
||||||
@@ -132,7 +134,13 @@ where A: AtomicFeatures {
|
|||||||
}
|
}
|
||||||
let val = dynfo.decode(AtomCtx(data, foreign.atom.drop, ctx)).await;
|
let val = dynfo.decode(AtomCtx(data, foreign.atom.drop, ctx)).await;
|
||||||
let value = *val.downcast::<A::Data>().expect("atom decode returned wrong type");
|
let value = *val.downcast::<A::Data>().expect("atom decode returned wrong type");
|
||||||
Ok(TypAtom { value, data: foreign })
|
Ok(TypAtom { value, untyped: foreign })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WeakSysCtx(Weak<MemoMap<TypeId, Box<dyn Any>>>);
|
||||||
|
impl WeakSysCtx {
|
||||||
|
pub fn upgrade(&self) -> Option<SysCtx> { Some(SysCtx(self.0.upgrade()?)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -150,6 +158,7 @@ impl SysCtx {
|
|||||||
this.add(id).add(i).add(reqnot).add(spawner).add(logger).add(cted);
|
this.add(id).add(i).add(reqnot).add(spawner).add(logger).add(cted);
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
pub fn downgrade(&self) -> WeakSysCtx { WeakSysCtx(Rc::downgrade(&self.0)) }
|
||||||
pub fn add<T: SysCtxEntry>(&self, t: T) -> &Self {
|
pub fn add<T: SysCtxEntry>(&self, t: T) -> &Self {
|
||||||
assert!(self.0.insert(TypeId::of::<T>(), Box::new(t)), "Key already exists");
|
assert!(self.0.insert(TypeId::of::<T>(), Box::new(t)), "Key already exists");
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -65,17 +65,24 @@ impl TokenVariant<api::ExprTicket> for Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x_tok(x: impl ToExpr) -> GenTok { GenTok::NewExpr(x.to_expr()) }
|
pub async fn x_tok(x: impl ToExpr) -> GenTok { GenTok::NewExpr(x.to_expr().await) }
|
||||||
pub fn ref_tok(path: Sym) -> GenTok { GenTok::NewExpr(sym_ref(path)) }
|
pub async fn ref_tok(path: Sym) -> GenTok { GenTok::NewExpr(sym_ref(path)) }
|
||||||
|
|
||||||
pub fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec<GenMember> {
|
pub fn lazy(
|
||||||
|
public: bool,
|
||||||
|
name: &str,
|
||||||
|
cb: impl AsyncFnOnce(Sym, SysCtx) -> MemKind + Clone + 'static,
|
||||||
|
) -> Vec<GenMember> {
|
||||||
vec![GenMember {
|
vec![GenMember {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
kind: MemKind::Const(value.to_expr()),
|
kind: MemKind::Lazy(LazyMemberFactory::new(cb)),
|
||||||
comments: vec![],
|
comments: vec![],
|
||||||
public,
|
public,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
pub fn cnst(public: bool, name: &str, value: impl ToExpr + Clone + 'static) -> Vec<GenMember> {
|
||||||
|
lazy(public, name, async |_, _| MemKind::Const(value.to_expr().await))
|
||||||
|
}
|
||||||
pub fn module(
|
pub fn module(
|
||||||
public: bool,
|
public: bool,
|
||||||
name: &str,
|
name: &str,
|
||||||
@@ -89,20 +96,19 @@ pub fn root_mod(name: &str, mems: impl IntoIterator<Item = Vec<GenMember>>) -> (
|
|||||||
(name.to_string(), kind)
|
(name.to_string(), kind)
|
||||||
}
|
}
|
||||||
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
|
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
|
||||||
let fac = LazyMemberFactory::new(move |sym, ctx| async {
|
let fac =
|
||||||
return MemKind::Const(build_lambdas(Fun::new(sym, ctx, xf).await, 0));
|
LazyMemberFactory::new(move |sym, ctx| async {
|
||||||
fn build_lambdas(fun: Fun, i: u64) -> GExpr {
|
return MemKind::Const(build_lambdas(Fun::new(sym, ctx, xf).await, 0).await);
|
||||||
if i < fun.arity().into() {
|
async fn build_lambdas(fun: Fun, i: u64) -> GExpr {
|
||||||
return lambda(i, [build_lambdas(fun, i + 1)]);
|
if i < fun.arity().into() {
|
||||||
|
return lambda(i, [build_lambdas(fun, i + 1).boxed_local().await]);
|
||||||
|
}
|
||||||
|
let arity = fun.arity();
|
||||||
|
seq((0..arity).map(|i| arg(i as u64)).chain([call(
|
||||||
|
[fun.to_expr().await].into_iter().chain((0..arity).map(|i| arg(i as u64))),
|
||||||
|
)]))
|
||||||
}
|
}
|
||||||
let arity = fun.arity();
|
});
|
||||||
seq(
|
|
||||||
(0..arity)
|
|
||||||
.map(|i| arg(i as u64))
|
|
||||||
.chain([call([fun.to_expr()].into_iter().chain((0..arity).map(|i| arg(i as u64))))]),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }]
|
vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }]
|
||||||
}
|
}
|
||||||
pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {
|
pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ impl AtomHand {
|
|||||||
};
|
};
|
||||||
if let Some(id) = drop {
|
if let Some(id) = drop {
|
||||||
let mut owned_g = ctx.owned_atoms.write().await;
|
let mut owned_g = ctx.owned_atoms.write().await;
|
||||||
if let Some(data) = owned_g.get(&id) {
|
if let Some(data) = owned_g.get(&id)
|
||||||
if let Some(atom) = data.upgrade() {
|
&& let Some(atom) = data.upgrade()
|
||||||
return atom;
|
{
|
||||||
}
|
return atom;
|
||||||
}
|
}
|
||||||
let new = create().await;
|
let new = create().await;
|
||||||
owned_g.insert(id, new.downgrade());
|
owned_g.insert(id, new.downgrade());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::{OrcErr, OrcRes, Reporter, mk_err, mk_errv};
|
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv};
|
||||||
use orchid_base::interner::{Interner, Tok};
|
use orchid_base::interner::{Interner, Tok};
|
||||||
use orchid_base::location::Pos;
|
use orchid_base::location::Pos;
|
||||||
use orchid_base::name::VName;
|
use orchid_base::name::VName;
|
||||||
@@ -16,7 +16,7 @@ pub enum AbsPathError {
|
|||||||
RootPath,
|
RootPath,
|
||||||
}
|
}
|
||||||
impl AbsPathError {
|
impl AbsPathError {
|
||||||
pub async fn err_obj(self, i: &Interner, pos: Pos, path: &str) -> OrcErr {
|
pub async fn err_obj(self, i: &Interner, pos: Pos, path: &str) -> OrcErrv {
|
||||||
let (descr, msg) = match self {
|
let (descr, msg) = match self {
|
||||||
AbsPathError::RootPath => (
|
AbsPathError::RootPath => (
|
||||||
i.i("Path ends on root module").await,
|
i.i("Path ends on root module").await,
|
||||||
@@ -30,7 +30,7 @@ impl AbsPathError {
|
|||||||
format!("{path} is leading outside the root."),
|
format!("{path} is leading outside the root."),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
mk_err(descr, msg, [pos.into()])
|
mk_errv(descr, msg, [pos])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -206,12 +206,16 @@ impl Extension {
|
|||||||
let sys = weak_sys.upgrade().expect("ResolveNames after sys drop");
|
let sys = weak_sys.upgrade().expect("ResolveNames after sys drop");
|
||||||
sys.name_resolver(*constid).await
|
sys.name_resolver(*constid).await
|
||||||
};
|
};
|
||||||
let mut responses = vec![const { None }; names.len()];
|
let responses = stream! {
|
||||||
for (i, name) in names.iter().enumerate() {
|
for name in names {
|
||||||
if let Some(abs) = resolver(&ctx.i.ex(*name).await[..]).await {
|
yield match resolver(&ctx.i.ex(*name).await[..]).await {
|
||||||
responses[i] = Some(abs.to_sym(&ctx.i).await.to_api())
|
Ok(abs) => Ok(abs.to_sym(&ctx.i).await.to_api()),
|
||||||
|
Err(e) => Err(e.to_api()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
hand.handle(rn, &responses).await
|
hand.handle(rn, &responses).await
|
||||||
},
|
},
|
||||||
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
|
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ use hashbrown::HashMap;
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use memo_map::MemoMap;
|
use memo_map::MemoMap;
|
||||||
use orchid_base::char_filter::char_filter_match;
|
use orchid_base::char_filter::char_filter_match;
|
||||||
use orchid_base::error::{OrcErrv, OrcRes};
|
use orchid_base::error::{OrcErrv, OrcRes, mk_errv_floating};
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
||||||
use orchid_base::interner::{Interner, Tok};
|
use orchid_base::interner::{Interner, Tok};
|
||||||
|
use orchid_base::iter_utils::IteratorPrint;
|
||||||
use orchid_base::location::SrcRange;
|
use orchid_base::location::SrcRange;
|
||||||
use orchid_base::name::{NameLike, Sym, VName};
|
use orchid_base::name::{NameLike, Sym, VName, VPath};
|
||||||
use orchid_base::parse::Comment;
|
use orchid_base::parse::Comment;
|
||||||
use orchid_base::reqnot::{ReqNot, Requester};
|
use orchid_base::reqnot::{ReqNot, Requester};
|
||||||
use orchid_base::tree::ttv_from_api;
|
use orchid_base::tree::ttv_from_api;
|
||||||
@@ -23,7 +24,7 @@ use substack::{Stackframe, Substack};
|
|||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::dealias::{absolute_path, walk};
|
use crate::dealias::walk;
|
||||||
use crate::expr::{ExprParseCtx, ExprWillPanic};
|
use crate::expr::{ExprParseCtx, ExprWillPanic};
|
||||||
use crate::expr_store::ExprStore;
|
use crate::expr_store::ExprStore;
|
||||||
use crate::extension::{Extension, WeakExtension};
|
use crate::extension::{Extension, WeakExtension};
|
||||||
@@ -202,16 +203,39 @@ impl System {
|
|||||||
pub(crate) async fn name_resolver(
|
pub(crate) async fn name_resolver(
|
||||||
&self,
|
&self,
|
||||||
orig: api::ParsedConstId,
|
orig: api::ParsedConstId,
|
||||||
) -> impl AsyncFnMut(&[Tok<String>]) -> Option<VName> + use<> {
|
) -> impl AsyncFnMut(&[Tok<String>]) -> OrcRes<VName> + use<> {
|
||||||
let root = self.0.ctx.root.read().await.upgrade().expect("find_names when root not in context");
|
let root = self.0.ctx.root.read().await.upgrade().expect("find_names when root not in context");
|
||||||
let orig = self.0.const_paths.get(&orig).expect("origin for find_names invalid").clone();
|
let orig = self.0.const_paths.get(&orig).expect("origin for find_names invalid").clone();
|
||||||
let ctx = self.0.ctx.clone();
|
let ctx = self.0.ctx.clone();
|
||||||
async move |rel| {
|
async move |rel| {
|
||||||
let cwd = orig.split_last_seg().1;
|
let cwd = orig.split_last_seg().1;
|
||||||
let abs = absolute_path(cwd, rel, &ctx.i).await.ok()?;
|
|
||||||
let root_data = &mut *root.0.write().await;
|
let root_data = &mut *root.0.write().await;
|
||||||
let walk_ctx = &mut (ctx.clone(), &mut root_data.consts);
|
let walk_ctx = &mut (ctx.clone(), &mut root_data.consts);
|
||||||
walk(&root_data.root, false, abs.iter(), walk_ctx).await.is_ok().then_some(abs)
|
let cmod = (walk(&root_data.root, false, cwd.iter().cloned(), walk_ctx).await)
|
||||||
|
.expect("the parent module of a constant should exist");
|
||||||
|
let (selector, tail) = rel.split_first().expect("Names cannot be empty");
|
||||||
|
if cmod.members.get(selector).is_some() {
|
||||||
|
return Ok(VName::new(cwd.iter().chain(rel).cloned()).unwrap());
|
||||||
|
}
|
||||||
|
match cmod.imports.get(selector) {
|
||||||
|
Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())),
|
||||||
|
Some(Err(dests)) =>
|
||||||
|
return Err(mk_errv_floating(
|
||||||
|
ctx.i.i("Ambiguous name").await,
|
||||||
|
format!(
|
||||||
|
"{selector} could refer to {}",
|
||||||
|
dests.iter().map(|ri| &ri.target).display("or")
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
if tail.is_empty() {
|
||||||
|
return Ok(VPath::new(cwd.iter().cloned()).name_with_suffix(selector.clone()));
|
||||||
|
}
|
||||||
|
Err(mk_errv_floating(
|
||||||
|
ctx.i.i("Invalid name").await,
|
||||||
|
format!("{selector} doesn't refer to a module"),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
//! once
|
//! once
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
use async_once_cell::OnceCell;
|
use async_once_cell::OnceCell;
|
||||||
use async_std::sync::RwLock;
|
use async_std::sync::RwLock;
|
||||||
@@ -10,7 +11,7 @@ use hashbrown::HashMap;
|
|||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::clone;
|
use orchid_base::clone;
|
||||||
use orchid_base::error::{OrcRes, Reporter, mk_err, mk_errv};
|
use orchid_base::error::{OrcRes, Reporter, mk_errv};
|
||||||
use orchid_base::interner::Tok;
|
use orchid_base::interner::Tok;
|
||||||
use orchid_base::location::{CodeGenInfo, Pos};
|
use orchid_base::location::{CodeGenInfo, Pos};
|
||||||
use orchid_base::name::{NameLike, Sym, VPath};
|
use orchid_base::name::{NameLike, Sym, VPath};
|
||||||
@@ -152,8 +153,8 @@ impl<'a> TreeFromApiCtx<'a> {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct ResolvedImport {
|
pub struct ResolvedImport {
|
||||||
target: Sym,
|
pub target: Sym,
|
||||||
pos: Pos,
|
pub pos: Pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
@@ -258,18 +259,16 @@ impl Module {
|
|||||||
match abs_path_res {
|
match abs_path_res {
|
||||||
Err(e) => ctx.rep.report(e.err_obj(&ctx.ctx.i, sr.pos(), &import.to_string()).await),
|
Err(e) => ctx.rep.report(e.err_obj(&ctx.ctx.i, sr.pos(), &import.to_string()).await),
|
||||||
Ok(abs_path) => {
|
Ok(abs_path) => {
|
||||||
imports.insert(
|
let target = abs_path.to_sym(&ctx.ctx.i).await;
|
||||||
key,
|
imports.insert(key, Ok(ResolvedImport { target, pos: sr.pos() }));
|
||||||
Ok(ResolvedImport { target: abs_path.to_sym(&ctx.ctx.i).await, pos: sr.pos() }),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for item in values {
|
for item in values {
|
||||||
ctx.rep.report(mk_err(
|
ctx.rep.report(mk_errv(
|
||||||
conflicting_imports_msg.clone(),
|
conflicting_imports_msg.clone(),
|
||||||
format!("{key} is imported multiple times from different modules"),
|
format!("{key} is imported multiple times from different modules"),
|
||||||
[item.sr.pos().into()],
|
[item.sr.pos()],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,11 +293,12 @@ impl Module {
|
|||||||
let self_referential_msg = ctx.ctx.i.i("Self-referential import").await;
|
let self_referential_msg = ctx.ctx.i.i("Self-referential import").await;
|
||||||
for (key, value) in imports.iter() {
|
for (key, value) in imports.iter() {
|
||||||
let Ok(import) = value else { continue };
|
let Ok(import) = value else { continue };
|
||||||
if import.target.strip_prefix(&path[..]).is_some_and(|t| t.starts_with(&[key.clone()])) {
|
if import.target.strip_prefix(&path[..]).is_some_and(|t| t.starts_with(slice::from_ref(key)))
|
||||||
ctx.rep.report(mk_err(
|
{
|
||||||
|
ctx.rep.report(mk_errv(
|
||||||
self_referential_msg.clone(),
|
self_referential_msg.clone(),
|
||||||
format!("import {} points to itself or a path within itself", &import.target),
|
format!("import {} points to itself or a path within itself", &import.target),
|
||||||
[import.pos.clone().into()],
|
[import.pos.clone()],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,3 +4,6 @@ mod std;
|
|||||||
pub use std::number::num_atom::{Float, HomoArray, Int, Num};
|
pub use std::number::num_atom::{Float, HomoArray, Int, Num};
|
||||||
pub use std::std_system::StdSystem;
|
pub use std::std_system::StdSystem;
|
||||||
pub use std::string::str_atom::OrcString;
|
pub use std::string::str_atom::OrcString;
|
||||||
|
|
||||||
|
pub use macros::macro_system::MacroSystem;
|
||||||
|
pub use macros::mactree::{MacTok, MacTree};
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use futures::AsyncWrite;
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_extension::atom::{Atomic, TypAtom};
|
use orchid_extension::atom::{Atomic, TypAtom};
|
||||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant, get_own_instance};
|
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
||||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||||
use orchid_extension::expr::Expr;
|
use orchid_extension::expr::Expr;
|
||||||
use orchid_extension::gen_expr::GExpr;
|
use orchid_extension::gen_expr::GExpr;
|
||||||
use orchid_extension::system::SysCtx;
|
|
||||||
|
|
||||||
use crate::macros::mactree::{MacTok, MacTree, map_mactree};
|
use crate::macros::mactree::{MacTok, MacTree, map_mactree};
|
||||||
|
|
||||||
@@ -24,16 +21,7 @@ impl Atomic for InstantiateTplCall {
|
|||||||
}
|
}
|
||||||
impl OwnedAtom for InstantiateTplCall {
|
impl OwnedAtom for InstantiateTplCall {
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
/// TODO: get serialization done for mactree
|
type Refs = Never;
|
||||||
type Refs = Vec<Expr>;
|
|
||||||
async fn serialize(
|
|
||||||
&self,
|
|
||||||
ctx: SysCtx,
|
|
||||||
write: Pin<&mut (impl AsyncWrite + ?Sized)>,
|
|
||||||
) -> Self::Refs {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
async fn deserialize(ctx: impl DeserializeCtx, refs: Self::Refs) -> Self { todo!() }
|
|
||||||
// Technically must be supported but shouldn't actually ever be called
|
// Technically must be supported but shouldn't actually ever be called
|
||||||
async fn call_ref(&self, arg: Expr) -> GExpr {
|
async fn call_ref(&self, arg: Expr) -> GExpr {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@@ -43,20 +31,19 @@ impl OwnedAtom for InstantiateTplCall {
|
|||||||
self.clone().call(arg).await
|
self.clone().call(arg).await
|
||||||
}
|
}
|
||||||
async fn call(mut self, arg: Expr) -> GExpr {
|
async fn call(mut self, arg: Expr) -> GExpr {
|
||||||
let arg = match TypAtom::try_from_expr(arg).await {
|
match TypAtom::<MacTree>::try_from_expr(arg).await {
|
||||||
Err(e) => return Err::<Never, _>(e).to_expr(),
|
Err(e) => return Err::<Never, _>(e).to_expr().await,
|
||||||
Ok(t) => get_own_instance(t).await,
|
Ok(t) => self.argv.push(own(t).await),
|
||||||
};
|
};
|
||||||
self.argv.push(arg);
|
|
||||||
if self.argv.len() < self.argc {
|
if self.argv.len() < self.argc {
|
||||||
return self.to_expr();
|
return self.to_expr().await;
|
||||||
}
|
}
|
||||||
let mut args = self.argv.into_iter();
|
let mut args = self.argv.into_iter();
|
||||||
map_mactree(&self.tpl, &mut false, &mut |tpl| match &*tpl.tok {
|
let ret = map_mactree(&self.tpl, &mut false, &mut |mt| match mt.tok() {
|
||||||
MacTok::Ph(_) => panic!("instantiate_tpl received a placeholder"),
|
MacTok::Slot => Some(args.next().expect("Not enough arguments to fill all slots")),
|
||||||
MacTok::Slot => Some(args.next().expect("Not enough arguments to fill all slots!")),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
});
|
||||||
.to_expr()
|
assert!(args.next().is_none(), "Too many arguments for all slots");
|
||||||
|
ret.to_expr().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
|
use std::pin::pin;
|
||||||
|
|
||||||
use async_std::stream;
|
use async_std::stream;
|
||||||
use async_stream::stream;
|
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_api::Paren;
|
||||||
|
use orchid_base::error::{OrcRes, Reporter};
|
||||||
|
use orchid_base::name::Sym;
|
||||||
use orchid_base::parse::{
|
use orchid_base::parse::{
|
||||||
Comment, ParseCtx, Parsed, Snippet, expect_tok, token_errv, try_pop_no_fluff,
|
Comment, ParseCtx, Parsed, Snippet, expect_tok, token_errv, try_pop_no_fluff,
|
||||||
};
|
};
|
||||||
use orchid_extension::parser::{
|
use orchid_base::sym;
|
||||||
PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, ParsedLineKind, ParsedMem, ParsedMemKind, Parser,
|
use orchid_extension::gen_expr::{atom, call, sym_ref};
|
||||||
};
|
use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser};
|
||||||
|
|
||||||
use crate::macros::mactree::{MacTok, MacTree, map_mactree_v};
|
use crate::macros::mactree::{MacTok, MacTree, glossary_v, map_mactree_v};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct LetLine;
|
pub struct LetLine;
|
||||||
@@ -23,6 +26,7 @@ impl Parser for LetLine {
|
|||||||
comments: Vec<Comment>,
|
comments: Vec<Comment>,
|
||||||
line: PSnippet<'a>,
|
line: PSnippet<'a>,
|
||||||
) -> OrcRes<Vec<ParsedLine>> {
|
) -> OrcRes<Vec<ParsedLine>> {
|
||||||
|
let sr = line.sr();
|
||||||
let Parsed { output: name_tok, tail } = try_pop_no_fluff(&ctx, line).await?;
|
let Parsed { output: name_tok, tail } = try_pop_no_fluff(&ctx, line).await?;
|
||||||
let Some(name) = name_tok.as_name() else {
|
let Some(name) = name_tok.as_name() else {
|
||||||
let err = token_errv(&ctx, name_tok, "Constant must have a name", |t| {
|
let err = token_errv(&ctx, name_tok, "Constant must have a name", |t| {
|
||||||
@@ -31,44 +35,41 @@ impl Parser for LetLine {
|
|||||||
return Err(err.await);
|
return Err(err.await);
|
||||||
};
|
};
|
||||||
let Parsed { tail, .. } = expect_tok(&ctx, tail, ctx.i().i("=").await).await?;
|
let Parsed { tail, .. } = expect_tok(&ctx, tail, ctx.i().i("=").await).await?;
|
||||||
let mut names = HashMap::new();
|
|
||||||
let aliased = parse_tokv(tail, &ctx).await;
|
let aliased = parse_tokv(tail, &ctx).await;
|
||||||
map_mactree_v(&aliased, &mut false, &mut |tpl| {
|
Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| {
|
||||||
if let MacTok::Name(n) = &*tpl.tok {
|
let rep = Reporter::new();
|
||||||
names.insert(n.clone(), n.clone());
|
let dealiased = dealias_mac_v(aliased, &ctx, &rep).await;
|
||||||
|
let macro_input = MacTok::S(Paren::Round, dealiased).at(sr.pos());
|
||||||
|
if let Some(e) = rep.errv() {
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
None
|
Ok(call([
|
||||||
});
|
sym_ref(sym!(macros::lower; ctx.i()).await),
|
||||||
Ok(vec![ParsedLine {
|
call([sym_ref(sym!(macros::resolve; ctx.i()).await), atom(macro_input)]),
|
||||||
comments,
|
]))
|
||||||
sr: line.sr(),
|
})])
|
||||||
kind: ParsedLineKind::Mem(ParsedMem {
|
|
||||||
exported,
|
|
||||||
name,
|
|
||||||
kind: ParsedMemKind::cnst(async move |ctx| {
|
|
||||||
let keys = names.keys().cloned().collect_vec();
|
|
||||||
let names_mut = &mut names;
|
|
||||||
stream! {
|
|
||||||
for await (canonical, local) in ctx.names(&keys).zip(stream::from_iter(&keys)) {
|
|
||||||
if let Some(name) = canonical {
|
|
||||||
*names_mut.get_mut(local).expect("Queried specifically the keys of this map") = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.collect::<()>()
|
|
||||||
.await;
|
|
||||||
let macro_input = map_mactree_v(&aliased, &mut false, &mut |tree| match &*tree.tok {
|
|
||||||
MacTok::Name(n) => names.get(n).map(|new_n| MacTok::Name(new_n.clone()).at(tree.pos())),
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
todo!("Run macros then convert this into an expr")
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn parse_tokv(line: PSnippet<'_>, ctx: &ParsCtx<'_>) -> Vec<MacTree> {
|
pub async fn dealias_mac_v(aliased: Vec<MacTree>, ctx: &ConstCtx, rep: &Reporter) -> Vec<MacTree> {
|
||||||
|
let keys = glossary_v(&aliased).collect_vec();
|
||||||
|
let mut names: HashMap<_, _> = HashMap::new();
|
||||||
|
let mut stream = pin!(ctx.names(&keys).zip(stream::from_iter(&keys)));
|
||||||
|
while let Some((canonical, local)) = stream.next().await {
|
||||||
|
match canonical {
|
||||||
|
Err(e) => rep.report(e),
|
||||||
|
Ok(name) => {
|
||||||
|
names.insert(local.clone(), name);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map_mactree_v(&aliased, &mut false, &mut |tree| match &*tree.tok {
|
||||||
|
MacTok::Name(n) => names.get(n).map(|new_n| MacTok::Name(new_n.clone()).at(tree.pos())),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn parse_tokv(line: PSnippet<'_>, ctx: &impl ParseCtx) -> Vec<MacTree> {
|
||||||
if let Some((idx, arg)) = line.iter().enumerate().find_map(|(i, x)| Some((i, x.as_lambda()?))) {
|
if let Some((idx, arg)) = line.iter().enumerate().find_map(|(i, x)| Some((i, x.as_lambda()?))) {
|
||||||
let (head, lambda) = line.split_at(idx as u32);
|
let (head, lambda) = line.split_at(idx as u32);
|
||||||
let (_, body) = lambda.pop_front().unwrap();
|
let (_, body) = lambda.pop_front().unwrap();
|
||||||
@@ -89,15 +90,15 @@ async fn parse_tokv(line: PSnippet<'_>, ctx: &ParsCtx<'_>) -> Vec<MacTree> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn parse_tokv_no_lambdas(line: &[PTokTree], ctx: &ParsCtx<'_>) -> Vec<MacTree> {
|
async fn parse_tokv_no_lambdas(line: &[PTokTree], ctx: &impl ParseCtx) -> Vec<MacTree> {
|
||||||
stream::from_iter(line).filter_map(|tt| parse_tok(tt, ctx)).collect().await
|
stream::from_iter(line).filter_map(|tt| parse_tok(tt, ctx)).collect().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn parse_tok(tree: &PTokTree, ctx: &ParsCtx<'_>) -> Option<MacTree> {
|
pub async fn parse_tok(tree: &PTokTree, ctx: &impl ParseCtx) -> Option<MacTree> {
|
||||||
let tok = match &tree.tok {
|
let tok = match &tree.tok {
|
||||||
PTok::Bottom(errv) => MacTok::Bottom(errv.clone()),
|
PTok::Bottom(errv) => MacTok::Bottom(errv.clone()),
|
||||||
PTok::BR | PTok::Comment(_) => return None,
|
PTok::BR | PTok::Comment(_) => return None,
|
||||||
PTok::Name(n) => MacTok::Name(ctx.module().suffix([n.clone()], ctx.i()).await),
|
PTok::Name(n) => MacTok::Name(Sym::new([n.clone()], ctx.i()).await.unwrap()),
|
||||||
PTok::NS(..) => match tree.as_multiname() {
|
PTok::NS(..) => match tree.as_multiname() {
|
||||||
Ok(mn) => MacTok::Name(mn.to_sym(ctx.i()).await),
|
Ok(mn) => MacTok::Name(mn.to_sym(ctx.i()).await),
|
||||||
Err(nested) => {
|
Err(nested) => {
|
||||||
|
|||||||
@@ -1,20 +1,83 @@
|
|||||||
|
use hashbrown::HashMap;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use orchid_base::error::Reporter;
|
||||||
|
use orchid_base::sym;
|
||||||
use orchid_extension::atom::TypAtom;
|
use orchid_extension::atom::TypAtom;
|
||||||
use orchid_extension::atom_owned::get_own_instance;
|
use orchid_extension::atom_owned::own;
|
||||||
|
use orchid_extension::conv::ToExpr;
|
||||||
|
use orchid_extension::coroutine_exec::exec;
|
||||||
|
use orchid_extension::gen_expr::{atom, call, sym_ref};
|
||||||
|
use orchid_extension::reflection::{ReflMemKind, refl};
|
||||||
use orchid_extension::tree::{GenMember, comments, fun, prefix};
|
use orchid_extension::tree::{GenMember, comments, fun, prefix};
|
||||||
|
use substack::Substack;
|
||||||
|
|
||||||
use crate::Int;
|
use crate::Int;
|
||||||
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
||||||
use crate::macros::mactree::MacTree;
|
use crate::macros::macro_line::{Macro, Matcher};
|
||||||
|
use crate::macros::mactree::{LowerCtx, MacTree};
|
||||||
|
use crate::macros::recur_state::RecurState;
|
||||||
|
use crate::macros::resolve::{ResolveCtx, resolve};
|
||||||
|
|
||||||
pub fn gen_macro_lib() -> Vec<GenMember> {
|
pub fn gen_macro_lib() -> Vec<GenMember> {
|
||||||
prefix("macros", [comments(
|
prefix("macros", [
|
||||||
["This is an internal function, you can't obtain a value of its argument type.", "hidden"],
|
comments(
|
||||||
fun(true, "instantiate_tpl", |tpl: TypAtom<MacTree>, right: Int| async move {
|
["This is an internal function, you can't obtain a value of its argument type.", "hidden"],
|
||||||
InstantiateTplCall {
|
fun(true, "instantiate_tpl", |tpl: TypAtom<MacTree>, right: Int| async move {
|
||||||
tpl: get_own_instance(tpl).await,
|
InstantiateTplCall {
|
||||||
argc: right.0.try_into().unwrap(),
|
tpl: own(tpl).await,
|
||||||
argv: Vec::new(),
|
argc: right.0.try_into().unwrap(),
|
||||||
}
|
argv: Vec::new(),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
fun(true, "resolve", |tpl: TypAtom<MacTree>| async move {
|
||||||
|
call([
|
||||||
|
sym_ref(sym!(macro::resolve_recur; tpl.untyped.ctx().i()).await),
|
||||||
|
atom(RecurState::Bottom),
|
||||||
|
tpl.untyped.ex().to_expr().await,
|
||||||
|
])
|
||||||
}),
|
}),
|
||||||
)])
|
fun(true, "lower", |tpl: TypAtom<MacTree>| async move {
|
||||||
|
let ctx = LowerCtx { sys: tpl.untyped.ctx().clone(), rep: &Reporter::new() };
|
||||||
|
let res = own(tpl).await.lower(ctx, Substack::Bottom).await;
|
||||||
|
if let Some(e) = Reporter::new().errv() { Err(e) } else { Ok(res) }
|
||||||
|
}),
|
||||||
|
fun(true, "resolve_recur", |state: TypAtom<RecurState>, tpl: TypAtom<MacTree>| async move {
|
||||||
|
exec(async move |mut h| {
|
||||||
|
let ctx = tpl.ctx().clone();
|
||||||
|
let root = refl(&ctx);
|
||||||
|
let tpl = own(tpl.clone()).await;
|
||||||
|
let mut macros = HashMap::new();
|
||||||
|
for n in tpl.glossary() {
|
||||||
|
if let Ok(ReflMemKind::Const) = root.get_by_path(n).await.map(|m| m.kind()) {
|
||||||
|
let Ok(mac) = h.exec::<TypAtom<Macro>>(sym_ref(n.clone())).await else { continue };
|
||||||
|
let mac = own(mac).await;
|
||||||
|
macros.entry(mac.0.own_kws[0].clone()).or_insert(mac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut named = HashMap::new();
|
||||||
|
let mut priod = Vec::new();
|
||||||
|
for (_, mac) in macros.iter() {
|
||||||
|
for rule in mac.0.rules.iter() {
|
||||||
|
if rule.glossary.is_subset(tpl.glossary()) {
|
||||||
|
match &rule.pattern {
|
||||||
|
Matcher::Named(m) =>
|
||||||
|
named.entry(m.head()).or_insert(Vec::new()).push((m, mac, rule)),
|
||||||
|
Matcher::Priod(p) => priod.push((mac.0.prio, (p, mac, rule))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let priod = priod.into_iter().sorted_unstable_by_key(|(p, _)| *p).map(|(_, r)| r).collect();
|
||||||
|
let mut rctx = ResolveCtx { h, recur: own(state).await, ctx: ctx.clone(), named, priod };
|
||||||
|
let resolve_res = resolve(&mut rctx, &tpl).await;
|
||||||
|
std::mem::drop(rctx);
|
||||||
|
match resolve_res {
|
||||||
|
Some(out_tree) => out_tree.to_expr().await,
|
||||||
|
None => tpl.to_expr().await,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
230
orchid-std/src/macros/macro_line.rs
Normal file
230
orchid-std/src/macros/macro_line.rs
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use async_once_cell::OnceCell;
|
||||||
|
use async_std::stream;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use never::Never;
|
||||||
|
use orchid_api::Paren;
|
||||||
|
use orchid_base::error::{OrcRes, Reporter, mk_errv};
|
||||||
|
use orchid_base::interner::Tok;
|
||||||
|
use orchid_base::location::Pos;
|
||||||
|
use orchid_base::name::Sym;
|
||||||
|
use orchid_base::parse::{
|
||||||
|
Comment, ParseCtx, Parsed, Snippet, expect_end, expect_tok, line_items, token_errv,
|
||||||
|
try_pop_no_fluff,
|
||||||
|
};
|
||||||
|
use orchid_base::tree::Token;
|
||||||
|
use orchid_base::{clone, sym};
|
||||||
|
use orchid_extension::atom::{Atomic, TypAtom};
|
||||||
|
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
||||||
|
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||||
|
use orchid_extension::gen_expr::{atom, call, sym_ref};
|
||||||
|
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||||
|
|
||||||
|
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
|
||||||
|
use crate::macros::mactree::{glossary_v, map_mactree_v};
|
||||||
|
use crate::macros::recur_state::{RecurState, RulePath};
|
||||||
|
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||||
|
use crate::{Int, MacTok};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MacroLine;
|
||||||
|
impl Parser for MacroLine {
|
||||||
|
const LINE_HEAD: &'static str = "macro";
|
||||||
|
async fn parse<'a>(
|
||||||
|
ctx: ParsCtx<'a>,
|
||||||
|
exported: bool,
|
||||||
|
comments: Vec<Comment>,
|
||||||
|
line: PSnippet<'a>,
|
||||||
|
) -> OrcRes<Vec<ParsedLine>> {
|
||||||
|
if exported {
|
||||||
|
return Err(mk_errv(
|
||||||
|
ctx.i().i("macros are always exported").await,
|
||||||
|
"The export keyword is forbidden here to avoid confusion\n\
|
||||||
|
because macros are exported by default",
|
||||||
|
[line.sr()],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let module = ctx.module();
|
||||||
|
let Parsed { output, tail } = try_pop_no_fluff(&ctx, line).await?;
|
||||||
|
let bad_first_item_err = || {
|
||||||
|
token_errv(&ctx, output, "Expected priority or block", |s| {
|
||||||
|
format!("Expected a priority number or a () block, found {s}")
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let (prio, body) = match &output.tok {
|
||||||
|
Token::S(Paren::Round, body) => (None, body),
|
||||||
|
Token::Handle(expr) => match TypAtom::<Int>::try_from_expr(expr.clone()).await {
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e + bad_first_item_err().await);
|
||||||
|
},
|
||||||
|
Ok(prio) => {
|
||||||
|
let Token::S(Paren::Round, block) = &output.tok else {
|
||||||
|
return Err(
|
||||||
|
token_errv(&ctx, output, "Expected () block", |s| {
|
||||||
|
format!("Expected a () block, found {s}")
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
(Some(prio), block)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => return Err(bad_first_item_err().await),
|
||||||
|
};
|
||||||
|
expect_end(&ctx, tail).await?;
|
||||||
|
let lines = line_items(&ctx, Snippet::new(output, body)).await;
|
||||||
|
let Some((kw_line, rule_lines)) = lines.split_first() else { return Ok(Vec::new()) };
|
||||||
|
let mut keywords = HashMap::new();
|
||||||
|
let Parsed { tail: kw_tail, .. } =
|
||||||
|
expect_tok(&ctx, kw_line.tail, ctx.i().i("keywords").await).await?;
|
||||||
|
for kw_tok in kw_tail.iter().filter(|kw| !kw.is_fluff()) {
|
||||||
|
match kw_tok.as_name() {
|
||||||
|
Some(kw) => {
|
||||||
|
keywords.insert(kw, kw_tok.sr());
|
||||||
|
},
|
||||||
|
None => ctx.rep().report(
|
||||||
|
token_errv(&ctx, kw_tok, "invalid macro keywords list", |tok| {
|
||||||
|
format!("The keywords list must be a sequence of names; received {tok}")
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let Some(macro_name) = keywords.keys().next().cloned() else {
|
||||||
|
return Err(mk_errv(
|
||||||
|
ctx.i().i("macro with no keywords").await,
|
||||||
|
"Macros must define at least one macro of their own.",
|
||||||
|
[kw_line.tail.sr()],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let mut rules = Vec::new();
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
for (idx, line) in rule_lines.iter().enumerate().map(|(n, v)| (n as u32, v)) {
|
||||||
|
let path = RulePath { module: module.clone(), main_kw: macro_name.clone(), rule: idx };
|
||||||
|
let sr = line.tail.sr();
|
||||||
|
let name = ctx.i().i(&path.name()).await;
|
||||||
|
let Parsed { tail, .. } = expect_tok(&ctx, line.tail, ctx.i().i("rule").await).await?;
|
||||||
|
let arrow_token = ctx.i().i("=>").await;
|
||||||
|
let Some((pattern, body)) = tail.split_once(|tok| tok.is_kw(arrow_token.clone())) else {
|
||||||
|
ctx.rep().report(mk_errv(
|
||||||
|
ctx.i().i("Missing => in rule").await,
|
||||||
|
"Rule lines are of the form `rule ...pattern => ...body`",
|
||||||
|
[line.tail.sr()],
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let pattern = parse_tokv(pattern, &ctx).await;
|
||||||
|
let mut placeholders = Vec::new();
|
||||||
|
map_mactree_v(&pattern, &mut false, &mut |tok| {
|
||||||
|
if let MacTok::Ph(ph) = tok.tok() {
|
||||||
|
placeholders.push((ph.clone(), tok.pos()))
|
||||||
|
}
|
||||||
|
None
|
||||||
|
});
|
||||||
|
let mut body_mactree = parse_tokv(body, &ctx).await;
|
||||||
|
for (ph, ph_pos) in placeholders.iter().rev() {
|
||||||
|
let name = ctx.module().suffix([ph.name.clone()], ctx.i()).await;
|
||||||
|
body_mactree = vec![
|
||||||
|
MacTok::Lambda(MacTok::Name(name).at(ph_pos.clone()), body_mactree).at(ph_pos.clone()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
let body_sr = body.sr();
|
||||||
|
rules.push((name.clone(), placeholders, rules.len() as u32, sr.pos(), pattern));
|
||||||
|
lines.push(ParsedLine::cnst(&sr, &line.output, true, name, async move |ctx| {
|
||||||
|
let rep = Reporter::new();
|
||||||
|
let body = dealias_mac_v(body_mactree, &ctx, &rep).await;
|
||||||
|
let macro_input = MacTok::S(Paren::Round, body).at(body_sr.pos());
|
||||||
|
if let Some(e) = rep.errv() {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
Ok(call([
|
||||||
|
sym_ref(sym!(macro::resolve_recur; ctx.i()).await),
|
||||||
|
atom(RecurState::base(path)),
|
||||||
|
macro_input.to_expr().await,
|
||||||
|
]))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
let mac_cell = Rc::new(OnceCell::new());
|
||||||
|
let keywords = Rc::new(keywords);
|
||||||
|
let rules = Rc::new(RefCell::new(Some(rules)));
|
||||||
|
for (kw, sr) in &*keywords {
|
||||||
|
clone!(mac_cell, keywords, rules, module, prio);
|
||||||
|
lines.push(ParsedLine::cnst(&sr.clone(), &comments, true, kw.clone(), async move |cctx| {
|
||||||
|
let mac = mac_cell
|
||||||
|
.get_or_init(async {
|
||||||
|
let rep = Reporter::new();
|
||||||
|
let rules = rules.borrow_mut().take().expect("once cell initializer runs");
|
||||||
|
let rules = stream::from_iter(rules)
|
||||||
|
.then(|(body_name, placeholders, index, pos, pattern_macv)| {
|
||||||
|
let cctx = &cctx;
|
||||||
|
let rep = &rep;
|
||||||
|
let prio = &prio;
|
||||||
|
async move {
|
||||||
|
let pattern_abs = dealias_mac_v(pattern_macv, cctx, rep).await;
|
||||||
|
let glossary = glossary_v(&pattern_abs).collect();
|
||||||
|
let pattern_res = match prio {
|
||||||
|
None => NamedMatcher::new(&pattern_abs, cctx.i()).await.map(Matcher::Named),
|
||||||
|
Some(_) => PriodMatcher::new(&pattern_abs, cctx.i()).await.map(Matcher::Priod),
|
||||||
|
};
|
||||||
|
let placeholders = placeholders.into_iter().map(|(ph, _)| ph.name).collect_vec();
|
||||||
|
match pattern_res {
|
||||||
|
Ok(pattern) =>
|
||||||
|
Some(Rule { index, pos, body_name, pattern, glossary, placeholders }),
|
||||||
|
Err(e) => {
|
||||||
|
rep.report(e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flat_map(stream::from_iter)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.await;
|
||||||
|
let own_kws = keywords.keys().cloned().collect_vec();
|
||||||
|
Macro(Rc::new(MacroData { module, prio: prio.map(|i| i.0 as u64), rules, own_kws }))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
atom(mac.clone())
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
Ok(lines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MacroData {
|
||||||
|
pub module: Sym,
|
||||||
|
pub prio: Option<u64>,
|
||||||
|
pub rules: Vec<Rule>,
|
||||||
|
pub own_kws: Vec<Tok<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Macro(pub Rc<MacroData>);
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Rule {
|
||||||
|
pub index: u32,
|
||||||
|
pub pos: Pos,
|
||||||
|
pub pattern: Matcher,
|
||||||
|
pub glossary: HashSet<Sym>,
|
||||||
|
pub placeholders: Vec<Tok<String>>,
|
||||||
|
pub body_name: Tok<String>,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Matcher {
|
||||||
|
Named(NamedMatcher),
|
||||||
|
Priod(PriodMatcher),
|
||||||
|
}
|
||||||
|
impl Atomic for Macro {
|
||||||
|
type Data = ();
|
||||||
|
type Variant = OwnedVariant;
|
||||||
|
}
|
||||||
|
impl OwnedAtom for Macro {
|
||||||
|
type Refs = Never;
|
||||||
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ use never::Never;
|
|||||||
use orchid_base::interner::Interner;
|
use orchid_base::interner::Interner;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
use orchid_base::reqnot::Receipt;
|
use orchid_base::reqnot::Receipt;
|
||||||
use orchid_extension::atom::AtomDynfo;
|
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
|
||||||
use orchid_extension::entrypoint::ExtReq;
|
use orchid_extension::entrypoint::ExtReq;
|
||||||
use orchid_extension::lexer::LexerObj;
|
use orchid_extension::lexer::LexerObj;
|
||||||
use orchid_extension::parser::ParserObj;
|
use orchid_extension::parser::ParserObj;
|
||||||
@@ -10,14 +10,18 @@ use orchid_extension::system::{System, SystemCard};
|
|||||||
use orchid_extension::system_ctor::SystemCtor;
|
use orchid_extension::system_ctor::SystemCtor;
|
||||||
use orchid_extension::tree::GenMember;
|
use orchid_extension::tree::GenMember;
|
||||||
|
|
||||||
|
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
||||||
use crate::macros::let_line::LetLine;
|
use crate::macros::let_line::LetLine;
|
||||||
use crate::macros::macro_lib::gen_macro_lib;
|
use crate::macros::macro_lib::gen_macro_lib;
|
||||||
|
use crate::macros::macro_line::MacroLine;
|
||||||
use crate::macros::mactree_lexer::MacTreeLexer;
|
use crate::macros::mactree_lexer::MacTreeLexer;
|
||||||
|
use crate::macros::recur_state::RecurState;
|
||||||
|
use crate::{MacTree, StdSystem};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MacroSystem;
|
pub struct MacroSystem;
|
||||||
impl SystemCtor for MacroSystem {
|
impl SystemCtor for MacroSystem {
|
||||||
type Deps = ();
|
type Deps = StdSystem;
|
||||||
type Instance = Self;
|
type Instance = Self;
|
||||||
const NAME: &'static str = "macros";
|
const NAME: &'static str = "macros";
|
||||||
const VERSION: f64 = 0.00_01;
|
const VERSION: f64 = 0.00_01;
|
||||||
@@ -26,12 +30,14 @@ impl SystemCtor for MacroSystem {
|
|||||||
impl SystemCard for MacroSystem {
|
impl SystemCard for MacroSystem {
|
||||||
type Ctor = Self;
|
type Ctor = Self;
|
||||||
type Req = Never;
|
type Req = Never;
|
||||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> { [] }
|
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
|
||||||
|
[Some(InstantiateTplCall::dynfo()), Some(MacTree::dynfo()), Some(RecurState::dynfo())]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl System for MacroSystem {
|
impl System for MacroSystem {
|
||||||
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
|
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
|
||||||
async fn prelude(_: &Interner) -> Vec<Sym> { vec![] }
|
async fn prelude(_: &Interner) -> Vec<Sym> { vec![] }
|
||||||
fn lexers() -> Vec<LexerObj> { vec![&MacTreeLexer] }
|
fn lexers() -> Vec<LexerObj> { vec![&MacTreeLexer] }
|
||||||
fn parsers() -> Vec<ParserObj> { vec![&LetLine] }
|
fn parsers() -> Vec<ParserObj> { vec![&LetLine, &MacroLine] }
|
||||||
fn env() -> Vec<GenMember> { gen_macro_lib() }
|
fn env() -> Vec<GenMember> { gen_macro_lib() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
|
use hashbrown::HashSet;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_api::Paren;
|
use orchid_api::Paren;
|
||||||
use orchid_base::error::OrcErrv;
|
use orchid_base::error::{OrcErrv, Reporter, mk_errv};
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants, fmt};
|
||||||
use orchid_base::interner::Tok;
|
use orchid_base::interner::Tok;
|
||||||
use orchid_base::location::Pos;
|
use orchid_base::location::Pos;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
@@ -15,16 +16,71 @@ use orchid_base::tl_cache;
|
|||||||
use orchid_base::tree::indent;
|
use orchid_base::tree::indent;
|
||||||
use orchid_extension::atom::Atomic;
|
use orchid_extension::atom::Atomic;
|
||||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
||||||
|
use orchid_extension::conv::ToExpr;
|
||||||
use orchid_extension::expr::Expr;
|
use orchid_extension::expr::Expr;
|
||||||
|
use orchid_extension::gen_expr::{GExpr, arg, bot, call, lambda, sym_ref};
|
||||||
|
use orchid_extension::system::SysCtx;
|
||||||
|
use substack::Substack;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LowerCtx<'a> {
|
||||||
|
pub sys: SysCtx,
|
||||||
|
pub rep: &'a Reporter,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MacTree {
|
pub struct MacTree {
|
||||||
pub pos: Pos,
|
pub pos: Pos,
|
||||||
pub tok: Rc<MacTok>,
|
pub tok: Rc<MacTok>,
|
||||||
|
pub glossary: Rc<HashSet<Sym>>,
|
||||||
}
|
}
|
||||||
impl MacTree {
|
impl MacTree {
|
||||||
pub fn tok(&self) -> &MacTok { &*self.tok }
|
pub fn tok(&self) -> &MacTok { &self.tok }
|
||||||
pub fn pos(&self) -> Pos { self.pos.clone() }
|
pub fn pos(&self) -> Pos { self.pos.clone() }
|
||||||
|
pub fn glossary(&self) -> &HashSet<Sym> { &self.glossary }
|
||||||
|
pub async fn lower(&self, ctx: LowerCtx<'_>, args: Substack<'_, Sym>) -> GExpr {
|
||||||
|
let expr = match self.tok() {
|
||||||
|
MacTok::Bottom(e) => bot(e.clone()),
|
||||||
|
MacTok::Lambda(arg, body) => {
|
||||||
|
let MacTok::Name(name) = &*arg.tok else {
|
||||||
|
let err = mk_errv(
|
||||||
|
ctx.sys.i().i("Syntax error after macros").await,
|
||||||
|
"This token ends up as a binding, consider replacing it with a name",
|
||||||
|
[arg.pos()],
|
||||||
|
);
|
||||||
|
ctx.rep.report(err.clone());
|
||||||
|
return bot(err);
|
||||||
|
};
|
||||||
|
lambda(args.len() as u64, lower_v(body, ctx, args.push(name.clone())).await)
|
||||||
|
},
|
||||||
|
MacTok::Name(name) => match args.iter().enumerate().find(|(_, n)| *n == name) {
|
||||||
|
None => sym_ref(name.clone()),
|
||||||
|
Some((i, _)) => arg((args.len() - i) as u64),
|
||||||
|
},
|
||||||
|
MacTok::Ph(ph) => {
|
||||||
|
let err = mk_errv(
|
||||||
|
ctx.sys.i().i("Placeholder in value").await,
|
||||||
|
format!("Placeholder {ph} is only supported in macro patterns"),
|
||||||
|
[self.pos()],
|
||||||
|
);
|
||||||
|
ctx.rep.report(err.clone());
|
||||||
|
return bot(err);
|
||||||
|
},
|
||||||
|
MacTok::S(Paren::Round, body) => call(lower_v(body, ctx, args).await),
|
||||||
|
MacTok::S(..) => {
|
||||||
|
let err = mk_errv(
|
||||||
|
ctx.sys.i().i("[] or {} after macros").await,
|
||||||
|
format!("{} didn't match any macro", fmt(self, ctx.sys.i()).await),
|
||||||
|
[self.pos()],
|
||||||
|
);
|
||||||
|
ctx.rep.report(err.clone());
|
||||||
|
return bot(err);
|
||||||
|
},
|
||||||
|
MacTok::Slot => panic!("Uninstantiated template should never be exposed"),
|
||||||
|
MacTok::Value(v) => v.clone().to_expr().await,
|
||||||
|
};
|
||||||
|
expr.at(self.pos())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Atomic for MacTree {
|
impl Atomic for MacTree {
|
||||||
type Data = ();
|
type Data = ();
|
||||||
@@ -34,11 +90,20 @@ impl OwnedAtom for MacTree {
|
|||||||
type Refs = ();
|
type Refs = ();
|
||||||
|
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
|
async fn print_atom<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
self.tok.print(c).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Format for MacTree {
|
||||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
self.tok.print(c).await
|
self.tok.print(c).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn lower_v(v: &[MacTree], ctx: LowerCtx<'_>, args: Substack<'_, Sym>) -> Vec<GExpr> {
|
||||||
|
join_all(v.iter().map(|t| t.lower(ctx.clone(), args.clone())).collect::<Vec<_>>()).await
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MacTok {
|
pub enum MacTok {
|
||||||
S(Paren, Vec<MacTree>),
|
S(Paren, Vec<MacTree>),
|
||||||
@@ -53,8 +118,17 @@ pub enum MacTok {
|
|||||||
Bottom(OrcErrv),
|
Bottom(OrcErrv),
|
||||||
}
|
}
|
||||||
impl MacTok {
|
impl MacTok {
|
||||||
|
pub fn build_glossary(&self) -> HashSet<Sym> {
|
||||||
|
match self {
|
||||||
|
MacTok::Bottom(_) | MacTok::Ph(_) | MacTok::Slot | MacTok::Value(_) => HashSet::new(),
|
||||||
|
MacTok::Name(sym) => HashSet::from([sym.clone()]),
|
||||||
|
MacTok::S(_, body) => body.iter().flat_map(|mt| &*mt.glossary).cloned().collect(),
|
||||||
|
MacTok::Lambda(arg, body) =>
|
||||||
|
body.iter().chain([arg]).flat_map(|mt| &*mt.glossary).cloned().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn at(self, pos: impl Into<Pos>) -> MacTree {
|
pub fn at(self, pos: impl Into<Pos>) -> MacTree {
|
||||||
MacTree { pos: pos.into(), tok: Rc::new(self) }
|
MacTree { pos: pos.into(), glossary: Rc::new(self.build_glossary()), tok: Rc::new(self) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Format for MacTok {
|
impl Format for MacTok {
|
||||||
@@ -77,7 +151,7 @@ impl Format for MacTok {
|
|||||||
},
|
},
|
||||||
[mtreev_fmt(body, c).await],
|
[mtreev_fmt(body, c).await],
|
||||||
),
|
),
|
||||||
Self::Slot => "SLOT".into(),
|
Self::Slot => "$SLOT".into(),
|
||||||
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
|
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
|
||||||
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
||||||
}
|
}
|
||||||
@@ -129,12 +203,12 @@ pub fn map_mactree<F: FnMut(MacTree) -> Option<MacTree>>(
|
|||||||
ro(changed, |changed| map_mactree(arg, changed, map)),
|
ro(changed, |changed| map_mactree(arg, changed, map)),
|
||||||
map_mactree_v(body, changed, map),
|
map_mactree_v(body, changed, map),
|
||||||
),
|
),
|
||||||
MacTok::Name(_) | MacTok::Value(_) | MacTok::Slot | MacTok::Ph(_) | MacTok::Bottom(_) =>
|
MacTok::Name(_) | MacTok::Value(_) => return src.clone(),
|
||||||
return src.clone(),
|
MacTok::Slot | MacTok::Ph(_) | MacTok::Bottom(_) => return src.clone(),
|
||||||
MacTok::S(p, body) => MacTok::S(*p, map_mactree_v(body, changed, map)),
|
MacTok::S(p, body) => MacTok::S(*p, map_mactree_v(body, changed, map)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if *changed { MacTree { pos: src.pos.clone(), tok: Rc::new(tok) } } else { src.clone() }
|
if *changed { tok.at(src.pos()) } else { src.clone() }
|
||||||
}
|
}
|
||||||
pub fn map_mactree_v<F: FnMut(MacTree) -> Option<MacTree>>(
|
pub fn map_mactree_v<F: FnMut(MacTree) -> Option<MacTree>>(
|
||||||
src: &[MacTree],
|
src: &[MacTree],
|
||||||
@@ -152,3 +226,7 @@ fn ro<T>(flag: &mut bool, cb: impl FnOnce(&mut bool) -> T) -> T {
|
|||||||
*flag |= new_flag;
|
*flag |= new_flag;
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn glossary_v(src: &[MacTree]) -> impl Iterator<Item = Sym> {
|
||||||
|
src.iter().flat_map(|mt| mt.glossary()).cloned()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use orchid_base::error::{OrcRes, mk_errv};
|
use orchid_base::error::{OrcRes, mk_errv};
|
||||||
|
use orchid_base::parse::ParseCtx;
|
||||||
|
use orchid_base::sym;
|
||||||
use orchid_base::tokens::PARENS;
|
use orchid_base::tokens::PARENS;
|
||||||
|
use orchid_base::tree::Paren;
|
||||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||||
use orchid_extension::tree::{GenTok, GenTokTree, x_tok};
|
use orchid_extension::parser::p_tree2gen;
|
||||||
|
use orchid_extension::tree::{GenTok, GenTokTree, ref_tok, x_tok};
|
||||||
|
|
||||||
|
use crate::macros::let_line::parse_tok;
|
||||||
use crate::macros::mactree::{MacTok, MacTree};
|
use crate::macros::mactree::{MacTok, MacTree};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -15,24 +19,41 @@ impl Lexer for MacTreeLexer {
|
|||||||
const CHAR_FILTER: &'static [RangeInclusive<char>] = &['\''..='\''];
|
const CHAR_FILTER: &'static [RangeInclusive<char>] = &['\''..='\''];
|
||||||
async fn lex<'a>(tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
async fn lex<'a>(tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||||
let Some(tail2) = tail.strip_prefix('\'') else {
|
let Some(tail2) = tail.strip_prefix('\'') else {
|
||||||
return Err(err_not_applicable(ctx.i()).await.into());
|
return Err(err_not_applicable(ctx.i()).await);
|
||||||
};
|
};
|
||||||
let tail3 = tail2.trim_start();
|
let tail3 = tail2.trim_start();
|
||||||
return match mac_tree(tail3, ctx).await {
|
let mut args = Vec::new();
|
||||||
Ok((tail4, mactree)) => Ok((tail4, x_tok(mactree).at(ctx.pos_tt(tail, tail4)))),
|
return match mac_tree(tail3, &mut args, ctx).await {
|
||||||
|
Ok((tail4, mactree)) => {
|
||||||
|
let range = ctx.pos_tt(tail, tail4);
|
||||||
|
let tok = match &args[..] {
|
||||||
|
[] => x_tok(mactree).await,
|
||||||
|
_ => {
|
||||||
|
let call = ([
|
||||||
|
ref_tok(sym!(macros::instantiate_tpl; ctx.i()).await).await.at(range.clone()),
|
||||||
|
x_tok(mactree).await.at(range.clone()),
|
||||||
|
]
|
||||||
|
.into_iter())
|
||||||
|
.chain(args.into_iter());
|
||||||
|
GenTok::S(Paren::Round, call.collect())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok((tail4, tok.at(range)))
|
||||||
|
},
|
||||||
Err(e) => Ok((tail2, GenTok::Bottom(e).at(ctx.pos_lt(1, tail2)))),
|
Err(e) => Ok((tail2, GenTok::Bottom(e).at(ctx.pos_lt(1, tail2)))),
|
||||||
};
|
};
|
||||||
async fn mac_tree<'a>(tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, MacTree)> {
|
async fn mac_tree<'a>(
|
||||||
|
tail: &'a str,
|
||||||
|
args: &mut Vec<GenTokTree>,
|
||||||
|
ctx: &'a LexContext<'a>,
|
||||||
|
) -> OrcRes<(&'a str, MacTree)> {
|
||||||
for (lp, rp, paren) in PARENS {
|
for (lp, rp, paren) in PARENS {
|
||||||
let Some(mut body_tail) = tail.strip_prefix(*lp) else { continue };
|
let Some(mut body_tail) = tail.strip_prefix(*lp) else { continue };
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
return loop {
|
return loop {
|
||||||
let tail2 = body_tail.trim();
|
let tail2 = body_tail.trim_start();
|
||||||
if let Some(tail3) = tail2.strip_prefix(*rp) {
|
if let Some(tail3) = tail2.strip_prefix(*rp) {
|
||||||
break Ok((tail3, MacTree {
|
break Ok((tail3, MacTok::S(*paren, items).at(ctx.pos_tt(tail, tail3).pos())));
|
||||||
pos: ctx.pos_tt(tail, tail3).pos(),
|
|
||||||
tok: Rc::new(MacTok::S(*paren, items)),
|
|
||||||
}));
|
|
||||||
} else if tail2.is_empty() {
|
} else if tail2.is_empty() {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
ctx.i().i("Unclosed block").await,
|
ctx.i().i("Unclosed block").await,
|
||||||
@@ -40,22 +61,36 @@ impl Lexer for MacTreeLexer {
|
|||||||
[ctx.pos_lt(1, tail)],
|
[ctx.pos_lt(1, tail)],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let (new_tail, new_item) = mac_tree(tail2, ctx).boxed_local().await?;
|
let (new_tail, new_item) = mac_tree(tail2, args, ctx).boxed_local().await?;
|
||||||
body_tail = new_tail;
|
body_tail = new_tail;
|
||||||
items.push(new_item);
|
items.push(new_item);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const INTERPOL: &[&str] = &["$", "..$"];
|
if let Some(tail2) = tail.strip_prefix("$") {
|
||||||
for pref in INTERPOL {
|
let (tail3, sub) = ctx.recurse(tail2).await?;
|
||||||
let Some(code) = tail.strip_prefix(pref) else { continue };
|
let sr = ctx.pos_tt(tail, tail3);
|
||||||
todo!("Register parameter, and push this onto the argument stack held in the atom")
|
args.push(p_tree2gen(sub));
|
||||||
|
return Ok((tail3, MacTok::Slot.at(sr.pos())));
|
||||||
|
}
|
||||||
|
if let Some(tail2) = tail.strip_prefix("\\") {
|
||||||
|
let tail2 = tail2.trim_start();
|
||||||
|
let (mut tail3, param) = mac_tree(tail2, args, ctx).boxed_local().await?;
|
||||||
|
let mut body = Vec::new();
|
||||||
|
loop {
|
||||||
|
let tail4 = tail3.trim_start();
|
||||||
|
if tail4.is_empty() || tail4.starts_with(|c| ")]}".contains(c)) {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let (tail5, body_tok) = mac_tree(tail4, args, ctx).boxed_local().await?;
|
||||||
|
body.push(body_tok);
|
||||||
|
tail3 = tail5;
|
||||||
|
}
|
||||||
|
Ok((tail3, MacTok::Lambda(param, body).at(ctx.pos_tt(tail, tail3).pos())))
|
||||||
|
} else {
|
||||||
|
let (tail2, sub) = ctx.recurse(tail).await?;
|
||||||
|
let parsed = parse_tok(&sub, ctx).await.expect("Unexpected invalid token");
|
||||||
|
Ok((tail2, parsed))
|
||||||
}
|
}
|
||||||
todo!("recursive lexer call");
|
|
||||||
return Err(mk_errv(
|
|
||||||
ctx.i().i("Expected token after '").await,
|
|
||||||
format!("Expected a token after ', found {tail:?}"),
|
|
||||||
[ctx.pos_lt(1, tail)],
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
mod instantiate_tpl;
|
mod instantiate_tpl;
|
||||||
mod let_line;
|
mod let_line;
|
||||||
mod macro_lib;
|
mod macro_lib;
|
||||||
mod macro_system;
|
mod macro_line;
|
||||||
|
pub mod macro_system;
|
||||||
pub mod mactree;
|
pub mod mactree;
|
||||||
mod mactree_lexer;
|
mod mactree_lexer;
|
||||||
|
pub mod recur_state;
|
||||||
|
mod resolve;
|
||||||
mod rule;
|
mod rule;
|
||||||
|
|
||||||
use mactree::{MacTok, MacTree};
|
use mactree::{MacTok, MacTree};
|
||||||
|
|||||||
59
orchid-std/src/macros/recur_state.rs
Normal file
59
orchid-std/src/macros/recur_state.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use never::Never;
|
||||||
|
use orchid_base::interner::Tok;
|
||||||
|
use orchid_base::name::Sym;
|
||||||
|
use orchid_extension::atom::Atomic;
|
||||||
|
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct RulePath {
|
||||||
|
pub module: Sym,
|
||||||
|
pub main_kw: Tok<String>,
|
||||||
|
pub rule: u32,
|
||||||
|
}
|
||||||
|
impl RulePath {
|
||||||
|
pub fn name(&self) -> String { format!("rule::{}::{}", self.main_kw, self.rule) }
|
||||||
|
}
|
||||||
|
impl fmt::Display for RulePath {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Rule {}::({})::{}", self.module, self.main_kw, self.rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum RecurState {
|
||||||
|
Bottom,
|
||||||
|
Recursive { path: RulePath, prev: Rc<RecurState> },
|
||||||
|
}
|
||||||
|
impl RecurState {
|
||||||
|
pub fn base(path: RulePath) -> Self {
|
||||||
|
RecurState::Recursive { path, prev: Rc::new(RecurState::Bottom) }
|
||||||
|
}
|
||||||
|
pub fn push(&self, new: RulePath) -> Option<Self> {
|
||||||
|
let mut cur = self;
|
||||||
|
while let Self::Recursive { path, prev } = cur {
|
||||||
|
if &new == path {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
cur = prev;
|
||||||
|
}
|
||||||
|
Some(Self::Recursive { path: new, prev: Rc::new(self.clone()) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Atomic for RecurState {
|
||||||
|
type Data = Option<()>;
|
||||||
|
type Variant = OwnedVariant;
|
||||||
|
}
|
||||||
|
impl OwnedAtom for RecurState {
|
||||||
|
type Refs = Never;
|
||||||
|
|
||||||
|
async fn val(&self) -> Cow<'_, Self::Data> {
|
||||||
|
Cow::Owned(match self {
|
||||||
|
Self::Bottom => None,
|
||||||
|
Self::Recursive { .. } => Some(()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
107
orchid-std/src/macros/resolve.rs
Normal file
107
orchid-std/src/macros/resolve.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
use futures::FutureExt;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use orchid_base::error::mk_errv;
|
||||||
|
use orchid_base::location::Pos;
|
||||||
|
use orchid_base::name::Sym;
|
||||||
|
use orchid_base::sym;
|
||||||
|
use orchid_base::tree::Paren;
|
||||||
|
use orchid_extension::conv::ToExpr;
|
||||||
|
use orchid_extension::coroutine_exec::ExecHandle;
|
||||||
|
use orchid_extension::gen_expr::{GExpr, bot, call, sym_ref};
|
||||||
|
use orchid_extension::system::SysCtx;
|
||||||
|
|
||||||
|
use crate::macros::macro_line::{Macro, Rule};
|
||||||
|
use crate::macros::recur_state::{RecurState, RulePath};
|
||||||
|
use crate::macros::rule::matcher::{NamedMatcher, PriodMatcher};
|
||||||
|
use crate::macros::rule::state::{MatchState, StateEntry};
|
||||||
|
use crate::{MacTok, MacTree};
|
||||||
|
|
||||||
|
pub struct ResolveCtx<'a> {
|
||||||
|
pub ctx: SysCtx,
|
||||||
|
pub recur: RecurState,
|
||||||
|
pub h: ExecHandle<'a>,
|
||||||
|
pub named: HashMap<Sym, Vec<(&'a NamedMatcher, &'a Macro, &'a Rule)>>,
|
||||||
|
pub priod: Vec<(&'a PriodMatcher, &'a Macro, &'a Rule)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resolve(ctx: &mut ResolveCtx<'_>, value: &MacTree) -> Option<MacTree> {
|
||||||
|
match value.tok() {
|
||||||
|
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
|
||||||
|
MacTok::Bottom(_) | MacTok::Value(_) | MacTok::Name(_) => None,
|
||||||
|
MacTok::Lambda(arg, body) =>
|
||||||
|
Some(MacTok::Lambda(arg.clone(), resolve_seq(ctx, body).await?).at(value.pos())),
|
||||||
|
MacTok::S(ptyp, body) => Some(MacTok::S(*ptyp, resolve_seq(ctx, body).await?).at(value.pos())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resolve_seq(ctx: &mut ResolveCtx<'_>, val: &[MacTree]) -> Option<Vec<MacTree>> {
|
||||||
|
let mut any_changed = false;
|
||||||
|
let mut i = 0;
|
||||||
|
let mut val = val.to_vec();
|
||||||
|
while i < val.len() {
|
||||||
|
let MacTok::Name(key) = val[i].tok() else { continue };
|
||||||
|
let Some(options) = ctx.named.get(key) else { continue };
|
||||||
|
let matches = (options.iter())
|
||||||
|
.filter_map(|r| Some((r.1, r.2, r.0.apply(&val[i..], |_| false)?)))
|
||||||
|
.collect_vec();
|
||||||
|
match matches.len() {
|
||||||
|
0 => i += 1,
|
||||||
|
1 => {
|
||||||
|
any_changed = true;
|
||||||
|
let (mac, rule, (state, tail)) = matches.into_iter().exactly_one().unwrap();
|
||||||
|
let end = val.len() - tail.len();
|
||||||
|
let new_i = val.len() - tail.len();
|
||||||
|
let body_call = mk_body_call(mac, rule, &state, &ctx.ctx, ctx.recur.clone()).await;
|
||||||
|
std::mem::drop(state);
|
||||||
|
val.splice(i..end, [MacTok::Value(ctx.h.register(body_call).await).at(Pos::None)]);
|
||||||
|
i = new_i;
|
||||||
|
},
|
||||||
|
2.. => todo!("Named macros conflict!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (matcher, mac, rule) in &ctx.priod {
|
||||||
|
let Some(state) = matcher.apply(&val, |_| false) else { continue };
|
||||||
|
return Some(vec![
|
||||||
|
MacTok::Value(
|
||||||
|
ctx.h.register(mk_body_call(mac, rule, &state, &ctx.ctx, ctx.recur.clone()).await).await,
|
||||||
|
)
|
||||||
|
.at(Pos::None),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
for expr in val.iter_mut() {
|
||||||
|
if let Some(new) = resolve(ctx, expr).boxed_local().await {
|
||||||
|
*expr = new;
|
||||||
|
any_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if any_changed { Some(val) } else { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn mk_body_call(
|
||||||
|
mac: &Macro,
|
||||||
|
rule: &Rule,
|
||||||
|
state: &MatchState<'_>,
|
||||||
|
ctx: &SysCtx,
|
||||||
|
recur: RecurState,
|
||||||
|
) -> GExpr {
|
||||||
|
let rule_path =
|
||||||
|
RulePath { module: mac.0.module.clone(), main_kw: mac.0.own_kws[0].clone(), rule: rule.index };
|
||||||
|
let Some(new_recur) = recur.push(rule_path.clone()) else {
|
||||||
|
return bot(mk_errv(
|
||||||
|
ctx.i().i("Circular macro dependency").await,
|
||||||
|
format!("The definition of {rule_path} is circular"),
|
||||||
|
[rule.pos.clone()],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let mut call_args = vec![sym_ref(mac.0.module.suffix([rule.body_name.clone()], ctx.i()).await)];
|
||||||
|
for name in rule.placeholders.iter() {
|
||||||
|
call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
|
||||||
|
StateEntry::Scalar(scal) => (**scal).clone().to_expr().await,
|
||||||
|
StateEntry::Vec(vec) => MacTok::S(Paren::Round, vec.to_vec()).at(Pos::None).to_expr().await,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
call_args
|
||||||
|
.push(call([sym_ref(sym!(macros::resolve_recur; ctx.i()).await), new_recur.to_expr().await]));
|
||||||
|
call(call_args)
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ pub type MaxVecSplit<'a> = (&'a [MacTree], (Tok<String>, u8, bool), &'a [MacTree
|
|||||||
/// Derive the details of the central vectorial and the two sides from a
|
/// Derive the details of the central vectorial and the two sides from a
|
||||||
/// slice of Expr's
|
/// slice of Expr's
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn split_at_max_vec(pattern: &[MacTree]) -> Option<MaxVecSplit> {
|
fn split_at_max_vec(pattern: &'_ [MacTree]) -> Option<MaxVecSplit<'_>> {
|
||||||
let rngidx = pattern
|
let rngidx = pattern
|
||||||
.iter()
|
.iter()
|
||||||
.position_max_by_key(|expr| vec_attrs(expr).map(|attrs| attrs.1 as i64).unwrap_or(-1))?;
|
.position_max_by_key(|expr| vec_attrs(expr).map(|attrs| attrs.1 as i64).unwrap_or(-1))?;
|
||||||
@@ -31,7 +31,6 @@ fn scal_cnt<'a>(iter: impl Iterator<Item = &'a MacTree>) -> usize {
|
|||||||
iter.take_while(|expr| vec_attrs(expr).is_none()).count()
|
iter.take_while(|expr| vec_attrs(expr).is_none()).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes<AnyMatcher> {
|
pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes<AnyMatcher> {
|
||||||
let left_split = scal_cnt(pattern.iter());
|
let left_split = scal_cnt(pattern.iter());
|
||||||
if pattern.len() <= left_split {
|
if pattern.len() <= left_split {
|
||||||
@@ -49,13 +48,11 @@ pub async fn mk_any(pattern: &[MacTree], i: &Interner) -> OrcRes<AnyMatcher> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pattern MUST NOT contain vectorial placeholders
|
/// Pattern MUST NOT contain vectorial placeholders
|
||||||
#[must_use]
|
|
||||||
async fn mk_scalv(pattern: &[MacTree], i: &Interner) -> OrcRes<Vec<ScalMatcher>> {
|
async fn mk_scalv(pattern: &[MacTree], i: &Interner) -> OrcRes<Vec<ScalMatcher>> {
|
||||||
join_all(pattern.iter().map(|pat| mk_scalar(pat, i))).await.into_iter().collect()
|
join_all(pattern.iter().map(|pat| mk_scalar(pat, i))).await.into_iter().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pattern MUST start and end with a vectorial placeholder
|
/// Pattern MUST start and end with a vectorial placeholder
|
||||||
#[must_use]
|
|
||||||
pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
||||||
debug_assert!(!pattern.is_empty(), "pattern cannot be empty");
|
debug_assert!(!pattern.is_empty(), "pattern cannot be empty");
|
||||||
debug_assert!(pattern.first().map(vec_attrs).is_some(), "pattern must start with a vectorial");
|
debug_assert!(pattern.first().map(vec_attrs).is_some(), "pattern must start with a vectorial");
|
||||||
@@ -116,7 +113,6 @@ pub async fn mk_vec(pattern: &[MacTree], i: &Interner) -> OrcRes<VecMatcher> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pattern MUST NOT be a vectorial placeholder
|
/// Pattern MUST NOT be a vectorial placeholder
|
||||||
#[must_use]
|
|
||||||
async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
||||||
Ok(match &*pattern.tok {
|
Ok(match &*pattern.tok {
|
||||||
MacTok::Name(n) => ScalMatcher::Name(n.clone()),
|
MacTok::Name(n) => ScalMatcher::Name(n.clone()),
|
||||||
@@ -140,8 +136,6 @@ async fn mk_scalar(pattern: &MacTree, i: &Interner) -> OrcRes<ScalMatcher> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use orchid_base::interner::Interner;
|
use orchid_base::interner::Interner;
|
||||||
use orchid_base::location::SrcRange;
|
use orchid_base::location::SrcRange;
|
||||||
use orchid_base::sym;
|
use orchid_base::sym;
|
||||||
@@ -149,15 +143,14 @@ mod test {
|
|||||||
use test_executors::spin_on;
|
use test_executors::spin_on;
|
||||||
|
|
||||||
use super::mk_any;
|
use super::mk_any;
|
||||||
|
use crate::macros::MacTok;
|
||||||
use crate::macros::mactree::{Ph, PhKind};
|
use crate::macros::mactree::{Ph, PhKind};
|
||||||
use crate::macros::{MacTok, MacTree};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scan() {
|
fn test_scan() {
|
||||||
spin_on(async {
|
spin_on(async {
|
||||||
let i = Interner::new_master();
|
let i = Interner::new_master();
|
||||||
let ex =
|
let ex = |tok: MacTok| async { tok.at(SrcRange::mock(&i).await.pos()) };
|
||||||
|tok: MacTok| async { MacTree { tok: Rc::new(tok), pos: SrcRange::mock(&i).await.pos() } };
|
|
||||||
let pattern = vec![
|
let pattern = vec![
|
||||||
ex(MacTok::Ph(Ph {
|
ex(MacTok::Ph(Ph {
|
||||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::error::OrcRes;
|
||||||
use orchid_base::interner::Interner;
|
use orchid_base::interner::{Interner, Tok};
|
||||||
use orchid_base::location::Pos;
|
use orchid_base::location::Pos;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
|
|
||||||
@@ -16,46 +15,47 @@ use super::vec_match::vec_match;
|
|||||||
use crate::macros::mactree::{Ph, PhKind};
|
use crate::macros::mactree::{Ph, PhKind};
|
||||||
use crate::macros::{MacTok, MacTree};
|
use crate::macros::{MacTok, MacTree};
|
||||||
|
|
||||||
pub fn first_is_vec(pattern: &[MacTree]) -> bool { vec_attrs(pattern.first().unwrap()).is_some() }
|
pub struct NamedMatcher {
|
||||||
pub fn last_is_vec(pattern: &[MacTree]) -> bool { vec_attrs(pattern.last().unwrap()).is_some() }
|
inner: AnyMatcher,
|
||||||
|
head: Sym,
|
||||||
pub struct NamedMatcher(AnyMatcher);
|
after_tok: Tok<String>,
|
||||||
|
}
|
||||||
impl NamedMatcher {
|
impl NamedMatcher {
|
||||||
pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> {
|
pub async fn new(pattern: &[MacTree], i: &Interner) -> OrcRes<Self> {
|
||||||
assert!(
|
let head = match pattern.first().map(|tree| tree.tok()) {
|
||||||
matches!(pattern.first().map(|tree| &*tree.tok), Some(MacTok::Name(_))),
|
Some(MacTok::Name(name)) => name.clone(),
|
||||||
"Named matchers must begin with a name"
|
_ => panic!("Named matchers must begin with a name"),
|
||||||
);
|
};
|
||||||
|
let after_tok = i.i("::after").await;
|
||||||
Ok(Self(match last_is_vec(pattern) {
|
let inner = match pattern.last().and_then(vec_attrs).is_some() {
|
||||||
true => mk_any(pattern, i).await,
|
true => mk_any(pattern, i).await?,
|
||||||
false => {
|
false => {
|
||||||
let kind = PhKind::Vector { priority: 0, at_least_one: false };
|
let kind = PhKind::Vector { priority: 0, at_least_one: false };
|
||||||
let tok = MacTok::Ph(Ph { name: i.i("::after").await, kind });
|
let suffix = [MacTok::Ph(Ph { name: after_tok.clone(), kind }).at(Pos::None)];
|
||||||
let suffix = [MacTree { pos: Pos::None, tok: Rc::new(tok) }];
|
mk_any(&pattern.iter().cloned().chain(suffix).collect_vec(), i).await?
|
||||||
mk_any(&pattern.iter().chain(&suffix).cloned().collect_vec(), i).await
|
|
||||||
},
|
},
|
||||||
}?))
|
};
|
||||||
|
Ok(Self { after_tok, inner, head })
|
||||||
}
|
}
|
||||||
|
pub fn head(&self) -> Sym { self.head.clone() }
|
||||||
/// Also returns the tail, if any, which should be matched further
|
/// Also returns the tail, if any, which should be matched further
|
||||||
/// Note that due to how priod works below, the main usable information from
|
/// Note that due to how priod works below, the main usable information from
|
||||||
/// the tail is its length
|
/// the tail is its length
|
||||||
pub async fn apply<'a>(
|
pub fn apply<'a>(
|
||||||
&self,
|
&self,
|
||||||
seq: &'a [MacTree],
|
seq: &'a [MacTree],
|
||||||
i: &Interner,
|
|
||||||
save_loc: impl Fn(Sym) -> bool,
|
save_loc: impl Fn(Sym) -> bool,
|
||||||
) -> Option<(MatchState<'a>, &'a [MacTree])> {
|
) -> Option<(MatchState<'a>, &'a [MacTree])> {
|
||||||
let mut state = any_match(&self.0, seq, &save_loc)?;
|
let mut state = any_match(&self.inner, seq, &save_loc)?;
|
||||||
match state.remove(i.i("::after").await) {
|
match state.remove(self.after_tok.clone()) {
|
||||||
Some(StateEntry::Scalar(_)) => panic!("::after can never be a scalar entry!"),
|
Some(StateEntry::Scalar(_)) => panic!("{} can never be a scalar entry!", self.after_tok),
|
||||||
Some(StateEntry::Vec(v)) => Some((state, v)),
|
Some(StateEntry::Vec(v)) => Some((state, v)),
|
||||||
None => Some((state, &[][..])),
|
None => Some((state, &[][..])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for NamedMatcher {
|
impl fmt::Display for NamedMatcher {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) }
|
||||||
}
|
}
|
||||||
impl fmt::Debug for NamedMatcher {
|
impl fmt::Debug for NamedMatcher {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedMatcher({self})") }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedMatcher({self})") }
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ impl<'a> MatchState<'a> {
|
|||||||
pub fn from_name(name: Sym, location: Pos) -> Self {
|
pub fn from_name(name: Sym, location: Pos) -> Self {
|
||||||
Self { name_posv: HashMap::from([(name, vec![location])]), placeholders: HashMap::new() }
|
Self { name_posv: HashMap::from([(name, vec![location])]), placeholders: HashMap::new() }
|
||||||
}
|
}
|
||||||
|
pub fn get(&self, key: &Tok<String>) -> Option<&StateEntry<'a>> { self.placeholders.get(key) }
|
||||||
pub fn remove(&mut self, name: Tok<String>) -> Option<StateEntry<'a>> {
|
pub fn remove(&mut self, name: Tok<String>) -> Option<StateEntry<'a>> {
|
||||||
self.placeholders.remove(&name)
|
self.placeholders.remove(&name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use orchid_extension::entrypoint::ExtensionData;
|
use orchid_extension::entrypoint::ExtensionData;
|
||||||
use orchid_extension::tokio::tokio_main;
|
use orchid_extension::tokio::tokio_main;
|
||||||
use orchid_std::StdSystem;
|
use orchid_std::{MacroSystem, StdSystem};
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
pub async fn main() { tokio_main(ExtensionData::new("orchid-std::main", &[&StdSystem])).await }
|
pub async fn main() {
|
||||||
|
tokio_main(ExtensionData::new("orchid-std::main", &[&StdSystem, &MacroSystem])).await
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::error::OrcRes;
|
||||||
use orchid_base::number::{num_to_err, parse_num};
|
use orchid_base::number::{num_to_errv, parse_num};
|
||||||
use orchid_extension::atom::ToAtom;
|
use orchid_extension::atom::ToAtom;
|
||||||
use orchid_extension::lexer::{LexContext, Lexer};
|
use orchid_extension::lexer::{LexContext, Lexer};
|
||||||
use orchid_extension::tree::{GenTokTree, x_tok};
|
use orchid_extension::tree::{GenTokTree, x_tok};
|
||||||
@@ -17,8 +17,8 @@ impl Lexer for NumLexer {
|
|||||||
let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len()));
|
let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len()));
|
||||||
let fac = match parse_num(chars) {
|
let fac = match parse_num(chars) {
|
||||||
Ok(numeric) => Num(numeric).to_atom_factory(),
|
Ok(numeric) => Num(numeric).to_atom_factory(),
|
||||||
Err(e) => return Err(num_to_err(e, ctx.pos(all), &ctx.src, ctx.ctx.i()).await.into()),
|
Err(e) => return Err(num_to_errv(e, ctx.pos(all), ctx.src(), ctx.ctx.i()).await),
|
||||||
};
|
};
|
||||||
Ok((tail, x_tok(fac).at(ctx.pos_lt(chars.len(), tail))))
|
Ok((tail, x_tok(fac).await.at(ctx.pos_lt(chars.len(), tail))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl OwnedAtom for StrAtom {
|
|||||||
async fn serialize(&self, _: SysCtx, sink: Pin<&mut (impl Write + ?Sized)>) -> Self::Refs {
|
async fn serialize(&self, _: SysCtx, sink: Pin<&mut (impl Write + ?Sized)>) -> Self::Refs {
|
||||||
self.deref().encode(sink).await
|
self.deref().encode(sink).await
|
||||||
}
|
}
|
||||||
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
format!("{:?}", &*self.0).into()
|
format!("{:?}", &*self.0).into()
|
||||||
}
|
}
|
||||||
async fn deserialize(mut ctx: impl DeserializeCtx, _: Self::Refs) -> Self {
|
async fn deserialize(mut ctx: impl DeserializeCtx, _: Self::Refs) -> Self {
|
||||||
@@ -69,7 +69,7 @@ impl From<Tok<String>> for IntStrAtom {
|
|||||||
impl OwnedAtom for IntStrAtom {
|
impl OwnedAtom for IntStrAtom {
|
||||||
type Refs = ();
|
type Refs = ();
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.to_api()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.to_api()) }
|
||||||
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
format!("{:?}i", *self.0).into()
|
format!("{:?}i", *self.0).into()
|
||||||
}
|
}
|
||||||
async fn serialize(&self, _: SysCtx, write: Pin<&mut (impl Write + ?Sized)>) {
|
async fn serialize(&self, _: SysCtx, write: Pin<&mut (impl Write + ?Sized)>) {
|
||||||
@@ -108,7 +108,7 @@ impl TryFromExpr for OrcString {
|
|||||||
}
|
}
|
||||||
let ctx = expr.ctx();
|
let ctx = expr.ctx();
|
||||||
match TypAtom::<IntStrAtom>::try_from_expr(expr).await {
|
match TypAtom::<IntStrAtom>::try_from_expr(expr).await {
|
||||||
Ok(t) => Ok(OrcString { ctx: t.data.ctx(), kind: OrcStringKind::Int(t) }),
|
Ok(t) => Ok(OrcString { ctx: t.untyped.ctx().clone(), kind: OrcStringKind::Int(t) }),
|
||||||
Err(e) => Err(mk_errv(ctx.i().i("A string was expected").await, "", e.pos_iter())),
|
Err(e) => Err(mk_errv(ctx.i().i("A string was expected").await, "", e.pos_iter())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::{OrcErr, OrcRes, mk_err, mk_errv};
|
use orchid_base::error::{OrcErr, OrcErrv, OrcRes, mk_errv};
|
||||||
use orchid_base::interner::Interner;
|
use orchid_base::interner::Interner;
|
||||||
use orchid_base::location::SrcRange;
|
use orchid_base::location::SrcRange;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
|
use orchid_base::parse::ParseCtx;
|
||||||
use orchid_base::sym;
|
use orchid_base::sym;
|
||||||
use orchid_base::tree::wrap_tokv;
|
use orchid_base::tree::wrap_tokv;
|
||||||
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
|
||||||
|
use orchid_extension::parser::p_tree2gen;
|
||||||
use orchid_extension::tree::{GenTokTree, ref_tok, x_tok};
|
use orchid_extension::tree::{GenTokTree, ref_tok, x_tok};
|
||||||
|
|
||||||
use super::str_atom::IntStrAtom;
|
use super::str_atom::IntStrAtom;
|
||||||
@@ -32,16 +34,16 @@ struct StringError {
|
|||||||
|
|
||||||
impl StringError {
|
impl StringError {
|
||||||
/// Convert into project error for reporting
|
/// Convert into project error for reporting
|
||||||
pub async fn into_proj(self, path: &Sym, pos: u32, i: &Interner) -> OrcErr {
|
pub async fn into_proj(self, path: &Sym, pos: u32, i: &Interner) -> OrcErrv {
|
||||||
let start = pos + self.pos;
|
let start = pos + self.pos;
|
||||||
mk_err(
|
mk_errv(
|
||||||
i.i("Failed to parse string").await,
|
i.i("Failed to parse string").await,
|
||||||
match self.kind {
|
match self.kind {
|
||||||
StringErrorKind::NotHex => "Expected a hex digit",
|
StringErrorKind::NotHex => "Expected a hex digit",
|
||||||
StringErrorKind::BadCodePoint => "The specified number is not a Unicode code point",
|
StringErrorKind::BadCodePoint => "The specified number is not a Unicode code point",
|
||||||
StringErrorKind::BadEscSeq => "Unrecognized escape sequence",
|
StringErrorKind::BadEscSeq => "Unrecognized escape sequence",
|
||||||
},
|
},
|
||||||
[SrcRange::new(start..start + 1, path).pos().into()],
|
[SrcRange::new(start..start + 1, path).pos()],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +99,7 @@ impl Lexer for StringLexer {
|
|||||||
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '`'..='`'];
|
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"', '`'..='`'];
|
||||||
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||||
let Some(mut tail) = all.strip_prefix('"') else {
|
let Some(mut tail) = all.strip_prefix('"') else {
|
||||||
return Err(err_not_applicable(ctx.ctx.i()).await.into());
|
return Err(err_not_applicable(ctx.ctx.i()).await);
|
||||||
};
|
};
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
let mut cur = String::new();
|
let mut cur = String::new();
|
||||||
@@ -110,15 +112,17 @@ impl Lexer for StringLexer {
|
|||||||
) -> GenTokTree {
|
) -> GenTokTree {
|
||||||
let str_val_res = parse_string(&str.split_off(0));
|
let str_val_res = parse_string(&str.split_off(0));
|
||||||
if let Err(e) = &str_val_res {
|
if let Err(e) = &str_val_res {
|
||||||
err.push(e.clone().into_proj(&ctx.src, ctx.pos(tail) - str.len() as u32, ctx.i()).await);
|
err.extend(e.clone().into_proj(ctx.src(), ctx.pos(tail) - str.len() as u32, ctx.i()).await);
|
||||||
}
|
}
|
||||||
let str_val = str_val_res.unwrap_or_default();
|
let str_val = str_val_res.unwrap_or_default();
|
||||||
x_tok(IntStrAtom::from(ctx.i().i(&*str_val).await)).at(ctx.pos_lt(str.len() as u32, tail))
|
x_tok(IntStrAtom::from(ctx.i().i(&*str_val).await))
|
||||||
as GenTokTree
|
.await
|
||||||
|
.at(ctx.pos_lt(str.len() as u32, tail)) as GenTokTree
|
||||||
}
|
}
|
||||||
let add_frag = |prev: Option<GenTokTree>, new: GenTokTree| async {
|
let add_frag = |prev: Option<GenTokTree>, new: GenTokTree| async {
|
||||||
let Some(prev) = prev else { return new };
|
let Some(prev) = prev else { return new };
|
||||||
let concat_fn = ref_tok(sym!(std::string::concat; ctx.i()).await)
|
let concat_fn = ref_tok(sym!(std::string::concat; ctx.i()).await)
|
||||||
|
.await
|
||||||
.at(SrcRange::zw(prev.sr.path(), prev.sr.start()));
|
.at(SrcRange::zw(prev.sr.path(), prev.sr.start()));
|
||||||
wrap_tokv([concat_fn, prev, new])
|
wrap_tokv([concat_fn, prev, new])
|
||||||
};
|
};
|
||||||
@@ -129,7 +133,7 @@ impl Lexer for StringLexer {
|
|||||||
ret = Some(add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await);
|
ret = Some(add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await);
|
||||||
let (new_tail, tree) = ctx.recurse(rest).await?;
|
let (new_tail, tree) = ctx.recurse(rest).await?;
|
||||||
tail = new_tail;
|
tail = new_tail;
|
||||||
ret = Some(add_frag(ret, tree).await);
|
ret = Some(add_frag(ret, p_tree2gen(tree)).await);
|
||||||
} else if tail.starts_with('\\') {
|
} else if tail.starts_with('\\') {
|
||||||
// parse_string will deal with it, we just have to skip the next char
|
// parse_string will deal with it, we just have to skip the next char
|
||||||
tail = &tail[2..];
|
tail = &tail[2..];
|
||||||
@@ -143,7 +147,7 @@ impl Lexer for StringLexer {
|
|||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
ctx.i().i("No string end").await,
|
ctx.i().i("No string end").await,
|
||||||
"String never terminated with \"",
|
"String never terminated with \"",
|
||||||
[SrcRange::new(range.clone(), &ctx.src)],
|
[SrcRange::new(range.clone(), ctx.src())],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,49 +5,66 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"editor.rulers": [
|
|
||||||
100 // Important; for accessibility reasons, code cannot be wider than 100ch
|
|
||||||
],
|
|
||||||
"[markdown]": {
|
"[markdown]": {
|
||||||
|
// markdown denotes line breaks with trailing space
|
||||||
|
"diffEditor.ignoreTrimWhitespace": false,
|
||||||
|
// Disable editor gadgets in markdown
|
||||||
"editor.unicodeHighlight.ambiguousCharacters": false,
|
"editor.unicodeHighlight.ambiguousCharacters": false,
|
||||||
"editor.unicodeHighlight.invisibleCharacters": false,
|
"editor.unicodeHighlight.invisibleCharacters": false,
|
||||||
"diffEditor.ignoreTrimWhitespace": false,
|
"editor.glyphMargin": false,
|
||||||
"editor.wordWrap": "bounded",
|
"editor.guides.indentation": false,
|
||||||
"editor.wordWrapColumn": 80,
|
"editor.lineNumbers": "off",
|
||||||
"editor.quickSuggestions": {
|
"editor.quickSuggestions": {
|
||||||
"comments": "off",
|
"comments": "off",
|
||||||
"strings": "off",
|
"strings": "off",
|
||||||
"other": "off"
|
"other": "off",
|
||||||
},
|
},
|
||||||
"editor.lineNumbers": "off",
|
|
||||||
"editor.glyphMargin": false,
|
|
||||||
"editor.rulers": [],
|
"editor.rulers": [],
|
||||||
"editor.guides.indentation": false,
|
"editor.wordWrap": "bounded",
|
||||||
|
"editor.wordWrapColumn": 80,
|
||||||
|
// wrap lines as we go
|
||||||
"editor.formatOnType": true,
|
"editor.formatOnType": true,
|
||||||
|
"editor.detectIndentation": false,
|
||||||
|
"editor.insertSpaces": false,
|
||||||
},
|
},
|
||||||
|
// Orchid is a human-made project
|
||||||
|
"chat.commandCenter.enabled": false,
|
||||||
|
// use spaces for indentation for Rust for now due to a rustfmt bug
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.stickyTabStops": true,
|
||||||
|
"editor.detectIndentation": false,
|
||||||
|
"editor.insertSpaces": true,
|
||||||
|
// Important; for accessibility reasons, code cannot be wider than 100ch
|
||||||
|
"editor.rulers": [ 100 ],
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"rust-analyzer.showUnlinkedFileNotification": false,
|
"git.confirmSync": false,
|
||||||
"rust-analyzer.checkOnSave": true,
|
"git.enableSmartCommit": true,
|
||||||
"rust-analyzer.check.command": "clippy",
|
"git.autofetch": true,
|
||||||
"rust-analyzer.rustfmt.extraArgs": [
|
"rust-analyzer.assist.emitMustUse": true,
|
||||||
"+nightly"
|
"rust-analyzer.assist.preferSelf": true,
|
||||||
],
|
|
||||||
"rust-analyzer.cargo.features": "all",
|
"rust-analyzer.cargo.features": "all",
|
||||||
|
"rust-analyzer.check.command": "clippy",
|
||||||
"rust-analyzer.check.features": "all",
|
"rust-analyzer.check.features": "all",
|
||||||
"files.associations": {
|
"rust-analyzer.checkOnSave": true,
|
||||||
"*.mjsd": "markdown"
|
"rust-analyzer.completion.fullFunctionSignatures.enable": true,
|
||||||
},
|
"rust-analyzer.completion.termSearch.enable": true,
|
||||||
|
"rust-analyzer.inlayHints.parameterHints.enable": false,
|
||||||
|
"rust-analyzer.inlayHints.typeHints.enable": false,
|
||||||
|
"rust-analyzer.rustfmt.extraArgs": [
|
||||||
|
"+nightly",
|
||||||
|
],
|
||||||
|
"rust-analyzer.showUnlinkedFileNotification": false,
|
||||||
"swissknife.notesEnabled": false,
|
"swissknife.notesEnabled": false,
|
||||||
},
|
},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"maptz.regionfolder",
|
"fill-labs.dependi",
|
||||||
"tamasfe.even-better-toml",
|
|
||||||
"yzhang.markdown-all-in-one",
|
|
||||||
"gruntfuggly.todo-tree",
|
"gruntfuggly.todo-tree",
|
||||||
"vadimcn.vscode-lldb",
|
"maptz.regionfolder",
|
||||||
"rust-lang.rust-analyzer",
|
"rust-lang.rust-analyzer",
|
||||||
"fill-labs.dependi"
|
"tamasfe.even-better-toml",
|
||||||
|
"vadimcn.vscode-lldb",
|
||||||
|
"yzhang.markdown-all-in-one",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user