mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
feat(common): Add string parser
This commit is contained in:
parent
f530195cac
commit
eb587dfb42
129
common/parser/strings.go
Normal file
129
common/parser/strings.go
Normal 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
|
||||||
|
}
|
177
common/parser/strings_test.go
Normal file
177
common/parser/strings_test.go
Normal 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
1
go.mod
@ -11,6 +11,7 @@ require (
|
|||||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/emirpasic/gods v1.18.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/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||||
github.com/k0kubun/pp v3.0.1+incompatible // indirect
|
github.com/k0kubun/pp v3.0.1+incompatible // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -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/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 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
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.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 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user