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`]