@@ -353,3 +353,307 @@ static Type GetParserValueType(object parser)
353353
354354 public override string ToString ( ) => Name ?? $ "LeftAssociative({ _parser } )";
355355}
356+
357+
358+ public sealed class LeftAssociativeWithContext < T , TInput > : Parser < T > , ICompilable , ISourceable
359+ {
360+ private readonly Parser < T > _parser ;
361+ private readonly ( Parser < TInput > Op , Func < ParseContext , T , T , T > Factory ) [ ] _operators ;
362+
363+ public LeftAssociativeWithContext ( Parser < T > parser , ( Parser < TInput > op , Func < ParseContext , T , T , T > factory ) [ ] operators )
364+ {
365+ _parser = parser ?? throw new ArgumentNullException ( nameof ( parser ) ) ;
366+ _operators = operators ?? throw new ArgumentNullException ( nameof ( operators ) ) ;
367+
368+ if ( _operators . Length == 0 )
369+ {
370+ throw new ArgumentException ( "At least one operator must be provided." , nameof ( operators ) ) ;
371+ }
372+ }
373+
374+ public override bool Parse ( ParseContext context , ref ParseResult < T > result )
375+ {
376+ context . EnterParser ( this ) ;
377+
378+ if ( ! _parser . Parse ( context , ref result ) )
379+ {
380+ context . ExitParser ( this ) ;
381+ return false ;
382+ }
383+
384+ var value = result . Value ;
385+ var end = result . End ;
386+
387+ while ( true )
388+ {
389+ var operatorPosition = context . Scanner . Cursor . Position ;
390+ var operatorResult = new ParseResult < TInput > ( ) ;
391+ Func < ParseContext , T , T , T > ? matchedFactory = null ;
392+
393+ foreach ( var ( op , factory ) in _operators )
394+ {
395+ if ( op . Parse ( context , ref operatorResult ) )
396+ {
397+ matchedFactory = factory ;
398+ break ;
399+ }
400+ }
401+
402+ if ( matchedFactory == null )
403+ {
404+ break ;
405+ }
406+
407+ var rightResult = new ParseResult < T > ( ) ;
408+ if ( ! _parser . Parse ( context , ref rightResult ) )
409+ {
410+ context . Scanner . Cursor . ResetPosition ( operatorPosition ) ;
411+ break ;
412+ }
413+
414+ value = matchedFactory ( context , value , rightResult . Value ) ;
415+ end = rightResult . End ;
416+ }
417+
418+ result = new ParseResult < T > ( result . Start , end , value ) ;
419+
420+ context . ExitParser ( this ) ;
421+ return true ;
422+ }
423+
424+ public CompilationResult Compile ( CompilationContext context )
425+ {
426+ var result = context . CreateCompilationResult < T > ( ) ;
427+
428+ var nextNum = context . NextNumber ;
429+ var currentValue = result . DeclareVariable < T > ( $ "leftAssocCtxValue{ nextNum } ") ;
430+ var matchedFactory = result . DeclareVariable < Func < ParseContext , T , T , T > > ( $ "matchedFactoryCtx{ nextNum } ") ;
431+ var operatorPosition = result . DeclareVariable < TextPosition > ( $ "leftAssocCtxPos{ nextNum } ") ;
432+
433+ var breakLabel = Expression . Label ( $ "leftAssocCtxBreak{ nextNum } ") ;
434+
435+ var firstParserResult = _parser . Build ( context ) ;
436+ var rightParserResult = _parser . Build ( context ) ;
437+
438+ var operatorChecks = new List < Expression > ( ) ;
439+ var allOperatorVariables = new List < ParameterExpression > ( ) ;
440+
441+ for ( int i = 0 ; i < _operators . Length ; i ++ )
442+ {
443+ var ( op , factory ) = _operators [ i ] ;
444+ var opCompileResult = op . Build ( context ) ;
445+
446+ foreach ( var variable in opCompileResult . Variables )
447+ {
448+ allOperatorVariables . Add ( variable ) ;
449+ }
450+
451+ var factoryConst = Expression . Constant ( factory ) ;
452+
453+ if ( i > 0 )
454+ {
455+ operatorChecks . Add (
456+ Expression . IfThen (
457+ Expression . Equal ( matchedFactory , Expression . Constant ( null , typeof ( Func < ParseContext , T , T , T > ) ) ) ,
458+ Expression . Block (
459+ opCompileResult . Body . Concat ( [
460+ Expression . IfThen (
461+ opCompileResult . Success ,
462+ Expression . Assign ( matchedFactory , factoryConst )
463+ )
464+ ] )
465+ )
466+ )
467+ ) ;
468+ }
469+ else
470+ {
471+ operatorChecks . AddRange ( opCompileResult . Body ) ;
472+ operatorChecks . Add (
473+ Expression . IfThen (
474+ opCompileResult . Success ,
475+ Expression . Assign ( matchedFactory , factoryConst )
476+ )
477+ ) ;
478+ }
479+ }
480+
481+ var scanner = Expression . Field ( context . ParseContext , nameof ( ParseContext . Scanner ) ) ;
482+ var cursor = Expression . Field ( scanner , nameof ( Scanner . Cursor ) ) ;
483+ var cursorPosition = Expression . Property ( cursor , nameof ( Cursor . Position ) ) ;
484+ var resetPosition = typeof ( Cursor ) . GetMethod ( nameof ( Cursor . ResetPosition ) , [ typeof ( TextPosition ) . MakeByRefType ( ) ] ) ! ;
485+
486+ var loopBody = Expression . Block (
487+ allOperatorVariables . Concat ( rightParserResult . Variables ) ,
488+ new Expression [ ]
489+ {
490+ Expression . Assign ( matchedFactory , Expression . Constant ( null , typeof ( Func < ParseContext , T , T , T > ) ) ) ,
491+ Expression . Assign ( operatorPosition , cursorPosition )
492+ }
493+ . Concat ( operatorChecks )
494+ . Concat ( [
495+ Expression . IfThen (
496+ Expression . Equal ( matchedFactory , Expression . Constant ( null , typeof ( Func < ParseContext , T , T , T > ) ) ) ,
497+ Expression . Break ( breakLabel )
498+ )
499+ ] )
500+ . Concat ( rightParserResult . Body )
501+ . Concat ( [
502+ Expression . IfThen (
503+ Expression . Not ( rightParserResult . Success ) ,
504+ Expression . Block (
505+ Expression . Call ( cursor , resetPosition , operatorPosition ) ,
506+ Expression . Break ( breakLabel )
507+ )
508+ ) ,
509+ Expression . Assign ( currentValue ,
510+ Expression . Invoke ( matchedFactory , context . ParseContext , currentValue , rightParserResult . Value ) )
511+ ] )
512+ ) ;
513+
514+ var loopExpr = Expression . Loop ( loopBody , breakLabel ) ;
515+
516+ result . Body . Add (
517+ Expression . Block (
518+ firstParserResult . Variables ,
519+ new Expression [ ] { Expression . Block ( firstParserResult . Body ) } . Concat ( [
520+ Expression . IfThenElse (
521+ firstParserResult . Success ,
522+ Expression . Block (
523+ Expression . Assign ( currentValue , firstParserResult . Value ) ,
524+ loopExpr ,
525+ Expression . Assign ( result . Success , Expression . Constant ( true ) ) ,
526+ context . DiscardResult ? Expression . Empty ( ) : Expression . Assign ( result . Value , currentValue )
527+ ) ,
528+ Expression . Assign ( result . Success , Expression . Constant ( false ) )
529+ )
530+ ] )
531+ )
532+ ) ;
533+
534+ return result ;
535+ }
536+
537+ public SourceResult GenerateSource ( SourceGenerationContext context )
538+ {
539+ ThrowHelper . ThrowIfNull ( context , nameof ( context ) ) ;
540+
541+ if ( _parser is not ISourceable parserSourceable )
542+ {
543+ throw new NotSupportedException ( "LeftAssociative requires the base parser to be source-generatable." ) ;
544+ }
545+
546+ var result = context . CreateResult ( typeof ( T ) ) ;
547+ var ctx = context . ParseContextName ;
548+ var cursorName = context . CursorName ;
549+ var valueTypeName = SourceGenerationContext . GetTypeName ( typeof ( T ) ) ;
550+
551+ var uniqueId = context . NextNumber ( ) ;
552+ var operatorMatchedName = $ "opMatched{ context . NextNumber ( ) } ";
553+
554+ result . Body . Add ( $ "bool { operatorMatchedName } = false;") ;
555+
556+ static Type GetParserValueType ( object parser )
557+ {
558+ var type = parser . GetType ( ) ;
559+ while ( type != null )
560+ {
561+ if ( type . IsGenericType && type . GetGenericTypeDefinition ( ) . FullName == "Parlot.Fluent.Parser`1" )
562+ {
563+ return type . GetGenericArguments ( ) [ 0 ] ;
564+ }
565+ type = type . BaseType ! ;
566+ }
567+ throw new InvalidOperationException ( "Unable to determine parser value type." ) ;
568+ }
569+
570+ var baseHelperName = context . Helpers
571+ . GetOrCreate ( parserSourceable , $ "{ context . MethodNamePrefix } _LeftAssocCtx{ uniqueId } ", valueTypeName , ( ) => parserSourceable . GenerateSource ( context ) )
572+ . MethodName ;
573+
574+ if ( context . DiscardResult )
575+ {
576+ result . Body . Add ( $ "if ({ baseHelperName } ({ ctx } , out _))") ;
577+ }
578+ else
579+ {
580+ result . Body . Add ( $ "if ({ baseHelperName } ({ ctx } , out { result . ValueVariable } ))") ;
581+ }
582+
583+ result . Body . Add ( "{" ) ;
584+ result . Body . Add ( " while (true)" ) ;
585+ result . Body . Add ( " {" ) ;
586+ result . Body . Add ( $ " { operatorMatchedName } = false;") ;
587+
588+ var operatorPositionName = $ "opPos{ context . NextNumber ( ) } ";
589+ result . Body . Add ( $ " var { operatorPositionName } = { cursorName } .Position;") ;
590+
591+ for ( int i = 0 ; i < _operators . Length ; i ++ )
592+ {
593+ var ( op , factory ) = _operators [ i ] ;
594+
595+ if ( op is not ISourceable opSourceable )
596+ {
597+ throw new NotSupportedException ( "LeftAssociative requires all operator parsers to be source-generatable." ) ;
598+ }
599+
600+ var factoryFieldName = context . RegisterLambda ( factory ) ;
601+
602+ var opValueTypeName = SourceGenerationContext . GetTypeName ( GetParserValueType ( opSourceable ) ) ;
603+ var opHelperName = context . Helpers
604+ . GetOrCreate ( opSourceable , $ "{ context . MethodNamePrefix } _LeftAssocCtx{ uniqueId } ", opValueTypeName , ( ) => opSourceable . GenerateSource ( context ) )
605+ . MethodName ;
606+
607+ var opResultName = $ "opResult{ context . NextNumber ( ) } ";
608+
609+ var indent = " " ;
610+ if ( i == 0 )
611+ {
612+ result . Body . Add ( $ "{ indent } if ({ opHelperName } ({ ctx } , out _))") ;
613+ }
614+ else
615+ {
616+ result . Body . Add ( $ "{ indent } if (!{ operatorMatchedName } )") ;
617+ result . Body . Add ( $ "{ indent } {{") ;
618+ result . Body . Add ( $ "{ indent } if ({ opHelperName } ({ ctx } , out _))") ;
619+ }
620+
621+ var innerIndent = i == 0 ? indent : $ "{ indent } ";
622+ result . Body . Add ( $ "{ innerIndent } {{") ;
623+ result . Body . Add ( $ "{ innerIndent } if ({ baseHelperName } ({ ctx } , out var { opResultName } RightValue))") ;
624+ result . Body . Add ( $ "{ innerIndent } {{") ;
625+ result . Body . Add ( $ "{ innerIndent } { operatorMatchedName } = true;") ;
626+
627+ if ( ! context . DiscardResult )
628+ {
629+ result . Body . Add ( $ "{ innerIndent } { result . ValueVariable } = { factoryFieldName } ({ ctx } , { result . ValueVariable } , { opResultName } RightValue);") ;
630+ }
631+ else
632+ {
633+ result . Body . Add ( $ "{ innerIndent } { factoryFieldName } ({ ctx } , { result . ValueVariable } , { opResultName } RightValue);") ;
634+ }
635+
636+ result . Body . Add ( $ "{ innerIndent } }}") ;
637+ result . Body . Add ( $ "{ innerIndent } else") ;
638+ result . Body . Add ( $ "{ innerIndent } {{") ;
639+ result . Body . Add ( $ "{ innerIndent } { cursorName } .ResetPosition({ operatorPositionName } );") ;
640+ result . Body . Add ( $ "{ innerIndent } break;") ;
641+ result . Body . Add ( $ "{ innerIndent } }}") ;
642+ result . Body . Add ( $ "{ innerIndent } }}") ;
643+
644+ if ( i > 0 )
645+ {
646+ result . Body . Add ( $ "{ indent } }}") ;
647+ }
648+ }
649+
650+ result . Body . Add ( $ " if (!{ operatorMatchedName } ) break;") ;
651+ result . Body . Add ( " }" ) ;
652+ result . Body . Add ( $ " { result . SuccessVariable } = true;") ;
653+ result . Body . Add ( "}" ) ;
654+
655+ return result ;
656+ }
657+
658+ public override string ToString ( ) => Name ?? $ "LeftAssociative({ _parser } )";
659+ }
0 commit comments