Skip to content

Commit 12c4182

Browse files
committed
'$part': access to stream length in part specification
1 parent 05453f8 commit 12c4182

File tree

3 files changed

+104
-44
lines changed

3 files changed

+104
-44
lines changed

src/base.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,13 @@ impl Node {
207207
}
208208
}
209209

210-
/*pub(crate) fn check_args_nonempty(&self) -> Result<(), BaseError> {
210+
pub(crate) fn check_args_nonempty(&self) -> Result<(), BaseError> {
211211
if self.args.is_empty() {
212212
Err("at least 1 argument required".into())
213213
} else {
214214
Ok(())
215215
}
216-
}*/
216+
}
217217

218218
/// Evaluates this `Node` to an `Item`. This is the point at which it is decided whether it
219219
/// describes an atomic constant or a stream.
@@ -471,6 +471,15 @@ impl From<Node> for Head {
471471
}
472472
}
473473

474+
impl PartialEq<str> for Head {
475+
fn eq(&self, other: &str) -> bool {
476+
match self {
477+
Head::Symbol(sym) => sym == other,
478+
_ => false
479+
}
480+
}
481+
}
482+
474483

475484
/// Special types of [`Head`] for language constructs with special syntax.
476485
#[derive(Debug, PartialEq, Clone)]

src/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt::{Display, Formatter, Debug};
33

44
/// The base error returned by helper functions. In most situations this is intended to be
55
/// turned into [`StreamError`] by supplementing a [`Expr`].
6-
#[derive(Debug, PartialEq)]
6+
#[derive(Debug, PartialEq, Clone)]
77
pub struct BaseError(String);
88

