@@ -141,9 +141,14 @@ pub(crate) fn tags_block<'src>() -> impl Parser<'src, &'src str, Vec<Tag>, Extra
141141 . delimited_by ( just ( '[' ) . then ( inline_pad ( ) ) , inline_pad ( ) . then ( just ( ']' ) ) )
142142}
143143
144- /// `(pi_expr, pi_expr, ...)`.
145- pub ( crate ) fn args_block < ' src > ( ) -> impl Parser < ' src , & ' src str , Vec < f64 > , Extra < ' src > > + Clone {
146- pi_expr ( )
144+ /// `(pi_expr, pi_expr, ...)`. Each argument is paired with whether it was
145+ /// written with the `*pi` (half-turn) form. Most callers ignore the flag,
146+ /// but the bare rotation mnemonics (`R_X`/`R_Y`/`R_Z`/`U3`) take their angle
147+ /// in half-turns and multiply by pi when lowering, so they reject a `*pi`
148+ /// argument — which would otherwise be multiplied by pi twice.
149+ pub ( crate ) fn args_block < ' src > ( )
150+ -> impl Parser < ' src , & ' src str , Vec < ( f64 , bool ) > , Extra < ' src > > + Clone {
151+ pi_expr_flagged ( )
147152 . separated_by ( inline_pad ( ) . then ( just ( ',' ) ) . then ( inline_pad ( ) ) )
148153 . allow_trailing ( )
149154 . collect :: < Vec < _ > > ( )
@@ -168,22 +173,21 @@ pub(crate) fn target_lexeme<'src>() -> impl Parser<'src, &'src str, RawTarget, E
168173 } )
169174}
170175
171- /// `<ident> [<tags>]? (<args>)?`. Returns name, tags, args, and the
172- /// span of the identifier (used for line-number reporting).
176+ /// `<ident> [<tags>]? (<args>)?`. Returns name, tags, args, whether any
177+ /// argument used the `*pi` (half-turn) form, and the span of the identifier
178+ /// (used for line-number reporting).
173179pub ( crate ) fn instruction_head < ' src > ( )
174- -> impl Parser < ' src , & ' src str , ( String , Vec < Tag > , Vec < f64 > , SimpleSpan < usize > ) , Extra < ' src > > + Clone
175- {
180+ -> impl Parser < ' src , & ' src str , ( String , Vec < Tag > , Vec < f64 > , bool , SimpleSpan < usize > ) , Extra < ' src > >
181+ + Clone {
176182 ident ( )
177183 . map_with ( |name, e| ( name, e. span ( ) ) )
178184 . then ( tags_block ( ) . or_not ( ) )
179185 . then ( args_block ( ) . or_not ( ) )
180186 . map ( |( ( ( name, span) , tags) , args) | {
181- (
182- name,
183- tags. unwrap_or_default ( ) ,
184- args. unwrap_or_default ( ) ,
185- span,
186- )
187+ let args = args. unwrap_or_default ( ) ;
188+ let args_had_pi = args. iter ( ) . any ( |& ( _, had_pi) | had_pi) ;
189+ let values = args. into_iter ( ) . map ( |( value, _) | value) . collect ( ) ;
190+ ( name, tags. unwrap_or_default ( ) , values, args_had_pi, span)
187191 } )
188192}
189193
@@ -212,10 +216,11 @@ pub(crate) fn instruction_line<'src>()
212216 . collect :: < Vec < RawTarget > > ( ) ,
213217 )
214218 . map (
215- |( ( name, tags, args, span) , targets) | RawSyntaxNode :: Instruction {
219+ |( ( name, tags, args, args_had_pi , span) , targets) | RawSyntaxNode :: Instruction {
216220 name,
217221 tags,
218222 args,
223+ args_had_pi,
219224 targets,
220225 span,
221226 } ,
@@ -371,14 +376,21 @@ mod tests {
371376 #[ test]
372377 fn args_block_parses_csv_floats ( ) {
373378 let a = run ( args_block ( ) , "(0.1, 0.2, 0.3)" ) ;
374- assert_eq ! ( a, vec![ 0.1 , 0.2 , 0.3 ] ) ;
379+ // Each arg is paired with whether it used the `*pi` form (none here).
380+ assert_eq ! ( a, vec![ ( 0.1 , false ) , ( 0.2 , false ) , ( 0.3 , false ) ] ) ;
375381 }
376382
377383 #[ test]
378384 fn args_block_with_pi_exprs ( ) {
379- let a = run ( args_block ( ) , "(pi, 0.5*pi)" ) ;
380- assert ! ( ( a[ 0 ] - std:: f64 :: consts:: PI ) . abs( ) < 1e-12 ) ;
381- assert ! ( ( a[ 1 ] - 0.5 * std:: f64 :: consts:: PI ) . abs( ) < 1e-12 ) ;
385+ // Args still accept pi-expressions (for stim.rs compatibility); the
386+ // `had_pi` flag records that they did so callers can reject it where
387+ // the half-turn convention applies.
388+ let a = run ( args_block ( ) , "(pi, 0.5*pi, 2.0)" ) ;
389+ assert ! ( ( a[ 0 ] . 0 - std:: f64 :: consts:: PI ) . abs( ) < 1e-12 ) ;
390+ assert ! ( a[ 0 ] . 1 ) ;
391+ assert ! ( ( a[ 1 ] . 0 - 0.5 * std:: f64 :: consts:: PI ) . abs( ) < 1e-12 ) ;
392+ assert ! ( a[ 1 ] . 1 ) ;
393+ assert_eq ! ( a[ 2 ] , ( 2.0 , false ) ) ;
382394 }
383395
384396 #[ test]
@@ -399,7 +411,7 @@ mod tests {
399411
400412 #[ test]
401413 fn instruction_head_with_tags_and_args ( ) {
402- let ( name, tags, args, _span) = run ( instruction_head ( ) , "S[T](0.5)" ) ;
414+ let ( name, tags, args, _had_pi , _span) = run ( instruction_head ( ) , "S[T](0.5)" ) ;
403415 assert_eq ! ( name, "S" ) ;
404416 assert_eq ! ( tags. len( ) , 1 ) ;
405417 assert_eq ! ( tags[ 0 ] . name, "T" ) ;
@@ -408,7 +420,7 @@ mod tests {
408420
409421 #[ test]
410422 fn instruction_head_no_tags_no_args ( ) {
411- let ( name, tags, args, _span) = run ( instruction_head ( ) , "H" ) ;
423+ let ( name, tags, args, _had_pi , _span) = run ( instruction_head ( ) , "H" ) ;
412424 assert_eq ! ( name, "H" ) ;
413425 assert ! ( tags. is_empty( ) ) ;
414426 assert ! ( args. is_empty( ) ) ;
0 commit comments