@@ -478,15 +478,102 @@ else instance
478478class ValidateAfterName :: Symbol -> Symbol -> Row Type -> Constraint
479479class ValidateAfterName acc rest cols
480480
481+-- End of input
481482instance FlushWord acc cols => ValidateAfterName acc "" cols
483+
484+-- Non-empty: branch on first character
482485else instance
483486 ( FlushWord acc cols
484− , Symbol.Cons "," tail rest
485− , SkipSpaces tail rest'
486− , ValidateColumns rest' cols
487+ , Symbol.Cons h t rest
488+ , ValidateAfterNameByHead h t cols
487489 ) =>
488490 ValidateAfterName acc rest cols
489491
492+class ValidateAfterNameByHead :: Symbol -> Symbol -> Row Type -> Constraint
493+class ValidateAfterNameByHead head tail cols
494+
495+-- Comma: done with this column, continue
496+instance
497+ ( SkipSpaces tail rest
498+ , ValidateColumns rest cols
499+ ) =>
500+ ValidateAfterNameByHead "," tail cols
501+
502+-- Anything else (e.g. "AS ..."): extract the word and handle
503+else instance
504+ ( Symbol.Append h t rest
505+ , ExtractWord rest word afterWord
506+ , HandleAfterColumnWord word afterWord cols
507+ ) =>
508+ ValidateAfterNameByHead h t cols
509+
510+-- Extract the first word from a Symbol (up to space or end)
511+class ExtractWord :: Symbol -> Symbol -> Symbol -> Constraint
512+class ExtractWord sym word rest | sym -> word rest
513+
514+instance ExtractWord "" "" ""
515+else instance
516+ ( Symbol.Cons h t sym
517+ , ExtractWordGo h t "" word rest
518+ ) =>
519+ ExtractWord sym word rest
520+
521+class ExtractWordGo :: Symbol -> Symbol -> Symbol -> Symbol -> Symbol -> Constraint
522+class ExtractWordGo head tail acc word rest | head tail acc -> word rest
523+
524+-- Stop on space
525+instance (SkipSpaces tail rest) => ExtractWordGo " " tail acc acc rest
526+-- Stop on comma (keep comma in rest)
527+else instance Symbol.Cons "," tail rest => ExtractWordGo "," tail acc acc rest
528+-- End of string
529+else instance Symbol.Append acc h word => ExtractWordGo h "" acc word ""
530+-- Regular char: accumulate
531+else instance
532+ ( Symbol.Append acc h acc'
533+ , Symbol.Cons nextH nextT tail
534+ , ExtractWordGo nextH nextT acc' word rest
535+ ) =>
536+ ExtractWordGo h tail acc word rest
537+
538+-- After a column name + space + word: if AS, skip alias; otherwise error
539+class HandleAfterColumnWord :: Symbol -> Symbol -> Row Type -> Constraint
540+class HandleAfterColumnWord word rest cols
541+
542+-- AS alias: skip the alias, then expect comma or end
543+instance SkipAlias rest cols => HandleAfterColumnWord "AS" rest cols
544+else instance SkipAlias rest cols => HandleAfterColumnWord "as" rest cols
545+
546+-- Skip the alias word, then expect comma or end of string
547+class SkipAlias :: Symbol -> Row Type -> Constraint
548+class SkipAlias sym cols
549+
550+instance SkipAlias "" cols
551+else instance
552+ ( ExtractWord sym _alias afterAlias
553+ , SkipSpaces afterAlias rest
554+ , ExpectCommaOrEnd rest cols
555+ ) =>
556+ SkipAlias sym cols
557+
558+class ExpectCommaOrEnd :: Symbol -> Row Type -> Constraint
559+class ExpectCommaOrEnd sym cols
560+
561+instance ExpectCommaOrEnd "" cols
562+else instance
563+ ( Symbol.Cons h t sym
564+ , ExpectCommaOrEndByHead h t cols
565+ ) =>
566+ ExpectCommaOrEnd sym cols
567+
568+class ExpectCommaOrEndByHead :: Symbol -> Symbol -> Row Type -> Constraint
569+class ExpectCommaOrEndByHead head tail cols
570+
571+instance
572+ ( SkipSpaces tail rest
573+ , ValidateColumns rest cols
574+ ) =>
575+ ExpectCommaOrEndByHead "," tail cols
576+
490577-- Skip leading spaces
491578class SkipSpaces :: Symbol -> Symbol -> Constraint
492579class SkipSpaces sym result | sym -> result