Handle different escape characters

pull/27/head
James Sturtevant 3 years ago
parent 3b2c8d38b6
commit 4984f7a96c

@ -193,6 +193,12 @@ func TestExpand(t *testing.T) {
input: `${stringZ//\//-}`,
output: "foo-bar-baz",
},
// escape outside of expansion shouldn't be processed
{
params: map[string]string{"default_var": "foo"},
input: "\\\\something ${var=${default_var}}",
output: "\\\\something foo",
},
// substitute with a blank string
{
params: map[string]string{"stringZ": "foo.bar"},

@ -50,6 +50,7 @@ func (t *Tree) Parse(buf string) (tree *Tree, err error) {
func (t *Tree) parseAny() (Node, error) {
t.scanner.accept = acceptRune
t.scanner.mode = scanIdent | scanLbrack | scanEscape
t.scanner.escapeChars = dollar
switch t.scanner.scan() {
case tokenIdent:
@ -86,6 +87,8 @@ func (t *Tree) parseAny() (Node, error) {
}
func (t *Tree) parseFunc() (Node, error) {
// Turn on all escape characters
t.scanner.escapeChars = escapeAll
switch t.scanner.peek() {
case '#':
return t.parseLenFunc()

@ -34,6 +34,10 @@ var tests = []struct {
Text: "$$string",
Node: &TextNode{Value: "$string"}, // should not escape double dollar
},
{
Text: `\\.\pipe\pipename`,
Node: &TextNode{Value: `\\.\pipe\pipename`},
},
//
// variable only
@ -297,6 +301,28 @@ var tests = []struct {
},
},
},
// text before and after function with \\ outside of function
{
Text: `\\ hello ${#string} world \\`,
Node: &ListNode{
Nodes: []Node{
&TextNode{
Value: `\\ hello `,
},
&ListNode{
Nodes: []Node{
&FuncNode{
Param: "string",
Name: "#",
},
&TextNode{
Value: ` world \\`,
},
},
},
},
},
},
// escaped function arguments
{
@ -359,6 +385,21 @@ var tests = []struct {
},
},
},
{
Text: `${string/position/\/leng\\th}`,
Node: &FuncNode{
Param: "string",
Name: "/",
Args: []Node{
&TextNode{
Value: "position",
},
&TextNode{
Value: "/leng\\th",
},
},
},
},
// functions in functions
{

@ -34,17 +34,25 @@ const (
scanEscape
)
// predefined mode bits to control escape tokens.
const (
dollar byte = 1 << iota
backslash
escapeAll = dollar | backslash
)
// returns true if rune is accepted.
type acceptFunc func(r rune, i int) bool
// scanner implements a lexical scanner that reads unicode
// characters and tokens from a string buffer.
type scanner struct {
buf string
pos int
start int
width int
mode byte
buf string
pos int
start int
width int
mode byte
escapeChars byte
accept acceptFunc
}
@ -98,6 +106,11 @@ func (s *scanner) string() string {
return s.buf[s.start:s.pos]
}
// tests if the bit exists for a given character bit
func (s *scanner) shouldEscape(character byte) bool {
return s.escapeChars&character != 0
}
// scan reads the next token or Unicode character from source and
// returns it. It returns EOF at the end of the source.
func (s *scanner) scan() token {
@ -176,25 +189,26 @@ func (s *scanner) scanRbrack(r rune) bool {
}
// scanEscaped reads the next token or Unicode character from source
// and returns true if it being escaped and should be sipped.
// and returns true if it being escaped and should be skipped.
func (s *scanner) scanEscaped(r rune) bool {
if s.mode&scanEscape == 0 {
return false
}
if r == '$' {
if r == '$' && s.shouldEscape(dollar) {
if s.peek() == '$' {
return true
}
}
if r != '\\' {
return false
}
switch s.peek() {
case '/', '\\':
return true
default:
return false
if r == '\\' && s.shouldEscape(backslash) {
switch s.peek() {
case '/', '\\':
return true
default:
return false
}
}
return false
}
//

Loading…
Cancel
Save