168 lines
3.7 KiB
Modula-2
168 lines
3.7 KiB
Modula-2
MODULE SemVer;
|
|
|
|
IMPORT Out;
|
|
|
|
TYPE
|
|
StringIdentifier = ARRAY 256 OF CHAR;
|
|
Version* = RECORD
|
|
Major*, Minor*, Patch*: INTEGER;
|
|
PreRelease*, Build*: StringIdentifier;
|
|
END;
|
|
|
|
PROCEDURE IsDigit(ch: CHAR): BOOLEAN;
|
|
BEGIN
|
|
RETURN (ch >= '0') & (ch <= '9');
|
|
END IsDigit;
|
|
|
|
PROCEDURE HasLeadingZero(s: ARRAY OF CHAR; i: INTEGER): BOOLEAN;
|
|
BEGIN
|
|
RETURN (s[i] = '0') & IsDigit(s[i + 1]);
|
|
END HasLeadingZero;
|
|
|
|
PROCEDURE ParseInt(s: ARRAY OF CHAR; VAR i, n: INTEGER; VAR ok: BOOLEAN);
|
|
VAR digit: INTEGER;
|
|
BEGIN
|
|
n := 0;
|
|
IF ~IsDigit(s[i]) THEN ok := FALSE; RETURN END;
|
|
|
|
WHILE (i < LEN(s)) & IsDigit(s[i]) DO
|
|
digit := ORD(s[i]) - ORD('0');
|
|
n := n * 10 + digit;
|
|
INC(i);
|
|
END;
|
|
ok := TRUE;
|
|
END ParseInt;
|
|
|
|
PROCEDURE IsIdentChar(ch: CHAR): BOOLEAN;
|
|
BEGIN
|
|
RETURN ((ch >= '0') & (ch <= '9')) OR
|
|
((ch >= 'A') & (ch <= 'Z')) OR
|
|
((ch >= 'a') & (ch <= 'z')) OR
|
|
(ch = '-');
|
|
END IsIdentChar;
|
|
|
|
PROCEDURE ParseIdentifiers(
|
|
s: ARRAY OF CHAR;
|
|
VAR i: INTEGER;
|
|
VAR dest: StringIdentifier;
|
|
checkLeadingZero: BOOLEAN;
|
|
VAR ok: BOOLEAN);
|
|
VAR
|
|
j, segLen: INTEGER;
|
|
ch: CHAR;
|
|
isNumeric, hasLeadingZero: BOOLEAN;
|
|
BEGIN
|
|
j := 0; ok := FALSE;
|
|
|
|
(* Require at least one character *)
|
|
IF (s[i] = '.') OR (s[i] = '+') OR (s[i] = 0X) THEN RETURN END;
|
|
|
|
LOOP
|
|
segLen := 0;
|
|
isNumeric := TRUE;
|
|
hasLeadingZero := FALSE;
|
|
|
|
(* Empty identifier is invalid *)
|
|
IF (s[i] = '.') OR (s[i] = '+') OR (s[i] = 0X) THEN RETURN END;
|
|
|
|
WHILE (s[i] # '.') & (s[i] # '+') & (s[i] # 0X) DO
|
|
ch := s[i];
|
|
|
|
IF ~IsIdentChar(ch) THEN RETURN END;
|
|
|
|
(* Check for leading-zero numeric identifiers *)
|
|
IF checkLeadingZero THEN
|
|
IF segLen = 0 THEN
|
|
IF ch = '0' THEN hasLeadingZero := TRUE END;
|
|
ELSE
|
|
IF hasLeadingZero & IsDigit(ch) THEN RETURN END;
|
|
END;
|
|
IF ~IsDigit(ch) THEN isNumeric := FALSE END;
|
|
END;
|
|
|
|
IF j >= LEN(dest) - 1 THEN RETURN END;
|
|
dest[j] := ch; INC(i); INC(j); INC(segLen);
|
|
END;
|
|
|
|
(* Add dot if needed *)
|
|
IF s[i] = '.' THEN
|
|
IF j >= LEN(dest) - 1 THEN RETURN END;
|
|
dest[j] := '.'; INC(i); INC(j);
|
|
ELSE
|
|
EXIT;
|
|
END;
|
|
END;
|
|
|
|
dest[j] := 0X;
|
|
ok := TRUE;
|
|
END ParseIdentifiers;
|
|
|
|
PROCEDURE ParsePreRelease(
|
|
s: ARRAY OF CHAR;
|
|
VAR i: INTEGER;
|
|
VAR PreRelease: StringIdentifier;
|
|
VAR ok: BOOLEAN);
|
|
BEGIN
|
|
IF s[i] # '-' THEN ok := FALSE; RETURN END;
|
|
INC(i);
|
|
ParseIdentifiers(s, i, PreRelease, TRUE (* checkLeadingZero *), ok);
|
|
END ParsePreRelease;
|
|
|
|
PROCEDURE ParseBuild(
|
|
s: ARRAY OF CHAR;
|
|
VAR i: INTEGER;
|
|
VAR Build: StringIdentifier;
|
|
VAR ok: BOOLEAN);
|
|
BEGIN
|
|
IF s[i] # '+' THEN ok := FALSE; RETURN END;
|
|
INC(i);
|
|
ParseIdentifiers(s, i, Build, FALSE (* checkLeadingZero *), ok);
|
|
END ParseBuild;
|
|
|
|
|
|
PROCEDURE Parse*(s: ARRAY OF CHAR; VAR v: Version; VAR finalok: BOOLEAN);
|
|
VAR
|
|
i: INTEGER;
|
|
ok: BOOLEAN;
|
|
BEGIN
|
|
(* Make sure it's clean *)
|
|
v.Major := 0;
|
|
v.Minor := 0;
|
|
v.Patch := 0;
|
|
v.PreRelease[0] := 0X;
|
|
v.Build[0] := 0X;
|
|
|
|
ok := FALSE;
|
|
finalok := FALSE;
|
|
i := 0;
|
|
|
|
(* Major *)
|
|
IF HasLeadingZero(s, i) THEN RETURN END;
|
|
ParseInt(s, i, v.Major, ok); IF ~ok THEN RETURN END;
|
|
IF s[i] # '.' THEN RETURN END; INC(i);
|
|
|
|
(* Minor *)
|
|
IF HasLeadingZero(s, i) THEN RETURN END;
|
|
ParseInt(s, i, v.Minor, ok); IF ~ok THEN RETURN END;
|
|
IF s[i] # '.' THEN RETURN END; INC(i);
|
|
|
|
(* Patch *)
|
|
IF HasLeadingZero(s, i) THEN RETURN END;
|
|
ParseInt(s, i, v.Patch, ok); IF ~ok THEN RETURN END;
|
|
|
|
(* PreRelease, if exists *)
|
|
IF s[i] = '-' THEN
|
|
ParsePreRelease(s, i, v.PreRelease, ok)
|
|
END;
|
|
|
|
(* Build, if exists *)
|
|
IF s[i] = '+' THEN
|
|
ParseBuild(s, i, v.Build, ok)
|
|
END;
|
|
|
|
(* Must end cleanly *)
|
|
IF s[i] # 0X THEN ok := FALSE; RETURN END;
|
|
|
|
finalok := TRUE;
|
|
END Parse;
|
|
END SemVer.
|