169 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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 = 0xF0001
 | |
| var RBRACKET rune = 0xF0002
 | |
| var ANY_CHAR rune = 0xF0003    // Represents any character - used for states where the allChars flag is on.
 | |
| var LPAREN_CHAR rune = 0xF0004 // Parentheses in regex are concatenated with this - it acts as a pseudio-parentheses
 | |
| var RPAREN_CHAR rune = 0xF0005
 | |
| var NONCAPLPAREN_CHAR rune = 0xF0006 // Represents a non-capturing group's LPAREN
 | |
| var ESC_BACKSLASH rune = 0xF0007     // Represents an escaped backslash
 | |
| var CHAR_RANGE rune = 0xF0008        // Represents a character range
 | |
| 
 | |
| var specialChars = []rune{'?', '*', '\\', '^', '$', '{', '}', '(', ')', '[', ']', '+', '|', '.', '~', '<', '>', LBRACKET, RBRACKET, NONCAPLPAREN_CHAR}
 | |
| 
 | |
| // An interface for int and rune, which are identical
 | |
| type character interface {
 | |
| 	int | rune
 | |
| }
 | |
| 
 | |
| // 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 isSpecialChar(c rune) bool {
 | |
| 	return slices.Contains(specialChars, c)
 | |
| 
 | |
| }
 | |
| 
 | |
| // Some special characters have metacharacter replacements. These characters, when encountered in their literal form, can be treated as regular characters.
 | |
| func isSpecialCharWithMetacharReplacement(c rune) bool {
 | |
| 	return slices.Contains([]rune{'[', ']'}, c)
 | |
| }
 | |
| 
 | |
| func isNormalChar(c rune) bool {
 | |
| 	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[T character](start, end T) []T {
 | |
| 	toRet := make([]T, 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
 | |
| }
 | |
| 
 | |
| func isHex(c rune) bool {
 | |
| 	return slices.Contains([]rune("0123456789abcdefABCDEF"), c)
 | |
| }
 | |
| 
 | |
| func isOctal(c rune) bool {
 | |
| 	return slices.Contains([]rune("01234567"), c)
 | |
| }
 | |
| 
 | |
| // Replace an element in a slice with another, given both values
 | |
| func replaceByValue[T comparable](slc []T, toReplace T, replaceWith T) []T {
 | |
| 	for i, val := range slc {
 | |
| 		if val == toReplace {
 | |
| 			slc[i] = replaceWith
 | |
| 		}
 | |
| 	}
 | |
| 	return slc
 | |
| }
 |