diff --git a/docs/exercises.md b/docs/exercises.md index 7a88f72..03d40d8 100644 --- a/docs/exercises.md +++ b/docs/exercises.md @@ -49,6 +49,14 @@ title: Exercises
  • 25_range_iterators - Range over Iterators
  • 26_errors - Error Handling
  • 27_custom_errors - Custom Errors
  • +
  • 28_defer - Defer
  • +
  • 29_go_routines - Go Routines
  • +
  • 30_channels - Channels
  • +
  • 31_mutexes - Mutexes
  • +
  • 32_sorting - Sorting
  • +
  • 33_string_formatting - String Formatting
  • +
  • 34_channel_buffering - Channel Buffering
  • +
  • 41_regex - Regular Expressions
  • 36_json - JSON Processing
  • 37_xml - XML Processing
  • diff --git a/internal/exercises/catalog.yaml b/internal/exercises/catalog.yaml index 37b779b..be980eb 100644 --- a/internal/exercises/catalog.yaml +++ b/internal/exercises/catalog.yaml @@ -169,6 +169,11 @@ concepts: test_regex: ".*" hints: - Use `make(chan T, N)` to create a buffered channel and `<-` to send and receive on it. +- slug: 35_channel_sync + title: Channel Synchronization + test_regex: ".*" + hints: + - Use a buffered boolean channel and wait till go routine completes. - slug: 39_channel_directions title: Channel Directions test_regex: ".*" @@ -178,11 +183,18 @@ concepts: - Send 50 jobs to the worker pool. - Receive results from the worker pool and store them in a `rs` slice. - Enforce type safety by specifying the channel directions in worker function. -- slug: 35_channel_sync - title: Channel Synchronization - test_regex: ".*" - hints: - - Use a buffered boolean channel and wait till go routine completes. +- slug: 41_regex + title: Regular Expressions + test_regex: ".*" + hints: + - Use regexp.MustCompile to create compiled regular expressions. + - Use MatchString to test if a string matches a pattern. + - Use FindAllString to extract all matches from text. + - Use ReplaceAllString to replace matches with new text. + - "Email regex: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + - "Phone regex: ^\\(\\d{3}\\) \\d{3}-\\d{4}$" + - "Number regex: \\d+\\.?\\d*" + - "Vowel regex: [aeiouAEIOU]" - slug: 37_xml title: XML Encoding and Decoding test_regex: ".*" diff --git a/internal/exercises/solutions/41_regex/regex.go b/internal/exercises/solutions/41_regex/regex.go new file mode 100644 index 0000000..29895eb --- /dev/null +++ b/internal/exercises/solutions/41_regex/regex.go @@ -0,0 +1,35 @@ +package regex + +import "regexp" + +// IsValidEmail returns true if the email address is valid. +func IsValidEmail(email string) bool { + pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` + re := regexp.MustCompile(pattern) + return re.MatchString(email) +} + +// ExtractNumbers returns all numbers found in the input string. +func ExtractNumbers(text string) []string { + pattern := `\d+\.?\d*` + re := regexp.MustCompile(pattern) + matches := re.FindAllString(text, -1) + if matches == nil { + return []string{} + } + return matches +} + +// ReplaceVowels replaces all vowels with asterisks. +func ReplaceVowels(text string) string { + pattern := `[aeiouAEIOU]` + re := regexp.MustCompile(pattern) + return re.ReplaceAllString(text, "*") +} + +// IsPhoneNumber returns true if the phone number matches format (XXX) XXX-XXXX. +func IsPhoneNumber(phone string) bool { + pattern := `^\(\d{3}\) \d{3}-\d{4}$` + re := regexp.MustCompile(pattern) + return re.MatchString(phone) +} diff --git a/internal/exercises/templates/41_regex/regex.go b/internal/exercises/templates/41_regex/regex.go new file mode 100644 index 0000000..d53b161 --- /dev/null +++ b/internal/exercises/templates/41_regex/regex.go @@ -0,0 +1,37 @@ +package regex + +// Task: +// Implement regular expression functions using Go's regexp package. +// +// 1. Implement IsValidEmail to validate email addresses using regex. +// 2. Implement ExtractNumbers to extract all numbers from a string. +// 3. Implement ReplaceVowels to replace all vowels with asterisks. +// 4. Implement IsPhoneNumber to validate phone numbers in format (XXX) XXX-XXXX. + +// IsValidEmail should return true if the email address is valid. +// A valid email should have the format: user@domain.com +func IsValidEmail(email string) bool { + // TODO: implement using regexp.MustCompile and MatchString + return false +} + +// ExtractNumbers should return all numbers found in the input string. +// Numbers can be integers or decimals (e.g., "123", "45.67"). +func ExtractNumbers(text string) []string { + // TODO: implement using regexp.FindAllString + return nil +} + +// ReplaceVowels should replace all vowels (a, e, i, o, u) with asterisks. +// Case-insensitive replacement. +func ReplaceVowels(text string) string { + // TODO: implement using regexp.MustCompile and ReplaceAllString + return "" +} + +// IsPhoneNumber should return true if the phone number matches format (XXX) XXX-XXXX. +// Example: (123) 456-7890 +func IsPhoneNumber(phone string) bool { + // TODO: implement using regexp.MustCompile and MatchString + return false +} diff --git a/internal/exercises/templates/41_regex/regex_test.go b/internal/exercises/templates/41_regex/regex_test.go new file mode 100644 index 0000000..087b45a --- /dev/null +++ b/internal/exercises/templates/41_regex/regex_test.go @@ -0,0 +1,99 @@ +package regex + +import "testing" + +func TestIsValidEmail(t *testing.T) { + tests := []struct { + email string + expected bool + }{ + {"user@domain.com", true}, + {"test.email@example.org", true}, + {"invalid-email", false}, + {"@domain.com", false}, + {"user@", false}, + {"user@domain", false}, + {"", false}, + } + + for _, test := range tests { + result := IsValidEmail(test.email) + if result != test.expected { + t.Errorf("IsValidEmail(%q) = %v, want %v", test.email, result, test.expected) + } + } +} + +func TestExtractNumbers(t *testing.T) { + tests := []struct { + text string + expected []string + }{ + {"I have 123 apples and 45.67 oranges", []string{"123", "45.67"}}, + {"The price is $99.99", []string{"99.99"}}, + {"No numbers here", []string{}}, + {"123", []string{"123"}}, + {"12.34.56", []string{"12.34", "56"}}, + {"", []string{}}, + } + + for _, test := range tests { + result := ExtractNumbers(test.text) + if len(result) != len(test.expected) { + t.Errorf("ExtractNumbers(%q) length = %d, want %d", test.text, len(result), len(test.expected)) + continue + } + for i, num := range result { + if num != test.expected[i] { + t.Errorf("ExtractNumbers(%q)[%d] = %q, want %q", test.text, i, num, test.expected[i]) + } + } + } +} + +func TestReplaceVowels(t *testing.T) { + tests := []struct { + text string + expected string + }{ + {"hello", "h*ll*"}, + {"HELLO", "H*LL*"}, + {"Hello World", "H*ll* W*rld"}, + {"bcdfg", "bcdfg"}, + {"", ""}, + {"aeiou", "*****"}, + {"AEIOU", "*****"}, + } + + for _, test := range tests { + result := ReplaceVowels(test.text) + if result != test.expected { + t.Errorf("ReplaceVowels(%q) = %q, want %q", test.text, result, test.expected) + } + } +} + +func TestIsPhoneNumber(t *testing.T) { + tests := []struct { + phone string + expected bool + }{ + {"(123) 456-7890", true}, + {"(555) 123-4567", true}, + {"(000) 000-0000", true}, + {"123-456-7890", false}, + {"(123)456-7890", false}, + {"(123) 4567890", false}, + {"123 456 7890", false}, + {"(123) 456-789", false}, + {"(12) 456-7890", false}, + {"", false}, + } + + for _, test := range tests { + result := IsPhoneNumber(test.phone) + if result != test.expected { + t.Errorf("IsPhoneNumber(%q) = %v, want %v", test.phone, result, test.expected) + } + } +}