Skip to content

Instantly share code, notes, and snippets.

@tajmone
Last active April 29, 2018 22:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tajmone/f32b4c905f679c16205fecf0973f5af9 to your computer and use it in GitHub Desktop.
Save tajmone/f32b4c905f679c16205fecf0973f5af9 to your computer and use it in GitHub Desktop.
PureBasic mod_SemVer
; ··············································································
; ··············································································
; ······················· 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
; ··············································································
; ··············································································
; ···················· 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
;}
@tajmone
Copy link
Author

tajmone commented Apr 29, 2018

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:

LICENSE — Since I haven't actually finished working on the module, I didn't include a license in it's source comments. But if anyone needs to reuse it he/she can do so under MIT License:

MIT License

Copyright Tristano Ajmone, 2018.
https://gist.github.com/tajmone

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

See also @aziascreations' SemanticVersioning.pb which I stumbled upon recently:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment