Skip to content

Commit 93958ba

Browse files
hyperpolymathclaude
andcommitted
feat: add #identifier syntax for symbolic literals
Symbolic type existed in AST but had no parse syntax. Added #prefix convention (e.g. #x, #pi, #alpha) matching existing 0x/0b patterns. Grammar, parser, formatter, and pretty-printer all updated. 84 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 060016b commit 93958ba

5 files changed

Lines changed: 39 additions & 3 deletions

File tree

crates/jtv-core/src/formatter.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,8 @@ impl Formatter {
487487
self.output.push_str(s);
488488
}
489489
Number::Symbolic(s) => {
490-
self.output.push_str(s);
490+
// Symbolic literals use '#' prefix (e.g., #x, #pi)
491+
self.output.push_str(&format!("#{}", s));
491492
}
492493
}
493494
}

crates/jtv-core/src/grammar.pest

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,13 @@ number = {
118118
| rational
119119
| hex
120120
| binary
121+
| symbolic
121122
| float
122123
| integer
123124
}
124125

126+
symbolic = @{ "#" ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
127+
125128
integer = @{ "-"? ~ ASCII_DIGIT+ }
126129
float = @{ "-"? ~ ASCII_DIGIT+ ~ "." ~ ASCII_DIGIT+ ~ (("e" | "E") ~ ("+" | "-")? ~ ASCII_DIGIT+)? }
127130
rational = @{ integer ~ "/" ~ ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }

crates/jtv-core/src/parser.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,11 @@ fn parse_number(pair: pest::iterators::Pair<Rule>) -> Result<Number> {
422422
}
423423
Rule::hex => Ok(Number::Hex(pair.as_str().to_string())),
424424
Rule::binary => Ok(Number::Binary(pair.as_str().to_string())),
425+
Rule::symbolic => {
426+
// Strip the '#' prefix to get the symbolic name
427+
let name = pair.as_str().trim_start_matches('#').to_string();
428+
Ok(Number::Symbolic(name))
429+
}
425430
_ => Err(JtvError::ParseError(format!(
426431
"Unknown number type: {:?}",
427432
pair.as_rule()
@@ -602,4 +607,30 @@ mod tests {
602607
let result = parse_program(code);
603608
assert!(result.is_ok());
604609
}
610+
611+
#[test]
612+
fn test_parse_symbolic_literal() {
613+
let code = "x = #pi + #e";
614+
let result = parse_program(code);
615+
assert!(result.is_ok(), "Failed to parse symbolic literals: {:?}", result.err());
616+
let program = result.unwrap();
617+
// Verify the AST contains symbolic numbers
618+
if let TopLevel::Control(ControlStmt::Assignment(ref assign)) = program.statements[0] {
619+
if let Expr::Data(DataExpr::Add(ref left, ref right)) = assign.value {
620+
assert_eq!(**left, DataExpr::Number(Number::Symbolic("pi".to_string())));
621+
assert_eq!(**right, DataExpr::Number(Number::Symbolic("e".to_string())));
622+
} else {
623+
panic!("Expected Add expression");
624+
}
625+
} else {
626+
panic!("Expected assignment");
627+
}
628+
}
629+
630+
#[test]
631+
fn test_parse_symbolic_with_underscore() {
632+
let code = "y = #my_var";
633+
let result = parse_program(code);
634+
assert!(result.is_ok(), "Failed to parse symbolic with underscore: {:?}", result.err());
635+
}
605636
}

crates/jtv-core/src/pretty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ impl PrettyPrinter {
417417
}
418418
Number::Hex(s) => s.clone(),
419419
Number::Binary(s) => s.clone(),
420-
Number::Symbolic(s) => s.clone(),
420+
Number::Symbolic(s) => format!("#{}", s),
421421
}
422422
}
423423

shared/grammar/jtv.ebnf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ function_type = "Fn" "(" [ type_annotation { "," type_annotation } ] ")" "->" ty
109109
return_type = type_annotation ;
110110
111111
(* ===== LITERALS ===== *)
112-
number = integer | float | rational | complex | hex | binary ;
112+
number = integer | float | rational | complex | hex | binary | symbolic ;
113113
114114
integer = [ "-" ] digit { digit } ;
115115
float = [ "-" ] digit { digit } "." digit { digit } [ exponent ] ;
@@ -118,6 +118,7 @@ rational = integer "/" integer ;
118118
complex = number "+" number "i" | number "i" ;
119119
hex = "0x" hex_digit { hex_digit } ;
120120
binary = "0b" ("0" | "1") { "0" | "1" } ;
121+
symbolic = "#" letter { letter | digit | "_" } ; (* Symbolic math names, e.g. #x, #pi, #alpha *)
121122
122123
list_literal = "[" [ data_expr { "," data_expr } ] "]" ;
123124
tuple_literal = "(" data_expr "," data_expr { "," data_expr } ")" ;

0 commit comments

Comments
 (0)