99
impl<T> From<T> for BaseError where T: Into<String> {
@@ -42,7 +42,7 @@ impl Display for StreamError {
4242

4343

4444
macro_rules! try_with {
45-
($blame:ident, $expr:expr) => {
45+
($blame:expr, $expr:expr) => {
4646
match (|| -> Result<_, BaseError> { $expr })() {
4747
Ok(result) => result,
4848
Err(err) => return Err(StreamError::new(err, $blame))

src/lang.rs

Lines changed: 91 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -102,48 +102,87 @@ impl From<String> for LiteralString {
102102
pub struct Part {
103103
source: Box<dyn Stream>,
104104
indices: Box<dyn Stream>,
105-
rest: Vec<Item>,
105+
rest: Vec<Expr>,
106106
env: Rc<Env>
107107
}
108108

109109
impl Part {
110110
fn eval(node: Node, env: &Rc<Env>) -> Result<Item, StreamError> {
111-
let mut node = node.eval_all(env)?;
112-
let source = try_with!(node, node.source_checked()?.to_stream());
113-
match node.args.first() {
114-
None => {
115-
Err(StreamError::new("at least 1 argument required", node))
116-
},
117-
Some(first) => {
118-
match first {
119-
Item::Number(index) => {
120-
try_with!(node, index.check_within(Number::one()..));
121-
match source.length() {
122-
Length::Exact(len) | Length::AtMost(len) if &len < index =>
123-
return Err(StreamError::new("index past end of stream", node)),
124-
_ => ()
125-
}
126-
let mut iter = source.iter();
127-
if iter.skip_n(&(index - 1))?.is_some() {
128-
return Err(StreamError::new("index past end of stream", node));
129-
}
130-
let item = match iter.next() {
131-
Some(value) => value?,
132-
None => return Err(StreamError::new("index past end of stream", node))
133-
};
134-
node.args.remove(0);
135-
if node.args.is_empty() {
136-
Ok(item)
137-
} else {
138-
Part::eval(ENode{head: node.head, source: Some(item), args: node.args}.into(), env)
111+
use once_cell::unsync::Lazy;
112+
let mut node = node.eval_source(env)?;
113+
let source = try_with!(node, node.source_checked()?.as_item()?.to_stream());
114+
try_with!(node, node.check_args_nonempty());
115+
let length = Lazy::new(|| match source.length() {
116+
Length::Exact(len) => Ok(len),
117+
Length::Infinite => Err(BaseError::from("stream is infinite")),
118+
_ => Ok(source.iter().count().into())
119+
});
120+
type R = Result<Number, BaseError>;
121+
fn subs_len(expr: &mut Expr, length: &Lazy<R, impl Fn() -> R>) ->
122+
Result<(), BaseError>
123+
{
124+
match expr {
125+
Expr::Imm(_) => Ok(()),
126+
Expr::Eval(node) => {
127+
if &node.head == "len" && node.source.is_none() {
128+
match Lazy::force(length) {
129+
Ok(len) => *expr = Expr::new_number(len.to_owned()),
130+
Err(err) => return Err(err.clone())
139131
}
140-
},
141-
Item::Stream(_) => {
142-
let indices = node.args.remove(0).to_stream().unwrap();
143-
Ok(Item::new_stream(Part{source, indices, rest: node.args, env: Rc::clone(env)}))
144-
},
145-
_ => Err(StreamError::new(format!("expected number or stream, found {:?}", first), node))
132+
return Ok(());
133+
}
134+
node.source.iter_mut().try_for_each(|sbox| subs_len(sbox, length))?;
135+
match &mut node.head {
136+
Head::Lang(LangItem::Part) => return Ok(()), // $part does not enter into args
137+
Head::Block(expr) => subs_len(expr, length)?,
138+
Head::Lang(LangItem::Args(head)) => {
139+
if let Head::Block(ref mut expr) = **head {
140+
subs_len(expr, length)?
141+
}
142+
},
143+
_ => ()
144+
}
145+
node.args.iter_mut().try_for_each(|arg| subs_len(arg, length))?;
146+
Ok(())
147+
}
148+
}
149+
}
150+
try_with!(node, subs_len(node.args.get_mut(0).unwrap(), &length));
151+
let first = node.args.remove(0).eval_env(env)?;
152+
match first {
153+
Item::Number(index) => {
154+
macro_rules! orig_node {
155+
() => { {
156+
node.args.insert(0, Expr::new_number(index));
157+
node
158+
} }
159+
}
160+
try_with!(orig_node!(), index.check_within(Number::one()..));
161+
match source.length() {
162+
Length::Exact(len) | Length::AtMost(len) if len < index =>
163+
return Err(StreamError::new("index past end of stream", orig_node!())),
164+
_ => ()
165+
}
166+
let mut iter = source.iter();
167+
if iter.skip_n(&(&index - 1))?.is_some() {
168+
return Err(StreamError::new("index past end of stream", orig_node!()));
169+
}
170+
let item = match iter.next() {
171+
Some(value) => value?,
172+
None => return Err(StreamError::new("index past end of stream", orig_node!()))
173+
};
174+
if node.args.is_empty() {
175+
Ok(item)
176+
} else {
177+
Part::eval(Node::new(node.head, Some(item.into()), node.args), env)
146178
}
179+
},
180+
Item::Stream(indices) => {
181+
Ok(Item::new_stream(Part{source, indices, rest: node.args, env: Rc::clone(env)}))
182+
},
183+
item => {
184+
node.args.insert(0, item.into());
185+
Err(StreamError::new(format!("expected number or stream, found {:?}", node.args[0]), node))
147186
}
148187
}
149188
}
@@ -158,8 +197,8 @@ impl Stream for Part {
158197
impl Describe for Part {
159198
fn describe(&self) -> String {
160199
let mut args = self.rest.clone();
161-
let source = self.source.to_item();
162-
args.insert(0, self.indices.to_item());
200+
let source = self.source.to_expr();
201+
args.insert(0, self.indices.to_expr());
163202
Node::describe_helper(&Head::Lang(LangItem::Part), Some(&source), &args)
164203
}
165204
}
@@ -190,9 +229,9 @@ impl Iterator for PartIter<'_> {
190229
};
191230
// TODO: smarter - number tracks increments, stream unfolds?
192231
let mut args = self.parent.rest.clone();
193-
args.insert(0, part);
194-
let node = ENode{head: LangItem::Part.into(), source: Some(self.parent.source.to_item()), args};
195-
Some(Part::eval(node.into(), &self.parent.env))
232+
args.insert(0, Expr::Imm(part));
233+
let node = Node::new(LangItem::Part, Some(self.parent.source.to_expr()), args);
234+
Some(Part::eval(node, &self.parent.env))
196235
}
197236
}
198237

@@ -227,6 +266,18 @@ fn test_part() {
227266
assert!(parse("seq[2,5]").unwrap().eval().is_err());
228267
assert_eq!(parse("seq[[2,5]]").unwrap().eval().unwrap().to_string(), "[2, 5]");
229268
assert_eq!(parse("seq[[[2,5]]]").unwrap().eval().unwrap().to_string(), "[[2, 5]]"); // subject to change
269+
270+
assert_eq!(parse("[1,2,3][len]").unwrap().eval().unwrap().to_string(), "3");
271+
assert_eq!(parse("[1,2,3][[len]]").unwrap().eval().unwrap().to_string(), "[3]");
272+
assert_eq!(parse("[1,2,3][len-seq]").unwrap().eval().unwrap().to_string(), "[2, 1, <!>");
273+
assert_eq!(parse("[[1], [1,2,3]][len, len]").unwrap().eval().unwrap().to_string(), "3");
274+
assert_eq!(parse("[[1], [1,2,3]][1..2, len]").unwrap().eval().unwrap().to_string(), "[1, 3]");
275+
assert_eq!(parse("[1,2,3][[1,2].len]").unwrap().eval().unwrap().to_string(), "2");
276+
assert!(parse("[1,2,3][seq[len]]").unwrap().eval().is_err());
277+
assert_eq!(parse("[1,2,3][range(len-1)[len]]").unwrap().eval().unwrap().to_string(), "2");
278+
assert_eq!(parse("[1,2,3,4][{#1..(#1+1)}(len/2)]").unwrap().eval().unwrap().to_string(), "[2, 3]");
279+
assert_eq!(parse("[1,2,3][{len}]").unwrap().eval().unwrap().to_string(), "3");
280+
assert_eq!(parse("[1,2,3][{len}@[]]").unwrap().eval().unwrap().to_string(), "3");
230281
}
231282

232283

0 commit comments

Comments
 (0)