/* Tool to sort attribute sets. Primarily useful for keeping all-packages.nix tidy. To compile: $ strc -i ../../maintainers/scripts/sort-attrs.str -la stratego-lib Typical invocation: $ sglr -m -p ~/Dev/nix/src/libexpr/nix.tbl -i all-packages.nix \ | implode-asfix --lex \ | ../../maintainers/scripts/sort-attrs \ | asfix-yield */ module sort-attrs imports libstratego-lib libstratego-sglr strategies no-wsp = !appl(prod([], cf(opt(layout())), no-attrs()), []) rules list-sep(s): [] -> [] list-sep(s): [x | xs] -> [[x | before] | [split | after]] where xs => (before, split, after) list-sep(s): [x | xs] -> [[x | xs]] where xs list-sep-end(s): xs -> [ (before, [split]) | after] where xs => (before, split, after) list-sep-end(s): xs -> [xs] where xs sort-attrs: appl(p@prod(_, _, attrs([term(cons("Attrs"))])), [ lit("{") , ws1 , appl(p2@list(cf(iter-star(sort("Bind")))), attrs) , ws2 , lit("}") ] ) -> appl(p, [lit("{"), , appl(p2, attrs'), ws2, lit("}")]) where "found it"; [ws1 | attrs] => withWSP; withWSP => groups; groups; [x''' | xs'] where x => (x', starts); [x' | xs] => [x'' | xs']; <[] <+ \x -> ["\n\n\n" | x]\ > starts => starts'; (starts', x'') => x''' \ })> groups => attrs'; "did it" attach-wsp: [a, b | cs] -> [(a, b) | cs] attach-wsp: [] -> [] strategies starts-section = ?x@(appl(prod([cf(layout())], cf(opt(layout())), no-attrs()), cs), attr); cs; !x rules sortable-section = ?[s]; !s; explode-string; not(fetch({x: ?x; !(x, 97); geq})) remove-section-start: (appl(prod([cf(layout())], cf(opt(layout())), no-attrs()), cs), attr) -> ((appl(prod([cf(layout())], cf(opt(layout())), no-attrs()), cs'), attr), starts) where !cs; list-sep-end(?10); // separate into lines, keeping the \n map(implode-string); partition(where(is-substring(!"###"))) => (starts, rest); rest => cs' regularise-empty-lines: (appl(prod([cf(layout())], cf(opt(layout())), no-attrs()), cs), attr) -> (appl(prod([cf(layout())], cf(opt(layout())), no-attrs()), cs''), attr) where // separate into lines, keeping the \n // last whitespace is significant, keep cs => (init, last); init => cs'; // remove whitespace-only lines [ "\n\n", cs', last] => cs'' // add one empty line /* Dirty hack: *do* keep the first empty line following a non-empty line. !!! order matters */ regularise-empty-lines': [] -> [] regularise-empty-lines': [x, y | xs] -> [x, y | xs] where x; y regularise-empty-lines': [x | xs] -> [x | xs] where x regularise-empty-lines': [x | xs] -> xs where x prepend-layout: (text, (appl(prod([cf(layout())], cf(opt(layout())), no-attrs()), cs), attr)) -> (appl(prod([cf(layout())], cf(opt(layout())), no-attrs()), cs''), attr) where cs => cs'; ( text, cs') => cs'' compare-attrs: x@ ( (_, appl(p1@prod(_, _, attrs([term(cons("Bind"))])), [id1 | xs1])) , (_, appl(p2@prod(_, _, attrs([term(cons("Bind"))])), [id2 | xs2])) ) -> x where (id1, id2) strategies main = io-wrap( oncetd(sort-attrs) )