feat(common): Add string parser

This commit is contained in:
Myzel394 2024-09-17 21:21:31 +02:00
parent f530195cac
commit eb587dfb42
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
4 changed files with 309 additions and 0 deletions

129
common/parser/strings.go Normal file
View File

@ -0,0 +1,129 @@
package parser
type ParseFeatures struct {
ParseDoubleQuotes bool
ParseEscapedCharacters bool
}
var FullFeatures = ParseFeatures{
ParseDoubleQuotes: true,
ParseEscapedCharacters: true,
}
type ParsedString struct {
Raw string
Value string
Features ParseFeatures
}
func ParseRawString(
raw string,
features ParseFeatures,
) ParsedString {
value := raw
// Parse double quotes
if features.ParseDoubleQuotes {
value = ParseDoubleQuotes(value)
}
// Parse escaped characters
if features.ParseEscapedCharacters {
value = ParseEscapedCharacters(value)
}
return ParsedString{
Raw: raw,
Value: value,
Features: features,
}
}
func ParseDoubleQuotes(
raw string,
) string {
value := raw
currentIndex := 0
for {
start, found := findNextDoubleQuote(value, currentIndex)
if found && start < (len(value)-1) {
currentIndex = max(0, start-1)
end, found := findNextDoubleQuote(value, start+1)
if found {
insideContent := value[start+1 : end]
value = modifyString(value, start, end+1, insideContent)
continue
}
}
break
}
return value
}
func ParseEscapedCharacters(
raw string,
) string {
value := raw
currentIndex := 0
for {
position, found := findNextEscapedCharacter(value, currentIndex)
if found {
currentIndex = max(0, position-1)
escapedCharacter := value[position+1]
value = modifyString(value, position, position+2, string(escapedCharacter))
} else {
break
}
}
return value
}
func modifyString(
input string,
start int,
end int,
newValue string,
) string {
return input[:start] + newValue + input[end:]
}
// Find the next non-escaped double quote in [raw] starting from [startIndex]
// When no double quote is found, return -1
// Return as the second argument whether a double quote was found
func findNextDoubleQuote(
raw string,
startIndex int,
) (int, bool) {
for index := startIndex; index < len(raw); index++ {
if raw[index] == '"' {
if index == 0 || raw[index-1] != '\\' {
return index, true
}
}
}
return -1, false
}
func findNextEscapedCharacter(
raw string,
startIndex int,
) (int, bool) {
for index := startIndex; index < len(raw); index++ {
if raw[index] == '\\' && index < len(raw)-1 {
return index, true
}
}
return -1, false
}

View File

@ -0,0 +1,177 @@
package parser
import (
"testing"
"github.com/google/go-cmp/cmp"
)
func TestStringsSingleWortQuotedFullFeatures(
t *testing.T,
) {
input := `hello "world"`
expected := ParsedString{
Raw: input,
Value: "hello world",
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsFullyQuotedFullFeatures(
t *testing.T,
) {
input := `"hello world"`
expected := ParsedString{
Raw: input,
Value: "hello world",
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsMultipleQuotesFullFeatures(
t *testing.T,
) {
input := `hello "world goodbye"`
expected := ParsedString{
Raw: input,
Value: "hello world goodbye",
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsSimpleEscapedFullFeatures(
t *testing.T,
) {
input := `hello \"world`
expected := ParsedString{
Raw: input,
Value: `hello "world`,
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsEscapedQuotesFullFeatures(
t *testing.T,
) {
input := `hello \"world\"`
expected := ParsedString{
Raw: input,
Value: `hello "world"`,
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsQuotesAndEscapedFullFeatures(
t *testing.T,
) {
input := `hello "world how\" are you"`
expected := ParsedString{
Raw: input,
Value: `hello world how" are you`,
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsIncompleteQuotesFullFeatures(
t *testing.T,
) {
input := `hello "world`
expected := ParsedString{
Raw: input,
Value: `hello "world`,
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsIncompleteQuoteEscapedFullFeatures(
t *testing.T,
) {
input := `hello "world\"`
expected := ParsedString{
Raw: input,
Value: `hello "world"`,
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsIncompleteQuotes2FullFeatures(
t *testing.T,
) {
input := `hello "world how" "are you`
expected := ParsedString{
Raw: input,
Value: `hello world how "are you`,
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}
func TestStringsIncompleteQuotes3FullFeatures(
t *testing.T,
) {
input := `hello "world how are you`
expected := ParsedString{
Raw: input,
Value: `hello "world how are you`,
Features: FullFeatures,
}
actual := ParseRawString(input, FullFeatures)
if !(cmp.Equal(expected, actual)) {
t.Errorf("Expected %v, got %v", expected, actual)
}
}

1
go.mod
View File

@ -11,6 +11,7 @@ require (
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
github.com/k0kubun/pp v3.0.1+incompatible // indirect

2
go.sum
View File

@ -4,6 +4,8 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=