diff --git a/README.md b/README.md index 591cef8..a095904 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ global = "global value"; # Primary section primary { string = "primary string value"; + single = 'single quotes are allowed too'; integer = 500; float = 80.80; boolean = true; diff --git a/forge.go b/forge.go index bf5b1f4..bf40833 100644 --- a/forge.go +++ b/forge.go @@ -32,7 +32,7 @@ // NULL: 'null' // INTEGER: ('-')? NUMBERS // FLOAT: ('-')? NUMBERS '.' NUMBERS -// STRING: '"' .* '"' +// STRING: ['"] .* ['"] // REFERENCE: (IDENTIFIER)? ('.' IDENTIFIER)+ // VALUE: BOOL | NULL | INTEGER | FLOAT | STRING | REFERENCE // @@ -46,8 +46,8 @@ // // Values // * String: -// Any value enclosed in double quotes (single quotes not allowed) (e.g. "string"). -// Double quotes and backslashes can be escaped with backslashes (e.g. "\"quoted\"" and "\\<--backslash") +// Any value enclosed in double or single quotes (e.g. "string" or 'string'). +// Double quotes, single quotes, and backslashes can be escaped with backslashes (e.g. "\"quoted\"", '\'quoted\'', and "\\<--backslash") // * Integer: // Any number without decimal places (e.g. 500) // * Float: diff --git a/forge_test.go b/forge_test.go index 0d55720..c210941 100644 --- a/forge_test.go +++ b/forge_test.go @@ -15,6 +15,8 @@ global = "global value"; primary { string = "primary string value"; string_with_quote = "some \"quoted\" str\\ing"; + single = 'hello world'; + single_with_quote = '\'hello\' "world"'; integer = 500; float = 80.80; negative = -50; @@ -56,6 +58,8 @@ func assertDirectives(values map[string]interface{}, t *testing.T) { primary := values["primary"].(map[string]interface{}) assertEqual(primary["string"], "primary string value", t) assertEqual(primary["string_with_quote"], "some \"quoted\" str\\ing", t) + assertEqual(primary["single"], "hello world", t) + assertEqual(primary["single_with_quote"], "'hello' \"world\"", t) assertEqual(primary["integer"], int64(500), t) assertEqual(primary["float"], float64(80.80), t) assertEqual(primary["negative"], int64(-50), t) diff --git a/scanner.go b/scanner.go index 24663f8..dff54aa 100644 --- a/scanner.go +++ b/scanner.go @@ -14,10 +14,6 @@ func isLetter(ch rune) bool { return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') } -func isEscapeCharacter(ch rune) bool { - return ch == '\\' || ch == '"' -} - func isDigit(ch rune) bool { return ('0' <= ch && ch <= '9') } @@ -124,11 +120,18 @@ func (scanner *Scanner) parseNumber(negative bool) { } } -func (scanner *Scanner) parseString() { +func (scanner *Scanner) parseString(delimiter rune) { scanner.curTok.ID = token.STRING - scanner.curTok.Literal = string(scanner.curCh) + scanner.curTok.Literal = "" // Whether or not we are trying to escape a character escape := false + + // If the first character is an escape + if scanner.curCh == '\\' { + escape = true + } else { + scanner.curTok.Literal = string(scanner.curCh) + } for { scanner.readRune() if escape == false { @@ -137,16 +140,18 @@ func (scanner *Scanner) parseString() { // A new backslash escape = true continue - } else if scanner.curCh == '"' { + } else if scanner.curCh == delimiter { // An unescaped quote, time to bail out break } - } else if isEscapeCharacter(scanner.curCh) { + // Continue as normal, no escaping necessary + } else if scanner.curCh == '\\' || scanner.curCh == delimiter { // A valid escape character, continue as normal escape = false } else { // We had a backslash, but an invalid escape character // TODO: "Unsupported escape character found" + break } scanner.curTok.Literal += string(scanner.curCh) } @@ -204,8 +209,8 @@ func (scanner *Scanner) NextToken() token.Token { switch ch { case '=': scanner.curTok.ID = token.EQUAL - case '"': - scanner.parseString() + case '"', '\'': + scanner.parseString(ch) case '{': scanner.curTok.ID = token.LBRACKET case '}': diff --git a/test.cfg b/test.cfg index 4d0b634..0ba9d11 100644 --- a/test.cfg +++ b/test.cfg @@ -4,6 +4,8 @@ global = "global value"; primary { string = "primary string value"; string_with_quote = "some \"quoted\" str\\ing"; + single = 'hello world'; + single_with_quote = '\'hello\' "world"'; integer = 500; float = 80.80; negative = -50;