aboutsummaryrefslogtreecommitdiff
path: root/nix/builder/parser.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nix/builder/parser.nix')
-rw-r--r--nix/builder/parser.nix141
1 files changed, 141 insertions, 0 deletions
diff --git a/nix/builder/parser.nix b/nix/builder/parser.nix
new file mode 100644
index 0000000..eb6f75e
--- /dev/null
+++ b/nix/builder/parser.nix
@@ -0,0 +1,141 @@
+# Parse go.mod in Nix
+# Returns a Nix structure with the contents of the go.mod passed in
+# in normalised form.
+
+let
+ inherit (builtins) elemAt mapAttrs split foldl' match filter typeOf hasAttr length;
+
+ # Strip lines with comments & other junk
+ stripStr = s: elemAt (split "^ *" (elemAt (split " *$" s) 0)) 2;
+ stripLines = initialLines: foldl' (acc: f: f acc) initialLines [
+ # Strip comments
+ (lines: map
+ (l: stripStr (elemAt (splitString "//" l) 0))
+ lines)
+
+ # Strip leading tabs characters
+ (lines: map (l: elemAt (match "(\t)?(.*)" l) 1) lines)
+
+ # Filter empty lines
+ (filter (l: l != ""))
+ ];
+
+ # Parse lines into a structure
+ parseLines = lines: (foldl'
+ (acc: l:
+ let
+ m = match "([^ )]*) *(.*)" l;
+ directive = elemAt m 0;
+ rest = elemAt m 1;
+
+ # Maintain parser state (inside parens or not)
+ inDirective =
+ if rest == "(" then directive
+ else if rest == ")" then null
+ else acc.inDirective
+ ;
+
+ in
+ {
+ data = (acc.data // (
+ if directive == "" && rest == ")" then { }
+ else if inDirective != null && rest == "(" && ! hasAttr inDirective acc.data then {
+ ${inDirective} = { };
+ }
+ else if rest == "(" || rest == ")" then { }
+ else if inDirective != null then {
+ ${inDirective} = acc.data.${inDirective} // { ${directive} = rest; };
+ } else if directive == "replace" then
+ (
+ let
+ segments = split " => " rest;
+ getSegment = elemAt segments;
+ in
+ assert length segments == 3; {
+ replace = acc.data.replace // {
+ ${getSegment 0} = "=> ${getSegment 2}";
+ };
+ }
+ )
+ else {
+ ${directive} = rest;
+ }
+ )
+ );
+ inherit inDirective;
+ })
+ {
+ inDirective = null;
+ data = {
+ require = { };
+ replace = { };
+ exclude = { };
+ };
+ }
+ lines
+ ).data;
+
+ normaliseDirectives = data: (
+ let
+ normaliseString = s:
+ let
+ m = builtins.match "([^ ]+) (.+)" s;
+ in
+ {
+ ${elemAt m 0} = elemAt m 1;
+ };
+ require = data.require or { };
+ replace = data.replace or { };
+ exclude = data.exclude or { };
+ in
+ data // {
+ require =
+ if typeOf require == "string" then normaliseString require
+ else require;
+ replace =
+ if typeOf replace == "string" then normaliseString replace
+ else replace;
+ }
+ );
+
+ parseVersion = ver:
+ let
+ m = elemAt (match "([^-]+)-?([^-]*)-?([^-]*)" ver);
+ v = elemAt (match "([^+]+)\\+?(.*)" (m 0));
+ in
+ {
+ version = v 0;
+ versionSuffix = v 1;
+ date = m 1;
+ rev = m 2;
+ };
+
+ parseReplace = data: (
+ data // {
+ replace =
+ mapAttrs
+ (_: v:
+ let
+ m = match "=> ([^ ]+) (.+)" v;
+ m2 = match "=> (.*+)" v;
+ in
+ if m != null then {
+ goPackagePath = elemAt m 0;
+ version = elemAt m 1;
+ } else {
+ path = elemAt m2 0;
+ })
+ data.replace;
+ }
+ );
+
+ splitString = sep: s: filter (t: t != [ ]) (split sep s);
+
+in
+contents:
+foldl' (acc: f: f acc) (splitString "\n" contents) [
+ stripLines
+ parseLines
+ normaliseDirectives
+ parseReplace
+]