forked from Orchid/orchid
Formatter introduced
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
const user := "dave"
|
const user = "dave"
|
||||||
const main := println "Hello $user!" exit_status::success
|
const main = println "Hello $user!" exit_status::success
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use orchid_api_derive::{Coding, Hierarchy};
|
|||||||
use orchid_api_traits::Request;
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ExprTicket, Expression, ExtHostReq, HostExtNotif, HostExtReq, OrcResult, SysId, TStrv,
|
ExprTicket, Expression, ExtHostReq, FormattingUnit, HostExtNotif, HostExtReq, OrcResult, SysId,
|
||||||
|
TStrv,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type AtomData = Vec<u8>;
|
pub type AtomData = Vec<u8>;
|
||||||
@@ -117,14 +118,14 @@ pub struct AtomDrop(pub SysId, pub AtomId);
|
|||||||
#[extends(AtomReq, HostExtReq)]
|
#[extends(AtomReq, HostExtReq)]
|
||||||
pub struct AtomPrint(pub Atom);
|
pub struct AtomPrint(pub Atom);
|
||||||
impl Request for AtomPrint {
|
impl Request for AtomPrint {
|
||||||
type Response = String;
|
type Response = FormattingUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
#[extends(ExtHostReq)]
|
#[extends(ExtHostReq)]
|
||||||
pub struct ExtAtomPrint(pub Atom);
|
pub struct ExtAtomPrint(pub Atom);
|
||||||
impl Request for ExtAtomPrint {
|
impl Request for ExtAtomPrint {
|
||||||
type Response = String;
|
type Response = FormattingUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests that apply to an existing atom instance
|
/// Requests that apply to an existing atom instance
|
||||||
|
|||||||
20
orchid-api/src/format.rs
Normal file
20
orchid-api/src/format.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
use orchid_api_derive::Coding;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct FormattingUnit {
|
||||||
|
pub subs: Vec<FormattingUnit>,
|
||||||
|
pub variants: Vec<FormattingVariant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct FormattingVariant {
|
||||||
|
pub bounded: bool,
|
||||||
|
pub elements: Vec<FormattingElement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub enum FormattingElement {
|
||||||
|
Sub { slot: u32, bounded: Option<bool> },
|
||||||
|
String(String),
|
||||||
|
Indent(Vec<FormattingElement>),
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
mod lexer;
|
mod lexer;
|
||||||
pub use lexer::*;
|
pub use lexer::*;
|
||||||
|
mod format;
|
||||||
|
pub use format::*;
|
||||||
mod macros;
|
mod macros;
|
||||||
pub use macros::*;
|
pub use macros::*;
|
||||||
mod atom;
|
mod atom;
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
use std::future::Future;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use never::Never;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
|
use crate::interner::Interner;
|
||||||
|
use crate::{api, match_mapping};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct FmtUnit {
|
pub struct FmtUnit {
|
||||||
pub subs: Vec<FmtUnit>,
|
pub subs: Vec<FmtUnit>,
|
||||||
@@ -15,6 +21,36 @@ impl FmtUnit {
|
|||||||
pub fn new(variants: Rc<Variants>, subs: impl IntoIterator<Item = FmtUnit>) -> Self {
|
pub fn new(variants: Rc<Variants>, subs: impl IntoIterator<Item = FmtUnit>) -> Self {
|
||||||
Self { subs: subs.into_iter().collect(), variants }
|
Self { subs: subs.into_iter().collect(), variants }
|
||||||
}
|
}
|
||||||
|
pub fn from_api(api: &api::FormattingUnit) -> Self {
|
||||||
|
Self {
|
||||||
|
subs: api.subs.iter().map(Self::from_api).collect(),
|
||||||
|
variants: Rc::new(Variants(
|
||||||
|
(api.variants.iter().map(|var| Variant {
|
||||||
|
bounded: var.bounded,
|
||||||
|
elements: var.elements.iter().map(FmtElement::from_api).collect(),
|
||||||
|
}))
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_api(&self) -> api::FormattingUnit {
|
||||||
|
api::FormattingUnit {
|
||||||
|
subs: self.subs.iter().map(Self::to_api).collect(),
|
||||||
|
variants: (self.variants.0.iter().map(|var| api::FormattingVariant {
|
||||||
|
bounded: var.bounded,
|
||||||
|
elements: var.elements.iter().map(FmtElement::to_api).collect(),
|
||||||
|
}))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn sequence(
|
||||||
|
delim: &str,
|
||||||
|
seq_bnd: Option<bool>,
|
||||||
|
seq: impl IntoIterator<Item = FmtUnit>,
|
||||||
|
) -> Self {
|
||||||
|
let items = seq.into_iter().collect_vec();
|
||||||
|
FmtUnit::new(Variants::sequence(items.len(), delim, seq_bnd), items)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl<T> From<T> for FmtUnit
|
impl<T> From<T> for FmtUnit
|
||||||
where Variants: From<T>
|
where Variants: From<T>
|
||||||
@@ -24,31 +60,71 @@ where Variants: From<T>
|
|||||||
impl FromStr for FmtUnit {
|
impl FromStr for FmtUnit {
|
||||||
type Err = Infallible;
|
type Err = Infallible;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(Self { subs: vec![], variants: Rc::new(Variants::new([s])) })
|
Ok(Self { subs: vec![], variants: Rc::new(Variants::default().bounded(s)) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub enum FmtElement {
|
pub enum FmtElement {
|
||||||
Sub(u8),
|
Sub { slot: u32, bounded: Option<bool> },
|
||||||
String(Rc<String>),
|
String(Rc<String>),
|
||||||
InlineSub(u8),
|
Indent(Vec<FmtElement>),
|
||||||
|
}
|
||||||
|
impl FmtElement {
|
||||||
|
pub fn str(s: &'_ str) -> Self { Self::String(Rc::new(s.to_string())) }
|
||||||
|
pub fn sub(slot: u32, bounded: Option<bool>) -> Self { Self::Sub { slot, bounded } }
|
||||||
|
pub fn bounded(i: u32) -> Self { Self::sub(i, Some(true)) }
|
||||||
|
pub fn unbounded(i: u32) -> Self { Self::sub(i, Some(false)) }
|
||||||
|
pub fn last(i: u32) -> Self { Self::sub(i, None) }
|
||||||
|
pub fn sequence(len: usize, bounded: Option<bool>) -> impl Iterator<Item = Self> {
|
||||||
|
let len32: u32 = len.try_into().unwrap();
|
||||||
|
(0..len32 - 1).map(FmtElement::unbounded).chain([FmtElement::sub(len32 - 1, bounded)])
|
||||||
|
}
|
||||||
|
pub fn from_api(api: &api::FormattingElement) -> Self {
|
||||||
|
match_mapping!(api, api::FormattingElement => FmtElement {
|
||||||
|
Indent(v => v.iter().map(FmtElement::from_api).collect()),
|
||||||
|
String(s => Rc::new(s.clone())),
|
||||||
|
Sub{ *slot, *bounded },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn to_api(&self) -> api::FormattingElement {
|
||||||
|
match_mapping!(self, FmtElement => api::FormattingElement {
|
||||||
|
Indent(v => v.iter().map(FmtElement::to_api).collect()),
|
||||||
|
String(s => s.to_string()),
|
||||||
|
Sub{ *slot, *bounded },
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct Variants(pub Vec<Vec<FmtElement>>);
|
pub struct Variant {
|
||||||
|
pub bounded: bool,
|
||||||
|
pub elements: Vec<FmtElement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||||
|
pub struct Variants(pub Vec<Variant>);
|
||||||
impl Variants {
|
impl Variants {
|
||||||
pub fn new<'a>(variants: impl IntoIterator<Item = &'a str>) -> Self {
|
fn parse_phs(s: &'_ str) -> Vec<FmtElement> {
|
||||||
let re = Regex::new(r"(?<tpl>\{\d+?-?\})|(\{\{)|(\}\})").unwrap();
|
let re = Regex::new(r"(?<tpl>\{\d+?[bl]?\})| (\{\{)|(\}\})").unwrap();
|
||||||
Self(Vec::from_iter(variants.into_iter().map(|s: &str| {
|
|
||||||
let matches = re.captures_iter(s);
|
let matches = re.captures_iter(s);
|
||||||
let slots = matches.into_iter().filter_map(|m| m.name("tpl")).map(|tpl| {
|
let slots = matches.into_iter().filter_map(|m| m.name("tpl")).map(|tpl| {
|
||||||
let no_opencurly = tpl.as_str().strip_prefix("{").expect("required by regex");
|
let no_opencurly = tpl.as_str().strip_prefix("{").expect("required by regex");
|
||||||
let maybe_dash = no_opencurly.strip_suffix("}").expect("required by regex");
|
let maybe_dash = no_opencurly.strip_suffix("}").expect("required by regex");
|
||||||
let (num, had_dash) =
|
// we know it's not empty
|
||||||
maybe_dash.strip_suffix('-').map_or((maybe_dash, false), |s| (s, true));
|
let last_char = maybe_dash.as_bytes()[maybe_dash.len() - 1] as char;
|
||||||
let idx = num.parse::<u8>().expect("Decimal digits required by regex");
|
let (num, bounded) = if !last_char.is_ascii_digit() {
|
||||||
(tpl.range(), idx, had_dash)
|
let bounded = match last_char {
|
||||||
|
'b' => Some(true),
|
||||||
|
'l' => None,
|
||||||
|
_ => panic!("Invalid modifier char"),
|
||||||
|
};
|
||||||
|
(&maybe_dash[0..maybe_dash.len() - 1], bounded)
|
||||||
|
} else {
|
||||||
|
(maybe_dash, Some(false))
|
||||||
|
};
|
||||||
|
let idx = num.parse::<u32>().expect("Decimal digits required by regex");
|
||||||
|
(tpl.range(), idx, bounded)
|
||||||
});
|
});
|
||||||
(iter::once(None).chain(slots.into_iter().map(Some)).chain(None).tuple_windows())
|
(iter::once(None).chain(slots.into_iter().map(Some)).chain(None).tuple_windows())
|
||||||
.flat_map(|(l, r)| {
|
.flat_map(|(l, r)| {
|
||||||
@@ -61,10 +137,8 @@ impl Variants {
|
|||||||
let str_item = FmtElement::String(Rc::new(string.to_string()));
|
let str_item = FmtElement::String(Rc::new(string.to_string()));
|
||||||
match r {
|
match r {
|
||||||
None => itertools::Either::Left([str_item]),
|
None => itertools::Either::Left([str_item]),
|
||||||
Some((_, idx, inline)) => itertools::Either::Right([str_item, match inline {
|
Some((_, idx, bounded)) =>
|
||||||
true => FmtElement::InlineSub(idx),
|
itertools::Either::Right([str_item, FmtElement::Sub { slot: idx, bounded }]),
|
||||||
false => FmtElement::Sub(idx),
|
|
||||||
}]),
|
|
||||||
}
|
}
|
||||||
.into_iter()
|
.into_iter()
|
||||||
})
|
})
|
||||||
@@ -74,16 +148,119 @@ impl Variants {
|
|||||||
tuple => Err(tuple),
|
tuple => Err(tuple),
|
||||||
})
|
})
|
||||||
.collect_vec()
|
.collect_vec()
|
||||||
})))
|
}
|
||||||
|
fn parse(s: &'_ str) -> Vec<FmtElement> {
|
||||||
|
let mut lines = s.lines();
|
||||||
|
let Some(mut cur) = lines.next() else { return vec![] };
|
||||||
|
return indent_blk(&mut cur, &mut lines, 0);
|
||||||
|
fn indent_blk<'a>(
|
||||||
|
cur: &mut &'a str,
|
||||||
|
lines: &mut impl Iterator<Item = &'a str>,
|
||||||
|
blk_lv: usize,
|
||||||
|
) -> Vec<FmtElement> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
loop {
|
||||||
|
let line_lv = cur.chars().take_while(|c| *c == '\t').count();
|
||||||
|
match line_lv.cmp(&blk_lv) {
|
||||||
|
Ordering::Greater => out.push(FmtElement::Indent(indent_blk(cur, lines, blk_lv + 1))),
|
||||||
|
Ordering::Equal => out.extend(Variants::parse_phs(&cur[blk_lv..])),
|
||||||
|
Ordering::Less => return out,
|
||||||
|
}
|
||||||
|
match lines.next() {
|
||||||
|
Some(line) => *cur = line,
|
||||||
|
None => return out,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn add(&mut self, bounded: bool, s: &'_ str) {
|
||||||
|
self.0.push(Variant { bounded, elements: Self::parse(s) })
|
||||||
|
}
|
||||||
|
// This option is available in all positions
|
||||||
|
pub fn bounded(mut self, s: &'_ str) -> Self {
|
||||||
|
self.add(true, s);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
// This option is only available in positions immediately preceding the end of
|
||||||
|
// the sequence or a parenthesized subsequence.
|
||||||
|
pub fn unbounded(mut self, s: &'_ str) -> Self {
|
||||||
|
self.add(false, s);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn sequence(len: usize, delim: &str, seq_bnd: Option<bool>) -> Rc<Self> {
|
||||||
|
let seq = Itertools::intersperse(FmtElement::sequence(len, seq_bnd), FmtElement::str(delim));
|
||||||
|
Rc::new(Variants(vec![Variant { bounded: true, elements: seq.collect_vec() }]))
|
||||||
|
}
|
||||||
|
pub fn units(self: &Rc<Self>, subs: impl IntoIterator<Item = FmtUnit>) -> FmtUnit {
|
||||||
|
FmtUnit::new(self.clone(), subs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Rc<String>> for Variants {
|
||||||
|
fn from(value: Rc<String>) -> Self {
|
||||||
|
Self(vec![Variant { elements: vec![FmtElement::String(value)], bounded: true }])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<String> for Variants {
|
impl From<String> for Variants {
|
||||||
fn from(value: String) -> Self { Self(vec![vec![FmtElement::String(Rc::new(value))]]) }
|
fn from(value: String) -> Self { Self::from(Rc::new(value)) }
|
||||||
}
|
|
||||||
impl From<Rc<String>> for Variants {
|
|
||||||
fn from(value: Rc<String>) -> Self { Self(vec![vec![FmtElement::String(value)]]) }
|
|
||||||
}
|
}
|
||||||
impl FromStr for Variants {
|
impl FromStr for Variants {
|
||||||
type Err = Infallible;
|
type Err = Infallible;
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(Self::new([s])) }
|
fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(Self::default().bounded(s)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn indent_str(s: &str, indent: u16) -> String {
|
||||||
|
s.replace("\n", &format!("\n{}", "\t".repeat(indent.into())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_slots<'a, 'b>(
|
||||||
|
elements: impl IntoIterator<Item = &'a FmtElement>,
|
||||||
|
values: &[FmtUnit],
|
||||||
|
indent: u16,
|
||||||
|
last_bounded: bool,
|
||||||
|
) -> String {
|
||||||
|
elements
|
||||||
|
.into_iter()
|
||||||
|
.map(|el| match el {
|
||||||
|
FmtElement::String(s) => indent_str(s, indent),
|
||||||
|
FmtElement::Sub { slot, bounded } =>
|
||||||
|
indent_str(&take_first(&values[*slot as usize], bounded.unwrap_or(last_bounded)), indent),
|
||||||
|
FmtElement::Indent(elements) => fill_slots(elements, values, indent + 1, last_bounded),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The simplest possible print strategy
|
||||||
|
pub fn take_first(unit: &FmtUnit, bounded: bool) -> String {
|
||||||
|
let first = unit.variants.0.iter().find(|v| v.bounded || bounded).expect("No bounded variant!");
|
||||||
|
fill_slots(&first.elements, &unit.subs, 0, bounded)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn take_first_fmt(v: &(impl Format + ?Sized), i: &Interner) -> String {
|
||||||
|
take_first(&v.print(&FmtCtxImpl { i }).await, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FmtCtxImpl<'a> {
|
||||||
|
pub i: &'a Interner,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FmtCtx {
|
||||||
|
fn i(&self) -> &Interner;
|
||||||
|
// fn print_as(&self, p: &(impl Format + ?Sized)) -> impl Future<Output =
|
||||||
|
// String> where Self: Sized {
|
||||||
|
// async {
|
||||||
|
// // for now, always take the first option which is probably the one-line
|
||||||
|
// form let variants = p.print(self).await;
|
||||||
|
// take_first(&variants, true)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
impl FmtCtx for FmtCtxImpl<'_> {
|
||||||
|
fn i(&self) -> &Interner { self.i }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Format {
|
||||||
|
fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> + 'a;
|
||||||
|
}
|
||||||
|
impl Format for Never {
|
||||||
|
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { match *self {} }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,5 +25,6 @@ pub mod pure_seq;
|
|||||||
pub mod reqnot;
|
pub mod reqnot;
|
||||||
pub mod sequence;
|
pub mod sequence;
|
||||||
pub mod side;
|
pub mod side;
|
||||||
|
mod tl_cache;
|
||||||
pub mod tokens;
|
pub mod tokens;
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ use std::rc::Rc;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::{LocalBoxFuture, join_all};
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
|
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||||
use crate::interner::Interner;
|
use crate::interner::Interner;
|
||||||
use crate::location::Pos;
|
use crate::location::Pos;
|
||||||
use crate::name::Sym;
|
use crate::name::Sym;
|
||||||
use crate::tree::{Paren, Ph};
|
use crate::tree::{Paren, Ph};
|
||||||
use crate::{api, match_mapping};
|
use crate::{api, match_mapping, tl_cache};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct MacroSlot<'a>(api::MacroTreeId, PhantomData<&'a ()>);
|
pub struct MacroSlot<'a>(api::MacroTreeId, PhantomData<&'a ()>);
|
||||||
@@ -49,6 +50,11 @@ impl<'a, A> MTree<'a, A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<A: Format> Format for MTree<'_, A> {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
self.tok.print(c).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum MTok<'a, A> {
|
pub enum MTok<'a, A> {
|
||||||
@@ -96,6 +102,38 @@ impl<'a, A> MTok<'a, A> {
|
|||||||
}
|
}
|
||||||
pub fn at(self, pos: Pos) -> MTree<'a, A> { MTree { pos, tok: Rc::new(self) } }
|
pub fn at(self, pos: Pos) -> MTree<'a, A> { MTree { pos, tok: Rc::new(self) } }
|
||||||
}
|
}
|
||||||
|
impl<A: Format> Format for MTok<'_, A> {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
match self {
|
||||||
|
Self::Atom(a) => a.print(c).await,
|
||||||
|
Self::Done(d) =>
|
||||||
|
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("(Done){0l}"))), [
|
||||||
|
d.print(c).await,
|
||||||
|
]),
|
||||||
|
Self::Lambda(arg, b) => FmtUnit::new(
|
||||||
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
|
.unbounded("\\{0b}.{1l}")
|
||||||
|
.bounded("(\\{0b}.{1b})"))),
|
||||||
|
[mtreev_fmt(arg, c).await, mtreev_fmt(b, c).await],
|
||||||
|
),
|
||||||
|
Self::Name(n) => format!("{n}").into(),
|
||||||
|
Self::Ph(ph) => format!("{ph}").into(),
|
||||||
|
Self::Ref(r) =>
|
||||||
|
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("(ref){0l}"))), [
|
||||||
|
r.print(c).await,
|
||||||
|
]),
|
||||||
|
Self::S(p, body) => FmtUnit::new(
|
||||||
|
match *p {
|
||||||
|
Paren::Round => Rc::new(Variants::default().bounded("({0b})")),
|
||||||
|
Paren::Curly => Rc::new(Variants::default().bounded("{{0b}}")),
|
||||||
|
Paren::Square => Rc::new(Variants::default().bounded("[{0b}]")),
|
||||||
|
},
|
||||||
|
[mtreev_fmt(body, c).await],
|
||||||
|
),
|
||||||
|
Self::Slot(slot) => format!("{:?}", slot.0).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn mtreev_from_api<'a, 'b, A>(
|
pub async fn mtreev_from_api<'a, 'b, A>(
|
||||||
apiv: impl IntoIterator<Item = &'b api::MacroTree>,
|
apiv: impl IntoIterator<Item = &'b api::MacroTree>,
|
||||||
@@ -121,3 +159,10 @@ pub async fn mtreev_to_api<'a: 'b, 'b, A: 'b>(
|
|||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn mtreev_fmt<'a: 'b, 'b, A: 'b + Format>(
|
||||||
|
v: impl IntoIterator<Item = &'b MTree<'a, A>>,
|
||||||
|
c: &(impl FmtCtx + ?Sized),
|
||||||
|
) -> FmtUnit {
|
||||||
|
FmtUnit::sequence(" ", None, join_all(v.into_iter().map(|t| t.print(c))).await)
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ macro_rules! match_mapping {
|
|||||||
$($branches:tt)*
|
$($branches:tt)*
|
||||||
} $({
|
} $({
|
||||||
$($extra:tt)*
|
$($extra:tt)*
|
||||||
})?) => {
|
})?) => {{
|
||||||
|
type Helper<T> = T;
|
||||||
match_mapping!(@BRANCH_MUNCH
|
match_mapping!(@BRANCH_MUNCH
|
||||||
(($input) ($($src)*) ($tgt) ($($($extra)*)?))
|
(($input) ($($src)*) ($tgt) ($($($extra)*)?))
|
||||||
()
|
()
|
||||||
@@ -25,7 +26,7 @@ macro_rules! match_mapping {
|
|||||||
)
|
)
|
||||||
// note: we're adding a comma to the input so the optional trailing comma becomes
|
// note: we're adding a comma to the input so the optional trailing comma becomes
|
||||||
// an optional second comma which is easier to match
|
// an optional second comma which is easier to match
|
||||||
};
|
}};
|
||||||
// ======== Process match branches
|
// ======== Process match branches
|
||||||
// Can't generate branches individually so gather them into a collection and render them here
|
// Can't generate branches individually so gather them into a collection and render them here
|
||||||
(@BRANCHES_DONE ( ($input:expr) $src:tt ($tgt:ty) ($($extra:tt)*) )
|
(@BRANCHES_DONE ( ($input:expr) $src:tt ($tgt:ty) ($($extra:tt)*) )
|
||||||
@@ -35,7 +36,7 @@ macro_rules! match_mapping {
|
|||||||
match $input {
|
match $input {
|
||||||
$(
|
$(
|
||||||
match_mapping!(@PAT ($src $variant) $($pat)*) =>
|
match_mapping!(@PAT ($src $variant) $($pat)*) =>
|
||||||
match_mapping!(@VAL (< $tgt >:: $variant) $($pat)*),
|
match_mapping!(@VAL (Helper::< $tgt >:: $variant) $($pat)*),
|
||||||
)*
|
)*
|
||||||
$($extra)*
|
$($extra)*
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::fmt::{self, Display};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::{Deref, Range};
|
use std::ops::{Deref, Range};
|
||||||
|
|
||||||
@@ -6,7 +7,8 @@ use itertools::Itertools;
|
|||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::error::{OrcRes, Reporter, mk_err, mk_errv};
|
use crate::error::{OrcRes, Reporter, mk_err, mk_errv};
|
||||||
use crate::interner::{Internable, Interned, Interner, Tok};
|
use crate::format::{Format, take_first_fmt};
|
||||||
|
use crate::interner::{Interner, Tok};
|
||||||
use crate::location::Pos;
|
use crate::location::Pos;
|
||||||
use crate::name::VPath;
|
use crate::name::VPath;
|
||||||
use crate::tree::{AtomRepr, ExtraTok, Paren, TokTree, Token};
|
use crate::tree::{AtomRepr, ExtraTok, Paren, TokTree, Token};
|
||||||
@@ -16,6 +18,8 @@ pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() }
|
|||||||
pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{}\\".contains(c) }
|
pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{}\\".contains(c) }
|
||||||
pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) }
|
pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) }
|
||||||
|
|
||||||
|
/// A cheaply copiable subsection of a document that holds onto context data and
|
||||||
|
/// one token for error reporting on empty subsections.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Snippet<'a, 'b, A: AtomRepr, X: ExtraTok> {
|
pub struct Snippet<'a, 'b, A: AtomRepr, X: ExtraTok> {
|
||||||
prev: &'a TokTree<'b, A, X>,
|
prev: &'a TokTree<'b, A, X>,
|
||||||
@@ -30,10 +34,7 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self { prev, cur, interner }
|
Self { prev, cur, interner }
|
||||||
}
|
}
|
||||||
pub async fn i<T: Interned>(&self, arg: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
|
pub fn i(&self) -> &'a Interner { self.interner }
|
||||||
self.interner.i(arg).await
|
|
||||||
}
|
|
||||||
pub fn interner(&self) -> &'a Interner { self.interner }
|
|
||||||
pub fn split_at(self, pos: u32) -> (Self, Self) {
|
pub fn split_at(self, pos: u32) -> (Self, Self) {
|
||||||
let Self { prev, cur, interner } = self;
|
let Self { prev, cur, interner } = self;
|
||||||
let fst = Self { prev, cur: &cur[..pos as usize], interner };
|
let fst = Self { prev, cur: &cur[..pos as usize], interner };
|
||||||
@@ -66,7 +67,9 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
|
|||||||
mut f: impl FnMut(&Token<'b, A, X>) -> bool,
|
mut f: impl FnMut(&Token<'b, A, X>) -> bool,
|
||||||
) -> impl Iterator<Item = Self> {
|
) -> impl Iterator<Item = Self> {
|
||||||
iter::from_fn(move || {
|
iter::from_fn(move || {
|
||||||
self.is_empty().then_some(())?;
|
if self.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let (ret, next) = self.split_once(&mut f).unwrap_or(self.split_at(self.len()));
|
let (ret, next) = self.split_once(&mut f).unwrap_or(self.split_at(self.len()));
|
||||||
self = next;
|
self = next;
|
||||||
Some(ret)
|
Some(ret)
|
||||||
@@ -77,6 +80,8 @@ impl<'a, 'b, A: AtomRepr, X: ExtraTok> Snippet<'a, 'b, A, X> {
|
|||||||
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::NS | Token::Comment(_)));
|
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::NS | Token::Comment(_)));
|
||||||
self.split_at(non_fluff_start.unwrap_or(self.len())).1
|
self.split_at(non_fluff_start.unwrap_or(self.len())).1
|
||||||
}
|
}
|
||||||
|
/// Format the argument using the context held in this snippet
|
||||||
|
pub async fn fmt(self, v: &(impl Format + ?Sized)) -> String { take_first_fmt(v, self.i()).await }
|
||||||
}
|
}
|
||||||
impl<A: AtomRepr, X: ExtraTok> Copy for Snippet<'_, '_, A, X> {}
|
impl<A: AtomRepr, X: ExtraTok> Copy for Snippet<'_, '_, A, X> {}
|
||||||
impl<A: AtomRepr, X: ExtraTok> Clone for Snippet<'_, '_, A, X> {
|
impl<A: AtomRepr, X: ExtraTok> Clone for Snippet<'_, '_, A, X> {
|
||||||
@@ -116,6 +121,10 @@ impl Comment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Comment {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "--[{}]--", self.text) }
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
||||||
snip: Snippet<'a, 'b, A, X>,
|
snip: Snippet<'a, 'b, A, X>,
|
||||||
) -> Vec<Parsed<'a, 'b, Vec<Comment>, A, X>> {
|
) -> Vec<Parsed<'a, 'b, Vec<Comment>, A, X>> {
|
||||||
@@ -134,7 +143,7 @@ pub async fn line_items<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
let comments = join_all(comments.drain(..).chain(cmts.cur).map(|t| async {
|
let comments = join_all(comments.drain(..).chain(cmts.cur).map(|t| async {
|
||||||
match &t.tok {
|
match &t.tok {
|
||||||
Token::Comment(c) =>
|
Token::Comment(c) =>
|
||||||
Comment { text: tail.i(&**c).await, pos: Pos::Range(t.range.clone()) },
|
Comment { text: tail.i().i(&**c).await, pos: Pos::Range(t.range.clone()) },
|
||||||
_ => unreachable!("All are comments checked above"),
|
_ => unreachable!("All are comments checked above"),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@@ -151,7 +160,8 @@ pub async fn try_pop_no_fluff<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
) -> ParseRes<'a, 'b, &'a TokTree<'b, A, X>, A, X> {
|
) -> ParseRes<'a, 'b, &'a TokTree<'b, A, X>, A, X> {
|
||||||
match snip.skip_fluff().pop_front() {
|
match snip.skip_fluff().pop_front() {
|
||||||
Some((output, tail)) => Ok(Parsed { output, tail }),
|
Some((output, tail)) => Ok(Parsed { output, tail }),
|
||||||
None => Err(mk_errv(snip.i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range(
|
None =>
|
||||||
|
Err(mk_errv(snip.i().i("Unexpected end").await, "Pattern ends abruptly", [Pos::Range(
|
||||||
snip.pos(),
|
snip.pos(),
|
||||||
)
|
)
|
||||||
.into()])),
|
.into()])),
|
||||||
@@ -161,7 +171,7 @@ pub async fn try_pop_no_fluff<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
pub async fn expect_end(snip: Snippet<'_, '_, impl AtomRepr, impl ExtraTok>) -> OrcRes<()> {
|
pub async fn expect_end(snip: Snippet<'_, '_, impl AtomRepr, impl ExtraTok>) -> OrcRes<()> {
|
||||||
match snip.skip_fluff().get(0) {
|
match snip.skip_fluff().get(0) {
|
||||||
Some(surplus) => Err(mk_errv(
|
Some(surplus) => Err(mk_errv(
|
||||||
snip.i("Extra code after end of line").await,
|
snip.i().i("Extra code after end of line").await,
|
||||||
"Code found after the end of the line",
|
"Code found after the end of the line",
|
||||||
[Pos::Range(surplus.range.clone()).into()],
|
[Pos::Range(surplus.range.clone()).into()],
|
||||||
)),
|
)),
|
||||||
@@ -177,8 +187,8 @@ pub async fn expect_tok<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
match &head.tok {
|
match &head.tok {
|
||||||
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
|
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
|
||||||
t => Err(mk_errv(
|
t => Err(mk_errv(
|
||||||
snip.i("Expected specific keyword").await,
|
snip.i().i("Expected specific keyword").await,
|
||||||
format!("Expected {tok} but found {:?}", t.print().await),
|
format!("Expected {tok} but found {:?}", snip.fmt(t).await),
|
||||||
[Pos::Range(head.range.clone()).into()],
|
[Pos::Range(head.range.clone()).into()],
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
@@ -201,11 +211,11 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
ctx: &(impl Reporter + ?Sized),
|
ctx: &(impl Reporter + ?Sized),
|
||||||
tail: Snippet<'a, 'b, A, X>,
|
tail: Snippet<'a, 'b, A, X>,
|
||||||
) -> ParseRes<'a, 'b, Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, A, X> {
|
) -> ParseRes<'a, 'b, Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, A, X> {
|
||||||
let comma = tail.i(",").await;
|
let comma = tail.i().i(",").await;
|
||||||
let globstar = tail.i("*").await;
|
let globstar = tail.i().i("*").await;
|
||||||
let Some((name, tail)) = tail.skip_fluff().pop_front() else {
|
let Some((name, tail)) = tail.skip_fluff().pop_front() else {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Expected name").await,
|
tail.i().i("Expected name").await,
|
||||||
"Expected a name, a list of names, or a globstar.",
|
"Expected a name, a list of names, or a globstar.",
|
||||||
[Pos::Range(tail.pos()).into()],
|
[Pos::Range(tail.pos()).into()],
|
||||||
));
|
));
|
||||||
@@ -213,7 +223,8 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
if let Some((Token::NS, tail)) = tail.skip_fluff().pop_front().map(|(tt, s)| (&tt.tok, s)) {
|
if let Some((Token::NS, tail)) = tail.skip_fluff().pop_front().map(|(tt, s)| (&tt.tok, s)) {
|
||||||
let n = match &name.tok {
|
let n = match &name.tok {
|
||||||
Token::Name(n) if n.starts_with(name_start) => Ok(n),
|
Token::Name(n) if n.starts_with(name_start) => Ok(n),
|
||||||
_ => Err(mk_err(tail.i("Unexpected name prefix").await, "Only names can precede ::", [
|
_ =>
|
||||||
|
Err(mk_err(tail.i().i("Unexpected name prefix").await, "Only names can precede ::", [
|
||||||
Pos::Range(name.range.clone()).into(),
|
Pos::Range(name.range.clone()).into(),
|
||||||
])),
|
])),
|
||||||
};
|
};
|
||||||
@@ -235,7 +246,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
n if *n == globstar => None,
|
n if *n == globstar => None,
|
||||||
n if n.starts_with(op_char) => {
|
n if n.starts_with(op_char) => {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Unescaped operator in multiname").await,
|
tail.i().i("Unescaped operator in multiname").await,
|
||||||
"Operators in multinames should be enclosed in []",
|
"Operators in multinames should be enclosed in []",
|
||||||
[Pos::Range(name.range.clone()).into()],
|
[Pos::Range(name.range.clone()).into()],
|
||||||
));
|
));
|
||||||
@@ -252,7 +263,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
ok.push((vec![], Some(n.clone()), Pos::Range(tt.range.clone()))),
|
ok.push((vec![], Some(n.clone()), Pos::Range(tt.range.clone()))),
|
||||||
Token::BR | Token::Comment(_) => (),
|
Token::BR | Token::Comment(_) => (),
|
||||||
_ => ctx.report(mk_err(
|
_ => ctx.report(mk_err(
|
||||||
tail.i("Non-operator in escapement in multiname").await,
|
tail.i().i("Non-operator in escapement in multiname").await,
|
||||||
"In multinames, [] functions as a literal name list reserved for operators",
|
"In multinames, [] functions as a literal name list reserved for operators",
|
||||||
[Pos::Range(name.range.clone()).into()],
|
[Pos::Range(name.range.clone()).into()],
|
||||||
)),
|
)),
|
||||||
@@ -269,7 +280,7 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
Ok(Parsed { output, tail }) => match tail.get(0) {
|
Ok(Parsed { output, tail }) => match tail.get(0) {
|
||||||
None => ok.extend(output),
|
None => ok.extend(output),
|
||||||
Some(t) => ctx.report(mk_err(
|
Some(t) => ctx.report(mk_err(
|
||||||
tail.i("Unexpected token in multiname group").await,
|
tail.i().i("Unexpected token in multiname group").await,
|
||||||
"Unexpected token. Likely missing a :: or , or wanted [] instead of ()",
|
"Unexpected token. Likely missing a :: or , or wanted [] instead of ()",
|
||||||
[Pos::Range(t.range.clone()).into()],
|
[Pos::Range(t.range.clone()).into()],
|
||||||
)),
|
)),
|
||||||
@@ -280,8 +291,8 @@ pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
|
|||||||
},
|
},
|
||||||
t => {
|
t => {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Unrecognized name end").await,
|
tail.i().i("Unrecognized name end").await,
|
||||||
format!("Names cannot end with {:?} tokens", t.print().await),
|
format!("Names cannot end with {:?} tokens", tail.fmt(t).await),
|
||||||
[Pos::Range(name.range.clone()).into()],
|
[Pos::Range(name.range.clone()).into()],
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
@@ -303,16 +314,11 @@ pub struct Import {
|
|||||||
pub path: VPath,
|
pub path: VPath,
|
||||||
pub name: Option<Tok<String>>,
|
pub name: Option<Tok<String>>,
|
||||||
}
|
}
|
||||||
impl Import {
|
|
||||||
// pub fn from_api(i: api::CompName) -> Self {
|
impl Display for Import {
|
||||||
// Self { path: VPath::new(i.path.into_iter().map(deintern)), name:
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
// i.name.map(deintern) } }
|
write!(f, "{}::{}", self.path.iter().join("::"), self.name.as_ref().map_or("*", |t| t.as_str()))
|
||||||
// pub fn to_api(&self) -> api::CompName {
|
}
|
||||||
// api::CompName {
|
|
||||||
// path: self.path.iter().map(|t| t.marker()).collect(),
|
|
||||||
// name: self.name.as_ref().map(|t| t.marker()),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
9
orchid-base/src/tl_cache.rs
Normal file
9
orchid-base/src/tl_cache.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! tl_cache {
|
||||||
|
($ty:ty : $expr:expr) => {{
|
||||||
|
thread_local! {
|
||||||
|
static V: $ty = $expr;
|
||||||
|
}
|
||||||
|
V.with(|v| v.clone())
|
||||||
|
}};
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::fmt::{self, Debug, Display};
|
use std::fmt::{self, Debug, Display};
|
||||||
use std::future::{Future, ready};
|
use std::future::Future;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use api::PhKind;
|
pub use api::PhKind;
|
||||||
@@ -16,16 +17,16 @@ use ordered_float::NotNan;
|
|||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::error::OrcErrv;
|
use crate::error::OrcErrv;
|
||||||
|
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||||
use crate::interner::{Interner, Tok};
|
use crate::interner::{Interner, Tok};
|
||||||
use crate::location::Pos;
|
use crate::location::Pos;
|
||||||
use crate::name::PathSlice;
|
use crate::name::PathSlice;
|
||||||
use crate::parse::Snippet;
|
use crate::parse::Snippet;
|
||||||
use crate::tokens::PARENS;
|
use crate::{api, match_mapping, tl_cache};
|
||||||
use crate::{api, match_mapping};
|
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
pub trait RecurCB<'a, A: AtomRepr, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>;
|
pub trait RecurCB<'a, A: AtomRepr, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>;
|
||||||
pub trait ExtraTok = Display + Clone + fmt::Debug;
|
pub trait ExtraTok = Format + Clone + fmt::Debug;
|
||||||
pub trait RefDoExtra<X> =
|
pub trait RefDoExtra<X> =
|
||||||
for<'b> FnMut(&'b X, Range<u32>) -> LocalBoxFuture<'b, api::TokenTree>;
|
for<'b> FnMut(&'b X, Range<u32>) -> LocalBoxFuture<'b, api::TokenTree>;
|
||||||
}
|
}
|
||||||
@@ -46,22 +47,15 @@ pub fn recur<'a, A: AtomRepr, X: ExtraTok>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AtomRepr: Clone {
|
pub trait AtomRepr: Clone + Format {
|
||||||
type Ctx: ?Sized;
|
type Ctx: ?Sized;
|
||||||
fn from_api(api: &api::Atom, pos: Pos, ctx: &mut Self::Ctx) -> impl Future<Output = Self>;
|
fn from_api(api: &api::Atom, pos: Pos, ctx: &mut Self::Ctx) -> impl Future<Output = Self>;
|
||||||
fn to_api(&self) -> impl Future<Output = orchid_api::Atom> + '_;
|
fn to_api(&self) -> impl Future<Output = orchid_api::Atom> + '_;
|
||||||
fn print(&self) -> impl Future<Output = String> + '_;
|
|
||||||
}
|
}
|
||||||
impl AtomRepr for Never {
|
impl AtomRepr for Never {
|
||||||
type Ctx = Never;
|
type Ctx = Never;
|
||||||
#[allow(unreachable_code)]
|
async fn from_api(_: &api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self { match *ctx {} }
|
||||||
fn from_api(_: &api::Atom, _: Pos, ctx: &mut Self::Ctx) -> impl Future<Output = Self> {
|
async fn to_api(&self) -> orchid_api::Atom { match *self {} }
|
||||||
ready(match *ctx {})
|
|
||||||
}
|
|
||||||
#[allow(unreachable_code)]
|
|
||||||
fn to_api(&self) -> impl Future<Output = orchid_api::Atom> + '_ { ready(match *self {}) }
|
|
||||||
#[allow(unreachable_code)]
|
|
||||||
fn print(&self) -> impl Future<Output = String> + '_ { ready(match *self {}) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||||
@@ -152,7 +146,11 @@ impl<'b, A: AtomRepr, X: ExtraTok> TokTree<'b, A, X> {
|
|||||||
body.insert(0, Token::LambdaHead(arg).at(arg_range));
|
body.insert(0, Token::LambdaHead(arg).at(arg_range));
|
||||||
Token::S(Paren::Round, body).at(s_range)
|
Token::S(Paren::Round, body).at(s_range)
|
||||||
}
|
}
|
||||||
pub async fn print(&self) -> String { self.tok.print().await }
|
}
|
||||||
|
impl<A: AtomRepr, X: ExtraTok> Format for TokTree<'_, A, X> {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
self.tok.print(c).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ttv_from_api<A: AtomRepr, X: ExtraTok>(
|
pub async fn ttv_from_api<A: AtomRepr, X: ExtraTok>(
|
||||||
@@ -245,32 +243,34 @@ impl<'a, A: AtomRepr, X: ExtraTok> Token<'a, A, X> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn print(&self) -> String {
|
}
|
||||||
|
impl<A: AtomRepr, X: ExtraTok> Format for Token<'_, A, X> {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
match self {
|
match self {
|
||||||
Self::Atom(a) => a.print().await,
|
Self::Atom(a) => a.print(c).await,
|
||||||
Self::BR => "\n".to_string(),
|
Self::BR => "\n".to_string().into(),
|
||||||
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()),
|
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
|
||||||
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())),
|
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
||||||
Self::Comment(c) => format!("--[{c}]-- "),
|
Self::Comment(c) => format!("--[{c}]--").into(),
|
||||||
Self::LambdaHead(arg) => format!("\\ {} . ", indent(&ttv_fmt(arg).await)),
|
Self::LambdaHead(arg) =>
|
||||||
Self::NS => ":: ".to_string(),
|
FmtUnit::new(tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}."))), [
|
||||||
Self::Name(n) => format!("{n} "),
|
ttv_fmt(arg, c).await,
|
||||||
Self::Slot(th) => format!("{th} "),
|
]),
|
||||||
Self::Ph(Ph { kind, name }) => match &kind {
|
Self::NS => "::".to_string().into(),
|
||||||
PhKind::Scalar => format!("${name}"),
|
Self::Name(n) => format!("{n}").into(),
|
||||||
PhKind::Vector { at_least_one, priority } => {
|
Self::Slot(th) => format!("{th}").into(),
|
||||||
let prefix = if *at_least_one { "..." } else { ".." };
|
Self::Ph(ph) => format!("{ph}").into(),
|
||||||
let suffix = if 0 < *priority { format!(":{priority}") } else { String::new() };
|
Self::S(p, b) => FmtUnit::new(
|
||||||
format!("{prefix}${name}{suffix}")
|
match *p {
|
||||||
|
Paren::Round => Rc::new(Variants::default().bounded("({0b})")),
|
||||||
|
Paren::Curly => Rc::new(Variants::default().bounded("{{{0b}}}")),
|
||||||
|
Paren::Square => Rc::new(Variants::default().bounded("[{0b}]")),
|
||||||
},
|
},
|
||||||
},
|
[ttv_fmt(b, c).await],
|
||||||
Self::S(p, b) => {
|
),
|
||||||
let (lp, rp, _) = PARENS.iter().find(|(_, _, par)| par == p).unwrap();
|
Self::X(x) => x.print(c).await,
|
||||||
format!("{lp} {}{rp} ", indent(&ttv_fmt(b).await))
|
Self::Macro(None) => "macro".to_string().into(),
|
||||||
},
|
Self::Macro(Some(prio)) => format!("macro({prio})").into(),
|
||||||
Self::X(x) => format!("{x} "),
|
|
||||||
Self::Macro(None) => "macro ".to_string(),
|
|
||||||
Self::Macro(Some(prio)) => format!("macro({prio})"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,8 +282,9 @@ pub fn ttv_range(ttv: &[TokTree<'_, impl AtomRepr, impl ExtraTok>]) -> Range<u32
|
|||||||
|
|
||||||
pub async fn ttv_fmt<'a: 'b, 'b>(
|
pub async fn ttv_fmt<'a: 'b, 'b>(
|
||||||
ttv: impl IntoIterator<Item = &'b TokTree<'a, impl AtomRepr + 'b, impl ExtraTok + 'b>>,
|
ttv: impl IntoIterator<Item = &'b TokTree<'a, impl AtomRepr + 'b, impl ExtraTok + 'b>>,
|
||||||
) -> String {
|
c: &(impl FmtCtx + ?Sized),
|
||||||
join_all(ttv.into_iter().map(|tt| tt.print())).await.join("")
|
) -> FmtUnit {
|
||||||
|
FmtUnit::sequence(" ", None, join_all(ttv.into_iter().map(|t| t.print(c))).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }
|
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }
|
||||||
@@ -301,6 +302,18 @@ impl Ph {
|
|||||||
api::Placeholder { name: self.name.to_api(), kind: self.kind }
|
api::Placeholder { name: self.name.to_api(), kind: self.kind }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Display for Ph {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let n = &self.name;
|
||||||
|
match self.kind {
|
||||||
|
PhKind::Scalar => write!(f, "${n}"),
|
||||||
|
PhKind::Vector { priority: 0, at_least_one: true } => write!(f, "...${}", self.name),
|
||||||
|
PhKind::Vector { priority: p, at_least_one: true } => write!(f, "...${}:{}", self.name, p),
|
||||||
|
PhKind::Vector { priority: 0, at_least_one: false } => write!(f, "..${}", self.name),
|
||||||
|
PhKind::Vector { priority: p, at_least_one: false } => write!(f, "..${}:{}", self.name, p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use futures::{FutureExt, StreamExt};
|
|||||||
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::{OrcErr, OrcRes, mk_err};
|
||||||
|
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;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
@@ -117,15 +118,17 @@ impl fmt::Display for ForeignAtom<'_> {
|
|||||||
impl fmt::Debug for ForeignAtom<'_> {
|
impl fmt::Debug for ForeignAtom<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ForeignAtom({self})") }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ForeignAtom({self})") }
|
||||||
}
|
}
|
||||||
|
impl Format for ForeignAtom<'_> {
|
||||||
|
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
FmtUnit::from_api(&self.ctx.reqnot.request(api::ExtAtomPrint(self.atom.clone())).await)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl AtomRepr for ForeignAtom<'_> {
|
impl AtomRepr for ForeignAtom<'_> {
|
||||||
type Ctx = SysCtx;
|
type Ctx = SysCtx;
|
||||||
async fn from_api(atom: &api::Atom, pos: Pos, ctx: &mut Self::Ctx) -> Self {
|
async fn from_api(atom: &api::Atom, pos: Pos, ctx: &mut Self::Ctx) -> Self {
|
||||||
Self { atom: atom.clone(), _life: PhantomData, ctx: ctx.clone(), expr: None, pos }
|
Self { atom: atom.clone(), _life: PhantomData, ctx: ctx.clone(), expr: None, pos }
|
||||||
}
|
}
|
||||||
async fn to_api(&self) -> orchid_api::Atom { self.atom.clone() }
|
async fn to_api(&self) -> orchid_api::Atom { self.atom.clone() }
|
||||||
async fn print(&self) -> String {
|
|
||||||
self.ctx.reqnot.request(api::ExtAtomPrint(self.atom.clone())).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NotTypAtom {
|
pub struct NotTypAtom {
|
||||||
@@ -266,6 +269,9 @@ impl<A: AtomicFeatures> Deref for TypAtom<'_, A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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<'_> {
|
||||||
|
fn i(&self) -> &Interner { &self.2.i }
|
||||||
|
}
|
||||||
|
|
||||||
pub trait AtomDynfo: 'static {
|
pub trait AtomDynfo: 'static {
|
||||||
fn tid(&self) -> TypeId;
|
fn tid(&self) -> TypeId;
|
||||||
@@ -273,7 +279,7 @@ pub trait AtomDynfo: 'static {
|
|||||||
fn decode<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>>;
|
fn decode<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>>;
|
||||||
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>;
|
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>;
|
||||||
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>;
|
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>;
|
||||||
fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, String>;
|
fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit>;
|
||||||
fn handle_req<'a, 'b: 'a, 'c: 'a>(
|
fn handle_req<'a, 'b: 'a, 'c: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ctx: AtomCtx<'a>,
|
ctx: AtomCtx<'a>,
|
||||||
@@ -317,6 +323,11 @@ impl fmt::Debug for AtomFactory {
|
|||||||
impl fmt::Display for AtomFactory {
|
impl fmt::Display for AtomFactory {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
|
||||||
}
|
}
|
||||||
|
impl Format for AtomFactory {
|
||||||
|
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
"AtomFactory".to_string().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn err_not_callable(i: &Interner) -> OrcErr {
|
pub async fn err_not_callable(i: &Interner) -> OrcErr {
|
||||||
mk_err(i.i("This atom is not callable").await, "Attempted to apply value as function", [])
|
mk_err(i.i("This atom is not callable").await, "Attempted to apply value as function", [])
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use never::Never;
|
|||||||
use orchid_api_traits::{Decode, Encode, enc_vec};
|
use orchid_api_traits::{Decode, Encode, enc_vec};
|
||||||
use orchid_base::clone;
|
use orchid_base::clone;
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::error::OrcRes;
|
||||||
|
use orchid_base::format::FmtUnit;
|
||||||
use orchid_base::id_store::{IdRecord, IdStore};
|
use orchid_base::id_store::{IdRecord, IdStore};
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
|||||||
}
|
}
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
}
|
}
|
||||||
fn print(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> LocalBoxFuture<'_, String> {
|
fn print(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
|
||||||
async move {
|
async move {
|
||||||
with_atom(id.unwrap(), &ctx, |a| clone!(ctx; async move { a.dyn_print(ctx).await })).await
|
with_atom(id.unwrap(), &ctx, |a| clone!(ctx; async move { a.dyn_print(ctx).await })).await
|
||||||
}
|
}
|
||||||
@@ -227,8 +228,8 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
|||||||
#[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(&self, ctx: SysCtx) -> impl Future<Output = String> {
|
fn print(&self, ctx: SysCtx) -> impl Future<Output = FmtUnit> {
|
||||||
async { format!("OwnedAtom({})", type_name::<Self>()) }
|
async { format!("OwnedAtom({})", type_name::<Self>()).into() }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn serialize(
|
fn serialize(
|
||||||
@@ -262,7 +263,7 @@ pub trait DynOwnedAtom: 'static {
|
|||||||
-> LocalBoxFuture<'static, GExpr>;
|
-> LocalBoxFuture<'static, GExpr>;
|
||||||
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes<Option<GExpr>>>;
|
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes<Option<GExpr>>>;
|
||||||
fn dyn_free(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, ()>;
|
fn dyn_free(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, ()>;
|
||||||
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, String>;
|
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, FmtUnit>;
|
||||||
fn dyn_serialize<'a>(
|
fn dyn_serialize<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ctx: SysCtx,
|
ctx: SysCtx,
|
||||||
@@ -291,7 +292,7 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
|
|||||||
fn dyn_free(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, ()> {
|
fn dyn_free(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, ()> {
|
||||||
self.free(ctx).boxed_local()
|
self.free(ctx).boxed_local()
|
||||||
}
|
}
|
||||||
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, String> { self.print(ctx).boxed_local() }
|
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, FmtUnit> { self.print(ctx).boxed_local() }
|
||||||
fn dyn_serialize<'a>(
|
fn dyn_serialize<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ctx: SysCtx,
|
ctx: SysCtx,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use futures::FutureExt;
|
|||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use orchid_api_traits::{Coding, enc_vec};
|
use orchid_api_traits::{Coding, enc_vec};
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::error::OrcRes;
|
||||||
|
use orchid_base::format::FmtUnit;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
@@ -39,7 +40,7 @@ pub struct ThinAtomDynfo<T: ThinAtom> {
|
|||||||
ms: OnceCell<MethodSet<T>>,
|
ms: OnceCell<MethodSet<T>>,
|
||||||
}
|
}
|
||||||
impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
||||||
fn print<'a>(&self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, String> {
|
fn print<'a>(&self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit> {
|
||||||
async move { T::decode(Pin::new(&mut &buf[..])).await.print(ctx).await }.boxed_local()
|
async move { T::decode(Pin::new(&mut &buf[..])).await.print(ctx).await }.boxed_local()
|
||||||
}
|
}
|
||||||
fn tid(&self) -> TypeId { TypeId::of::<T>() }
|
fn tid(&self) -> TypeId { TypeId::of::<T>() }
|
||||||
@@ -123,7 +124,7 @@ pub trait ThinAtom:
|
|||||||
async move { Err(err_not_command(&ctx.i).await.into()) }
|
async move { Err(err_not_command(&ctx.i).await.into()) }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn print(&self, ctx: SysCtx) -> impl Future<Output = String> {
|
fn print(&self, ctx: SysCtx) -> impl Future<Output = FmtUnit> {
|
||||||
async { format!("ThinAtom({})", type_name::<Self>()) }
|
async { format!("ThinAtom({})", type_name::<Self>()).into() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
|
|||||||
hand.handle(ser, &refs_opt.map(|refs| (buf, refs))).await
|
hand.handle(ser, &refs_opt.map(|refs| (buf, refs))).await
|
||||||
},
|
},
|
||||||
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
||||||
hand.handle(print, &nfo.print(actx).await).await,
|
hand.handle(print, &nfo.print(actx).await.to_api()).await,
|
||||||
api::AtomReq::Fwded(fwded) => {
|
api::AtomReq::Fwded(fwded) => {
|
||||||
let api::Fwded(_, key, payload) = &fwded;
|
let api::Fwded(_, key, payload) = &fwded;
|
||||||
let mut reply = Vec::new();
|
let mut reply = Vec::new();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::fmt;
|
|||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
use derive_destructure::destructure;
|
use derive_destructure::destructure;
|
||||||
|
use orchid_base::format::{FmtCtx, FmtUnit, Format, take_first_fmt};
|
||||||
use orchid_base::location::Pos;
|
use orchid_base::location::Pos;
|
||||||
use orchid_base::reqnot::Requester;
|
use orchid_base::reqnot::Requester;
|
||||||
use orchid_base::tree::AtomRepr;
|
use orchid_base::tree::AtomRepr;
|
||||||
@@ -78,18 +79,20 @@ impl AtomHand {
|
|||||||
self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req)).await
|
self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req)).await
|
||||||
}
|
}
|
||||||
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
|
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
|
||||||
pub async fn to_string(&self) -> String {
|
pub async fn to_string(&self) -> String { take_first_fmt(self, &self.0.owner.ctx().i).await }
|
||||||
self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())).await
|
|
||||||
}
|
|
||||||
pub fn downgrade(&self) -> WeakAtomHand { WeakAtomHand(Rc::downgrade(&self.0)) }
|
pub fn downgrade(&self) -> WeakAtomHand { WeakAtomHand(Rc::downgrade(&self.0)) }
|
||||||
}
|
}
|
||||||
|
impl Format for AtomHand {
|
||||||
|
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
FmtUnit::from_api(&self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())).await)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl AtomRepr for AtomHand {
|
impl AtomRepr for AtomHand {
|
||||||
type Ctx = Ctx;
|
type Ctx = Ctx;
|
||||||
async fn from_api(atom: &orchid_api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self {
|
async fn from_api(atom: &orchid_api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self {
|
||||||
Self::new(atom.clone(), ctx).await
|
Self::new(atom.clone(), ctx).await
|
||||||
}
|
}
|
||||||
async fn to_api(&self) -> orchid_api::Atom { self.api_ref() }
|
async fn to_api(&self) -> orchid_api::Atom { self.api_ref() }
|
||||||
async fn print(&self) -> String { self.to_string().await }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WeakAtomHand(Weak<AtomData>);
|
pub struct WeakAtomHand(Weak<AtomData>);
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt;
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
use async_std::sync::RwLock;
|
use async_std::sync::RwLock;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use orchid_api::ExprTicket;
|
||||||
use orchid_base::error::OrcErrv;
|
use orchid_base::error::OrcErrv;
|
||||||
|
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||||
use orchid_base::location::Pos;
|
use orchid_base::location::Pos;
|
||||||
use orchid_base::match_mapping;
|
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
use orchid_base::tree::AtomRepr;
|
use orchid_base::tree::{AtomRepr, indent};
|
||||||
|
use orchid_base::{match_mapping, tl_cache};
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::atom::AtomHand;
|
use crate::atom::AtomHand;
|
||||||
@@ -34,15 +39,6 @@ impl Expr {
|
|||||||
.expect("this is a ref, it cannot be null"),
|
.expect("this is a ref, it cannot be null"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// pub fn canonicalize(&self) -> api::ExprTicket {
|
|
||||||
// if !self.is_canonical.swap(true, Ordering::Relaxed) {
|
|
||||||
// KNOWN_EXPRS.write().unwrap().entry(self.id()).or_insert_with(||
|
|
||||||
// self.clone()); }
|
|
||||||
// self.id()
|
|
||||||
// }
|
|
||||||
// pub fn resolve(tk: api::ExprTicket) -> Option<Self> {
|
|
||||||
// KNOWN_EXPRS.read().unwrap().get(&tk).cloned()
|
|
||||||
// }
|
|
||||||
pub async fn from_api(api: &api::Expression, ctx: &mut ExprParseCtx) -> Self {
|
pub async fn from_api(api: &api::Expression, ctx: &mut ExprParseCtx) -> Self {
|
||||||
if let api::ExpressionKind::Slot(tk) = &api.kind {
|
if let api::ExpressionKind::Slot(tk) = &api.kind {
|
||||||
return ctx.exprs().get_expr(*tk).expect("Invalid slot");
|
return ctx.exprs().get_expr(*tk).expect("Invalid slot");
|
||||||
@@ -60,6 +56,43 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Format for Expr {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
return print_expr(self, c, &mut HashSet::new()).await;
|
||||||
|
async fn print_expr<'a>(
|
||||||
|
expr: &'a Expr,
|
||||||
|
c: &'a (impl FmtCtx + ?Sized + 'a),
|
||||||
|
visited: &mut HashSet<ExprTicket>,
|
||||||
|
) -> FmtUnit {
|
||||||
|
if visited.contains(&expr.id()) {
|
||||||
|
return "CYCLIC_EXPR".to_string().into();
|
||||||
|
}
|
||||||
|
visited.insert(expr.id());
|
||||||
|
match &*expr.0.kind.read().await {
|
||||||
|
ExprKind::Arg => "Arg".to_string().into(),
|
||||||
|
ExprKind::Atom(a) => a.print(c).await,
|
||||||
|
ExprKind::Bottom(e) if e.len() == 1 => format!("Bottom({e})").into(),
|
||||||
|
ExprKind::Bottom(e) => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).into(),
|
||||||
|
ExprKind::Call(f, x) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
|
.unbounded("{0} {1l}")
|
||||||
|
.bounded("({0} {1b})")))
|
||||||
|
.units([f.print(c).await, x.print(c).await]),
|
||||||
|
ExprKind::Const(c) => format!("{c}").into(),
|
||||||
|
ExprKind::Lambda(None, body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
|
.unbounded("\\.{0l}")
|
||||||
|
.bounded("(\\.{0b})")))
|
||||||
|
.units([body.print(c).await]),
|
||||||
|
ExprKind::Lambda(Some(path), body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
|
.unbounded("\\{0b}. {1l}")
|
||||||
|
.bounded("(\\{0b}. {1b})")))
|
||||||
|
.units([format!("{path}").into(), body.print(c).await]),
|
||||||
|
ExprKind::Seq(l, r) =>
|
||||||
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0b}]{1l}")))
|
||||||
|
.units([l.print(c).await, r.print(c).await]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ExprKind {
|
pub enum ExprKind {
|
||||||
@@ -128,6 +161,21 @@ impl PathSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl fmt::Display for PathSet {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fn print_step(step: Step) -> &'static str { if step == Step::Left { "l" } else { "r" } }
|
||||||
|
let step_s = self.steps.iter().copied().map(print_step).join("");
|
||||||
|
match &self.next {
|
||||||
|
Some((left, right)) => {
|
||||||
|
if !step_s.is_empty() {
|
||||||
|
write!(f, "{step_s}>")?;
|
||||||
|
}
|
||||||
|
write!(f, "({left}|{right})")
|
||||||
|
},
|
||||||
|
None => write!(f, "{step_s}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WeakExpr(Weak<ExprData>);
|
pub struct WeakExpr(Weak<ExprData>);
|
||||||
impl WeakExpr {
|
impl WeakExpr {
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ use orchid_api::HostMsgSet;
|
|||||||
use orchid_api_traits::Request;
|
use orchid_api_traits::Request;
|
||||||
use orchid_base::builtin::ExtInit;
|
use orchid_base::builtin::ExtInit;
|
||||||
use orchid_base::clone;
|
use orchid_base::clone;
|
||||||
|
use orchid_base::format::{FmtCtxImpl, Format};
|
||||||
use orchid_base::interner::Tok;
|
use orchid_base::interner::Tok;
|
||||||
use orchid_base::logging::Logger;
|
use orchid_base::logging::Logger;
|
||||||
use orchid_base::reqnot::{ReqNot, Requester as _};
|
use orchid_base::reqnot::{ReqNot, Requester as _};
|
||||||
use orchid_base::tree::AtomRepr;
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::atom::AtomHand;
|
use crate::atom::AtomHand;
|
||||||
@@ -152,8 +152,11 @@ impl Extension {
|
|||||||
req_in.send(ReqPair(rm.clone(), rep_in)).await.unwrap();
|
req_in.send(ReqPair(rm.clone(), rep_in)).await.unwrap();
|
||||||
hand.handle(&rm, &rep_out.recv().await.unwrap()).await
|
hand.handle(&rm, &rep_out.recv().await.unwrap()).await
|
||||||
},
|
},
|
||||||
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) =>
|
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
|
||||||
hand.handle(eap, &AtomHand::new(atom.clone(), &ctx).await.print().await).await,
|
let atom = AtomHand::new(atom.clone(), &ctx).await;
|
||||||
|
let unit = atom.print(&FmtCtxImpl { i: &this.ctx().i }).await;
|
||||||
|
hand.handle(eap, &unit.to_api()).await
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ pub async fn parse_item(
|
|||||||
) -> OrcRes<Vec<Item>> {
|
) -> OrcRes<Vec<Item>> {
|
||||||
match item.pop_front() {
|
match item.pop_front() {
|
||||||
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
|
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
|
||||||
n if *n == item.i("export").await => match try_pop_no_fluff(postdisc).await? {
|
n if *n == item.i().i("export").await => match try_pop_no_fluff(postdisc).await? {
|
||||||
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
|
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
|
||||||
parse_exportable_item(ctx, path, comments, true, n.clone(), tail).await,
|
parse_exportable_item(ctx, path, comments, true, n.clone(), tail).await,
|
||||||
Parsed { output: TokTree { tok: Token::NS, .. }, tail } => {
|
Parsed { output: TokTree { tok: Token::NS, .. }, tail } => {
|
||||||
@@ -70,12 +70,12 @@ pub async fn parse_item(
|
|||||||
([], Some(n)) =>
|
([], Some(n)) =>
|
||||||
ok.push(Item { comments: comments.clone(), pos, kind: ItemKind::Export(n) }),
|
ok.push(Item { comments: comments.clone(), pos, kind: ItemKind::Export(n) }),
|
||||||
(_, Some(_)) => ctx.reporter().report(mk_err(
|
(_, Some(_)) => ctx.reporter().report(mk_err(
|
||||||
tail.i("Compound export").await,
|
tail.i().i("Compound export").await,
|
||||||
"Cannot export compound names (names containing the :: separator)",
|
"Cannot export compound names (names containing the :: separator)",
|
||||||
[pos.into()],
|
[pos.into()],
|
||||||
)),
|
)),
|
||||||
(_, None) => ctx.reporter().report(mk_err(
|
(_, None) => ctx.reporter().report(mk_err(
|
||||||
tail.i("Wildcard export").await,
|
tail.i().i("Wildcard export").await,
|
||||||
"Exports cannot contain the globstar *",
|
"Exports cannot contain the globstar *",
|
||||||
[pos.into()],
|
[pos.into()],
|
||||||
)),
|
)),
|
||||||
@@ -85,12 +85,12 @@ pub async fn parse_item(
|
|||||||
Ok(ok)
|
Ok(ok)
|
||||||
},
|
},
|
||||||
Parsed { output, tail } => Err(mk_errv(
|
Parsed { output, tail } => Err(mk_errv(
|
||||||
tail.i("Malformed export").await,
|
tail.i().i("Malformed export").await,
|
||||||
"`export` can either prefix other lines or list names inside ::( ) or ::[ ]",
|
"`export` can either prefix other lines or list names inside ::( ) or ::[ ]",
|
||||||
[Pos::Range(output.range.clone()).into()],
|
[Pos::Range(output.range.clone()).into()],
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
n if *n == item.i("import").await => parse_import(ctx, postdisc).await.map(|v| {
|
n if *n == item.i().i("import").await => parse_import(ctx, postdisc).await.map(|v| {
|
||||||
Vec::from_iter(v.into_iter().map(|(t, pos)| Item {
|
Vec::from_iter(v.into_iter().map(|(t, pos)| Item {
|
||||||
comments: comments.clone(),
|
comments: comments.clone(),
|
||||||
pos,
|
pos,
|
||||||
@@ -99,10 +99,11 @@ pub async fn parse_item(
|
|||||||
}),
|
}),
|
||||||
n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc).await,
|
n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc).await,
|
||||||
},
|
},
|
||||||
Some(_) =>
|
Some(_) => Err(mk_errv(
|
||||||
Err(mk_errv(item.i("Expected a line type").await, "All lines must begin with a keyword", [
|
item.i().i("Expected a line type").await,
|
||||||
Pos::Range(item.pos()).into(),
|
"All lines must begin with a keyword",
|
||||||
])),
|
[Pos::Range(item.pos()).into()],
|
||||||
|
)),
|
||||||
None => unreachable!("These lines are filtered and aggregated in earlier stages"),
|
None => unreachable!("These lines are filtered and aggregated in earlier stages"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,20 +125,20 @@ pub async fn parse_exportable_item(
|
|||||||
discr: Tok<String>,
|
discr: Tok<String>,
|
||||||
tail: ParsSnippet<'_>,
|
tail: ParsSnippet<'_>,
|
||||||
) -> OrcRes<Vec<Item>> {
|
) -> OrcRes<Vec<Item>> {
|
||||||
let kind = if discr == tail.i("mod").await {
|
let kind = if discr == tail.i().i("mod").await {
|
||||||
let (name, body) = parse_module(ctx, path, tail).await?;
|
let (name, body) = parse_module(ctx, path, tail).await?;
|
||||||
ItemKind::Member(Member::new(name, MemberKind::Mod(body)))
|
ItemKind::Member(Member::new(name, MemberKind::Mod(body)))
|
||||||
} else if discr == tail.i("const").await {
|
} else if discr == tail.i().i("const").await {
|
||||||
let (name, val) = parse_const(tail).await?;
|
let (name, val) = parse_const(tail).await?;
|
||||||
let locator = CodeLocator::to_const(tail.i(&path.push(name.clone()).unreverse()).await);
|
let locator = CodeLocator::to_const(tail.i().i(&path.push(name.clone()).unreverse()).await);
|
||||||
ItemKind::Member(Member::new(name, MemberKind::Const(Code::from_code(locator, val))))
|
ItemKind::Member(Member::new(name, MemberKind::Const(Code::from_code(locator, val))))
|
||||||
} else if let Some(sys) = ctx.systems().find(|s| s.can_parse(discr.clone())) {
|
} else if let Some(sys) = ctx.systems().find(|s| s.can_parse(discr.clone())) {
|
||||||
let line = sys.parse(tail.to_vec(), exported, comments).await?;
|
let line = sys.parse(tail.to_vec(), exported, comments).await?;
|
||||||
return parse_items(ctx, path, Snippet::new(tail.prev(), &line, tail.interner())).await;
|
return parse_items(ctx, path, Snippet::new(tail.prev(), &line, tail.i())).await;
|
||||||
} else {
|
} else {
|
||||||
let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
|
let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Unrecognized line type").await,
|
tail.i().i("Unrecognized line type").await,
|
||||||
format!("Line types are: const, mod, macro, grammar, {ext_lines}"),
|
format!("Line types are: const, mod, macro, grammar, {ext_lines}"),
|
||||||
[Pos::Range(tail.prev().range.clone()).into()],
|
[Pos::Range(tail.prev().range.clone()).into()],
|
||||||
));
|
));
|
||||||
@@ -154,18 +155,18 @@ pub async fn parse_module(
|
|||||||
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
|
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
|
||||||
Parsed { output, .. } => {
|
Parsed { output, .. } => {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Missing module name").await,
|
tail.i().i("Missing module name").await,
|
||||||
format!("A name was expected, {} was found", output.print().await),
|
format!("A name was expected, {} was found", tail.fmt(output).await),
|
||||||
[Pos::Range(output.range.clone()).into()],
|
[Pos::Range(output.range.clone()).into()],
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let Parsed { output, tail: surplus } = try_pop_no_fluff(tail).await?;
|
let Parsed { output, tail: surplus } = try_pop_no_fluff(tail).await?;
|
||||||
expect_end(surplus).await?;
|
expect_end(surplus).await?;
|
||||||
let Some(body) = output.as_s(Paren::Round, tail.interner()) else {
|
let Some(body) = output.as_s(Paren::Round, tail.i()) else {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Expected module body").await,
|
tail.i().i("Expected module body").await,
|
||||||
format!("A ( block ) was expected, {} was found", output.print().await),
|
format!("A ( block ) was expected, {} was found", tail.fmt(output).await),
|
||||||
[Pos::Range(output.range.clone()).into()],
|
[Pos::Range(output.range.clone()).into()],
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
@@ -177,16 +178,16 @@ pub async fn parse_const(tail: ParsSnippet<'_>) -> OrcRes<(Tok<String>, Vec<Pars
|
|||||||
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
|
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
|
||||||
let Some(name) = output.as_name() else {
|
let Some(name) = output.as_name() else {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Missing module name").await,
|
tail.i().i("Missing module name").await,
|
||||||
format!("A name was expected, {} was found", output.print().await),
|
format!("A name was expected, {} was found", tail.fmt(output).await),
|
||||||
[Pos::Range(output.range.clone()).into()],
|
[Pos::Range(output.range.clone()).into()],
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
|
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
|
||||||
if !output.is_kw(tail.i("=").await) {
|
if !output.is_kw(tail.i().i("=").await) {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Missing walrus := separator").await,
|
tail.i().i("Missing = separator").await,
|
||||||
format!("Expected operator := , found {}", output.print().await),
|
format!("Expected = , found {}", tail.fmt(output).await),
|
||||||
[Pos::Range(output.range.clone()).into()],
|
[Pos::Range(output.range.clone()).into()],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -200,7 +201,7 @@ pub async fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
|
|||||||
let (range, tok, tail) = match &ttree.tok {
|
let (range, tok, tail) = match &ttree.tok {
|
||||||
Token::S(p, b) => (
|
Token::S(p, b) => (
|
||||||
ttree.range.clone(),
|
ttree.range.clone(),
|
||||||
MTok::S(*p, parse_mtree(Snippet::new(ttree, b, snip.interner())).boxed_local().await?),
|
MTok::S(*p, parse_mtree(Snippet::new(ttree, b, snip.i())).boxed_local().await?),
|
||||||
tail,
|
tail,
|
||||||
),
|
),
|
||||||
Token::Name(tok) => {
|
Token::Name(tok) => {
|
||||||
@@ -210,7 +211,7 @@ pub async fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
|
|||||||
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
|
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
|
||||||
let Some(seg) = output.as_name() else {
|
let Some(seg) = output.as_name() else {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Namespaced name interrupted").await,
|
tail.i().i("Namespaced name interrupted").await,
|
||||||
"In expression context, :: must always be followed by a name.\n\
|
"In expression context, :: must always be followed by a name.\n\
|
||||||
::() is permitted only in import and export items",
|
::() is permitted only in import and export items",
|
||||||
[Pos::Range(output.range.clone()).into()],
|
[Pos::Range(output.range.clone()).into()],
|
||||||
@@ -220,15 +221,11 @@ pub async fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
|
|||||||
snip = tail;
|
snip = tail;
|
||||||
end = output.range.end;
|
end = output.range.end;
|
||||||
}
|
}
|
||||||
(
|
(ttree.range.start..end, MTok::Name(Sym::new(segments, snip.i()).await.unwrap()), snip)
|
||||||
ttree.range.start..end,
|
|
||||||
MTok::Name(Sym::new(segments, snip.interner()).await.unwrap()),
|
|
||||||
snip,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
Token::NS => {
|
Token::NS => {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Unexpected :: in macro pattern").await,
|
tail.i().i("Unexpected :: in macro pattern").await,
|
||||||
":: can only follow a name outside export statements",
|
":: can only follow a name outside export statements",
|
||||||
[Pos::Range(ttree.range.clone()).into()],
|
[Pos::Range(ttree.range.clone()).into()],
|
||||||
));
|
));
|
||||||
@@ -236,10 +233,10 @@ pub async fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
|
|||||||
Token::Ph(ph) => (ttree.range.clone(), MTok::Ph(ph.clone()), tail),
|
Token::Ph(ph) => (ttree.range.clone(), MTok::Ph(ph.clone()), tail),
|
||||||
Token::Atom(_) | Token::Macro(_) => {
|
Token::Atom(_) | Token::Macro(_) => {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("Unsupported token in macro patterns").await,
|
tail.i().i("Unsupported token in macro patterns").await,
|
||||||
format!(
|
format!(
|
||||||
"Macro patterns can only contain names, braces, and lambda, not {}.",
|
"Macro patterns can only contain names, braces, and lambda, not {}.",
|
||||||
ttree.print().await
|
tail.fmt(ttree).await
|
||||||
),
|
),
|
||||||
[Pos::Range(ttree.range.clone()).into()],
|
[Pos::Range(ttree.range.clone()).into()],
|
||||||
));
|
));
|
||||||
@@ -249,13 +246,13 @@ pub async fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
|
|||||||
Token::LambdaHead(arg) => (
|
Token::LambdaHead(arg) => (
|
||||||
ttree.range.start..snip.pos().end,
|
ttree.range.start..snip.pos().end,
|
||||||
MTok::Lambda(
|
MTok::Lambda(
|
||||||
parse_mtree(Snippet::new(ttree, arg, snip.interner())).boxed_local().await?,
|
parse_mtree(Snippet::new(ttree, arg, snip.i())).boxed_local().await?,
|
||||||
parse_mtree(tail).boxed_local().await?,
|
parse_mtree(tail).boxed_local().await?,
|
||||||
),
|
),
|
||||||
Snippet::new(ttree, &[], snip.interner()),
|
Snippet::new(ttree, &[], snip.i()),
|
||||||
),
|
),
|
||||||
Token::Slot(_) | Token::X(_) =>
|
Token::Slot(_) | Token::X(_) =>
|
||||||
panic!("Did not expect {} in parsed token tree", &ttree.tok.print().await),
|
panic!("Did not expect {} in parsed token tree", tail.fmt(ttree).await),
|
||||||
};
|
};
|
||||||
mtreev.push(MTree { pos: Pos::Range(range.clone()), tok: Rc::new(tok) });
|
mtreev.push(MTree { pos: Pos::Range(range.clone()), tok: Rc::new(tok) });
|
||||||
snip = tail;
|
snip = tail;
|
||||||
@@ -272,7 +269,7 @@ pub async fn parse_macro(
|
|||||||
Parsed { tail, output: o @ TokTree { tok: Token::S(Paren::Round, b), .. } } => (tail, o, b),
|
Parsed { tail, output: o @ TokTree { tok: Token::S(Paren::Round, b), .. } } => (tail, o, b),
|
||||||
Parsed { output, .. } => {
|
Parsed { output, .. } => {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
tail.i("m").await,
|
tail.i().i("m").await,
|
||||||
"Macro blocks must either start with a block or a ..$:number",
|
"Macro blocks must either start with a block or a ..$:number",
|
||||||
[Pos::Range(output.range.clone()).into()],
|
[Pos::Range(output.range.clone()).into()],
|
||||||
));
|
));
|
||||||
@@ -281,24 +278,22 @@ pub async fn parse_macro(
|
|||||||
expect_end(surplus).await?;
|
expect_end(surplus).await?;
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
for (i, item) in
|
for (i, item) in line_items(Snippet::new(prev, block, tail.i())).await.into_iter().enumerate() {
|
||||||
line_items(Snippet::new(prev, block, tail.interner())).await.into_iter().enumerate()
|
|
||||||
{
|
|
||||||
let Parsed { tail, output } = try_pop_no_fluff(item.tail).await?;
|
let Parsed { tail, output } = try_pop_no_fluff(item.tail).await?;
|
||||||
if !output.is_kw(tail.i("rule").await) {
|
if !output.is_kw(tail.i().i("rule").await) {
|
||||||
errors.extend(mk_errv(
|
errors.extend(mk_errv(
|
||||||
tail.i("non-rule in macro").await,
|
tail.i().i("non-rule in macro").await,
|
||||||
format!("Expected `rule`, got {}", output.print().await),
|
format!("Expected `rule`, got {}", tail.fmt(output).await),
|
||||||
[Pos::Range(output.range.clone()).into()],
|
[Pos::Range(output.range.clone()).into()],
|
||||||
));
|
));
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let arrow = tail.i("=>").await;
|
let arrow = tail.i().i("=>").await;
|
||||||
let (pat, body) = match tail.split_once(|t| t.is_kw(arrow.clone())) {
|
let (pat, body) = match tail.split_once(|t| t.is_kw(arrow.clone())) {
|
||||||
Some((a, b)) => (a, b),
|
Some((a, b)) => (a, b),
|
||||||
None => {
|
None => {
|
||||||
errors.extend(mk_errv(
|
errors.extend(mk_errv(
|
||||||
tail.i("no => in macro rule").await,
|
tail.i().i("no => in macro rule").await,
|
||||||
"The pattern and body of a rule must be separated by a =>",
|
"The pattern and body of a rule must be separated by a =>",
|
||||||
[Pos::Range(tail.pos()).into()],
|
[Pos::Range(tail.pos()).into()],
|
||||||
));
|
));
|
||||||
@@ -310,7 +305,7 @@ pub async fn parse_macro(
|
|||||||
pos: Pos::Range(tail.pos()),
|
pos: Pos::Range(tail.pos()),
|
||||||
pattern: parse_mtree(pat).await?,
|
pattern: parse_mtree(pat).await?,
|
||||||
kind: RuleKind::Native(Code::from_code(
|
kind: RuleKind::Native(Code::from_code(
|
||||||
CodeLocator::to_rule(tail.i(&path.unreverse()).await, macro_i, i as u16),
|
CodeLocator::to_rule(tail.i().i(&path.unreverse()).await, macro_i, i as u16),
|
||||||
body.to_vec(),
|
body.to_vec(),
|
||||||
)),
|
)),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use orchid_base::async_once_cell::OnceCell;
|
|||||||
use orchid_base::char_filter::char_filter_match;
|
use orchid_base::char_filter::char_filter_match;
|
||||||
use orchid_base::clone;
|
use orchid_base::clone;
|
||||||
use orchid_base::error::{OrcErrv, OrcRes};
|
use orchid_base::error::{OrcErrv, OrcRes};
|
||||||
|
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
||||||
use orchid_base::interner::Tok;
|
use orchid_base::interner::Tok;
|
||||||
use orchid_base::parse::Comment;
|
use orchid_base::parse::Comment;
|
||||||
use orchid_base::reqnot::{ReqNot, Requester};
|
use orchid_base::reqnot::{ReqNot, Requester};
|
||||||
@@ -97,12 +98,14 @@ impl System {
|
|||||||
this.ctx.owned_atoms.write().await.remove(&drop);
|
this.ctx.owned_atoms.write().await.remove(&drop);
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
pub async fn print(&self) -> String {
|
pub fn downgrade(&self) -> WeakSystem { WeakSystem(Rc::downgrade(&self.0)) }
|
||||||
|
}
|
||||||
|
impl Format for System {
|
||||||
|
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
let ctor = (self.0.ext.system_ctors().find(|c| c.id() == self.0.decl_id))
|
let ctor = (self.0.ext.system_ctors().find(|c| c.id() == self.0.decl_id))
|
||||||
.expect("System instance with no associated constructor");
|
.expect("System instance with no associated constructor");
|
||||||
format!("System({} @ {} #{})", ctor.name(), ctor.priority(), self.0.id.0)
|
format!("System({} @ {} #{})", ctor.name(), ctor.priority(), self.0.id.0).into()
|
||||||
}
|
}
|
||||||
pub fn downgrade(&self) -> WeakSystem { WeakSystem(Rc::downgrade(&self.0)) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WeakSystem(Weak<SystemInstData>);
|
pub struct WeakSystem(Weak<SystemInstData>);
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::{Mutex, OnceLock};
|
use std::sync::{Mutex, OnceLock};
|
||||||
|
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
|
use futures::future::join_all;
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::clone;
|
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::error::OrcRes;
|
||||||
|
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||||
use orchid_base::interner::Tok;
|
use orchid_base::interner::Tok;
|
||||||
use orchid_base::location::Pos;
|
use orchid_base::location::Pos;
|
||||||
use orchid_base::macros::mtreev_from_api;
|
use orchid_base::macros::{mtreev_fmt, mtreev_from_api};
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::name::Sym;
|
||||||
use orchid_base::parse::{Comment, Import};
|
use orchid_base::parse::{Comment, Import};
|
||||||
use orchid_base::tree::{AtomRepr, TokTree, Token};
|
use orchid_base::tree::{AtomRepr, TokTree, Token, ttv_fmt};
|
||||||
|
use orchid_base::{clone, tl_cache};
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
@@ -78,6 +81,22 @@ impl Item {
|
|||||||
Self { pos: Pos::from_api(&tree.location, &sys.ctx().i).await, comments, kind }
|
Self { pos: Pos::from_api(&tree.location, &sys.ctx().i).await, comments, kind }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Format for Item {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
let comment_text = self.comments.iter().join("\n");
|
||||||
|
let item_text = match &self.kind {
|
||||||
|
ItemKind::Import(i) => format!("import {i}").into(),
|
||||||
|
ItemKind::Export(e) => format!("export {e}").into(),
|
||||||
|
ItemKind::Macro(None, rules) =>
|
||||||
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("macro {{\n\t{0}\n}}")))
|
||||||
|
.units([Variants::sequence(rules.len(), "\n", None)
|
||||||
|
.units(join_all(rules.iter().map(|r| r.print(c))).await)]),
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0}\n{1}")))
|
||||||
|
.units([comment_text.into(), item_text])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Member {
|
pub struct Member {
|
||||||
pub name: Tok<String>,
|
pub name: Tok<String>,
|
||||||
@@ -172,6 +191,24 @@ pub struct Rule {
|
|||||||
pub pattern: Vec<MacTree>,
|
pub pattern: Vec<MacTree>,
|
||||||
pub kind: RuleKind,
|
pub kind: RuleKind,
|
||||||
}
|
}
|
||||||
|
impl Format for Rule {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
FmtUnit::new(
|
||||||
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0b}\n{1} => {2b}"))),
|
||||||
|
[
|
||||||
|
self.comments.iter().join("\n").into(),
|
||||||
|
mtreev_fmt(&self.pattern, c).await,
|
||||||
|
match &self.kind {
|
||||||
|
RuleKind::Native(code) => code.print(c).await,
|
||||||
|
RuleKind::Remote(sys, id) => FmtUnit::new(
|
||||||
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0} #{1}"))),
|
||||||
|
[sys.print(c).await, format!("{id:?}").into()],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RuleKind {
|
pub enum RuleKind {
|
||||||
@@ -193,6 +230,17 @@ impl Code {
|
|||||||
Self { locator, source: Some(code), bytecode: OnceLock::new() }
|
Self { locator, source: Some(code), bytecode: OnceLock::new() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Format for Code {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
if let Some(bc) = self.bytecode.get() {
|
||||||
|
return bc.print(c).await;
|
||||||
|
}
|
||||||
|
if let Some(src) = &self.source {
|
||||||
|
return ttv_fmt(src, c).await;
|
||||||
|
}
|
||||||
|
panic!("Code must be initialized with at least one state")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Selects a code element
|
/// Selects a code element
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use async_std::io::Write;
|
|||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
use orchid_api_traits::{Encode, Request};
|
use orchid_api_traits::{Encode, Request};
|
||||||
use orchid_base::error::{OrcRes, mk_errv};
|
use orchid_base::error::{OrcRes, mk_errv};
|
||||||
|
use orchid_base::format::FmtUnit;
|
||||||
use orchid_base::interner::Tok;
|
use orchid_base::interner::Tok;
|
||||||
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom};
|
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom};
|
||||||
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||||
@@ -66,7 +67,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(&self, _ctx: SysCtx) -> String { format!("{:?}i", *self.0) }
|
async fn print(&self, _ctx: SysCtx) -> FmtUnit { 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)>) {
|
||||||
self.0.encode(write).await
|
self.0.encode(write).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use clap::{Parser, Subcommand};
|
|||||||
use futures::{Stream, TryStreamExt, io};
|
use futures::{Stream, TryStreamExt, io};
|
||||||
use orchid_base::clone;
|
use orchid_base::clone;
|
||||||
use orchid_base::error::ReporterImpl;
|
use orchid_base::error::ReporterImpl;
|
||||||
|
use orchid_base::format::{FmtCtxImpl, take_first};
|
||||||
use orchid_base::logging::{LogStrategy, Logger};
|
use orchid_base::logging::{LogStrategy, Logger};
|
||||||
use orchid_base::parse::Snippet;
|
use orchid_base::parse::Snippet;
|
||||||
use orchid_base::tree::ttv_fmt;
|
use orchid_base::tree::ttv_fmt;
|
||||||
@@ -83,7 +84,7 @@ async fn main() -> io::Result<ExitCode> {
|
|||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
file.read_to_string(&mut buf).unwrap();
|
file.read_to_string(&mut buf).unwrap();
|
||||||
let lexemes = lex(ctx.i.i(&buf).await, &systems, ctx).await.unwrap();
|
let lexemes = lex(ctx.i.i(&buf).await, &systems, ctx).await.unwrap();
|
||||||
println!("{}", ttv_fmt(&lexemes).await)
|
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i: &ctx.i }).await, true))
|
||||||
},
|
},
|
||||||
Commands::Parse { file } => {
|
Commands::Parse { file } => {
|
||||||
let systems = init_systems(&args.system, &extensions).await.unwrap();
|
let systems = init_systems(&args.system, &extensions).await.unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user