ptx_parser/parser/
common.rs

1use std::borrow::Cow;
2
3use crate::{
4    lexer::PtxToken,
5    parser::{ParseErrorKind, PtxParseError, PtxParser, PtxTokenStream, Span},
6    r#type::common::*,
7};
8
9pub(crate) fn unexpected_value(
10    span: Span,
11    expected: &[&str],
12    found: impl Into<Cow<'static, str>>,
13) -> PtxParseError {
14    PtxParseError {
15        kind: ParseErrorKind::UnexpectedToken {
16            expected: expected.iter().map(|s| s.to_string()).collect(),
17            found: found.into().to_string(),
18        },
19        span,
20    }
21}
22
23pub(crate) fn invalid_literal(span: Span, literal: impl Into<Cow<'static, str>>) -> PtxParseError {
24    PtxParseError {
25        kind: ParseErrorKind::InvalidLiteral(literal.into().to_string()),
26        span,
27    }
28}
29
30pub(crate) fn parse_register_name(
31    stream: &mut PtxTokenStream,
32) -> Result<(String, Span), PtxParseError> {
33    let (mut name, mut span) = stream.expect_register()?;
34
35    loop {
36        // Peek to decide whether the next token should be treated as a component.
37        let next = match stream.peek() {
38            Ok((token, _)) => token,
39            Err(_) => break,
40        };
41
42        match next {
43            PtxToken::Dot => {
44                // Peek ahead to see if this is a valid register component
45                if let Some((PtxToken::Identifier(component_name), _)) =
46                    stream.tokens.get(stream.index + 1)
47                {
48                    // Only consume if it's a valid single-character register component
49                    // Exclude multi-character .b* patterns (e.g., .b0, .b3210) which are instruction-specific modifiers
50                    if matches!(component_name.as_str(), "x" | "y" | "z" | "w" | "r" | "g" | "b" | "a") {
51                        // consume the dot and identifier
52                        stream.consume()?;
53                        let (component, component_span) = stream.expect_identifier()?;
54
55                        name.push('.');
56                        name.push_str(&component);
57
58                        span.end = component_span.end;
59                    } else {
60                        // Not a valid register component, stop parsing
61                        break;
62                    }
63                } else {
64                    break;
65                }
66            }
67            _ => break,
68        }
69    }
70
71    Ok((name, span))
72}
73
74pub(crate) fn numeric_literal(token: &PtxToken) -> Option<&String> {
75    match token {
76        PtxToken::DecimalInteger(value)
77        | PtxToken::HexInteger(value)
78        | PtxToken::BinaryInteger(value)
79        | PtxToken::OctalInteger(value)
80        | PtxToken::FloatExponent(value)
81        | PtxToken::Float(value)
82        | PtxToken::HexFloat(value) => Some(value),
83        _ => None,
84    }
85}
86
87pub(crate) fn is_numeric_token(token: &PtxToken) -> bool {
88    numeric_literal(token).is_some()
89}
90
91pub(crate) fn parse_u64_literal(stream: &mut PtxTokenStream) -> Result<(u64, Span), PtxParseError> {
92    let (token, span) = stream.consume()?;
93    let span = span.clone();
94
95    let value = match token {
96        PtxToken::DecimalInteger(text) => text
97            .parse::<u64>()
98            .map_err(|_| invalid_literal(span.clone(), text.clone()))?,
99        PtxToken::HexInteger(text) => {
100            let stripped = text
101                .strip_prefix("0x")
102                .or_else(|| text.strip_prefix("0X"))
103                .ok_or_else(|| invalid_literal(span.clone(), text.clone()))?;
104            u64::from_str_radix(stripped, 16)
105                .map_err(|_| invalid_literal(span.clone(), text.clone()))?
106        }
107        PtxToken::BinaryInteger(text) => {
108            let stripped = text
109                .strip_prefix("0b")
110                .or_else(|| text.strip_prefix("0B"))
111                .ok_or_else(|| invalid_literal(span.clone(), text.clone()))?;
112            u64::from_str_radix(stripped, 2)
113                .map_err(|_| invalid_literal(span.clone(), text.clone()))?
114        }
115        PtxToken::OctalInteger(text) => {
116            let stripped = &text[1..];
117            u64::from_str_radix(stripped, 8)
118                .map_err(|_| invalid_literal(span.clone(), text.clone()))?
119        }
120        _ => {
121            return Err(unexpected_value(
122                span,
123                &["unsigned integer literal"],
124                format!("{token:?}"),
125            ));
126        }
127    };
128
129    Ok((value, span))
130}
131
132impl PtxParser for CodeLinkage {
133    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
134        let (directive, span) = stream.expect_directive()?;
135        match directive.as_str() {
136            "visible" => Ok(CodeLinkage::Visible),
137            "extern" => Ok(CodeLinkage::Extern),
138            "weak" => Ok(CodeLinkage::Weak),
139            other => Err(unexpected_value(
140                span,
141                &[".visible", ".extern", ".weak"],
142                format!(".{other}"),
143            )),
144        }
145    }
146}
147
148impl PtxParser for DataLinkage {
149    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
150        let (directive, span) = stream.expect_directive()?;
151        match directive.as_str() {
152            "visible" => Ok(DataLinkage::Visible),
153            "extern" => Ok(DataLinkage::Extern),
154            "weak" => Ok(DataLinkage::Weak),
155            "common" => Ok(DataLinkage::Common),
156            other => Err(unexpected_value(
157                span,
158                &[".visible", ".extern", ".weak", ".common"],
159                format!(".{other}"),
160            )),
161        }
162    }
163}
164
165impl PtxParser for CodeOrDataLinkage {
166    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
167        let (directive, span) = stream.expect_directive()?;
168        match directive.as_str() {
169            "visible" => Ok(CodeOrDataLinkage::Visible),
170            "extern" => Ok(CodeOrDataLinkage::Extern),
171            "weak" => Ok(CodeOrDataLinkage::Weak),
172            "common" => Ok(CodeOrDataLinkage::Common),
173            other => Err(unexpected_value(
174                span,
175                &[".visible", ".extern", ".weak", ".common"],
176                format!(".{other}"),
177            )),
178        }
179    }
180}
181
182impl PtxParser for TexType {
183    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
184        let (directive, span) = stream.expect_directive()?;
185        match directive.as_str() {
186            "texref" => Ok(TexType::TexRef),
187            "samplerref" => Ok(TexType::SamplerRef),
188            "surfref" => Ok(TexType::SurfRef),
189            other => Err(unexpected_value(
190                span,
191                &[".texref", ".samplerref", ".surfref"],
192                format!(".{other}"),
193            )),
194        }
195    }
196}
197
198impl PtxParser for AddressSpace {
199    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
200        let (directive, span) = stream.expect_directive()?;
201        match directive.as_str() {
202            "global" => Ok(AddressSpace::Global),
203            "const" => Ok(AddressSpace::Const),
204            "shared" => Ok(AddressSpace::Shared),
205            "local" => Ok(AddressSpace::Local),
206            "param" => Ok(AddressSpace::Param),
207            "reg" => Ok(AddressSpace::Reg),
208            other => Err(unexpected_value(
209                span,
210                &[".global", ".const", ".shared", ".local", ".param", ".reg"],
211                format!(".{other}"),
212            )),
213        }
214    }
215}
216
217impl PtxParser for AttributeDirective {
218    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
219        let (directive, span) = stream.expect_directive()?;
220        match directive.as_str() {
221            "unified" => {
222                stream.expect(&PtxToken::LParen)?;
223                let (uuid1, _) = parse_u64_literal(stream)?;
224                stream.expect(&PtxToken::Comma)?;
225                let (uuid2, _) = parse_u64_literal(stream)?;
226                stream.expect(&PtxToken::RParen)?;
227                Ok(AttributeDirective::Unified(uuid1, uuid2))
228            }
229            "managed" => Ok(AttributeDirective::Managed),
230            other => Err(unexpected_value(
231                span,
232                &[".unified", ".managed"],
233                format!(".{other}"),
234            )),
235        }
236    }
237}
238
239impl PtxParser for DataType {
240    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
241        let (directive, span) = stream.expect_directive()?;
242        match directive.as_str() {
243            "u8" => Ok(DataType::U8),
244            "u16" => Ok(DataType::U16),
245            "u32" => Ok(DataType::U32),
246            "u64" => Ok(DataType::U64),
247            "s8" => Ok(DataType::S8),
248            "s16" => Ok(DataType::S16),
249            "s32" => Ok(DataType::S32),
250            "s64" => Ok(DataType::S64),
251            "f16" => Ok(DataType::F16),
252            "f16x2" => Ok(DataType::F16x2),
253            "f32" => Ok(DataType::F32),
254            "f64" => Ok(DataType::F64),
255            "b8" => Ok(DataType::B8),
256            "b16" => Ok(DataType::B16),
257            "b32" => Ok(DataType::B32),
258            "b64" => Ok(DataType::B64),
259            "b128" => Ok(DataType::B128),
260            "pred" => Ok(DataType::Pred),
261            other => Err(unexpected_value(
262                span,
263                &[
264                    ".u8", ".u16", ".u32", ".u64", ".s8", ".s16", ".s32", ".s64", ".f16", ".f16x2",
265                    ".f32", ".f64", ".b8", ".b16", ".b32", ".b64", ".b128", ".pred",
266                ],
267                format!(".{other}"),
268            )),
269        }
270    }
271}
272
273impl PtxParser for Sign {
274    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
275        if stream
276            .consume_if(|token| matches!(token, PtxToken::Plus))
277            .is_some()
278        {
279            return Ok(Sign::Positive);
280        }
281        if stream
282            .consume_if(|token| matches!(token, PtxToken::Minus))
283            .is_some()
284        {
285            return Ok(Sign::Negative);
286        }
287
288        let (token, span) = stream.peek()?;
289        Err(unexpected_value(
290            span.clone(),
291            &["+", "-"],
292            format!("{token:?}"),
293        ))
294    }
295}
296
297impl PtxParser for Immediate {
298    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
299        // Check for optional minus sign
300        let has_minus = stream
301            .consume_if(|token| matches!(token, PtxToken::Minus))
302            .is_some();
303
304        let (token, span) = stream.peek()?;
305        let value = numeric_literal(token).cloned();
306        match value {
307            Some(value) => {
308                let literal = if has_minus {
309                    format!("-{}", value)
310                } else {
311                    value.clone()
312                };
313                stream.consume()?;
314                Ok(Immediate(literal))
315            }
316            None => {
317                // If we consumed a minus, we need to restore position by going back one token
318                if has_minus {
319                    let mut current_pos = stream.position();
320                    if current_pos.index > 0 {
321                        current_pos.index -= 1;
322                        current_pos.char_offset = 0;
323                        stream.set_position(current_pos);
324                    }
325                }
326                Err(unexpected_value(
327                    span.clone(),
328                    &["numeric literal"],
329                    format!("{token:?}"),
330                ))
331            }
332        }
333    }
334}
335
336impl PtxParser for RegisterOperand {
337    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
338        if !stream.check(|token| matches!(token, PtxToken::Register(_))) {
339            let (token, span) = stream.peek()?;
340            return Err(unexpected_value(
341                span.clone(),
342                &["register"],
343                format!("{token:?}"),
344            ));
345        }
346        let (name, _) = parse_register_name(stream)?;
347        Ok(RegisterOperand(name))
348    }
349}
350
351impl PtxParser for PredicateRegister {
352    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
353        let (name, span) = parse_register_name(stream)?;
354        if name.starts_with("%p") {
355            Ok(PredicateRegister(name))
356        } else {
357            Err(invalid_literal(
358                span,
359                format!("expected predicate register starting with %p, found {name}"),
360            ))
361        }
362    }
363}
364
365impl PtxParser for Label {
366    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
367        let (name, _) = stream.expect_identifier()?;
368        Ok(Label(name))
369    }
370}
371
372impl PtxParser for SpecialRegister {
373    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
374        let (name, span) = parse_register_name(stream)?;
375        // Preserve component information (.x/.y/.z) for certain special registers.
376        // If a component is present, return the axis-aware variant; otherwise fall through
377        // to the general match below.
378        let name_str = name.as_str();
379        if let Some(rest) = name_str.strip_prefix("%cluster_ctaid") {
380            if rest.is_empty() {
381                return Ok(SpecialRegister::ClusterCtaid(Axis::None));
382            } else if rest == ".x" {
383                return Ok(SpecialRegister::ClusterCtaid(Axis::X));
384            } else if rest == ".y" {
385                return Ok(SpecialRegister::ClusterCtaid(Axis::Y));
386            } else if rest == ".z" {
387                return Ok(SpecialRegister::ClusterCtaid(Axis::Z));
388            }
389        }
390        if let Some(rest) = name_str.strip_prefix("%cluster_ctarank") {
391            if rest.is_empty() {
392                return Ok(SpecialRegister::ClusterCtarank(Axis::None));
393            } else if rest == ".x" {
394                return Ok(SpecialRegister::ClusterCtarank(Axis::X));
395            } else if rest == ".y" {
396                return Ok(SpecialRegister::ClusterCtarank(Axis::Y));
397            } else if rest == ".z" {
398                return Ok(SpecialRegister::ClusterCtarank(Axis::Z));
399            }
400        }
401        if let Some(rest) = name_str.strip_prefix("%nctaid") {
402            if rest.is_empty() {
403                return Ok(SpecialRegister::Nctaid(Axis::None));
404            } else if rest == ".x" {
405                return Ok(SpecialRegister::Nctaid(Axis::X));
406            } else if rest == ".y" {
407                return Ok(SpecialRegister::Nctaid(Axis::Y));
408            } else if rest == ".z" {
409                return Ok(SpecialRegister::Nctaid(Axis::Z));
410            }
411        }
412        if let Some(rest) = name_str.strip_prefix("%tid") {
413            if rest.is_empty() {
414                return Ok(SpecialRegister::Tid(Axis::None));
415            } else if rest == ".x" {
416                return Ok(SpecialRegister::Tid(Axis::X));
417            } else if rest == ".y" {
418                return Ok(SpecialRegister::Tid(Axis::Y));
419            } else if rest == ".z" {
420                return Ok(SpecialRegister::Tid(Axis::Z));
421            }
422        }
423        if let Some(rest) = name_str.strip_prefix("%cluster_nctaid") {
424            if rest.is_empty() {
425                return Ok(SpecialRegister::ClusterNctaid(Axis::None));
426            } else if rest == ".x" {
427                return Ok(SpecialRegister::ClusterNctaid(Axis::X));
428            } else if rest == ".y" {
429                return Ok(SpecialRegister::ClusterNctaid(Axis::Y));
430            } else if rest == ".z" {
431                return Ok(SpecialRegister::ClusterNctaid(Axis::Z));
432            }
433        }
434        if let Some(rest) = name_str.strip_prefix("%cluster_nctarank") {
435            if rest.is_empty() {
436                return Ok(SpecialRegister::ClusterNctarank(Axis::None));
437            } else if rest == ".x" {
438                return Ok(SpecialRegister::ClusterNctarank(Axis::X));
439            } else if rest == ".y" {
440                return Ok(SpecialRegister::ClusterNctarank(Axis::Y));
441            } else if rest == ".z" {
442                return Ok(SpecialRegister::ClusterNctarank(Axis::Z));
443            }
444        }
445        if let Some(rest) = name_str.strip_prefix("%ntid") {
446            if rest.is_empty() {
447                return Ok(SpecialRegister::Ntid(Axis::None));
448            } else if rest == ".x" {
449                return Ok(SpecialRegister::Ntid(Axis::X));
450            } else if rest == ".y" {
451                return Ok(SpecialRegister::Ntid(Axis::Y));
452            } else if rest == ".z" {
453                return Ok(SpecialRegister::Ntid(Axis::Z));
454            }
455        }
456        if let Some(rest) = name_str.strip_prefix("%ctaid") {
457            if rest.is_empty() {
458                return Ok(SpecialRegister::Ctaid(Axis::None));
459            } else if rest == ".x" {
460                return Ok(SpecialRegister::Ctaid(Axis::X));
461            } else if rest == ".y" {
462                return Ok(SpecialRegister::Ctaid(Axis::Y));
463            } else if rest == ".z" {
464                return Ok(SpecialRegister::Ctaid(Axis::Z));
465            }
466        }
467
468        match name.as_str() {
469            "%aggr_smem_size" => Ok(SpecialRegister::AggrSmemSize),
470            "%dynamic_smem_size" => Ok(SpecialRegister::DynamicSmemSize),
471            "%lanemask_gt" => Ok(SpecialRegister::LanemaskGt),
472            "%reserved_smem_offset_begin" => Ok(SpecialRegister::ReservedSmemOffsetBegin),
473            "%clock" => Ok(SpecialRegister::Clock),
474            "%lanemask_le" => Ok(SpecialRegister::LanemaskLe),
475            "%reserved_smem_offset_cap" => Ok(SpecialRegister::ReservedSmemOffsetCap),
476            "%clock64" => Ok(SpecialRegister::Clock64),
477            "%globaltimer" => Ok(SpecialRegister::Globaltimer),
478            "%lanemask_lt" => Ok(SpecialRegister::LanemaskLt),
479            "%reserved_smem_offset_end" => Ok(SpecialRegister::ReservedSmemOffsetEnd),
480            "%cluster_ctaid" | "%cluster_ctaid.x" | "%cluster_ctaid.y" | "%cluster_ctaid.z" => {
481                Ok(SpecialRegister::ClusterCtaid(Axis::None))
482            }
483            "%globaltimer_hi" => Ok(SpecialRegister::GlobaltimerHi),
484            "%nclusterid" => Ok(SpecialRegister::Nclusterid),
485            "%smid" => Ok(SpecialRegister::Smid),
486            "%cluster_ctarank" | "%cluster_ctarank.x" | "%cluster_ctarank.y"
487            | "%cluster_ctarank.z" => Ok(SpecialRegister::ClusterCtarank(Axis::None)),
488            "%globaltimer_lo" => Ok(SpecialRegister::GlobaltimerLo),
489            "%nctaid" | "%nctaid.x" | "%nctaid.y" | "%nctaid.z" => {
490                Ok(SpecialRegister::Nctaid(Axis::None))
491            }
492            "%tid" | "%tid.x" | "%tid.y" | "%tid.z" => Ok(SpecialRegister::Tid(Axis::None)),
493            "%cluster_nctaid" | "%cluster_nctaid.x" | "%cluster_nctaid.y" | "%cluster_nctaid.z" => {
494                Ok(SpecialRegister::ClusterNctaid(Axis::None))
495            }
496            "%gridid" => Ok(SpecialRegister::Gridid),
497            "%nsmid" => Ok(SpecialRegister::Nsmid),
498            "%total_smem_size" => Ok(SpecialRegister::TotalSmemSize),
499            "%cluster_nctarank"
500            | "%cluster_nctarank.x"
501            | "%cluster_nctarank.y"
502            | "%cluster_nctarank.z" => Ok(SpecialRegister::ClusterNctarank(Axis::None)),
503            "%is_explicit_cluster" => Ok(SpecialRegister::IsExplicitCluster),
504            "%ntid" | "%ntid.x" | "%ntid.y" | "%ntid.z" => Ok(SpecialRegister::Ntid(Axis::None)),
505            "%warpid" => Ok(SpecialRegister::Warpid),
506            "%clusterid" => Ok(SpecialRegister::Clusterid),
507            "%laneid" => Ok(SpecialRegister::Laneid),
508            "%nwarpid" => Ok(SpecialRegister::Nwarpid),
509            "%WARPSZ" => Ok(SpecialRegister::WARPSZ),
510            "%ctaid" | "%ctaid.x" | "%ctaid.y" | "%ctaid.z" => {
511                Ok(SpecialRegister::Ctaid(Axis::None))
512            }
513            "%lanemask_eq" => Ok(SpecialRegister::LanemaskEq),
514            "%current_graph_exec" => Ok(SpecialRegister::CurrentGraphExec),
515            "%lanemask_ge" => Ok(SpecialRegister::LanemaskGe),
516            other => {
517                if let Some(num) = other.strip_prefix("%envreg") {
518                    let value = num
519                        .parse::<u8>()
520                        .map_err(|_| invalid_literal(span.clone(), name.clone()))?;
521                    if value <= 31 {
522                        return Ok(SpecialRegister::Envreg(value));
523                    }
524                    return Err(invalid_literal(
525                        span,
526                        format!("envreg index out of range: {value}"),
527                    ));
528                }
529
530                if let Some(num) = other.strip_prefix("%pm") {
531                    if let Some(rest) = num.strip_suffix("_64") {
532                        let value = rest
533                            .parse::<u8>()
534                            .map_err(|_| invalid_literal(span.clone(), name.clone()))?;
535                        if value <= 7 {
536                            return Ok(SpecialRegister::Pm64(value));
537                        }
538                        return Err(invalid_literal(
539                            span,
540                            format!("pm index out of range: {value}"),
541                        ));
542                    }
543
544                    let value = num
545                        .parse::<u8>()
546                        .map_err(|_| invalid_literal(span.clone(), name.clone()))?;
547                    if value <= 7 {
548                        return Ok(SpecialRegister::Pm(value));
549                    }
550                    return Err(invalid_literal(
551                        span,
552                        format!("pm index out of range: {value}"),
553                    ));
554                }
555
556                if let Some(num) = other.strip_prefix("%reserved_smem_offset_") {
557                    let value = num
558                        .parse::<u8>()
559                        .map_err(|_| invalid_literal(span.clone(), name.clone()))?;
560                    if value <= 1 {
561                        return Ok(SpecialRegister::ReservedSmemOffset(value));
562                    }
563                    return Err(invalid_literal(
564                        span,
565                        format!("reserved_smem_offset index out of range: {value}"),
566                    ));
567                }
568
569                Err(invalid_literal(
570                    span,
571                    format!("unknown special register {name}"),
572                ))
573            }
574        }
575    }
576}
577
578impl PtxParser for Operand {
579    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
580        let saved_pos = stream.position();
581        if let Ok(immediate) = Immediate::parse(stream) {
582            return Ok(Operand::Immediate(immediate));
583        }
584        stream.set_position(saved_pos);
585
586        if stream.check(|token| matches!(token, PtxToken::Register(_))) {
587            return Ok(Operand::Register(RegisterOperand::parse(stream)?));
588        }
589
590        if stream.check(|token| matches!(token, PtxToken::Identifier(_))) {
591            let (identifier, _) = stream.expect_identifier()?;
592            
593            // Check for arithmetic expression: identifier + immediate
594            let saved_pos_after_ident = stream.position();
595            if stream.expect(&PtxToken::Plus).is_ok() {
596                if let Ok(offset) = Immediate::parse(stream) {
597                    return Ok(Operand::SymbolOffset(identifier, offset));
598                }
599                // If parsing offset failed, backtrack
600                stream.set_position(saved_pos_after_ident);
601            }
602            
603            return Ok(Operand::Symbol(identifier));
604        }
605
606        let (token, span) = stream.peek()?;
607        Err(unexpected_value(
608            span.clone(),
609            &["operand"],
610            format!("{token:?}"),
611        ))
612    }
613}
614
615impl PtxParser for VectorOperand {
616    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
617        let (_, brace_span) = stream.expect(&PtxToken::LBrace)?;
618        let mut operands = Vec::new();
619
620        loop {
621            operands.push(Operand::parse(stream)?);
622            if stream
623                .consume_if(|token| matches!(token, PtxToken::Comma))
624                .is_some()
625            {
626                continue;
627            }
628            break;
629        }
630
631        stream.expect(&PtxToken::RBrace)?;
632
633        match operands.len() {
634            1 => Ok(VectorOperand::Vector1(operands.remove(0))),
635            2 => Ok(VectorOperand::Vector2([
636                operands.remove(0),
637                operands.remove(0),
638            ])),
639            3 => Ok(VectorOperand::Vector3([
640                operands.remove(0),
641                operands.remove(0),
642                operands.remove(0),
643            ])),
644            4 => Ok(VectorOperand::Vector4([
645                operands.remove(0),
646                operands.remove(0),
647                operands.remove(0),
648                operands.remove(0),
649            ])),
650            8 => Ok(VectorOperand::Vector8([
651                operands.remove(0),
652                operands.remove(0),
653                operands.remove(0),
654                operands.remove(0),
655                operands.remove(0),
656                operands.remove(0),
657                operands.remove(0),
658                operands.remove(0),
659            ])),
660            other => Err(invalid_literal(
661                brace_span.clone(),
662                format!("expected operand vector of length 1..=4 or 8, found {other}"),
663            )),
664        }
665    }
666}
667
668impl PtxParser for GeneralOperand {
669    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
670        if stream.check(|token| matches!(token, PtxToken::LBrace)) {
671            Ok(GeneralOperand::Vec(VectorOperand::parse(stream)?))
672        } else {
673            Ok(GeneralOperand::Single(Operand::parse(stream)?))
674        }
675    }
676}
677
678impl PtxParser for TexHandler2 {
679    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
680        stream.expect(&PtxToken::LBracket)?;
681        let first = GeneralOperand::parse(stream)?;
682        stream.expect(&PtxToken::Comma)?;
683        let second = GeneralOperand::parse(stream)?;
684        stream.expect(&PtxToken::RBracket)?;
685        Ok(TexHandler2([first, second]))
686    }
687}
688
689impl PtxParser for TexHandler3 {
690    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
691        stream.expect(&PtxToken::LBracket)?;
692        let handle = GeneralOperand::parse(stream)?;
693        stream.expect(&PtxToken::Comma)?;
694        let sampler = GeneralOperand::parse(stream)?;
695        stream.expect(&PtxToken::Comma)?;
696        let coords = GeneralOperand::parse(stream)?;
697        stream.expect(&PtxToken::RBracket)?;
698
699        Ok(TexHandler3 {
700            handle,
701            sampler,
702            coords,
703        })
704    }
705}
706
707impl PtxParser for TexHandler3Optional {
708    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
709        stream.expect(&PtxToken::LBracket)?;
710        let handle = GeneralOperand::parse(stream)?;
711        stream.expect(&PtxToken::Comma)?;
712        let second = GeneralOperand::parse(stream)?;
713
714        let (sampler, coords) = if stream
715            .consume_if(|token| matches!(token, PtxToken::Comma))
716            .is_some()
717        {
718            let coords = GeneralOperand::parse(stream)?;
719            (Some(second), coords)
720        } else {
721            (None, second)
722        };
723
724        stream.expect(&PtxToken::RBracket)?;
725
726        Ok(TexHandler3Optional {
727            handle,
728            sampler,
729            coords,
730        })
731    }
732}
733
734impl PtxParser for AddressBase {
735    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
736        if stream.check(|token| matches!(token, PtxToken::Register(_))) {
737            Ok(AddressBase::Register(RegisterOperand::parse(stream)?))
738        } else if stream.check(|token| matches!(token, PtxToken::Identifier(_))) {
739            Ok(AddressBase::Variable(VariableSymbol::parse(stream)?))
740        } else {
741            let (token, span) = stream.peek()?;
742            Err(unexpected_value(
743                span.clone(),
744                &["register", "identifier"],
745                format!("{token:?}"),
746            ))
747        }
748    }
749}
750
751impl PtxParser for AddressOffset {
752    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
753        if stream
754            .consume_if(|token| matches!(token, PtxToken::Plus))
755            .is_some()
756        {
757            if stream.check(|token| matches!(token, PtxToken::Register(_))) {
758                Ok(AddressOffset::Register(RegisterOperand::parse(stream)?))
759            } else {
760                Ok(AddressOffset::Immediate(
761                    Sign::Positive,
762                    Immediate::parse(stream)?,
763                ))
764            }
765        } else if stream
766            .consume_if(|token| matches!(token, PtxToken::Minus))
767            .is_some()
768        {
769            Ok(AddressOffset::Immediate(
770                Sign::Negative,
771                Immediate::parse(stream)?,
772            ))
773        } else {
774            let (token, span) = stream.peek()?;
775            Err(unexpected_value(
776                span.clone(),
777                &["+", "-"],
778                format!("{token:?}"),
779            ))
780        }
781    }
782}
783
784impl PtxParser for AddressOperand {
785    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
786        if stream.check(|token| matches!(token, PtxToken::Identifier(_))) {
787            let saved = stream.position();
788            let (identifier, _) = stream.expect_identifier()?;
789            if stream
790                .consume_if(|token| matches!(token, PtxToken::LBracket))
791                .is_some()
792            {
793                let immediate = Immediate::parse(stream)?;
794                stream.expect(&PtxToken::RBracket)?;
795                return Ok(AddressOperand::Array(VariableSymbol(identifier), immediate));
796            } else {
797                stream.set_position(saved);
798            }
799        }
800
801        stream.expect(&PtxToken::LBracket)?;
802
803        if stream.check(|token| matches!(token, PtxToken::Minus)) {
804            let pos = stream.position();
805            stream.consume()?;
806            if stream.check(|token| is_numeric_token(token)) {
807                let mut immediate = Immediate::parse(stream)?;
808                immediate.0.insert(0, '-');
809                stream.expect(&PtxToken::RBracket)?;
810                return Ok(AddressOperand::ImmediateAddress(immediate));
811            } else {
812                stream.set_position(pos);
813            }
814        }
815
816        if stream.check(|token| is_numeric_token(token)) {
817            let immediate = Immediate::parse(stream)?;
818            stream.expect(&PtxToken::RBracket)?;
819            return Ok(AddressOperand::ImmediateAddress(immediate));
820        }
821
822        let base = AddressBase::parse(stream)?;
823        let offset = if stream.check(|token| matches!(token, PtxToken::Plus | PtxToken::Minus)) {
824            Some(AddressOffset::parse(stream)?)
825        } else {
826            None
827        };
828        stream.expect(&PtxToken::RBracket)?;
829
830        Ok(AddressOperand::Offset(base, offset))
831    }
832}
833
834impl PtxParser for FunctionSymbol {
835    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
836        let (name, _) = stream.expect_identifier()?;
837        Ok(FunctionSymbol(name))
838    }
839}
840
841impl PtxParser for VariableSymbol {
842    fn parse(stream: &mut PtxTokenStream) -> Result<Self, PtxParseError> {
843        let (name, _) = stream.expect_identifier()?;
844        Ok(VariableSymbol(name))
845    }
846}