@@ -245,7 +245,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
245245
246246// test builtin_expr
247247// fn foo() {
248- // builtin#asm(0 );
248+ // builtin#asm("" );
249249// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
250250// builtin#offset_of(Foo, bar.baz.0);
251251// }
@@ -297,18 +297,175 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
297297 p. expect ( T ! [ ')' ] ) ;
298298 Some ( m. complete ( p, FORMAT_ARGS_EXPR ) )
299299 } else if p. at_contextual_kw ( T ! [ asm] ) {
300- p. bump_remap ( T ! [ asm] ) ;
301- p. expect ( T ! [ '(' ] ) ;
302- // FIXME: We just put expression here so highlighting kind of keeps working
303- expr ( p) ;
304- p. expect ( T ! [ ')' ] ) ;
305- Some ( m. complete ( p, ASM_EXPR ) )
300+ parse_asm_expr ( p, m)
306301 } else {
307302 m. abandon ( p) ;
308303 None
309304 }
310305}
311306
307+ // test asm_expr
308+ // fn foo() {
309+ // builtin#asm(
310+ // "mov {tmp}, {x}",
311+ // "shl {tmp}, 1",
312+ // "shl {x}, 2",
313+ // "add {x}, {tmp}",
314+ // x = inout(reg) x,
315+ // tmp = out(reg) _,
316+ // );
317+ // }
318+ fn parse_asm_expr ( p : & mut Parser < ' _ > , m : Marker ) -> Option < CompletedMarker > {
319+ p. bump_remap ( T ! [ asm] ) ;
320+ p. expect ( T ! [ '(' ] ) ;
321+ if expr ( p) . is_none ( ) {
322+ p. err_and_bump ( "expected asm template" ) ;
323+ }
324+ let mut allow_templates = true ;
325+ while !p. at ( EOF ) && !p. at ( T ! [ ')' ] ) {
326+ p. expect ( T ! [ , ] ) ;
327+ // accept trailing commas
328+ if p. at ( T ! [ ')' ] ) {
329+ break ;
330+ }
331+
332+ // Parse clobber_abi
333+ if p. eat_contextual_kw ( T ! [ clobber_abi] ) {
334+ parse_clobber_abi ( p) ;
335+ allow_templates = false ;
336+ continue ;
337+ }
338+
339+ // Parse options
340+ if p. eat_contextual_kw ( T ! [ options] ) {
341+ parse_options ( p) ;
342+ allow_templates = false ;
343+ continue ;
344+ }
345+
346+ // Parse operand names
347+ if p. at ( T ! [ ident] ) && p. nth_at ( 1 , T ! [ =] ) {
348+ name ( p) ;
349+ p. bump ( T ! [ =] ) ;
350+ allow_templates = false ;
351+ true
352+ } else {
353+ false
354+ } ;
355+
356+ let op = p. start ( ) ;
357+ if p. eat ( T ! [ in] ) {
358+ parse_reg ( p) ;
359+ expr ( p) ;
360+ op. complete ( p, ASM_REG_OPERAND ) ;
361+ } else if p. eat_contextual_kw ( T ! [ out] ) {
362+ parse_reg ( p) ;
363+ expr ( p) ;
364+ op. complete ( p, ASM_REG_OPERAND ) ;
365+ } else if p. eat_contextual_kw ( T ! [ lateout] ) {
366+ parse_reg ( p) ;
367+ expr ( p) ;
368+ op. complete ( p, ASM_REG_OPERAND ) ;
369+ } else if p. eat_contextual_kw ( T ! [ inout] ) {
370+ parse_reg ( p) ;
371+ expr ( p) ;
372+ if p. eat ( T ! [ =>] ) {
373+ expr ( p) ;
374+ }
375+ op. complete ( p, ASM_REG_OPERAND ) ;
376+ } else if p. eat_contextual_kw ( T ! [ inlateout] ) {
377+ parse_reg ( p) ;
378+ expr ( p) ;
379+ if p. eat ( T ! [ =>] ) {
380+ expr ( p) ;
381+ }
382+ op. complete ( p, ASM_REG_OPERAND ) ;
383+ } else if p. eat_contextual_kw ( T ! [ label] ) {
384+ block_expr ( p) ;
385+ op. complete ( p, ASM_LABEL ) ;
386+ } else if p. eat ( T ! [ const ] ) {
387+ expr ( p) ;
388+ op. complete ( p, ASM_CONST ) ;
389+ } else if p. eat_contextual_kw ( T ! [ sym] ) {
390+ expr ( p) ;
391+ op. complete ( p, ASM_SYM ) ;
392+ } else if allow_templates {
393+ op. abandon ( p) ;
394+ if expr ( p) . is_none ( ) {
395+ p. err_and_bump ( "expected asm template" ) ;
396+ }
397+ continue ;
398+ } else {
399+ op. abandon ( p) ;
400+ p. err_and_bump ( "expected asm operand" ) ;
401+ if p. at ( T ! [ '}' ] ) {
402+ break ;
403+ }
404+ continue ;
405+ } ;
406+ allow_templates = false ;
407+ }
408+ p. expect ( T ! [ ')' ] ) ;
409+ Some ( m. complete ( p, ASM_EXPR ) )
410+ }
411+
412+ fn parse_options ( p : & mut Parser < ' _ > ) {
413+ p. expect ( T ! [ '(' ] ) ;
414+
415+ while !p. eat ( T ! [ ')' ] ) && !p. at ( EOF ) {
416+ const OPTIONS : & [ SyntaxKind ] = & [
417+ T ! [ pure] ,
418+ T ! [ nomem] ,
419+ T ! [ readonly] ,
420+ T ! [ preserves_flags] ,
421+ T ! [ noreturn] ,
422+ T ! [ nostack] ,
423+ T ! [ may_unwind] ,
424+ T ! [ att_syntax] ,
425+ T ! [ raw] ,
426+ ] ;
427+
428+ if !OPTIONS . iter ( ) . any ( |& syntax| p. eat ( syntax) ) {
429+ p. err_and_bump ( "expected asm option" ) ;
430+ continue ;
431+ }
432+
433+ // Allow trailing commas
434+ if p. eat ( T ! [ ')' ] ) {
435+ break ;
436+ }
437+ p. expect ( T ! [ , ] ) ;
438+ }
439+ }
440+
441+ fn parse_clobber_abi ( p : & mut Parser < ' _ > ) {
442+ p. expect ( T ! [ '(' ] ) ;
443+
444+ while !p. eat ( T ! [ ')' ] ) && !p. at ( EOF ) {
445+ if !p. expect ( T ! [ string] ) {
446+ break ;
447+ }
448+
449+ // Allow trailing commas
450+ if p. eat ( T ! [ ')' ] ) {
451+ break ;
452+ }
453+ p. expect ( T ! [ , ] ) ;
454+ }
455+ }
456+
457+ fn parse_reg ( p : & mut Parser < ' _ > ) {
458+ p. expect ( T ! [ '(' ] ) ;
459+ if p. at ( T ! [ ident] ) {
460+ name_ref ( p)
461+ } else if p. at ( T ! [ string] ) {
462+ p. bump_any ( )
463+ } else {
464+ p. err_and_bump ( "expected register name" ) ;
465+ }
466+ p. expect ( T ! [ ')' ] ) ;
467+ }
468+
312469// test array_expr
313470// fn foo() {
314471// [];
0 commit comments