package main import ( "slices" "unicode" ) var whitespaceChars = []rune{' ', '\t', '\n'} var digitChars = []rune{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'} var wordChars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_") var LBRACKET rune = 0xF0000 var RBRACKET rune = 0xF0001 var ANY_CHAR rune = 0xF0002 // Represents any character - used for states where the allChars flag is on. var LPAREN_CHAR rune = 0xF0003 // Parentheses in regex are concatenated with this - it acts as a pseudio-parentheses var RPAREN_CHAR rune = 0xF0004 var NONCAPLPAREN_CHAR rune = 0xF0005 // Represents a non-capturing group's LPAREN // Returns true if str[idx] and str[idx-1] are separated by a word boundary. func isWordBoundary(str []rune, idx int) bool { str_runes := []rune(str) wbounded := idx == 0 || idx >= len(str) || (!slices.Contains(wordChars, str_runes[idx-1]) && slices.Contains(wordChars, str_runes[idx])) || (slices.Contains(wordChars, str_runes[idx-1]) && !slices.Contains(wordChars, str_runes[idx])) return wbounded } func isNormalChar(c rune) bool { specialChars := []rune(`?*\^${}()+|[].~<>`) specialChars = append(specialChars, LBRACKET, RBRACKET, NONCAPLPAREN_CHAR) return !slices.Contains(specialChars, c) } func assert(cond bool) { if cond != true { panic("Assertion Failed") } } func deleteFromSlice[T comparable](slc []T, val T) []T { toReturn := make([]T, 0, len(slc)) for _, v := range slc { if v != val { toReturn = append(toReturn, v) } } return toReturn } // Ensure that the given elements are only appended to the given slice if they // don't already exist. Returns the new slice, and the number of unique items appended. func unique_append[T comparable](slc []T, items ...T) ([]T, int) { num_appended := 0 for _, item := range items { if !slices.Contains(slc, item) { slc = append(slc, item) num_appended++ } } return slc, num_appended } // Returns true only if all the given elements are equal func allEqual[T comparable](items ...T) bool { first := items[0] for _, item := range items { if item != first { return false } } return true } // Returns all elements in slice A that are NOT in slice B func setDifference[T comparable](s1 []T, s2 []T) []T { toReturn := make([]T, 0, len(s1)) for _, val := range s1 { if !slices.Contains(s2, val) { toReturn = append(toReturn, val) } } return toReturn } // Map function - convert a slice of T to a slice of V, based on a function // that maps a T to a V func Map[T, V any](slc []T, fn func(T) V) []V { toReturn := make([]V, len(slc)) for i, val := range slc { toReturn[i] = fn(val) } return toReturn } // Reduce function - reduces a slice of a type into a value of the type, // based on the given function. func Reduce[T any](slc []T, fn func(T, T) T) T { if len(slc) == 0 { panic("Reduce on empty slice.") } for len(slc) > 1 { v1 := slc[0] v2 := slc[1] slc = slc[1:] slc[0] = fn(v1, v2) } return slc[0] } // Generate numbers in a range - start (inclusive) to end (exclusive) func genRange(start, end int) []int { toRet := make([]int, end-start) for i := start; i < end; i++ { toRet[i-start] = i } return toRet } // Returns a rune-slice containing all possible cases of the given rune. // At the moment, this includes: // 1. Upper case // 2. Lower case // 3. Title case func allCases(r rune) []rune { return []rune{unicode.ToLower(r), unicode.ToUpper(r), unicode.ToTitle(r)} } // Expands a slice to the given length func expandSlice[T any](slc []T, newSize int) []T { toRet := make([]T, newSize) copy(toRet, slc) return toRet }