Browse Source

Add support for single quotes

this fixes #18
pull/21/head
Brett Langdon 11 years ago
parent
commit
43f1f42de4
5 changed files with 25 additions and 13 deletions
  1. +1
    -0
      README.md
  2. +3
    -3
      forge.go
  3. +4
    -0
      forge_test.go
  4. +15
    -10
      scanner.go
  5. +2
    -0
      test.cfg

+ 1
- 0
README.md View File

@ -26,6 +26,7 @@ global = "global value";
# Primary section # Primary section
primary { primary {
string = "primary string value"; string = "primary string value";
single = 'single quotes are allowed too';
integer = 500; integer = 500;
float = 80.80; float = 80.80;
boolean = true; boolean = true;


+ 3
- 3
forge.go View File

@ -32,7 +32,7 @@
// NULL: 'null' // NULL: 'null'
// INTEGER: ('-')? NUMBERS // INTEGER: ('-')? NUMBERS
// FLOAT: ('-')? NUMBERS '.' NUMBERS // FLOAT: ('-')? NUMBERS '.' NUMBERS
// STRING: '"' .* '"'
// STRING: ['"] .* ['"]
// REFERENCE: (IDENTIFIER)? ('.' IDENTIFIER)+ // REFERENCE: (IDENTIFIER)? ('.' IDENTIFIER)+
// VALUE: BOOL | NULL | INTEGER | FLOAT | STRING | REFERENCE // VALUE: BOOL | NULL | INTEGER | FLOAT | STRING | REFERENCE
// //
@ -46,8 +46,8 @@
// //
// Values // Values
// * String: // * 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: // * Integer:
// Any number without decimal places (e.g. 500) // Any number without decimal places (e.g. 500)
// * Float: // * Float:


+ 4
- 0
forge_test.go View File

@ -15,6 +15,8 @@ global = "global value";
primary { primary {
string = "primary string value"; string = "primary string value";
string_with_quote = "some \"quoted\" str\\ing"; string_with_quote = "some \"quoted\" str\\ing";
single = 'hello world';
single_with_quote = '\'hello\' "world"';
integer = 500; integer = 500;
float = 80.80; float = 80.80;
negative = -50; negative = -50;
@ -56,6 +58,8 @@ func assertDirectives(values map[string]interface{}, t *testing.T) {
primary := values["primary"].(map[string]interface{}) primary := values["primary"].(map[string]interface{})
assertEqual(primary["string"], "primary string value", t) assertEqual(primary["string"], "primary string value", t)
assertEqual(primary["string_with_quote"], "some \"quoted\" str\\ing", 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["integer"], int64(500), t)
assertEqual(primary["float"], float64(80.80), t) assertEqual(primary["float"], float64(80.80), t)
assertEqual(primary["negative"], int64(-50), t) assertEqual(primary["negative"], int64(-50), t)


+ 15
- 10
scanner.go View File

@ -14,10 +14,6 @@ func isLetter(ch rune) bool {
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')
} }
func isEscapeCharacter(ch rune) bool {
return ch == '\\' || ch == '"'
}
func isDigit(ch rune) bool { func isDigit(ch rune) bool {
return ('0' <= ch && ch <= '9') 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.ID = token.STRING
scanner.curTok.Literal = string(scanner.curCh)
scanner.curTok.Literal = ""
// Whether or not we are trying to escape a character // Whether or not we are trying to escape a character
escape := false escape := false
// If the first character is an escape
if scanner.curCh == '\\' {
escape = true
} else {
scanner.curTok.Literal = string(scanner.curCh)
}
for { for {
scanner.readRune() scanner.readRune()
if escape == false { if escape == false {
@ -137,16 +140,18 @@ func (scanner *Scanner) parseString() {
// A new backslash // A new backslash
escape = true escape = true
continue continue
} else if scanner.curCh == '"' {
} else if scanner.curCh == delimiter {
// An unescaped quote, time to bail out // An unescaped quote, time to bail out
break 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 // A valid escape character, continue as normal
escape = false escape = false
} else { } else {
// We had a backslash, but an invalid escape character // We had a backslash, but an invalid escape character
// TODO: "Unsupported escape character found" // TODO: "Unsupported escape character found"
break
} }
scanner.curTok.Literal += string(scanner.curCh) scanner.curTok.Literal += string(scanner.curCh)
} }
@ -204,8 +209,8 @@ func (scanner *Scanner) NextToken() token.Token {
switch ch { switch ch {
case '=': case '=':
scanner.curTok.ID = token.EQUAL scanner.curTok.ID = token.EQUAL
case '"':
scanner.parseString()
case '"', '\'':
scanner.parseString(ch)
case '{': case '{':
scanner.curTok.ID = token.LBRACKET scanner.curTok.ID = token.LBRACKET
case '}': case '}':


+ 2
- 0
test.cfg View File

@ -4,6 +4,8 @@ global = "global value";
primary { primary {
string = "primary string value"; string = "primary string value";
string_with_quote = "some \"quoted\" str\\ing"; string_with_quote = "some \"quoted\" str\\ing";
single = 'hello world';
single_with_quote = '\'hello\' "world"';
integer = 500; integer = 500;
float = 80.80; float = 80.80;
negative = -50; negative = -50;


Loading…
Cancel
Save