diff --git a/cogs/syntax-tests.scm b/cogs/syntax-tests.scm index dda44482f..58c253279 100644 --- a/cogs/syntax-tests.scm +++ b/cogs/syntax-tests.scm @@ -146,14 +146,6 @@ (check-equal? "catch-all lists, 1 arg" (catchall-list (a b)) '(a)) (check-equal? "catch-all lists, 2 args" (catchall-list (a b) (c d)) '(a c)) -(define-syntax improper-rest - (syntax-rules () - [(_ (a . b)) 'b])) - -(skip-compile (check-equal? "improper-rest, 0 args" (improper-rest 1) '())) -(skip-compile (check-equal? "improper-rest, 1 arg" (improper-rest 1 2) '(2))) -(skip-compile (check-equal? "improper-rest, 2 args" (improper-rest 1 a (b)) '(a (b)))) - (define bound-x 3) (define-syntax lexical-capture @@ -162,6 +154,14 @@ (let ([bound-x 'inner]) (check-equal? "hygiene, lexical capture" (lexical-capture) 3)) +(define-syntax improper-rest + (syntax-rules () + [(_ (a . b)) 'b])) + + (check-equal? "improper-rest, 0 args" (improper-rest (1)) '()) + (check-equal? "improper-rest, 1 arg" (improper-rest (1 2)) '(2)) + (check-equal? "improper-rest, 2 args" (improper-rest (1 a (b))) '(a (b))) + (check-equal? "improper lists in syntax" ;; equivalent to (let [(x 1)] x) (let . ([(x . (1 . ())) @@ -211,6 +211,12 @@ '(y . z)) (check-equal? "improper list pattern, tail arguments, constant in cdr" (improper-tail x . "y") "y") +(define-syntax improper-tail-const + (syntax-rules () + [(_ a . #t) (quote a)])) + +(check-equal? "improper list pattern, constant tail" (improper-tail-const x . #t) 'x) + (define-syntax improper-tail-nested (syntax-rules () [(_ (a . b)) (quote b)])) @@ -270,6 +276,34 @@ (check-equal? "macro expansion correctly works within another syntax rules" (t 10) 11) + +(define-syntax with-u8 + (syntax-rules () + [(_ #u8(1) a) a])) + +(check-equal? "bytevector patterns" (with-u8 #u8(1) #(a b)) #(a b)) + +(check-syntax-error? "bytevector patterns, unmatched constant" + '((define-syntax with-u8 + (syntax-rules () + [(_ #u8(1) a) a])) + (with-u8 #u8(3) 1)) + "macro expansion unable to match case") + +(define-syntax with-vec + (syntax-rules () + [(_ #(a)) 'a])) + +(check-equal? "vector pattern" (with-vec #((x y))) '(x y)) + +(define-syntax into-vec + (syntax-rules () + [(_ a b) #(b)])) + +(check-equal? "vector pattern replacement" (into-vec x y) #(y)) + +(check-equal? "vector quasiquoting" `#(,(list 'a)) #((a))) + ;; -------------- Report ------------------ (define stats (get-test-stats)) diff --git a/crates/steel-core/src/compiler/passes/analysis.rs b/crates/steel-core/src/compiler/passes/analysis.rs index 0f8c34574..af5b5996f 100644 --- a/crates/steel-core/src/compiler/passes/analysis.rs +++ b/crates/steel-core/src/compiler/passes/analysis.rs @@ -2004,6 +2004,8 @@ impl<'a> VisitorMutUnitRef<'a> for AnalysisPass<'a> { // println!("Free identifier: {}", a); } } + + fn visit_vector(&mut self, _: &'a steel_parser::ast::Vector) {} } impl<'a> VisitorMutUnitRef<'a> for Analysis { diff --git a/crates/steel-core/src/compiler/passes/mod.rs b/crates/steel-core/src/compiler/passes/mod.rs index eeb08ef25..dff670bde 100644 --- a/crates/steel-core/src/compiler/passes/mod.rs +++ b/crates/steel-core/src/compiler/passes/mod.rs @@ -131,11 +131,7 @@ pub trait Folder { } #[inline] - fn visit_vector(&mut self, mut v: Vector) -> ExprKind { - let args: Vec<_> = v.args.into_iter().map(|exp| self.visit(exp)).collect(); - - v.args = args; - + fn visit_vector(&mut self, v: Vector) -> ExprKind { ExprKind::Vector(v) } } @@ -225,12 +221,7 @@ pub trait VisitorMutUnit { #[inline] fn visit_require(&mut self, _s: &Require) {} - #[inline] - fn visit_vector(&mut self, v: &Vector) { - for arg in &v.args { - self.visit(arg); - } - } + fn visit_vector(&mut self, _v: &Vector) {} } pub trait VisitorMutControlFlow { @@ -545,9 +536,7 @@ pub trait VisitorMutRefUnit { #[inline] fn visit_require(&mut self, _s: &mut Require) {} - fn visit_vector(&mut self, v: &mut Vector) { - for arg in &mut v.args { - self.visit(arg); - } - } + // vectors are constants, most analysis passes should ignore them + #[inline] + fn visit_vector(&mut self, v: &mut Vector) {} } diff --git a/crates/steel-core/src/parser/expand_visitor.rs b/crates/steel-core/src/parser/expand_visitor.rs index e4d9ec227..e686e20fa 100644 --- a/crates/steel-core/src/parser/expand_visitor.rs +++ b/crates/steel-core/src/parser/expand_visitor.rs @@ -365,10 +365,6 @@ impl<'a> VisitorMutRef for Expander<'a> { } fn visit_vector(&mut self, v: &mut super::ast::Vector) -> Self::Output { - for arg in &mut v.args { - self.visit(arg)?; - } - Ok(()) } } @@ -993,10 +989,6 @@ impl<'a> VisitorMutRef for KernelExpander<'a> { } fn visit_vector(&mut self, v: &mut super::ast::Vector) -> Self::Output { - for arg in &mut v.args { - self.visit(arg)?; - } - Ok(()) } } diff --git a/crates/steel-core/src/parser/expander.rs b/crates/steel-core/src/parser/expander.rs index 12f02c0ac..cd08e85ea 100644 --- a/crates/steel-core/src/parser/expander.rs +++ b/crates/steel-core/src/parser/expander.rs @@ -1,5 +1,5 @@ use crate::compiler::program::{BEGIN, DEFINE, IF}; -use crate::parser::ast::{Atom, ExprKind, List, Macro, PatternPair}; +use crate::parser::ast::{Atom, ExprKind, List, Macro, PatternPair, Vector}; use crate::parser::parser::SyntaxObject; use crate::parser::rename_idents::RenameIdentifiersVisitor; use crate::parser::replace_idents::replace_identifiers; @@ -378,9 +378,10 @@ pub enum MacroPattern { Single(InternedString), Syntax(InternedString), Many(InternedString), - Nested(Vec), - ManyNested(Vec), + Nested(Vec, bool), + ManyNested(Vec, bool), CharacterLiteral(char), + BytesLiteral(Vec), IntLiteral(isize), StringLiteral(String), FloatLiteral(f64), @@ -400,7 +401,7 @@ impl std::fmt::Debug for MacroPattern { MacroPattern::Single(s) => f.debug_tuple("Single").field(&s.resolve()).finish(), MacroPattern::Syntax(s) => f.debug_tuple("Syntax").field(&s.resolve()).finish(), MacroPattern::Many(m) => f.debug_tuple("Many").field(&m.resolve()).finish(), - MacroPattern::Nested(n) => f.debug_tuple("Nested").field(n).finish(), + MacroPattern::Nested(n, v) => f.debug_tuple("Nested").field(n).field(v).finish(), MacroPattern::CharacterLiteral(c) => { f.debug_tuple("CharacterLiteral").field(c).finish() } @@ -410,16 +411,19 @@ impl std::fmt::Debug for MacroPattern { MacroPattern::BooleanLiteral(b) => f.debug_tuple("BooleanLiteral").field(b).finish(), MacroPattern::QuotedExpr(s) => f.debug_tuple("QuotedExpr").field(s).finish(), MacroPattern::Quote(i) => f.debug_tuple("Quote").field(&i.resolve()).finish(), - MacroPattern::ManyNested(n) => f.debug_tuple("ManyNested").field(n).finish(), + MacroPattern::ManyNested(n, v) => { + f.debug_tuple("ManyNested").field(n).field(v).finish() + } MacroPattern::Keyword(k) => f.debug_tuple("Keyword").field(k).finish(), MacroPattern::Rest(r) => f.debug_tuple("Rest").field(r).finish(), + MacroPattern::BytesLiteral(v) => f.debug_tuple("BytesLiteral").field(v).finish(), } } } impl MacroPattern { pub fn is_many(&self) -> bool { - matches!(self, MacroPattern::Many(_) | MacroPattern::ManyNested(_)) + matches!(self, MacroPattern::Many(_) | MacroPattern::ManyNested(..)) } fn mangle(&mut self, special_forms: &[InternedString]) { @@ -449,10 +453,10 @@ impl MacroPattern { } } // Silly, needs revisiting - ManyNested(m) => { + ManyNested(m, _) => { m.iter_mut().for_each(|x| x.mangle(special_forms)); } - Nested(v) => { + Nested(v, _) => { v.iter_mut().for_each(|x| x.mangle(special_forms)); } Rest(v) => { @@ -625,11 +629,11 @@ impl MacroPattern { } } TokenType::Ellipses => { - if let Some(MacroPattern::Nested(inner)) = pattern_vec.pop() { + if let Some(MacroPattern::Nested(inner, vec)) = pattern_vec.pop() { let improper = list.improper && i + 1 == len; check_ellipsis!(improper, span); - pattern_vec.push(MacroPattern::ManyNested(inner)); + pattern_vec.push(MacroPattern::ManyNested(inner, vec)); } else { stop!(BadSyntax => "cannot bind pattern to ellipsis"; span) } @@ -641,13 +645,29 @@ impl MacroPattern { ExprKind::List(l) => { let (patterns, _) = Self::parse_from_list(l, macro_name, special_forms, bindings, false)?; - pattern_vec.push(MacroPattern::Nested(patterns)) + + pattern_vec.push(MacroPattern::Nested(patterns, false)) } ExprKind::Quote(q) => pattern_vec.push(MacroPattern::QuotedExpr(q)), + ExprKind::Vector(Vector { + args, bytes: false, .. + }) => { + let list = List::new(args); + + let (patterns, _) = + Self::parse_from_list(list, macro_name, special_forms, bindings, false)?; + + pattern_vec.push(MacroPattern::Nested(patterns, true)) + } + ExprKind::Vector(v @ Vector { bytes: true, .. }) => { + let bytes = v.as_bytes().collect(); + + pattern_vec.push(MacroPattern::BytesLiteral(bytes)); + } _ => { // TODO: Add pattern matching on other kinds of forms here - probably just a special IR // for the pattern to match against here - stop!(BadSyntax => format!("illegal special form found in macro pattern: {}", expr)); + stop!(BadSyntax => format!("illegal special form found in macro pattern: {}", expr) ; opt expr.span()); } } } @@ -711,13 +731,27 @@ fn match_list_pattern(patterns: &[MacroPattern], list: &[ExprKind], improper: bo } continue; } - MacroPattern::ManyNested(subpatterns) if has_ellipsis => { + MacroPattern::ManyNested(subpatterns, is_vec) if has_ellipsis => { for _ in 0..expected_many_captures { - let Some((_, ExprKind::List(l))) = exprs_iter.next() else { + let Some((_, next)) = exprs_iter.next() else { return false; }; - if !match_list_pattern(subpatterns, &l.args, l.improper) { + // FIXME: this is not quite correct. It does not handle + // the "improper-list-becomes-non-list" e.g. + // (a ... . b) matching "foobar" + let (args, improper) = match (next, is_vec) { + (ExprKind::List(l), false) => (&l.args, l.improper), + ( + ExprKind::Vector(Vector { + bytes: false, args, .. + }), + true, + ) => (args, false), + _ => return false, + }; + + if !match_list_pattern(subpatterns, &args, improper) { return false; } } @@ -754,7 +788,7 @@ fn match_list_pattern(patterns: &[MacroPattern], list: &[ExprKind], improper: bo fn match_rest_pattern(pattern: &MacroPattern, exprs: &[ExprKind], improper: bool) -> bool { match pattern { MacroPattern::Single(_) => true, - MacroPattern::Nested(patterns) => match_list_pattern(patterns, exprs, improper), + MacroPattern::Nested(patterns, false) => match_list_pattern(patterns, exprs, improper), _ => false, } } @@ -762,7 +796,7 @@ fn match_rest_pattern(pattern: &MacroPattern, exprs: &[ExprKind], improper: bool fn match_single_pattern(pattern: &MacroPattern, expr: &ExprKind) -> bool { match pattern { MacroPattern::Many(_) => unreachable!(), - MacroPattern::ManyNested(_) => unreachable!(), + MacroPattern::ManyNested(..) => unreachable!(), MacroPattern::Rest(_) => unreachable!(), MacroPattern::Single(_) => true, // TODO: @Matt - if the atom is bound locally, then do not match on this @@ -893,6 +927,24 @@ fn match_single_pattern(pattern: &MacroPattern, expr: &ExprKind) -> bool { }) if s.as_str() == b.as_str() => true, _ => return false, }, + MacroPattern::BytesLiteral(v) => match expr { + ExprKind::Vector(Vector { + args, bytes: true, .. + }) if args.len() == v.len() => { + let byte_atoms = args.iter().map(|expr| { + if let ExprKind::Atom(atom) = expr { + atom.byte() + } else { + None + } + }); + + byte_atoms + .zip(v.iter().copied()) + .all(|(atom, byte)| atom == Some(byte)) + } + _ => false, + }, MacroPattern::QuotedExpr(q) => { // println!("MATCHING QUOTED EXPR: {}", q); match expr { @@ -936,18 +988,23 @@ fn match_single_pattern(pattern: &MacroPattern, expr: &ExprKind) -> bool { // Now that we have ManyNested - need to figure out // the recursive step better here - MacroPattern::Nested(vec) => { - if let ExprKind::List(l) = expr { + MacroPattern::Nested(patterns, is_vec) => { + match expr { // Make the recursive call on the next layer - match_list_pattern(vec, l, l.improper) - } else if let ExprKind::Quote(_) = expr { + ExprKind::List(l) => !is_vec && match_list_pattern(patterns, l, l.improper), + ExprKind::Vector(v) => { + *is_vec && !v.bytes && match_list_pattern(patterns, &v.args, false) + } // TODO: Come back here - matches!(vec.as_slice(), &[MacroPattern::Quote(_)]) - } else { - if let Some(pat) = non_list_match(vec) { - match_single_pattern(pat, expr) - } else { - false + ExprKind::Quote(_) => { + !is_vec && matches!(patterns.as_slice(), &[MacroPattern::Quote(_)]) + } + _ => { + if let Some(pat) = non_list_match(patterns) { + match_single_pattern(pat, expr) + } else { + false + } } } } @@ -1010,34 +1067,45 @@ fn collect_bindings( bindings.insert(*ident, List::new(captured).into()); binding_kind.insert(*ident, BindingKind::Many); } - MacroPattern::Nested(children) => { + MacroPattern::Nested(children, is_vec) => { let (_, child) = expr_iter .next() .ok_or_else(throw!(ArityMismatch => "Macro expected a pattern"))?; - if let ExprKind::List(l) = child { - collect_bindings(children, l, bindings, binding_kind, l.improper)?; - } else if let ExprKind::Quote(q) = child { - if let &[MacroPattern::Quote(x)] = children.as_slice() { - bindings.insert(x, q.expr.clone()); - } else { - stop!(BadSyntax => "macro expected a list of values, not including keywords, found: {}", child) + match child { + ExprKind::List(l) => { + if !is_vec { + collect_bindings(children, l, bindings, binding_kind, l.improper)?; + } } - } else { - if let Some(pat) = non_list_match(&children) { - collect_bindings( - &[pat.clone()], - &[child.clone()], - bindings, - binding_kind, - false, - )?; - } else { - stop!(BadSyntax => "macro expected a list of values, not including keywords, found: {}", child) + ExprKind::Vector(v) => { + if *is_vec { + collect_bindings(children, &v.args, bindings, binding_kind, false)?; + } + } + ExprKind::Quote(q) => { + if let &[MacroPattern::Quote(x)] = children.as_slice() { + bindings.insert(x, q.expr.clone()); + } else { + stop!(BadSyntax => "macro expected a list of values, not including keywords, found: {}", child) + } + } + _ => { + if let Some(pat) = non_list_match(&children) { + collect_bindings( + &[pat.clone()], + &[child.clone()], + bindings, + binding_kind, + false, + )?; + } else { + stop!(BadSyntax => "macro expected a list of values, not including keywords, found: {}", child) + } } } } - MacroPattern::ManyNested(children) => { + MacroPattern::ManyNested(children, is_vec) => { let exprs_to_destruct = { let mut exprs = smallvec::SmallVec::<[_; 8]>::new(); @@ -1048,27 +1116,32 @@ fn collect_bindings( exprs }; - for i in 0..children.len() { + for (i, child) in children.iter().enumerate() { + // for i in 0..children.len() { let mut values_to_bind = Vec::new(); - let is_ellipses_pattern = matches!(&children[i], MacroPattern::Many(_)); + let is_ellipses_pattern = matches!(child, MacroPattern::Many(_)); for expr in &exprs_to_destruct { - if let ExprKind::List(l) = expr { - // Not what we want to do here! - - if is_ellipses_pattern { - // Bind the "rest" of the values into this - values_to_bind.push(List::new(l[i..].to_vec()).into()); - } else { - values_to_bind.push(l[i].clone()); + match expr { + ExprKind::List(l) if !is_vec => { + // Not what we want to do here! + + if is_ellipses_pattern { + // Bind the "rest" of the values into this + values_to_bind.push(List::new(l[i..].to_vec()).into()); + } else { + values_to_bind.push(l[i].clone()); + } } - } else { - unreachable!() + ExprKind::Vector(v) if *is_vec => { + values_to_bind.push(v.args[i].clone()); + } + _ => unreachable!(), } } - match &children[i] { + match child { MacroPattern::Single(ident) => { let list_of_values = List::new(values_to_bind).into(); @@ -1086,19 +1159,24 @@ fn collect_bindings( binding_kind.insert(*ident, BindingKind::Many); } // These I'll need to handle - MacroPattern::Nested(nested_children) => { + MacroPattern::Nested(nested_children, is_vec) => { let mut final_bindings: HashMap<_, Vec<_>> = HashMap::new(); for expr in values_to_bind { - let list_expr = - expr.list_or_else(throw!(BadSyntax => "Unreachable!"))?; + let (list, improper) = match expr { + ExprKind::List(l) if !is_vec => (l.args, l.improper), + ExprKind::Vector(vec) if !is_vec => (vec.args, false), + _ => stop!(BadSyntax => "Unreachable"), + }; + let mut new_bindings = FxHashMap::default(); + collect_bindings( nested_children, - list_expr, + &list, &mut new_bindings, binding_kind, - list_expr.improper, + improper, )?; for (key, value) in new_bindings { @@ -1117,7 +1195,7 @@ fn collect_bindings( bindings.insert(key, List::new(value).into()); } } - MacroPattern::ManyNested(_) => { + MacroPattern::ManyNested(..) => { stop!(BadSyntax => "Internal compiler error - unexpected ellipses expansion found within ellipses expansion") } _ => { @@ -1254,10 +1332,13 @@ mod match_list_pattern_tests { let pattern_args = vec![ MacroPattern::Syntax("->>".into()), MacroPattern::Single("a".into()), - MacroPattern::Nested(vec![ - MacroPattern::Single("b".into()), - MacroPattern::Many("c".into()), - ]), + MacroPattern::Nested( + vec![ + MacroPattern::Single("b".into()), + MacroPattern::Many("c".into()), + ], + false, + ), ]; let list_expr = List::new(vec![ @@ -1296,10 +1377,13 @@ mod match_list_pattern_tests { MacroPattern::Syntax("->>".into()), MacroPattern::Single("a".into()), MacroPattern::Single("bad".into()), - MacroPattern::Nested(vec![ - MacroPattern::Single("b".into()), - MacroPattern::Many("c".into()), - ]), + MacroPattern::Nested( + vec![ + MacroPattern::Single("b".into()), + MacroPattern::Many("c".into()), + ], + false, + ), ]; let list_expr = List::new(vec![ @@ -1500,10 +1584,13 @@ mod collect_bindings_tests { let pattern_args = vec![ MacroPattern::Syntax("->>".into()), MacroPattern::Single("a".into()), - MacroPattern::Nested(vec![ - MacroPattern::Single("b".into()), - MacroPattern::Many("c".into()), - ]), + MacroPattern::Nested( + vec![ + MacroPattern::Single("b".into()), + MacroPattern::Many("c".into()), + ], + false, + ), ]; let list_expr = List::new(vec![ diff --git a/crates/steel-core/src/rvals/cycles.rs b/crates/steel-core/src/rvals/cycles.rs index 25ef55a9f..40c133b84 100644 --- a/crates/steel-core/src/rvals/cycles.rs +++ b/crates/steel-core/src/rvals/cycles.rs @@ -1445,6 +1445,12 @@ impl<'a> RecursiveEqualityHandler<'a> { } continue; } + (MutFunc(l), MutFunc(r)) => { + if l as usize != r as usize { + return false; + } + continue; + } (SymbolV(l), SymbolV(r)) => { if l != r { return false; diff --git a/crates/steel-core/src/scheme/stdlib.scm b/crates/steel-core/src/scheme/stdlib.scm index d806086cb..71e033c9f 100644 --- a/crates/steel-core/src/scheme/stdlib.scm +++ b/crates/steel-core/src/scheme/stdlib.scm @@ -90,13 +90,16 @@ [(quasiquote (#%unquote x)) x] [(quasiquote ((#%unquote-splicing x))) (append x '())] + [(quasiquote #((#%unquote-splicing x))) (list->vector (append x '()))] [(quasiquote ((#%unquote-splicing x) xs ...)) (append x (quasiquote (xs ...)))] + [(quasiquote #((#%unquote-splicing x) xs ...)) (list->vector (append x (quasiquote (xs ...))))] ;; TODO: Do unquote-splicing as well, follow the same rules as unquote [(quasiquote ((unquote-splicing x))) (append (list (list 'unquote-splicing (quasiquote x))) '())] [(quasiquote ((unquote-splicing x) xs ...)) (append (list (list 'unquote-splicing (quasiquote x))) (quasiquote (xs ...)))] [(quasiquote (x xs ...)) (cons (quasiquote x) (quasiquote (xs ...)))] + [(quasiquote #(x xs ...)) (list->vector (cons (quasiquote x) (quasiquote (xs ...))))] [(quasiquote x) 'x])) ; (define-syntax #%unquote diff --git a/crates/steel-core/src/steel_vm/const_evaluation.rs b/crates/steel-core/src/steel_vm/const_evaluation.rs index f09cb024e..ce34a11d5 100644 --- a/crates/steel-core/src/steel_vm/const_evaluation.rs +++ b/crates/steel-core/src/steel_vm/const_evaluation.rs @@ -907,22 +907,7 @@ impl<'a> ConsumingVisitor for ConstantEvaluator<'a> { } fn visit_vector(&mut self, v: crate::parser::ast::Vector) -> Self::Output { - if v.bytes { - return Ok(v.into()); - } - - let args: Vec<_> = v - .args - .into_iter() - .map(|expr| self.visit(expr)) - .collect::>()?; - - Ok(crate::parser::ast::Vector { - args, - bytes: false, - span: v.span, - } - .into()) + return Ok(v.into()); } } @@ -1033,13 +1018,5 @@ impl<'a> VisitorMut for CollectSet<'a> { self.scopes.pop_layer(); } - fn visit_vector(&mut self, v: &crate::parser::ast::Vector) -> Self::Output { - if v.bytes { - return; - } - - for expr in &v.args { - self.visit(expr); - } - } + fn visit_vector(&mut self, v: &crate::parser::ast::Vector) -> Self::Output {} } diff --git a/crates/steel-parser/src/ast.rs b/crates/steel-parser/src/ast.rs index 5a27b77ef..a5328d6ef 100644 --- a/crates/steel-parser/src/ast.rs +++ b/crates/steel-parser/src/ast.rs @@ -1012,6 +1012,20 @@ impl Vector { ParenMod::Vector } } + + pub fn as_bytes(&self) -> impl Iterator + '_ { + self.args.iter().flat_map(move |expr| { + let byte = if let ExprKind::Atom(atom) = expr { + atom.byte() + } else { + None + }; + + debug_assert!(!(self.bytes && byte.is_none())); + + byte + }) + } } impl fmt::Display for Vector { diff --git a/crates/steel-parser/src/parser.rs b/crates/steel-parser/src/parser.rs index 4d1a24442..786d87eb1 100644 --- a/crates/steel-parser/src/parser.rs +++ b/crates/steel-parser/src/parser.rs @@ -1037,12 +1037,6 @@ impl<'a> Parser<'a> { &self.source_name.clone(), )); - if matches!(current_frame.paren_mod, Some(ParenMod::Bytes)) - && atom.byte().is_none() - { - return Err(ParseError::SyntaxError("bytevector literals can only contain integer literals in the 0-255 range".into(), atom.syn.span, None)); - } - current_frame.push(ExprKind::Atom(atom))? } } @@ -1679,6 +1673,20 @@ impl Frame { } } + let valid_for_bytes = match (self.paren_mod, &expr) { + (Some(ParenMod::Bytes), ExprKind::Atom(atom)) if atom.byte().is_some() => true, + (Some(ParenMod::Bytes), _) => false, + _ => true, + }; + + if !valid_for_bytes { + return Err(ParseError::SyntaxError( + "bytevector literals can only contain integer literals in the 0-255 range".into(), + expr.span().unwrap_or_default(), + None, + )); + } + Ok(self.exprs.push(expr)) } @@ -2824,6 +2832,11 @@ mod parser_tests { "bytevector literals can only contain integer literals in the 0-255 range", ); + assert_syntax_err( + "#u8(())", + "bytevector literals can only contain integer literals in the 0-255 range", + ); + assert_syntax_err("#u8(1 . 2)", "bytevector literals cannot contain dots"); assert_syntax_err("#(1 . 2)", "vector literals cannot contain dots");