diff --git a/site/primitives.json b/site/primitives.json index 38308a088..3a47831e6 100644 --- a/site/primitives.json +++ b/site/primitives.json @@ -347,6 +347,15 @@ "description": "Invert the behavior of a function, but keep its signature", "experimental": true }, + "around": { + "ascii": "'", + "glyph": "’", + "args": 2, + "outputs": 3, + "class": "Stack", + "description": "Duplicate the top of the stack around the second element", + "experimental": true + }, "assert": { "glyph": "⍤", "args": 2, diff --git a/site/src/editor/utils.rs b/site/src/editor/utils.rs index 0b9aa8f8e..19da51d20 100644 --- a/site/src/editor/utils.rs +++ b/site/src/editor/utils.rs @@ -457,8 +457,6 @@ pub fn gen_code_view(code: &str) -> View { SpanKind::String => "string-literal-span", SpanKind::Comment | SpanKind::OutputComment => "comment-span", SpanKind::Strand => "strand-span", - SpanKind::StackSwizzle(sw) => sig_class(sw.signature()), - SpanKind::ArraySwizzle(sw) => sig_class(sw.signature()), SpanKind::Subscript(None, _) => "number-literal", SpanKind::Subscript(Some(prim), n) => { prim_sig_class(*prim, prim.subscript_sig(*n)) @@ -570,20 +568,6 @@ pub fn gen_code_view(code: &str) -> View { view!({text}).into_view(), ) } - SpanKind::StackSwizzle(sw) => { - let class = format!("code-span {}", color_class); - let title = format!("stack swizzle {}", sw.signature()); - frag_views.push( - view!({text}).into_view(), - ) - } - SpanKind::ArraySwizzle(sw) => { - let class = format!("code-span {}", color_class); - let title = format!("array swizzle {}", sw.signature()); - frag_views.push( - view!({text}).into_view(), - ) - } SpanKind::Label => { let label = text.trim_start_matches('$'); let mut components = [0f32; 3]; diff --git a/src/algorithm/invert.rs b/src/algorithm/invert.rs index e90b41198..be5c33f5c 100644 --- a/src/algorithm/invert.rs +++ b/src/algorithm/invert.rs @@ -308,7 +308,6 @@ static INVERT_PATTERNS: &[&dyn InvertPattern] = { &InvertPatternFn(invert_split_pattern, "split"), &InvertPatternFn(invert_rows_pattern, "rows"), &InvertPatternFn(invert_dup_pattern, "dup"), - &InvertPatternFn(invert_stack_swizzle_pattern, "stack swizzle"), &InvertPatternFn(invert_select_pattern, "select"), &pat!(Sqrt, (Dup, Mul)), &pat!((Dup, Add), (2, Div)), @@ -1394,20 +1393,6 @@ fn under_dup_pattern<'a>( Ok((input, (befores, afters))) } -fn invert_stack_swizzle_pattern<'a>( - input: &'a [Instr], - _: &mut Compiler, -) -> InversionResult<(&'a [Instr], EcoVec)> { - let [Instr::StackSwizzle(swizzle, span), input @ ..] = input else { - return generic(); - }; - let instrs = eco_vec![Instr::StackSwizzle( - swizzle.inverse().ok_or(Generic)?, - *span - )]; - Ok((input, instrs)) -} - fn invert_select_pattern<'a>( input: &'a [Instr], _: &mut Compiler, diff --git a/src/assembly.rs b/src/assembly.rs index 8b98cfc39..eab566614 100644 --- a/src/assembly.rs +++ b/src/assembly.rs @@ -5,8 +5,9 @@ use ecow::{eco_vec, EcoString, EcoVec}; use serde::*; use crate::{ - is_ident_char, CodeSpan, FuncSlice, Function, InputSrc, Instr, IntoInputSrc, LocalName, Module, - Signature, Span, Uiua, UiuaResult, Value, + is_ident_char, CodeSpan, DynamicFunction, FuncSlice, Function, Ident, ImplPrimitive, InputSrc, + Instr, IntoInputSrc, LocalName, Module, Primitive, Signature, Span, TempStack, Uiua, + UiuaResult, Value, }; /// A compiled Uiua assembly @@ -713,3 +714,132 @@ impl Inputs { } } } + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +enum InstrRep { + #[serde(rename = "#")] + Comment(Ident), + CallGlobal(usize, bool, Signature), + BindGlobal(usize, usize), + BeginArray, + EndArray(bool, usize), + Call(usize), + CallRecursive(usize), + Recur(usize), + PushFunc(Function), + Switch(usize, Signature, usize, bool), + Format(EcoVec, usize), + MatchFormatPattern(EcoVec, usize), + Label(EcoString, usize, bool), + ValidateType(usize, EcoString, u8, usize), + Dynamic(DynamicFunction), + Unpack(usize, usize, bool), + TouchStack(usize, usize), + PushTemp(TempStack, usize, usize), + PopTemp(TempStack, usize, usize), + CopyToTemp(TempStack, usize, usize), + PushSig(Signature), + PopSig, + SetOutputComment(usize, usize), + #[serde(untagged)] + Push(Value), + #[serde(untagged)] + Prim(Primitive, usize), + #[serde(untagged)] + ImplPrim(ImplPrimitive, usize), +} + +impl From for InstrRep { + fn from(value: Instr) -> Self { + match value { + Instr::Comment(ident) => Self::Comment(ident), + Instr::Push(value) => Self::Push(value), + Instr::CallGlobal { index, call, sig } => Self::CallGlobal(index, call, sig), + Instr::BindGlobal { span, index } => Self::BindGlobal(span, index), + Instr::BeginArray => Self::BeginArray, + Instr::EndArray { boxed, span } => Self::EndArray(boxed, span), + Instr::Prim(prim, span) => Self::Prim(prim, span), + Instr::ImplPrim(prim, span) => Self::ImplPrim(prim, span), + Instr::Call(span) => Self::Call(span), + Instr::CallRecursive(span) => Self::CallRecursive(span), + Instr::Recur(span) => Self::Recur(span), + Instr::PushFunc(func) => Self::PushFunc(func), + Instr::Switch { + count, + sig, + span, + under_cond, + } => Self::Switch(count, sig, span, under_cond), + Instr::Format { parts, span } => Self::Format(parts, span), + Instr::MatchFormatPattern { parts, span } => Self::MatchFormatPattern(parts, span), + Instr::Label { + label, + span, + remove, + } => Self::Label(label, span, remove), + Instr::ValidateType { + index, + name, + type_num, + span, + } => Self::ValidateType(index, name, type_num, span), + Instr::Dynamic(func) => Self::Dynamic(func), + Instr::Unpack { count, span, unbox } => Self::Unpack(count, span, unbox), + Instr::TouchStack { count, span } => Self::TouchStack(count, span), + Instr::PushTemp { stack, count, span } => Self::PushTemp(stack, count, span), + Instr::PopTemp { stack, count, span } => Self::PopTemp(stack, count, span), + Instr::CopyToTemp { stack, count, span } => Self::CopyToTemp(stack, count, span), + Instr::PushSig(sig) => Self::PushSig(sig), + Instr::PopSig => Self::PopSig, + Instr::SetOutputComment { i, n } => Self::SetOutputComment(i, n), + } + } +} + +impl From for Instr { + fn from(value: InstrRep) -> Self { + match value { + InstrRep::Comment(ident) => Self::Comment(ident), + InstrRep::Push(value) => Self::Push(value), + InstrRep::CallGlobal(index, call, sig) => Self::CallGlobal { index, call, sig }, + InstrRep::BindGlobal(span, index) => Self::BindGlobal { span, index }, + InstrRep::BeginArray => Self::BeginArray, + InstrRep::EndArray(boxed, span) => Self::EndArray { boxed, span }, + InstrRep::Prim(prim, span) => Self::Prim(prim, span), + InstrRep::ImplPrim(prim, span) => Self::ImplPrim(prim, span), + InstrRep::Call(span) => Self::Call(span), + InstrRep::CallRecursive(span) => Self::CallRecursive(span), + InstrRep::Recur(span) => Self::Recur(span), + InstrRep::PushFunc(func) => Self::PushFunc(func), + InstrRep::Switch(count, sig, span, under_cond) => Self::Switch { + count, + sig, + span, + under_cond, + }, + InstrRep::Format(parts, span) => Self::Format { parts, span }, + InstrRep::MatchFormatPattern(parts, span) => Self::MatchFormatPattern { parts, span }, + InstrRep::Label(label, span, remove) => Self::Label { + label, + span, + remove, + }, + InstrRep::ValidateType(index, name, type_num, span) => Self::ValidateType { + index, + name, + type_num, + span, + }, + InstrRep::Dynamic(func) => Self::Dynamic(func), + InstrRep::Unpack(count, span, unbox) => Self::Unpack { count, span, unbox }, + InstrRep::TouchStack(count, span) => Self::TouchStack { count, span }, + InstrRep::PushTemp(stack, count, span) => Self::PushTemp { stack, count, span }, + InstrRep::PopTemp(stack, count, span) => Self::PopTemp { stack, count, span }, + InstrRep::CopyToTemp(stack, count, span) => Self::CopyToTemp { stack, count, span }, + InstrRep::PushSig(sig) => Self::PushSig(sig), + InstrRep::PopSig => Self::PopSig, + InstrRep::SetOutputComment(i, n) => Self::SetOutputComment { i, n }, + } + } +} diff --git a/src/ast.rs b/src/ast.rs index 1750a86b4..660d44dd3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -7,7 +7,7 @@ use crate::{ function::{FunctionId, Signature}, lex::{CodeSpan, Sp}, parse::ident_modifier_args, - ArraySwizzle, Ident, Primitive, SemanticComment, StackSwizzle, SUBSCRIPT_NUMS, + Ident, Primitive, SemanticComment, SUBSCRIPT_NUMS, }; /// A top-level item @@ -137,8 +137,6 @@ pub enum Word { Primitive(Primitive), Modified(Box), Placeholder(PlaceholderOp), - StackSwizzle(StackSwizzle), - ArraySwizzle(ArraySwizzle), Comment(String), Spaces, BreakLine, @@ -191,8 +189,6 @@ impl PartialEq for Word { } (Self::Placeholder(_), Self::Placeholder(_)) => false, (Self::Comment(a), Self::Comment(b)) => a == b, - (Self::StackSwizzle(a), Self::StackSwizzle(b)) => a == b, - (Self::ArraySwizzle(a), Self::ArraySwizzle(b)) => a == b, _ => discriminant(self) == discriminant(other), } } @@ -266,8 +262,6 @@ impl fmt::Debug for Word { Word::Spaces => write!(f, "' '"), Word::Comment(comment) => write!(f, "# {comment}"), Word::Placeholder(op) => write!(f, "{op}"), - Word::StackSwizzle(swizzle) => write!(f, "{swizzle}"), - Word::ArraySwizzle(swizzle) => write!(f, "{swizzle}"), Word::BreakLine => write!(f, "break_line"), Word::FlipLine => write!(f, "unbreak_line"), Word::SemanticComment(comment) => write!(f, "{comment}"), diff --git a/src/check.rs b/src/check.rs index f06533ed6..de9e7db2a 100644 --- a/src/check.rs +++ b/src/check.rs @@ -365,7 +365,6 @@ impl VirtualEnv { Instr::MatchFormatPattern { parts, .. } => { self.handle_args_outputs(1, parts.len().saturating_sub(1))? } - Instr::StackSwizzle(sw, _) => self.handle_sig(sw.signature())?, Instr::Dynamic(f) => self.handle_sig(f.signature)?, Instr::Unpack { count, .. } => self.handle_args_outputs(1, *count)?, Instr::TouchStack { count, .. } => self.handle_args_outputs(*count, *count)?, diff --git a/src/compile/mod.rs b/src/compile/mod.rs index 33fa41ae9..1795ac6c0 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -1425,8 +1425,6 @@ code: Word::Placeholder(_) => { // We could error here, but it's easier to handle it higher up } - Word::StackSwizzle(sw) => self.stack_swizzle(sw, word.span, call), - Word::ArraySwizzle(sw) => self.array_swizzle(sw, word.span, call)?, Word::SemanticComment(sc) => match sc { SemanticComment::Experimental => self.scope.experimental = true, SemanticComment::NoInline => { @@ -2254,121 +2252,6 @@ code: } Ok(()) } - fn stack_swizzle(&mut self, swiz: StackSwizzle, span: CodeSpan, call: bool) { - self.experimental_error(&span, || { - "Swizzles are experimental. To use them, add \ - `# Experimental!` to the top of the file." - }); - self.emit_diagnostic( - "Stack swizzles are deprecated and will be removed in the future.", - DiagnosticKind::Warning, - span.clone(), - ); - let sig = swiz.signature(); - let spandex = self.add_span(span.clone()); - let equivalent = match (swiz.indices.as_slice(), swiz.fix.as_slice()) { - ([0], [false]) => Some(Primitive::Identity), - ([0], [true]) => Some(Primitive::Fix), - ([1, 0], [false, false]) => Some(Primitive::Flip), - ([0, 0], [false, false]) => Some(Primitive::Dup), - ([1, 0, 1], [false, false, false]) => Some(Primitive::Over), - _ => None, - }; - let mut instr = if let Some(prim) = equivalent { - self.emit_diagnostic( - format!( - "This swizzle is equivalent to {}. \ - Use that instead.", - prim.format() - ), - DiagnosticKind::Style, - span.clone(), - ); - Instr::Prim(prim, spandex) - } else { - Instr::StackSwizzle(swiz, spandex) - }; - if !call { - instr = Instr::PushFunc(self.make_function( - FunctionId::Anonymous(span), - sig, - eco_vec![instr].into(), - )); - } - self.push_instr(instr); - } - fn array_swizzle(&mut self, swiz: ArraySwizzle, span: CodeSpan, call: bool) -> UiuaResult { - if !self.scope.experimental { - self.add_error( - span.clone(), - "Swizzles are experimental. To use them, add \ - `# Experimental!` to the top of the file.", - ); - } - self.emit_diagnostic( - "Array swizzles are deprecated and will be removed in the future.", - DiagnosticKind::Warning, - span.clone(), - ); - let sig = swiz.signature(); - let mut instrs = EcoVec::new(); - let normal_ordered = (swiz.indices.iter().enumerate()).all(|(i, &idx)| i == idx as usize); - let spandex = self.add_span(span.clone()); - if normal_ordered { - let val = Value::from(swiz.indices.len()); - instrs.extend([Instr::push(val), Instr::Prim(Primitive::Take, spandex)]); - } else { - let arr = Array::from_iter(swiz.indices.iter().map(|&i| i as f64)); - instrs.extend([Instr::push(arr), Instr::Prim(Primitive::Select, spandex)]); - } - if swiz.unbox.iter().all(|&b| b) { - instrs.push(Instr::Unpack { - count: sig.outputs, - span: spandex, - unbox: true, - }) - } else { - instrs.push(Instr::Unpack { - count: sig.outputs, - span: spandex, - unbox: false, - }); - if !swiz.unbox.iter().all(|&b| !b) { - let mut boxed_indices: Vec = (swiz.unbox.iter().enumerate()) - .filter_map(|(i, b)| b.then_some(i)) - .collect(); - let mut curr_index = boxed_indices.pop().unwrap(); - if curr_index != 0 { - instrs.push(Instr::PushTemp { - stack: TempStack::Inline, - count: curr_index, - span: spandex, - }); - } - instrs.push(Instr::ImplPrim(ImplPrimitive::UnBox, spandex)); - for i in boxed_indices.into_iter().rev() { - let diff = curr_index - i; - instrs.extend([ - Instr::pop_inline(diff, spandex), - Instr::ImplPrim(ImplPrimitive::UnBox, spandex), - ]); - curr_index = i; - } - if curr_index != 0 { - instrs.push(Instr::pop_inline(curr_index, spandex)); - } - } - } - if !call { - instrs = eco_vec![Instr::PushFunc(self.make_function( - FunctionId::Anonymous(span), - sig, - instrs.into(), - ))]; - } - self.push_all_instrs(instrs); - Ok(()) - } pub(crate) fn inlinable(&self, instrs: &[Instr], flags: FunctionFlags) -> bool { use ImplPrimitive::*; use Primitive::*; diff --git a/src/format.rs b/src/format.rs index 66a6c48e1..40f7a224a 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1097,8 +1097,6 @@ impl<'a> Formatter<'a> { self.format_words(&m.operands, true, depth); } Word::Placeholder(op) => self.push(&word.span, &op.to_string()), - Word::StackSwizzle(s) => self.push(&word.span, &s.to_string()), - Word::ArraySwizzle(s) => self.push(&word.span, &s.to_string()), Word::Subscript(sub) => match &sub.word.value { Word::Modified(m) => { self.format_modifier(&m.modifier); @@ -1406,8 +1404,6 @@ pub(crate) fn word_is_multiline(word: &Word) -> bool { Word::Modified(m) => m.operands.iter().any(|word| word_is_multiline(&word.value)), Word::Placeholder(_) => false, Word::Subscript(sub) => word_is_multiline(&sub.word.value), - Word::StackSwizzle(_) => false, - Word::ArraySwizzle(_) => false, Word::Comment(_) => true, Word::Spaces => false, Word::BreakLine | Word::FlipLine => false, diff --git a/src/function.rs b/src/function.rs index dee43ae8e..134257b3f 100644 --- a/src/function.rs +++ b/src/function.rs @@ -166,9 +166,8 @@ instr!( span: usize, } ), - (15, StackSwizzle(swiz(StackSwizzle), span(usize))), ( - 16, + 15, Label { label: EcoString, remove: bool, @@ -176,7 +175,7 @@ instr!( } ), ( - 17, + 16, ValidateType { index: usize, type_num: u8, @@ -184,9 +183,9 @@ instr!( span: usize, } ), - (18, Dynamic(func(DynamicFunction))), + (17, Dynamic(func(DynamicFunction))), ( - 19, + 18, Unpack { count: usize, unbox: bool, @@ -194,14 +193,14 @@ instr!( } ), ( - 20, + 19, TouchStack { count: usize, span: usize, } ), ( - 21, + 20, PushTemp { stack: TempStack, count: usize, @@ -209,7 +208,7 @@ instr!( } ), ( - 22, + 21, PopTemp { stack: TempStack, count: usize, @@ -217,16 +216,16 @@ instr!( } ), ( - 23, + 22, CopyToTemp { stack: TempStack, count: usize, span: usize, } ), - (24, SetOutputComment { i: usize, n: usize }), - (25, PushSig(sig(Signature))), - (26, PopSig), + (23, SetOutputComment { i: usize, n: usize }), + (24, PushSig(sig(Signature))), + (25, PopSig), ); type FmtParts = EcoVec; @@ -356,92 +355,6 @@ impl<'a> fmt::Display for FmtInstrs<'a> { } } -/// A swizzle for the stack -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)] -pub struct StackSwizzle { - /// The indices of the stack elements - pub indices: EcoVec, - /// The fix mask - pub fix: EcoVec, -} - -impl fmt::Display for StackSwizzle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "λ")?; - for (&i, &fix) in self.indices.iter().zip(&self.fix) { - let mut c = b'a' + i; - if fix { - c = c.to_ascii_uppercase(); - } - write!(f, "{}", c as char)?; - } - Ok(()) - } -} - -impl StackSwizzle { - pub(crate) fn args(&self) -> usize { - (self.indices.iter().max().copied()).map_or(0, |max| max as usize + 1) - } - /// Get the signature of the swizzle - pub fn signature(&self) -> Signature { - Signature::new(self.args(), self.indices.len()) - } - /// Get the inverse of the swizzle - pub fn inverse(&self) -> Option { - if self.args() != self.indices.len() { - return None; - } - let set: HashSet<_> = self.indices.iter().copied().collect(); - if set.len() != self.indices.len() { - return None; - } - let mut indices = eco_vec![0; self.indices.len()]; - let slice = indices.make_mut(); - for (i, &j) in self.indices.iter().enumerate() { - slice[j as usize] = i as u8; - } - Some(Self { - indices, - fix: self.fix.clone(), - }) - } -} - -/// A swizzle for an array -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)] -pub struct ArraySwizzle { - /// The indices of the array elements - pub indices: EcoVec, - /// The (un)box mask - pub unbox: EcoVec, -} - -impl fmt::Display for ArraySwizzle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "⋊")?; - for (&i, &b) in self.indices.iter().zip(&self.unbox) { - let mut c = if i < 0 { - b'z' + 1 - i.unsigned_abs() - } else { - b'a' + i.unsigned_abs() - }; - if b { - c = c.to_ascii_uppercase(); - } - write!(f, "{}", c as char)?; - } - Ok(()) - } -} - -impl ArraySwizzle { - /// Get the signature of the swizzle - pub fn signature(&self) -> Signature { - Signature::new(1, self.indices.len()) - } -} - /// Levels of purity for an operation #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Purity { @@ -621,7 +534,6 @@ impl fmt::Display for Instr { Instr::CopyToTemp { stack, count, .. } => { write!(f, "") } - Instr::StackSwizzle(swizzle, _) => write!(f, "{swizzle}"), Instr::SetOutputComment { i, n, .. } => write!(f, ""), Instr::PushSig(sig) => write!(f, "{sig}"), Instr::PopSig => write!(f, "-|"), diff --git a/src/lex.rs b/src/lex.rs index f7730fe60..39ec29bb1 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -16,9 +16,7 @@ use serde::*; use serde_tuple::*; use unicode_segmentation::UnicodeSegmentation; -use crate::{ - ast::PlaceholderOp, ArraySwizzle, Ident, Inputs, Primitive, StackSwizzle, WILDCARD_CHAR, -}; +use crate::{ast::PlaceholderOp, Ident, Inputs, Primitive, WILDCARD_CHAR}; /// Subscript digit characters pub const SUBSCRIPT_NUMS: [char; 10] = ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉']; @@ -509,8 +507,6 @@ pub enum Token { MultilineFormatStr(Vec), Simple(AsciiToken), Glyph(Primitive), - StackSwizzle(StackSwizzle), - ArraySwizzle(ArraySwizzle), Subscript(usize), LeftArrow, LeftStrokeArrow, @@ -582,18 +578,6 @@ impl Token { _ => None, } } - pub(crate) fn as_stack_swizzle(&self) -> Option<&StackSwizzle> { - match self { - Token::StackSwizzle(s) => Some(s), - _ => None, - } - } - pub(crate) fn as_array_swizzle(&self) -> Option<&ArraySwizzle> { - match self { - Token::ArraySwizzle(s) => Some(s), - _ => None, - } - } pub(crate) fn as_subscript(&self) -> Option { match self { Token::Subscript(n) => Some(*n), @@ -634,8 +618,6 @@ impl fmt::Display for Token { } Ok(()) } - Token::StackSwizzle(s) => s.fmt(f), - Token::ArraySwizzle(s) => s.fmt(f), Token::Simple(t) => t.fmt(f), Token::Glyph(p) => p.fmt(f), Token::LeftArrow => write!(f, "←"), @@ -680,9 +662,8 @@ pub enum AsciiToken { GreaterEqual, Backtick, Tilde, - TripleMinus, Quote, - Quote2, + TripleMinus, Placeholder(PlaceholderOp), } @@ -711,7 +692,6 @@ impl fmt::Display for AsciiToken { AsciiToken::Tilde => write!(f, "~"), AsciiToken::TripleMinus => write!(f, "---"), AsciiToken::Quote => write!(f, "'"), - AsciiToken::Quote2 => write!(f, "''"), AsciiToken::Placeholder(op) => write!(f, "{op}"), } } @@ -909,29 +889,8 @@ impl<'a> Lexer<'a> { ";" if self.next_char_exact(";") => self.end(DoubleSemicolon, start), ";" => self.end(Semicolon, start), "-" if self.next_chars_exact(["-", "-"]) => self.end(TripleMinus, start), - "'" if self.next_char_exact("'") => { - if let Some(swiz) = self.array_swizzle() { - self.end(ArraySwizzle(swiz), start) - } else { - self.end(Quote2, start) - } - } - "'" => { - if let Some(swiz) = self.stack_swizzle() { - self.end(StackSwizzle(swiz), start) - } else { - self.end(Quote, start) - } - } - "λ" => { - let swiz = self.stack_swizzle().unwrap_or_default(); - self.end(StackSwizzle(swiz), start) - } - "⋊" => { - let swiz = self.array_swizzle().unwrap_or_default(); - self.end(ArraySwizzle(swiz), start) - } "~" => self.end(Tilde, start), + "'" => self.end(Quote, start), "`" => { if self.number("-") { self.end(Number, start) @@ -1454,44 +1413,6 @@ impl<'a> Lexer<'a> { c.into() })) } - fn stack_swizzle(&mut self) -> Option { - let mut indices = EcoVec::new(); - let mut fix = EcoVec::new(); - while let Some(c) = self.next_char_if(|c| c.chars().all(|c| c.is_ascii_alphabetic())) { - for c in c.chars() { - let is_upper = c.is_ascii_uppercase(); - let c = c.to_ascii_lowercase(); - indices.push(c as u8 - b'a'); - fix.push(is_upper); - } - } - if indices.is_empty() { - None - } else { - Some(StackSwizzle { indices, fix }) - } - } - fn array_swizzle(&mut self) -> Option { - let mut indices = EcoVec::new(); - let mut unbox = EcoVec::new(); - while let Some(c) = self.next_char_if(|c| c.chars().all(|c| c.is_ascii_alphabetic())) { - for c in c.chars() { - let is_upper = c.is_ascii_uppercase(); - let c = c.to_ascii_lowercase(); - if c <= 'm' { - indices.push(c as i8 - b'a' as i8); - } else { - indices.push(c as i8 - b'z' as i8 - 1); - } - unbox.push(is_upper); - } - } - if indices.is_empty() { - None - } else { - Some(ArraySwizzle { indices, unbox }) - } - } fn parse_string_contents( &mut self, start: Loc, diff --git a/src/lsp.rs b/src/lsp.rs index b279671b5..87c3c08d8 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -14,9 +14,8 @@ use crate::{ ident_modifier_args, instrs_are_pure, is_custom_glyph, lex::{CodeSpan, Sp}, parse::parse, - ArraySwizzle, Assembly, BindingInfo, BindingKind, Compiler, DocComment, Ident, InputSrc, - Inputs, PreEvalMode, Primitive, Purity, SafeSys, Shape, Signature, StackSwizzle, SysBackend, - UiuaError, Value, CONSTANTS, + Assembly, BindingInfo, BindingKind, Compiler, DocComment, Ident, InputSrc, Inputs, PreEvalMode, + Primitive, Purity, SafeSys, Shape, Signature, SysBackend, UiuaError, Value, CONSTANTS, }; /// Kinds of span in Uiua code, meant to be used in the language server or other IDE tools @@ -41,8 +40,6 @@ pub enum SpanKind { Placeholder(PlaceholderOp), Delimiter, FuncDelim(Signature), - StackSwizzle(StackSwizzle), - ArraySwizzle(ArraySwizzle), Subscript(Option, usize), } @@ -568,12 +565,6 @@ impl Spanner { spans.push(sub.n.clone().map(|n| SpanKind::Subscript(None, n))); } }, - Word::StackSwizzle(sw) => { - spans.push(word.span.clone().sp(SpanKind::StackSwizzle(sw.clone()))) - } - Word::ArraySwizzle(sw) => { - spans.push(word.span.clone().sp(SpanKind::ArraySwizzle(sw.clone()))) - } } } spans.retain(|sp| !sp.span.as_str(self.inputs(), str::is_empty)); @@ -870,26 +861,6 @@ mod server { inline_function_sig = Some(span.clone().sp(inline.sig)); } } - // Hovering a stack swizzle - let mut stack_swizzle: Option> = None; - for span_kind in &doc.spans { - if let SpanKind::StackSwizzle(s) = &span_kind.value { - if span_kind.span.contains_line_col(line, col) && span_kind.span.src == path { - stack_swizzle = Some(span_kind.span.clone().sp(s)); - break; - } - } - } - // Hovering an array swizzle - let mut array_swizzle: Option> = None; - for span_kind in &doc.spans { - if let SpanKind::ArraySwizzle(s) = &span_kind.value { - if span_kind.span.contains_line_col(line, col) && span_kind.span.src == path { - array_swizzle = Some(span_kind.span.clone().sp(s)); - break; - } - } - } // Hovering an array let mut array_shape: Option> = None; for (span, arr_meta) in &doc.code_meta.array_shapes { @@ -1007,22 +978,6 @@ mod server { }), range: Some(uiua_span_to_lsp(&sig.span)), } - } else if let Some(sw) = stack_swizzle { - Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: format!("stack swizzle `{}`", sw.value.signature()), - }), - range: Some(uiua_span_to_lsp(&sw.span)), - } - } else if let Some(sw) = array_swizzle { - Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: format!("array swizzle `{}`", sw.value.signature()), - }), - range: Some(uiua_span_to_lsp(&sw.span)), - } } else if let Some(shape) = array_shape { Hover { contents: HoverContents::Markup(MarkupContent { @@ -1453,14 +1408,6 @@ mod server { }, BindingDocsKind::Module { .. } => MODULE_STT, }, - SpanKind::StackSwizzle(sw) => match sw.signature().args { - 1 => MONADIC_FUNCTION_STT, - 2 => DYADIC_FUNCTION_STT, - 3 => TRIADIC_FUNCTION_STT, - 4 => TETRADIC_FUNCTION_STT, - _ => continue, - }, - SpanKind::ArraySwizzle(_) => MONADIC_FUNCTION_STT, SpanKind::Subscript(Some(prim), n) => { let Some(stt) = for_prim(*prim, prim.subscript_sig(*n)) else { continue; diff --git a/src/main.rs b/src/main.rs index cacc8d777..677cd1641 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1086,9 +1086,7 @@ fn color_code(code: &str, compiler: &Compiler) -> String { | SpanKind::Whitespace | SpanKind::Placeholder(_) | SpanKind::Delimiter - | SpanKind::FuncDelim(_) - | SpanKind::StackSwizzle(_) - | SpanKind::ArraySwizzle(_) => None, + | SpanKind::FuncDelim(_) => None, }; span.span.as_str(&inputs, |s| { colored.push_str(&if let Some(color) = color { diff --git a/src/parse.rs b/src/parse.rs index 1c6a3f4fe..839179fe4 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -994,10 +994,6 @@ impl<'i> Parser<'i> { span.sp(Word::BreakLine) } else if let Some(sc) = self.next_token_map(Token::as_semantic_comment) { sc.map(Word::SemanticComment) - } else if let Some(pack) = self.next_token_map(Token::as_stack_swizzle) { - pack.cloned().map(Word::StackSwizzle) - } else if let Some(pack) = self.next_token_map(Token::as_array_swizzle) { - pack.cloned().map(Word::ArraySwizzle) } else { return None; }; diff --git a/src/primitive/defs.rs b/src/primitive/defs.rs index cbd7ab700..7a09ef83f 100644 --- a/src/primitive/defs.rs +++ b/src/primitive/defs.rs @@ -504,6 +504,11 @@ primitive!( /// [over] is often used in examples of functions with two inputs to show both inputs and the output. /// ex: [+,, +3 4 5] (2(3), Over, Stack, ("over", ',')), + /// Duplicate the top of the stack around the second element + /// + /// ex: # Experimental! + /// : [' 1 2 3 4 5] + (2(3), Around, Stack, ("around", AsciiToken::Quote, '’')), /// Swap the top two values on the stack /// /// ex: [: 1 2 3 4 5] diff --git a/src/primitive/mod.rs b/src/primitive/mod.rs index d2f77dfdb..a2edb71a4 100644 --- a/src/primitive/mod.rs +++ b/src/primitive/mod.rs @@ -468,7 +468,7 @@ impl Primitive { use SysOp::*; matches!( self, - (Anti | Off | Backward | Above) + (Anti | Off | Backward | Above | Around) | (Tuples | Choose | Permute) | Struct | (Last | Sort | Chunks | Base | Coordinate | Fft | Case | Layout) @@ -792,6 +792,13 @@ impl Primitive { env.push(a); env.push(b); } + Primitive::Around => { + let a = env.pop(1)?; + let b = env.pop(2)?; + env.push(a.clone()); + env.push(b); + env.push(a); + } Primitive::Pop => { env.pop(1)?; } diff --git a/src/run.rs b/src/run.rs index 9a5900b0f..05ef42efe 100644 --- a/src/run.rs +++ b/src/run.rs @@ -547,10 +547,6 @@ code: let parts = parts.clone(); self.with_span(*span, |env| invert::match_format_pattern(parts, env)) } - Instr::StackSwizzle(swizzle, span) => { - let swizzle = swizzle.clone(); - self.with_span(*span, |env| env.stack_swizzle(&swizzle)) - } &Instr::Label { ref label, span, @@ -1647,21 +1643,6 @@ code: .channel }) } - fn stack_swizzle(&mut self, swizzle: &StackSwizzle) -> UiuaResult { - let args = swizzle.args(); - self.touch_array_stack(args)?; - let end = self.rt.stack.len(); - let start = end - args; - for (&i, &fix) in swizzle.indices.iter().zip(&swizzle.fix).rev() { - let mut val = self.rt.stack[end - 1 - i as usize].clone(); - if fix { - val.fix(); - } - self.rt.stack.push(val); - } - self.rt.stack.drain(start..start + args); - Ok(()) - } } /// A trait for types that can be used as argument specifiers for [`Uiua::pop`]