From d16f8d2f38fc42146fcec266dc379034d54fbd70 Mon Sep 17 00:00:00 2001 From: Rob Farrow Date: Thu, 3 Jun 2021 12:20:35 -0700 Subject: [PATCH] better test for encoding values with quotes --- README.md | 12 ++++----- encoder.go | 36 +++++++++++++++++++------- encoder_test.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6cb7dd0..9fce6cf 100644 --- a/README.md +++ b/README.md @@ -83,9 +83,9 @@ Multiple top-level forms are allowed and returned as an array of Nodes by the de Writes the Node object in brief format. ```go -var Node Node +var node Node var out io.Writer -err := Node.Encode(out) +err := node.Encode(out) ``` ### Brief XML Output @@ -93,9 +93,9 @@ err := Node.Encode(out) Writes the Node object in XML format. ```go -var Node Node +var node Node var out io.Writer -err := Node.WriteXML(out) +err := node.WriteXML(out) ``` XML output uses a template. This serves as an example of using brief with a template. @@ -179,7 +179,7 @@ Lookup is a Node method which gets a context value from a value spec. Slice is a Node method which creates a slice of strings from a sequence of value specs. ```text/template -{{ print .Slice "project.id" "project" }} +{{ .Slice "project.id" "project" }} ``` #### Join @@ -187,7 +187,7 @@ Slice is a Node method which creates a slice of strings from a sequence of value Join is a Node method which combines sequence of strings using a separator from a sequence of value specs. ```text/template -{{ print .Join "/" "project.id" "project" }} +{{ .Join "/" "project.id" "project" }} ``` #### Printf diff --git a/encoder.go b/encoder.go index 54a367c..164098d 100644 --- a/encoder.go +++ b/encoder.go @@ -3,7 +3,7 @@ package brief import ( "fmt" "strings" - "unicode" + "text/scanner" ) // Encode converts a node into brief format @@ -20,17 +20,33 @@ func (node *Node) Encode() []byte { return []byte(out.String()) } -func isIdentRune(ch rune, i int) bool { - return ch == '_' || unicode.IsLetter(ch) || unicode.IsDigit(ch) && i > 0 -} - -func isSymbol(token string) bool { - for i, ch := range token { - if !isIdentRune(ch, i) { +// NoQuote tests if the value is an identifier or number +func NoQuote(value string) bool { + var s scanner.Scanner + s.Init(strings.NewReader(value)) + tok := s.Scan() + var minus bool + for { + switch tok { + case scanner.Ident: + return !minus && s.TokenText() == value + case scanner.Float, scanner.Int: + numval := s.TokenText() + if minus { + numval = "-" + numval + } + return numval == value + case '-': + if minus { + return false + } + minus = true + tok = s.Scan() + default: return false } } - return true + } func (node *Node) write(out *strings.Builder) []*Node { @@ -40,7 +56,7 @@ func (node *Node) write(out *strings.Builder) []*Node { out.WriteString(":" + node.Name) } for key, val := range node.Keys { - if isSymbol(val) { + if NoQuote(val) { out.WriteString(fmt.Sprintf(" %s:%s", key, val)) continue } diff --git a/encoder_test.go b/encoder_test.go index 946820c..ffb5024 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -26,3 +26,72 @@ func TestEncoder(t *testing.T) { t.Fatal("before and after not the same") } } + +func TestNoQuote(t *testing.T) { + tests := []struct { + value string + valid bool + }{ + { + value: "one", + valid: true, + }, + { + value: "-one", + valid: false, + }, + { + value: "-", + valid: false, + }, + { + value: "24", + valid: true, + }, + { + value: "33.0e4", + valid: true, + }, + { + value: "-33.0e4", + valid: true, + }, + { + value: "-22", + valid: true, + }, + { + value: "- 22", + valid: false, + }, + { + value: "24a", + valid: false, + }, + { + value: "33.0e-4", + valid: true, + }, + { + value: "x-22", + valid: false, + }, + { + value: "- - 33", + valid: false, + }, + { + value: "go-flags", + valid: false, + }, + { + value: "go flags", + valid: false, + }, + } + for i, test := range tests { + if brief.NoQuote(test.value) != test.valid { + t.Errorf("%d> failed %s", i, test.value) + } + } +}