Skip to content

Commit e3e26fa

Browse files
authored
Allow prime in binding names (#813)
* Allow prime in binding names * Add note about primes in bindings to tutorial
1 parent bbfd65a commit e3e26fa

File tree

5 files changed

+52
-5
lines changed

5 files changed

+52
-5
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ This version is not yet released. If you are reading this on the website, then t
2222
- Modules containing data definition variants now contains a `Variants` binding that lists their names
2323
- Add array pack syntactic sugar. This lets you write code like `[⊃(+|×)]` as `⊃[+|×]`.
2424
- Subscripts can now be typed with `,` instead of `__`s
25+
- Binding names can now end with prime characters ``, ``, and ``
26+
- These will format from `'` at the end of a binding name
2527
- Add numeric subscripts for [`keep ▽`](https://uiua.org/docs/keep) to keep along a number of dimensions
2628
- Add [`under ⍜`](https://uiua.org/docs/under) capability to [`fork ⊃`](https://uiua.org/docs/fork)s of monadic functions
2729
- This allows using [`un °`](https://uiua.org/docs/un)[`by ⊸`](https://uiua.org/docs/by)[`fork ⊃`](https://uiua.org/docs/fork) to set multiple properties at once

parser/src/lex.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,9 +1323,15 @@ impl<'a> Lexer<'a> {
13231323
self.end(Subscr(sub), start)
13241324
}
13251325
// Identifiers and unformatted glyphs
1326-
c if is_custom_glyph(c) || c.chars().all(is_ident_char) || "&!‼".contains(c) => {
1326+
c if is_custom_glyph(c)
1327+
|| c.chars().all(is_ident_char)
1328+
|| "&!‼'′″‴".contains(c) =>
1329+
{
13271330
// Get ident start
13281331
let mut ident = self.ident(start, c).to_string();
1332+
while let Some(ch) = self.next_char_if(|ch| "'′″‴".contains(ch)) {
1333+
ident.push_str(ch);
1334+
}
13291335
let mut exclam_count = match c {
13301336
"!" => 1,
13311337
"‼" => 2,
@@ -1766,7 +1772,7 @@ fn is_formatted_subscript(c: &str) -> bool {
17661772
}
17671773

17681774
pub(crate) fn canonicalize_ident(ident: &str) -> Ident {
1769-
canonicalize_subscripts(canonicalize_exclams(ident))
1775+
canonicalize_subscripts(canonicalize_primes(canonicalize_exclams(ident).as_str()))
17701776
}
17711777

17721778
/// Rewrite the identifier with the same number of exclamation points
@@ -1790,6 +1796,38 @@ fn place_exclams(ident: &str, count: usize) -> Ident {
17901796
new
17911797
}
17921798

1799+
/// Rewrite the identifier with the same number of primes
1800+
/// using triple, double, and single prime characters as needed
1801+
fn canonicalize_primes(ident: &str) -> Ident {
1802+
let mut count = 0;
1803+
for ch in ident.chars() {
1804+
count += match ch {
1805+
'\'' => 1,
1806+
'′' => 1,
1807+
'″' => 2,
1808+
'‴' => 3,
1809+
_ => 0,
1810+
};
1811+
}
1812+
place_primes(ident, count)
1813+
}
1814+
1815+
fn place_primes(ident: &str, count: usize) -> Ident {
1816+
let mut new: Ident = ident.trim_end_matches(['\'', '′', '″', '‴']).into();
1817+
let num_triple = count / 3;
1818+
let trailing_num = count % 3;
1819+
for _ in 0..num_triple {
1820+
new.push('‴');
1821+
}
1822+
match trailing_num {
1823+
0 => {}
1824+
1 => new.push('′'),
1825+
2 => new.push('″'),
1826+
_ => unreachable!(),
1827+
}
1828+
new
1829+
}
1830+
17931831
/// Rewrite the identifier with numerals preceded by `,` replaced with subscript characters
17941832
fn canonicalize_subscripts(ident: Ident) -> Ident {
17951833
if !ident.contains(',') {

parser/src/parse.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ impl Parser<'_> {
558558
chars.extend(Primitive::non_deprecated().filter_map(|p| p.glyph()));
559559
chars.extend(' '..='~');
560560
chars.extend(SUBSCRIPT_DIGITS);
561-
chars.extend("←↚‼₋⌞⌟↓".chars());
561+
chars.extend("←↚‼′″‴₋⌞⌟↓".chars());
562562
chars.sort_unstable();
563563
chars
564564
};
@@ -738,7 +738,12 @@ impl Parser<'_> {
738738
self.errors
739739
.push(name.span.clone().sp(ParseError::AmpersandBindingName));
740740
}
741-
if name.value.trim_end_matches(['!', '‼']).chars().count() >= 2
741+
if name
742+
.value
743+
.trim_end_matches(['!', '‼', '\'', '′', '″', '‴'])
744+
.chars()
745+
.count()
746+
>= 2
742747
&& name.value.chars().next().unwrap().is_ascii_lowercase()
743748
{
744749
let captialized: String = name

site/src/tutorial.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,8 @@ fn TutorialBindings() -> impl IntoView {
657657
<p>"Bindings "<em>"can"</em>" contain subscript numbers. These will format from "<code>","</code>" followed by some digits. Try formatting the example below!"</p>
658658
<Editor example="X,1 = 5\nSha,256 = \"TODO\""/>
659659
<p>"Subscripts in binding names are only allowed at the end."</p>
660+
<p>"Bindings are allowed to end with "<code>"′"</code>" characters to help indicate related bindings. These format from "<code>"'"</code>". Try formatting the following example."</p>
661+
<Editor example="X = [1_2 3_4]\nX' = [2_3 4_5]\nX'' = [2_4 6_8]"/>
660662
<p><strong>"Bindings are case-sensitive."</strong></p>
661663
<p>"The parser can sometimes mistake all-lowercase binding names for unformatted built-in functions."</p>
662664
<p>"Here, the parser thinks that "<code>"part"</code>" is "<Prim prim=Partition/>"."</p>

src/run_prim.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2479,7 +2479,7 @@ mod tests {
24792479
"repository": {{
24802480
"idents": {{
24812481
"name": "variable.parameter.uiua",
2482-
"match": "\\b[a-zA-Z]+(₋?[₀₁₂₃₄₅₆₇₈₉]|,`?\\d+)*[!‼]*\\b"
2482+
"match": "\\b[a-zA-Z]+['′″‴]*(₋?[₀₁₂₃₄₅₆₇₈₉]|,`?\\d+)*[!‼]*\\b"
24832483
}},
24842484
"comments": {{
24852485
"name": "comment.line.uiua",

0 commit comments

Comments
 (0)