haskell.nix/lib/spdx/parser.nix
toonn 07df700531
Spdx license expressions (#859)
* Add parser combinators to parse SPDX license expressions

The most important parser is `compoundExpression` it parses any SPDX
license expression into a list of SPDX simple-expressions.
The rationale is Nixpkgs' license metadata isn't capable of
distinguishing between the AND and OR relationships.
License exceptions aren't currently taken into account.

* Add tests for the SPDX parser combinators

I simply added a file with expressions that are expected to fail to
parse or parse successfully.

* Add the SPDX license list

The SPDX license list as attrsets following the nixpkgs lib.licenses
convention.

This uses fetchurl which is not ideal because it's not available during
pure evaluation.

It does not yet include the SPDX exceptions list.

* Refactor cabal-licenses.nix to use spdx/licenses.nix

The handling of the generic licenses is undecided still. Some have been
removed because they have better official identifiers.

* Refactor license mapping in builders

The common code in the comp- and setup-builders has been extracted and
refactored to use the SPDX expression parser.

* Use spdx-license-list-data from nixpkgs

This conveniently solves the impurity problem with using fetchurl : )
I'm not sure threading `pkgs` through everything to get access to the
spdx license list package is the right way to go about this.

* hscolour to "LGPL-2.1-only" and remove "LGPL"

* Use evalPackages for spdx and move shim to overlay

* Better fix for LGPL packages.

Co-authored-by: Hamish Mackenzie <Hamish.Mackenzie@iohk.io>
2021-01-14 14:33:40 +13:00

116 lines
3.7 KiB
Nix

let String = { mempty = ""; mappend = a: b: a + b; };
List = { singleton = x: [x]; };
pair = a: b: { _1 = a; _2 = b; };
Pair = { pure = s: pair String.mempty s;
mappend = a: b: pair (String.mappend a._1 b._1) b._2;
map = f: p: pair (f p._1) p._2;
};
compose = f: g: x: f (g x);
in with builtins; rec {
# Parser a = String -> [(a, String)]
string = str: s:
let strL = stringLength str;
sL = stringLength s;
in if str == substring 0 strL s
then [ (pair str (substring strL sL s)) ]
else [];
regex = capture_groups: re: s:
let result = match "(${re})(.*)" s;
in if result == null
then []
else [ (pair (elemAt result 0) (elemAt result (1 + capture_groups))) ];
# Left-biased choice
choice = cs: s:
if cs == []
then []
else let c = head cs;
cs' = tail cs;
result = c s;
in if result == []
then choice cs' s
else result;
optional = p: s:
let result = p s;
in if result == []
then [ (Pair.pure s) ]
else result;
chain = ps: s:
if ps == []
then [ (Pair.pure s) ]
else let p = head ps;
ps' = tail ps;
result = p s;
in if result == []
then []
else let r = head result;
rest = chain ps' r._2;
in if rest == []
then []
else let r' = head rest;
in [ (Pair.mappend r r') ];
idstring = regex 0 "[-\.0-9a-zA-Z]+";
license-ref = let documentref = chain [ (string "DocumentRef-")
idstring
(string ":")
];
in chain [
(optional documentref)
(string "LicenseRef-")
idstring
];
simple-expression = choice [ license-ref
(chain [ idstring (string "+") ])
idstring
];
compound-expression = let wrap = compose (map (Pair.map List.singleton));
in choice [
(s: let result = simple-expression s;
in if result == []
then []
else let r = head result;
rest = chain [(string " WITH ") idstring ] r._2;
in if rest == []
then []
else [(pair r._1 (head rest)._2)]
)
(s: let result = simple-expression s;
in if result == []
then []
else let r = head result;
firstLicense = r._1;
operator = choice [ (string " AND ")
(string " OR ")
]
r._2;
in if operator == []
then []
else let s' = (head operator)._2;
licenses = compound-expression s';
in if licenses == []
then []
else let ls = head licenses;
in [(pair ([firstLicense] ++ ls._1) ls._2)]
)
(wrap simple-expression)
(s: let openParen = string "(" s;
in if openParen == []
then []
else let result = compound-expression (head openParen)._2;
in if result == []
then []
else let r = head result;
closeParen = string ")" r._2;
in if closeParen == []
then []
else [(pair r._1 (head closeParen)._2)])
];
}