diff --git a/v1/ast/interning.go b/v1/ast/interning.go index 075fb1e757..012cffb9a9 100644 --- a/v1/ast/interning.go +++ b/v1/ast/interning.go @@ -63,6 +63,29 @@ func HasInternedIntNumberTerm(i int) bool { return i >= -1 && i < len(intNumberTerms) } +func InternedStringTerm(s string) *Term { + if term, ok := internedStringTerms[s]; ok { + return term + } + + return StringTerm(s) +} + +var internedStringTerms = map[string]*Term{ + "": InternedEmptyString, + "0": StringTerm("0"), + "1": StringTerm("1"), + "2": StringTerm("2"), + "3": StringTerm("3"), + "4": StringTerm("4"), + "5": StringTerm("5"), + "6": StringTerm("6"), + "7": StringTerm("7"), + "8": StringTerm("8"), + "9": StringTerm("9"), + "10": StringTerm("10"), +} + var stringToIntNumberTermMap = map[string]*Term{ "-1": minusOneTerm, "0": intNumberTerms[0], diff --git a/v1/ast/parser.go b/v1/ast/parser.go index ce3680224b..61fb41c58a 100644 --- a/v1/ast/parser.go +++ b/v1/ast/parser.go @@ -1639,6 +1639,10 @@ func (p *Parser) parseNumber() *Term { func (p *Parser) parseString() *Term { if p.s.lit[0] == '"' { + if p.s.lit == "\"\"" { + return NewTerm(InternedEmptyString.Value).SetLocation(p.s.Loc()) + } + var s string err := json.Unmarshal([]byte(p.s.lit), &s) if err != nil { diff --git a/v1/topdown/object.go b/v1/topdown/object.go index 4db8fa8272..61da5478c1 100644 --- a/v1/topdown/object.go +++ b/v1/topdown/object.go @@ -21,6 +21,10 @@ func builtinObjectUnion(_ BuiltinContext, operands []*ast.Term, iter func(*ast.T return err } + if objA.Compare(objB) == 0 { + return iter(operands[0]) + } + r := mergeWithOverwrite(objA, objB) return iter(ast.NewTerm(r)) diff --git a/v1/topdown/regex.go b/v1/topdown/regex.go index 6c1f6794cc..2c434dda87 100644 --- a/v1/topdown/regex.go +++ b/v1/topdown/regex.go @@ -260,6 +260,9 @@ func builtinRegexReplace(bctx BuiltinContext, operands []*ast.Term, iter func(*a } res := re.ReplaceAllString(string(base), string(value)) + if res == string(base) { + return iter(operands[0]) + } return iter(ast.StringTerm(res)) } diff --git a/v1/topdown/strings.go b/v1/topdown/strings.go index 0b7c8a5180..742b788dc4 100644 --- a/v1/topdown/strings.go +++ b/v1/topdown/strings.go @@ -321,6 +321,10 @@ func builtinSubstring(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter return iter(ast.StringTerm(sbase[startIndex:])) } + if startIndex == 0 && length >= len(sbase) { + return iter(operands[0]) + } + upto := startIndex + length if len(sbase) < upto { upto = len(sbase) @@ -328,6 +332,10 @@ func builtinSubstring(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter return iter(ast.StringTerm(sbase[startIndex:upto])) } + if startIndex == 0 && length >= utf8.RuneCountInString(sbase) { + return iter(operands[0]) + } + runes := []rune(base) if startIndex >= len(runes) { @@ -638,7 +646,7 @@ func builtinSprintf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) if s == "%d" && astArr.Len() == 1 { if n, ok := astArr.Elem(0).Value.(ast.Number); ok { if i, ok := n.Int(); ok { - return iter(ast.StringTerm(strconv.Itoa(i))) + return iter(ast.InternedStringTerm(strconv.Itoa(i))) } } }