Last active
April 29, 2018 22:54
-
-
Save tajmone/f32b4c905f679c16205fecf0973f5af9 to your computer and use it in GitHub Desktop.
PureBasic mod_SemVer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; ·············································································· | |
; ·············································································· | |
; ······················· Semantic Versioning 2.0 Module ······················· | |
; ·············································································· | |
; ····························· by Tristano Ajmone ····························· | |
; ·············································································· | |
; ·············································································· | |
; "mod_semver.pbi" | PureBASIC 5.60 | |
; TODO: Add a DisableDebug global var (or flag) to enable/disable debug msgs: | |
; -- Off by default. | |
; -- Use #PB_Compiler_Debugger comp.dir so that Debug vars and code | |
; is not included in final exe (in compiled binaries is always 0). | |
; TODO: Add function to Query ext app version info -- via RunProgram() with | |
; #PB_Program_Hide (doesn't need to be in a console) | |
; TODO: Add InitFailSafe (macro) to ensure that if a public procedure is | |
; called without first initializing the module it gets init by the | |
; procedure itself. | |
DeclareModule SemVer | |
#MOD_SEMVER_VERSION$ = "0.1.0" | |
Declare Init() | |
Declare Deinit() | |
Declare.s GetVersionFromString(SemVer$) | |
Declare ValidateVersion(SemVer$) | |
Declare ValidateConstraint(SemVer$) | |
Declare Satisfy(Constraint$, Version$) | |
EndDeclareModule | |
Module SemVer | |
; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ | |
;- DEFINITIONS | |
;{////////////////////////////////////////////////////////////////////////////// | |
Global IsInitialized = #False ; Bool: is SemVer module is initialized? | |
; ============================================================================== | |
; Constraint Types | |
;{============================================================================== | |
Enumeration | |
#SEMVER_CONSTR_EXACT ; Exact version required (default). | |
#SEMVER_CONSTR_MINOR ; Same MAJOR and MINOR version required. | |
#SEMVER_CONSTR_MAJOR ; Same MAJOR version required. | |
EndEnumeration | |
;}============================================================================== | |
; SemVer Structered Types | |
;{============================================================================== | |
Structure SemVer | |
Version.s ; <= Store a string representation of the full version info. | |
; Set MAJ.MIN.PATCH to ".u" (Unicode): | |
; -- Always 2 bytes long (x86/64) | |
; -- positive range values (0 to +65535). | |
Major.u | |
Minor.u | |
Patch.u | |
; Set PRERELEASE and BUILD as strings: | |
Prerelease.s | |
Build.s | |
EndStructure | |
Structure SemVerRange Extends SemVer | |
; Add a constraint to SemVer structure -- ".a" Ascii (1 byte: 0 to +255). | |
Constraint.a | |
EndStructure | |
;} | |
;}\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ | |
;- REGEX RAW DATA | |
;{////////////////////////////////////////////////////////////////////////////// | |
; Constants, Enums, and Data required for building the SemVer RegExs. | |
; /\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?\b/ig; | |
; FLAGS: /ig | i= Ignore Case | g= Global Match (find all matches rather than stopping after the first match) | |
; ============================================================================== | |
;- RegEx Building-Blocks | |
;{============================================================================== | |
; Reusable RegEx snippets ... | |
; ------------------------------------------------------------------------------ | |
; Validation RegEx Snippets | |
;{------------------------------------------------------------------------------ | |
; RegEx snippets for validating SemVer conformity (non-capturing). They enforce | |
; sanity checks on wellformedness of identifiers ... | |
; ·············································································· | |
; ** Constraint Operators ** (non-capturing optional group) | |
#ValRex_ConstrOP = "[\^~]?" | |
; ·············································································· | |
; ** Numeric Identifier ** (non-capturing group) | |
#ValRex_NumericID = "(?:0|[1-9]\d*)" | |
; -- One or more digits, no leading zeros. | |
; ·············································································· | |
; ** Alphanumeric Identifier ** | |
; FIXME: Could optimize to "\d*[-a-z][-\da-z]*" ? | |
#ValRex_AlphanID = "[-a-z][-\da-z]*|\d+[-a-z]+[-\da-z]*" | |
; Allowed chars: digits, Ascii letters, hyphen. | |
; -- A letter or hyphen followed by zero or more valid chars, | |
; -- One or more digits followed by at least one valid char (not digits only). | |
; ·············································································· | |
; ** Identifier Segment ** (non-capturing group) | |
#ValRex_IDSegment = "(?:" + #ValRex_NumericID + "|" + #ValRex_AlphanID + ")" | |
; -- Either a Numeric Identifier or an Alphanumeric Identifier | |
; ·············································································· | |
#ValRex_MAJ$ = #ValRex_NumericID + "\." | |
#ValRex_MIN$ = #ValRex_NumericID + "\." | |
#ValRex_PATCH$ = #ValRex_NumericID | |
; ·············································································· | |
#ValRex_PRERELEASE$ = "(?:-" + #ValRex_IDSegment + "(?:\." + #ValRex_IDSegment + ")*)?" | |
; (entire group is optional): | |
; -- An hyphen followed by an Identifier (Numeric or Alphanum, non-empty) | |
; -- Zero or more dot-separated Identifiers (Numeric or Alphanum, non-empty) | |
; ·············································································· | |
#ValRex_BUILD$ = "(?:\+" + #ValRex_IDSegment + "(?:\." + #ValRex_IDSegment + ")*)?" | |
; (entire group is optional): | |
; -- A plus char followed by an Identifier (Numeric or Alphanum, non-empty) | |
; -- Zero or more dot-separated Identifiers (Numeric or Alphanum, non-empty) | |
; ·············································································· | |
; ** Version Validation Pattern ** (common to all validation RegExs) | |
#ValRex_VerPattern$ = #ValRex_MAJ$ + #ValRex_MIN$ + #ValRex_PATCH$ + #ValRex_PRERELEASE$ + #ValRex_BUILD$ | |
;}------------------------------------------------------------------------------ | |
; Capturing RegEx Snippets | |
;{------------------------------------------------------------------------------ | |
; RegEx snippets for capturing SemVer segments via named groups. They don't | |
; enforce any checks since it's assumed the Version string was validated before | |
; deserialization ... | |
; ·············································································· | |
#CapRex_IDSegment = "[-\da-z]+" | |
#CapRex_CONSTRAINT$ = "(?<CONSTRAINT>[\^~])?" | |
#CapRex_MAJ$ = "(?<MAJOR>\d+)\." | |
#CapRex_MIN$ = "(?<MINOR>\d+)\." | |
#CapRex_PATCH$ = "(?<PATCH>\d+)" | |
#CapRex_PRERELEASE$ = "((?:-)(?<PRERE>" + #CapRex_IDSegment + "(?:\." + #CapRex_IDSegment + ")*)?)" | |
; (entire group is optional): | |
; -- An hyphen (not captured) followed by an Identifier (Numeric or Alphanum, non-empty) | |
; -- Zero or more dot-separated Identifiers (Numeric or Alphanum, non-empty) | |
; ·············································································· | |
#CapRex_BUILD$ = "((?:\+)(?<BUILD>" + #CapRex_IDSegment + "(?:\." + #CapRex_IDSegment + ")*)?)" | |
; (entire group is optional): | |
; -- A plus char (not captured) followed by an Identifier (Numeric or Alphanum, non-empty) | |
; -- Zero or more dot-separated Identifiers (Numeric or Alphanum, non-empty) | |
;}------------------------------------------------------------------------------ | |
; Buidling The Final RegExs | |
; ------------------------------------------------------------------------------ | |
; Building the final RegExs from the snippets... | |
; ·············································································· | |
#RE_ValidateVer$ = "(?i)^v?"+ #ValRex_VerPattern$ + "$" | |
; Case Insens | Allow a trailing "v" or "V" (unspaced) | Allow leading & trailing | |
; spaces | String might not contain anything else beside these. | |
; ·············································································· | |
#RE_ValidateConstr$ = "(?i)^" + #ValRex_ConstrOP + #ValRex_VerPattern$ + "$" | |
; ·············································································· | |
#RE_GetVersionFromString$ = "(?i)(?:.*?)\bv?("+ #ValRex_VerPattern$ + ")\b" | |
;}============================================================================== | |
;- RegEx Definitions Data | |
; ============================================================================== | |
Enumeration | |
#RE_ValidateVer | |
#RE_ValidateConstr | |
#RE_GetVersionFromString | |
#RE_SemVerGetGroups | |
#RE_SemVerSpec | |
EndEnumeration | |
TotalRegExs = #PB_Compiler_EnumerationValue -1 | |
DataSection | |
RegExDefinitions: | |
Data.s #RE_ValidateVer$ | |
Data.s #RE_ValidateConstr$ | |
Data.s #RE_GetVersionFromString$ | |
Data.s "(?i)\bv?"+ #CapRex_MAJ$ + #CapRex_MIN$ + #CapRex_PATCH$ + #CapRex_PRERELEASE$ + #CapRex_BUILD$ + "\b" ; #RE_SemVerGetGroups | |
Data.s "(?i)\b"+ #CapRex_CONSTRAINT$ + #CapRex_MAJ$ + #CapRex_MIN$ + #CapRex_PATCH$ + #CapRex_PRERELEASE$ + #CapRex_BUILD$ + "\b" ; #RE_SemVerSpec | |
EndDataSection | |
;}\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ | |
; PRIVATE PROCEDURES | |
;{////////////////////////////////////////////////////////////////////////////// | |
; ****************************************************************************** | |
; * Serialize SemVer String * | |
; ****************************************************************************** | |
Procedure SerializeSemVerStr(*SemVerObj.SemVer, SemVerStr.s) | |
; Extract SemVer from a valide SemVer string. | |
; ------------------------------------------------------------------------------ | |
Debug ">>> SerializeSemVerStr()" | |
If ExamineRegularExpression(#RE_SemVerGetGroups, SemVerStr) | |
While NextRegularExpressionMatch(#RE_SemVerGetGroups) | |
MAJOR$ = RegularExpressionNamedGroup(#RE_SemVerGetGroups, "MAJOR") | |
MINOR$ = RegularExpressionNamedGroup(#RE_SemVerGetGroups, "MINOR") | |
PATCH$ = RegularExpressionNamedGroup(#RE_SemVerGetGroups, "PATCH") | |
PRERE$ = RegularExpressionNamedGroup(#RE_SemVerGetGroups, "PRERE") | |
BUILD$ = RegularExpressionNamedGroup(#RE_SemVerGetGroups, "BUILD") | |
Wend | |
EndIf | |
DBG$ + "MAJOR: " + MAJOR$ +" | " | |
DBG$ + "MINOR: " + MINOR$ +" | " | |
DBG$ + "PATCH: " + PATCH$ +" | " | |
DBG$ + "PRERE: " + PRERE$ +" | " | |
DBG$ + "BUILD: " + BUILD$ | |
*SemVerObj\Major = Val( MAJOR$ ) | |
*SemVerObj\Minor = Val( MINOR$ ) | |
*SemVerObj\Patch = Val( PATCH$ ) | |
*SemVerObj\Prerelease = PRERE$ | |
*SemVerObj\Build = BUILD$ | |
Debug DBG$ | |
; ProcedureReturn DBG$ | |
Debug "<<< SerializeSemVerStr()" | |
EndProcedure | |
; ****************************************************************************** | |
; * Deserialize SemVer Obj * | |
; ****************************************************************************** | |
Procedure.s DeserializeSemVerObj(*SemVerObj.SemVer) | |
; Extract SemVer from a valide SemVer string. | |
; ------------------------------------------------------------------------------ | |
If ExamineRegularExpression(#RE_SemVerGetGroups, SemVer$) | |
While NextRegularExpressionMatch(#RE_SemVerGetGroups) | |
*SemVerObj\Major = Val(RegularExpressionNamedGroup(#RE_SemVerGetGroups, "MAJOR")) | |
*SemVerObj\Minor = Val(RegularExpressionNamedGroup(#RE_SemVerGetGroups, "MINOR")) | |
*SemVerObj\Patch = Val(RegularExpressionNamedGroup(#RE_SemVerGetGroups, "PATCH")) | |
*SemVerObj\Prerelease = RegularExpressionNamedGroup(#RE_SemVerGetGroups, "PRERE") | |
*SemVerObj\Build = RegularExpressionNamedGroup(#RE_SemVerGetGroups, "BUILD") | |
Wend | |
EndIf | |
EndProcedure | |
; ****************************************************************************** | |
; * Debug SemVer Obj * | |
; ****************************************************************************** | |
Procedure.s DebugSemVerObj(*SemVerObj.SemVer) | |
Debug("SemVerObj\Major: " + Str( *SemVerObj\Major ) ) | |
Debug("SemVerObj\Minor: " + Str( *SemVerObj\Minor ) ) | |
Debug("SemVerObj\Patch: " + Str( *SemVerObj\Patch ) ) | |
Debug("SemVerObj\Prerelease: " + *SemVerObj\Prerelease ) | |
Debug("SemVerObj\Build: " + *SemVerObj\Build ) | |
EndProcedure | |
;}\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ | |
; PUBLIC PROCEDURES | |
;{////////////////////////////////////////////////////////////////////////////// | |
; ****************************************************************************** | |
; * Initialize SemVer Module * | |
; ****************************************************************************** | |
Procedure Init() | |
; Initialize the module's RegExs (intended for optimizing memory). | |
; Must be called before using any procedures of this module -- ie: at first | |
; module usage or after having manually terminate it. | |
; ------------------------------------------------------------------------------ | |
If Not IsInitialized | |
Shared TotalRegExs | |
Restore RegExDefinitions | |
For RENum = 0 To TotalRegExs | |
Read.s REDef$ | |
If Not CreateRegularExpression(RENum, REDef$) | |
Debug ">> ERROR creating RegEx ("+ Str(RENum) +"): "+ #LF$ + REDef$ | |
Else | |
Debug ">> RegEx created succes ("+ Str(RENum) +"): "+ #LF$ + REDef$ | |
EndIf | |
Next | |
IsInitialized = #True | |
Debug(">> SemVer initialized!") | |
Else | |
Debug("!! SemVer is already initialized!") | |
EndIf | |
EndProcedure | |
; ****************************************************************************** | |
; * Deinitialize SemVer Module * | |
; ****************************************************************************** | |
Procedure Deinit() | |
; Deinitialize the module by freeing its RegExs from memory. | |
; SemVer procedures will no longer be usable until the module is re-initialized | |
; again via SemVer::Init(). | |
; ------------------------------------------------------------------------------ | |
If IsInitialized | |
Shared TotalRegExs | |
For RENum = 0 To TotalRegExs | |
FreeRegularExpression(RENum) | |
Next | |
IsInitialized = #False | |
Debug("<< SemVer deinitialized!") | |
Else | |
Debug("!! SemVer is already deinitialized!") | |
EndIf | |
EndProcedure | |
; ****************************************************************************** | |
; * Validate a Version String * | |
; ****************************************************************************** | |
; Returns #True/#False | |
Procedure ValidateVersion(SemVer$) | |
ProcedureReturn MatchRegularExpression(#RE_ValidateVer, SemVer$) | |
EndProcedure | |
; ****************************************************************************** | |
; * Validate a Constraint String * | |
; ****************************************************************************** | |
; Returns #True/#False | |
Procedure ValidateConstraint(Constraint$) | |
ProcedureReturn MatchRegularExpression(#RE_ValidateConstr, Constraint$) | |
EndProcedure | |
; ****************************************************************************** | |
; * Get SemVer From String * | |
; ****************************************************************************** | |
Procedure.s GetVersionFromString(SemVer$) | |
; Extract a valid SemVer string from a string (eg: an app info text). | |
; ------------------------------------------------------------------------------ | |
If ExamineRegularExpression(#RE_GetVersionFromString, SemVer$) | |
If NextRegularExpressionMatch(#RE_GetVersionFromString) | |
Result$ = RegularExpressionGroup(#RE_GetVersionFromString, 1) | |
Else | |
Debug("!! GetVersionFromString > NO MATCH!") | |
EndIf | |
EndIf | |
ProcedureReturn Result$ | |
EndProcedure | |
; ****************************************************************************** | |
; * Validate a Dependency Against Constraints * | |
; ****************************************************************************** | |
Procedure Satisfy(Constraint$, Version$) | |
; ValidateVersion a Dependency version against a given Range. | |
; ------------------------------------------------------------------------------ | |
Debug ">>> Satisfy(" + Constraint$ +", "+ Version$ +")" | |
Dependency.SemVer | |
SerializeSemVerStr(@Dependency, Version$) | |
DebugSemVerObj(@Dependency) | |
; Debug("SemVerObj\Major: " + Str( Dependency\Major ) ) | |
; Debug("SemVerObj\Minor: " + Str( Dependency\Minor ) ) | |
; Debug("SemVerObj\Patch: " + Str( Dependency\Patch ) ) | |
; Debug("SemVerObj\Prerelease: " + Dependency\Prerelease ) | |
; Debug("SemVerObj\Build: " + Dependency\Build ) | |
Range.SemVerRange | |
Debug "<<< Satisfy()" | |
EndProcedure | |
;}////// END :: PROCEDURES ///////////////////////////////////////////////////// | |
EndModule | |
; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ | |
; MODULE TEST / EXAMPLE | |
; ////////////////////////////////////////////////////////////////////////////// | |
; Only executed/included in source if this file is compiled/run on its own... | |
CompilerIf #PB_Compiler_IsMainFile | |
CompilerEndIf |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; ·············································································· | |
; ·············································································· | |
; ···················· Test Semantic Versioning 2.0 Module ····················· | |
; ·············································································· | |
; ····························· by Tristano Ajmone ····························· | |
; ·············································································· | |
; ·············································································· | |
XIncludeFile "mod_semver.pbi" | |
SemVer::Init() ; Initialize SemVer module | |
; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ | |
;- DEFINITIONS | |
;{////////////////////////////////////////////////////////////////////////////// | |
; Define some constants, macros, etc. | |
Declare TestA( *TestProcPoint, maxLen = 12 ) | |
Declare.i TestA_GetMaxLen() | |
Declare TestB( *TestProcPoint, maxLen = 12 ) | |
Declare.i TestB_GetMaxLen() | |
Prototype.i ProtoProcTestA(StringParam.s) | |
Prototype.s ProtoProcTestB(StringParam.s) | |
; === MACRO > LineSeparator ==================================================== | |
Macro LineSeparator | |
Debug LSet("", 80, "=") | |
EndMacro | |
; === MACRO > ThinLineSeparator ================================================ | |
Macro ThinLineSeparator | |
Debug LSet("", 80, "-") | |
EndMacro | |
Enumeration | |
#stop | |
#continue | |
#subset | |
EndEnumeration | |
;}////// END :: DEFINITIONS //////////////////////////////////////////////////// | |
; ============================================================================== | |
;- Test > ValidateVersion() | |
; ============================================================================== | |
LineSeparator | |
Debug "Test ValidateVersion(VerStr$) --- Validates a SemVer string by returning a boolean." | |
Restore ValidateVersionDataset | |
maxLen = TestA_GetMaxLen() | |
; Debug("maxLen: "+ Str(maxLen)) ; DELME | |
Restore ValidateVersionDataset | |
TestA( SemVer::@ValidateVersion(), maxLen ) | |
; ============================================================================== | |
; Test > ValidateConstraint() | |
; ============================================================================== | |
LineSeparator | |
Debug "Test ValidateConstraint(ConstrStr$) --- Validates a version Constraint string by returning a boolean." | |
Restore ValidateConstraintDataset | |
maxLen = TestA_GetMaxLen() | |
; Debug("maxLen: "+ Str(maxLen)) ; DELME | |
Restore ValidateConstraintDataset | |
TestA( SemVer::@ValidateConstraint(), maxLen ) | |
; ============================================================================== | |
; Test > GetVersionFromString() | |
; ============================================================================== | |
LineSeparator | |
Debug "Test GetVersionFromString(Str$) --- Extract and return a valid SemVer string from a string." | |
Restore GetVersionFromStringDataset | |
maxLen = TestB_GetMaxLen() | |
Debug("maxLen: "+ Str(maxLen)) ; DELME | |
Restore GetVersionFromStringDataset | |
TestB( SemVer::@GetVersionFromString(), maxLen ) | |
; ·············································································· | |
; ****************************************************************************** | |
; * * | |
; * PROCEDURES * | |
; * * | |
;{****************************************************************************** | |
; ****************************************************************************** | |
; * TestA_GetMaxLen() -- Calc longest str param for TestA() * | |
; ****************************************************************************** | |
Procedure.i TestA_GetMaxLen() | |
; ------------------------------------------------------------------------------ | |
; Calculate Longest String Param | |
; ------------------------------------------------------------------------------ | |
; Needed for alignment purposes... | |
maxLen = 0 | |
While #True | |
Read.s Ver$ | |
Read.s TestDesc$ | |
Read.i Expect | |
Read.i carryon | |
If carryon = #subset : Read.s SubSetDesc$ : EndIf | |
If Len(Ver$) > maxLen | |
maxLen = Len(Ver$) | |
; Debug "New maxLen: " + Str(maxLen) | |
EndIf | |
If Not carryon : Break : EndIf | |
Wend | |
ProcedureReturn maxLen | |
EndProcedure | |
; ****************************************************************************** | |
; TestA() -- Signle Str Param, Bool Result | |
; ****************************************************************************** | |
; Test a procedure that takes a string parameter and returns a boolean. | |
Procedure TestA( *TestProcPoint, maxLen = 12 ) | |
; maxLen must be set to the longest Str parameter for aligning purposes. | |
TestProc.ProtoProcTestA = *TestProcPoint | |
carryon = #True | |
While carryon | |
Read.s Ver$ | |
Read.s TestDesc$ | |
Read.i Expect | |
Read.i carryon | |
If carryon = #subset | |
ThinLineSeparator | |
Read.s SubSetDesc$ | |
Debug SubSetDesc$ | |
ThinLineSeparator | |
EndIf | |
Result = TestProc(Ver$) | |
; ----------------------------- Build output text ------------------------------ | |
Out$ = "" | |
If Result = Expect | |
Out$ + " [PASSED] " | |
Else | |
Out$ + "*[FAILED] " | |
EndIf | |
If Result | |
Out$ + "true " | |
Else | |
Out$ + "false" | |
EndIf | |
Out$ + ~" -> \"" + Ver$ + ~"\"" | |
If TestDesc$ <> #Null$ | |
Out$ + LSet(" .", maxLen - Len(Ver$), ".") | |
Out$ + " <- " + TestDesc$ | |
EndIf | |
; ------------------------------------------------------------------------------ | |
Debug Out$ | |
Wend | |
EndProcedure | |
; ****************************************************************************** | |
; * TestB_GetMaxLen() -- Calc longest str param for TestB() * | |
; ****************************************************************************** | |
Procedure.i TestB_GetMaxLen() | |
; ------------------------------------------------------------------------------ | |
; Calculate Longest String Param | |
; ------------------------------------------------------------------------------ | |
; Needed for alignment purposes... | |
maxLen = 0 | |
While #True | |
Read.s StrContext$ | |
Read.s Expect$ | |
Read.s TestDesc$ | |
Read.i carryon | |
If carryon = #subset : Read.s SubSetDesc$ : EndIf | |
If Len(StrContext$) > maxLen | |
maxLen = Len(StrContext$) | |
; Debug "New maxLen: " + Str(maxLen) | |
EndIf | |
If Not carryon : Break : EndIf | |
Wend | |
ProcedureReturn maxLen | |
EndProcedure | |
; ****************************************************************************** | |
; TestB() -- Single Str Param, Str Result | |
; ****************************************************************************** | |
; Test a procedure that takes a string parameter and returns a string. | |
Procedure TestB( *TestProcPoint, maxLen = 12 ) | |
; maxLen must be set to the longest Str parameter for aligning purposes. | |
TestProc.ProtoProcTestB = *TestProcPoint | |
carryon = #True | |
While carryon | |
Read.s StrContext$ | |
Read.s Expect$ | |
Read.s TestDesc$ | |
Read.i carryon | |
If carryon = #subset | |
ThinLineSeparator | |
Read.s SubSetDesc$ | |
Debug SubSetDesc$ | |
ThinLineSeparator | |
EndIf | |
Result$ = TestProc(StrContext$) | |
; ----------------------------- Build output text ------------------------------ | |
Out$ = "" | |
If Result$ = Expect$ | |
Out$ + " [PASSED] " | |
Else | |
Out$ + "*[FAILED] " | |
EndIf | |
Out$ + ~"\"" + StrContext$ + ~"\"" | |
Out$ + LSet(" .", maxLen - Len(StrContext$), ".") | |
Out$ + ~" -> \"" + Result$ + ~"\"" | |
If TestDesc$ <> #Null$ | |
Out$ + " <- " + TestDesc$ | |
EndIf | |
; ------------------------------------------------------------------------------ | |
Debug Out$ | |
Wend | |
EndProcedure | |
;}****************************************************************************** | |
; * * | |
; * DATA SECTIONS * | |
; * * | |
;{****************************************************************************** | |
; ============================================================================== | |
; DataSections String Constants & Macros | |
;{============================================================================== | |
; Some constants for test descriptions, including quotes from SemVer 2.0 rules. | |
#MAJOR$ = "Major: " | |
#PREREL$ = "PreRe: " | |
#BUILD$ = "Build: " | |
#SemVerRef$ = " (SemVer 2.0.0)" | |
#mod_semverRef$ = " (mod_sever "+ SemVer::#MOD_SEMVER_VERSION$ +" specs)" | |
#IdsNotEmptyRule$ = ~"\"Identifiers MUST NOT be empty\" rule "+ #SemVerRef$ | |
#NoLeadZeroRule$ = ~"\"Numeric identifiers MUST NOT include leading zeroes\" rule"+ #SemVerRef$ | |
#MAJ_LeadZero$ = #MAJOR$ + #NoLeadZeroRule$ | |
#PREREL_EmptyId$ = #PREREL$ + #IdsNotEmptyRule$ | |
#PREREL_LeadZero$ = #PREREL$ + #NoLeadZeroRule$ | |
#BUILD_EmptyId$ = #BUILD$ + #IdsNotEmptyRule$ | |
#BUILD_LeadZero$ = #BUILD$ + #NoLeadZeroRule$ | |
#CONSTR_V_Symb_Not_Allowed$ = ~"Constraints can't contain a \"v\" symbol rule" + #mod_semverRef$ | |
#CONSTR_Invalid_OP$ = "Invalid constraint operator" + #mod_semverRef$ | |
Macro UnimplemConstrOp(operator) | |
~"The \"" + operator + ~"\" operator is not implemented!" + #mod_semverRef$ | |
EndMacro | |
;}============================================================================== | |
;- DataSection > ValidateVersion() | |
;{============================================================================== | |
DataSection | |
ValidateVersionDataset: | |
; Data.s SemVerStr, Description | |
; Data.i ExpectedResult, ( #continue | #stop | #subset : Data.s SubSetDescription ) | |
; ------------------------------------------------------------------------------ | |
; Valid Version Strings | |
; ------------------------------------------------------------------------------ | |
; These must pass the validation test ... | |
Data.s "0.0.0", "" | |
Data.i #True, #subset : Data.s "Valid Version Strings" | |
Data.s "v1.0.0", "" | |
Data.i #True, #continue | |
Data.s "V1.0.0", "" | |
Data.i #True, #continue | |
Data.s "v2.1.20-alpha.1", "" | |
Data.i #True, #continue | |
Data.s "v2.1.20-alpha.10.beta.0+build.unicorn", "" | |
Data.i #True, #continue | |
Data.s "v2.1.20-alpha-test", "" | |
Data.i #True, #continue | |
; ------------------------------------------------------------------------------ | |
; Malformed Version Strings | |
; ------------------------------------------------------------------------------ | |
; These must fail the validation test ... | |
Data.s "x1.0.0", "" | |
Data.i #False, #subset : Data.s "Malformed Version Strings" | |
Data.s "1.9.2.21", ~"The \".21\" final segment is not valid SemVer!" | |
Data.i #True, #continue | |
Data.s "1.A.0", "" | |
Data.i #False, #continue | |
Data.s "1.0.0-04.2.1", #PREREL_LeadZero$ | |
Data.i #False, #continue | |
Data.s "1.0.0-+build.winter", #PREREL_EmptyId$ | |
Data.i #False, #continue | |
Data.s "1.0.0-xxx..zzz", #PREREL_EmptyId$ | |
Data.i #False, #continue | |
Data.s "1.0.0+", #BUILD_EmptyId$ | |
Data.i #False, #continue | |
Data.s "1.0.0+build60..2005", #BUILD_EmptyId$ | |
Data.i #False, #continue | |
Data.s "1.0.0alpha+build60..2005", #BUILD_EmptyId$ | |
Data.i #False, #continue | |
Data.s "v01.0.0", #MAJ_LeadZero$ | |
Data.i #False, #stop; <= End of Dataset! | |
EndDataSection | |
;}============================================================================== | |
;- DataSection > ValidateConstraint() | |
;{============================================================================== | |
DataSection | |
ValidateConstraintDataset: | |
; Data.s ConstrStr, Description | |
; Data.i ExpectedResult, ( #continue | #stop | #subset : Data.s SubSetDescription ) | |
; ------------------------------------------------------------------------------ | |
; Valid Constraint Strings | |
; ------------------------------------------------------------------------------ | |
; These must pass the validation test ... | |
Data.s "0.0.0", "" | |
Data.i #True, #subset : Data.s "Valid Constraint Strings" | |
Data.s "1.0.0", "" | |
Data.i #True, #continue | |
Data.s "^1.0.0", "" | |
Data.i #True, #continue | |
Data.s "~1.0.0", "" | |
Data.i #True, #continue | |
Data.s "^2.1.20-alpha.1", "" | |
Data.i #True, #continue | |
Data.s "~2.1.20-alpha.10.beta.0+build.unicorn", "" | |
Data.i #True, #continue | |
Data.s "~2.1.20-alpha-test", "" | |
Data.i #True, #continue | |
; ------------------------------------------------------------------------------ | |
; Malformed Constraint Strings | |
; ------------------------------------------------------------------------------ | |
; These must fail the validation test ... | |
Data.s "v1.0.0", #CONSTR_V_Symb_Not_Allowed$ | |
Data.i #False, #subset : Data.s "Malformed Constraint Strings" | |
; Constraint Operators which are not yet implemented.... | |
Data.s ">1.2.0", UnimplemConstrOp(">") | |
Data.i #False, #continue | |
Data.s ">=1.2.0", UnimplemConstrOp(">=") | |
Data.i #False, #continue | |
Data.s "<1.2.0", UnimplemConstrOp("<") | |
Data.i #False, #continue | |
Data.s "!1.2.0", UnimplemConstrOp("!") | |
Data.i #False, #continue | |
; Invalid Constraint Operators | |
Data.s "=1.2.0", #CONSTR_Invalid_OP$ | |
Data.i #False, #continue | |
Data.s "@1.2.0", #CONSTR_Invalid_OP$ | |
Data.i #False, #continue | |
Data.s "^1.A.0", "" | |
Data.i #False, #continue | |
Data.s "^1.0.0-04.2.1", #PREREL_LeadZero$ | |
Data.i #False, #continue | |
Data.s "~1.0.0-+build.winter", #PREREL_EmptyId$ | |
Data.i #False, #continue | |
Data.s "~1.0.0-xxx..zzz", #PREREL_EmptyId$ | |
Data.i #False, #continue | |
Data.s "~1.0.0+", #BUILD_EmptyId$ | |
Data.i #False, #continue | |
Data.s "~1.0.0+build60..2005", #BUILD_EmptyId$ | |
Data.i #False, #continue | |
Data.s "~1.0.0alpha+build60..2005", #BUILD_EmptyId$ | |
Data.i #False, #continue | |
Data.s "~01.0.0", #MAJ_LeadZero$ | |
Data.i #False, #stop; <= End of Dataset! | |
EndDataSection | |
;}============================================================================== | |
;- DataSection > GetVersionFromString() | |
; ============================================================================== | |
DataSection | |
GetVersionFromStringDataset: | |
; Data.s String, ExpectedResultStr, Description | |
; Data.i ( #continue | #stop | #subset : Data.s SubSetDescription ) | |
; ------------------------------------------------------------------------------ | |
; Strings With Valid Version SubStrings | |
; ------------------------------------------------------------------------------ | |
; These must pass the validation test ... | |
Data.s "pandoc 1.19.2.1 Compiled...", "1.19.2", "NOTE: Not strictly SemVer, but the valid part is kept! (user discretion)" | |
Data.i #subset : Data.s "Strings With Valid Version" | |
Data.s "pp 1.11 (mingw32 x86_64, ghc 8.0)", "1.11", "" | |
Data.i #continue | |
Data.s "XYZ v0.4.11-alpha1 (MIT License)", "0.4.11-alpha1", "" | |
Data.i #stop ; <= End of Dataset! | |
EndDataSection | |
;} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is a SemVer validation module I was working on for PureBasic. I don't remember how polished the actual module is, because I ended up using a modified version in a project of mine:
See also @aziascreations'
SemanticVersioning.pb
which I stumbled upon recently: