mirror of
https://github.com/google/go-jsonnet.git
synced 2026-05-05 12:06:11 +02:00
String indexing, change of string representation (#34)
* String indexing, change of string representation Also std.pow
This commit is contained in:
parent
c26c50c04b
commit
3c94bde4dc
35
builtins.go
35
builtins.go
@ -50,7 +50,7 @@ func builtinPlus(e *evaluator, xp, yp potentialValue) (value, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makeValueString(left.value + right.value), nil
|
||||
return concatStrings(left, right), nil
|
||||
case valueObject:
|
||||
right, err := e.evaluateObject(yp)
|
||||
if err != nil {
|
||||
@ -74,7 +74,7 @@ func builtinMinus(e *evaluator, xp, yp potentialValue) (value, error) {
|
||||
return makeValueNumber(x.value - y.value), nil
|
||||
}
|
||||
|
||||
func builtinGreater(e *evaluator, xp, yp potentialValue) (value, error) {
|
||||
func builtinLess(e *evaluator, xp, yp potentialValue) (value, error) {
|
||||
x, err := e.evaluate(xp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -85,20 +85,20 @@ func builtinGreater(e *evaluator, xp, yp potentialValue) (value, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makeValueBoolean(left.value > right.value), nil
|
||||
return makeValueBoolean(left.value < right.value), nil
|
||||
case *valueString:
|
||||
right, err := e.evaluateString(yp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makeValueBoolean(left.value > right.value), nil
|
||||
return makeValueBoolean(stringLessThan(left, right)), nil
|
||||
default:
|
||||
return nil, e.typeErrorGeneral(x)
|
||||
}
|
||||
}
|
||||
|
||||
func builtinLess(e *evaluator, xp, yp potentialValue) (value, error) {
|
||||
return builtinGreater(e, yp, xp)
|
||||
func builtinGreater(e *evaluator, xp, yp potentialValue) (value, error) {
|
||||
return builtinLess(e, yp, xp)
|
||||
}
|
||||
|
||||
func builtinGreaterEq(e *evaluator, xp, yp potentialValue) (value, error) {
|
||||
@ -144,7 +144,7 @@ func builtinLength(e *evaluator, xp potentialValue) (value, error) {
|
||||
case *valueArray:
|
||||
num = len(x.elements)
|
||||
case *valueString:
|
||||
num = len(x.value)
|
||||
num = x.length()
|
||||
case *valueFunction:
|
||||
num = len(x.parameters())
|
||||
default:
|
||||
@ -273,7 +273,7 @@ func primitiveEquals(e *evaluator, xp potentialValue, yp potentialValue) (value,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makeValueBoolean(left.value == right.value), nil
|
||||
return makeValueBoolean(stringEqual(left, right)), nil
|
||||
case *valueNull:
|
||||
return makeValueBoolean(true), nil
|
||||
case *valueFunction:
|
||||
@ -358,13 +358,25 @@ func builtinObjectHasEx(e *evaluator, objp potentialValue, fnamep potentialValue
|
||||
return nil, err
|
||||
}
|
||||
for _, fieldname := range objectFields(obj, hidden.value) {
|
||||
if fieldname == fname.value {
|
||||
if fieldname == string(fname.value) {
|
||||
return makeValueBoolean(true), nil
|
||||
}
|
||||
}
|
||||
return makeValueBoolean(false), nil
|
||||
}
|
||||
|
||||
func builtinPow(e *evaluator, basep potentialValue, expp potentialValue) (value, error) {
|
||||
base, err := e.evaluateNumber(basep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exp, err := e.evaluateNumber(expp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makeDoubleCheck(e, math.Pow(base.value, exp.value))
|
||||
}
|
||||
|
||||
type unaryBuiltin func(*evaluator, potentialValue) (value, error)
|
||||
type binaryBuiltin func(*evaluator, potentialValue, potentialValue) (value, error)
|
||||
type ternaryBuiltin func(*evaluator, potentialValue, potentialValue, potentialValue) (value, error)
|
||||
@ -451,8 +463,8 @@ var bopBuiltins = []*BinaryBuiltin{
|
||||
ast.BopLess: &BinaryBuiltin{name: "operator<,", function: builtinLess, parameters: ast.Identifiers{"x", "y"}},
|
||||
ast.BopLessEq: &BinaryBuiltin{name: "operator<=", function: builtinLessEq, parameters: ast.Identifiers{"x", "y"}},
|
||||
|
||||
ast.BopManifestEqual: todo,
|
||||
ast.BopManifestUnequal: todo,
|
||||
// bopManifestEqual: <desugared>,
|
||||
// bopManifestUnequal: <desugared>,
|
||||
|
||||
ast.BopBitwiseAnd: todo,
|
||||
ast.BopBitwiseXor: todo,
|
||||
@ -490,4 +502,5 @@ var funcBuiltins = map[string]evalCallable{
|
||||
"atan": &UnaryBuiltin{name: "atan", function: builtinAtan, parameters: ast.Identifiers{"x"}},
|
||||
"log": &UnaryBuiltin{name: "log", function: builtinLog, parameters: ast.Identifiers{"x"}},
|
||||
"exp": &UnaryBuiltin{name: "exp", function: builtinExp, parameters: ast.Identifiers{"x"}},
|
||||
"pow": &BinaryBuiltin{name: "pow", function: builtinPow, parameters: ast.Identifiers{"base", "exp"}},
|
||||
}
|
||||
|
||||
@ -313,7 +313,7 @@ func (i *interpreter) evaluate(a ast.Node, context *TraceContext) (value, error)
|
||||
var fieldName string
|
||||
switch fieldNameValue := fieldNameValue.(type) {
|
||||
case *valueString:
|
||||
fieldName = fieldNameValue.value
|
||||
fieldName = fieldNameValue.getString()
|
||||
case *valueNull:
|
||||
// Omitted field.
|
||||
continue
|
||||
@ -339,7 +339,7 @@ func (i *interpreter) evaluate(a ast.Node, context *TraceContext) (value, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, e.Error(msg.value)
|
||||
return nil, e.Error(msg.getString())
|
||||
|
||||
case *ast.Index:
|
||||
targetValue, err := e.evalInCurrentContext(ast.Target)
|
||||
@ -353,11 +353,14 @@ func (i *interpreter) evaluate(a ast.Node, context *TraceContext) (value, error)
|
||||
switch target := targetValue.(type) {
|
||||
// TODO(sbarzowski) better error handling if bad index type
|
||||
case valueObject:
|
||||
indexString := index.(*valueString).value
|
||||
indexString := index.(*valueString).getString()
|
||||
return target.index(e, indexString)
|
||||
case *valueArray:
|
||||
indexInt := int(index.(*valueNumber).value)
|
||||
return e.evaluate(target.elements[indexInt])
|
||||
case *valueString:
|
||||
indexInt := int(index.(*valueNumber).value)
|
||||
return target.index(e, indexInt)
|
||||
}
|
||||
|
||||
return nil, e.Error(fmt.Sprintf("Value non indexable: %v", reflect.TypeOf(targetValue)))
|
||||
@ -417,7 +420,7 @@ func (i *interpreter) evaluate(a ast.Node, context *TraceContext) (value, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return superIndex(e, i.stack.getSelfBinding(), indexStr.value)
|
||||
return superIndex(e, i.stack.getSelfBinding(), indexStr.getString())
|
||||
|
||||
case *ast.Function:
|
||||
return &valueFunction{
|
||||
@ -614,7 +617,7 @@ func (i *interpreter) manifestJSON(trace *TraceElement, v value, multiline bool,
|
||||
}
|
||||
|
||||
case *valueString:
|
||||
buf.WriteString(unparseString(v.value))
|
||||
buf.WriteString(unparseString(v.getString()))
|
||||
|
||||
default:
|
||||
return makeRuntimeError(
|
||||
|
||||
1
testdata/pow.golden
vendored
Normal file
1
testdata/pow.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
1024
|
||||
1
testdata/pow.input
vendored
Normal file
1
testdata/pow.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.pow(2, 10)
|
||||
1
testdata/pow2.golden
vendored
Normal file
1
testdata/pow2.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
100
|
||||
1
testdata/pow2.input
vendored
Normal file
1
testdata/pow2.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.pow(10, 2)
|
||||
1
testdata/pow3.golden
vendored
Normal file
1
testdata/pow3.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
-1
|
||||
1
testdata/pow3.input
vendored
Normal file
1
testdata/pow3.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.pow(-1, 3)
|
||||
1
testdata/pow4.golden
vendored
Normal file
1
testdata/pow4.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
RUNTIME ERROR: Not a number
|
||||
1
testdata/pow4.input
vendored
Normal file
1
testdata/pow4.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.pow(-1, 0.2)
|
||||
1
testdata/pow5.golden
vendored
Normal file
1
testdata/pow5.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
1.1486983549970351
|
||||
1
testdata/pow5.input
vendored
Normal file
1
testdata/pow5.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.pow(2, 0.2)
|
||||
1
testdata/pow6.golden
vendored
Normal file
1
testdata/pow6.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
179754255558423237941456473541041914055900576989066323324790225489927405882855355678287600996768561249516179005117487900995816670389973592784065259754879901383672166545582558984633189841112133404180226485873094649946308637416044832879263264479292996247265876810814717146304544813220293475007274508971205984256
|
||||
2
testdata/pow6.input
vendored
Normal file
2
testdata/pow6.input
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// roughly the largest possible double
|
||||
std.pow(1.1, 7447.081)
|
||||
1
testdata/pow7.golden
vendored
Normal file
1
testdata/pow7.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
RUNTIME ERROR: Overflow
|
||||
2
testdata/pow7.input
vendored
Normal file
2
testdata/pow7.input
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// slightly more than the largest possible double
|
||||
std.pow(1.1, 7447.082)
|
||||
1
testdata/std_substr.golden
vendored
Normal file
1
testdata/std_substr.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
"bc"
|
||||
1
testdata/std_substr.input
vendored
Normal file
1
testdata/std_substr.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
std.substr("abcd", 1, 2)
|
||||
1
testdata/string_comparison1.golden
vendored
Normal file
1
testdata/string_comparison1.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
true
|
||||
1
testdata/string_comparison1.input
vendored
Normal file
1
testdata/string_comparison1.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"a" < "b"
|
||||
1
testdata/string_comparison2.golden
vendored
Normal file
1
testdata/string_comparison2.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
false
|
||||
1
testdata/string_comparison2.input
vendored
Normal file
1
testdata/string_comparison2.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"a" > "b"
|
||||
1
testdata/string_comparison3.golden
vendored
Normal file
1
testdata/string_comparison3.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
false
|
||||
1
testdata/string_comparison3.input
vendored
Normal file
1
testdata/string_comparison3.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"a" == "b"
|
||||
1
testdata/string_comparison4.golden
vendored
Normal file
1
testdata/string_comparison4.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
true
|
||||
1
testdata/string_comparison4.input
vendored
Normal file
1
testdata/string_comparison4.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"a" < "aa"
|
||||
1
testdata/string_comparison5.golden
vendored
Normal file
1
testdata/string_comparison5.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
false
|
||||
1
testdata/string_comparison5.input
vendored
Normal file
1
testdata/string_comparison5.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"aa" < "a"
|
||||
1
testdata/string_comparison6.golden
vendored
Normal file
1
testdata/string_comparison6.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
true
|
||||
1
testdata/string_comparison6.input
vendored
Normal file
1
testdata/string_comparison6.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"ą" < "ć"
|
||||
1
testdata/string_comparison7.golden
vendored
Normal file
1
testdata/string_comparison7.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
false
|
||||
1
testdata/string_comparison7.input
vendored
Normal file
1
testdata/string_comparison7.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"ą" < "z"
|
||||
1
testdata/string_index.golden
vendored
Normal file
1
testdata/string_index.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
"a"
|
||||
1
testdata/string_index.input
vendored
Normal file
1
testdata/string_index.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"abcd"[0]
|
||||
1
testdata/string_index2.golden
vendored
Normal file
1
testdata/string_index2.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
"d"
|
||||
1
testdata/string_index2.input
vendored
Normal file
1
testdata/string_index2.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"abcd"[3]
|
||||
1
testdata/string_index_negative.golden
vendored
Normal file
1
testdata/string_index_negative.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
RUNTIME ERROR: Index -1 out of bounds, not within [0, 4)
|
||||
1
testdata/string_index_negative.input
vendored
Normal file
1
testdata/string_index_negative.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"abcd"[-1]
|
||||
1
testdata/string_index_out_of_bounds.golden
vendored
Normal file
1
testdata/string_index_out_of_bounds.golden
vendored
Normal file
@ -0,0 +1 @@
|
||||
RUNTIME ERROR: Index 4 out of bounds, not within [0, 4)
|
||||
1
testdata/string_index_out_of_bounds.input
vendored
Normal file
1
testdata/string_index_out_of_bounds.input
vendored
Normal file
@ -0,0 +1 @@
|
||||
"abcd"[4]
|
||||
58
value.go
58
value.go
@ -61,11 +61,65 @@ func (v *valueBase) aValue() {}
|
||||
|
||||
type valueString struct {
|
||||
valueBase
|
||||
value string
|
||||
// We use rune slices instead of strings for quick indexing
|
||||
value []rune
|
||||
}
|
||||
|
||||
func (s *valueString) index(e *evaluator, index int) (value, error) {
|
||||
if 0 <= index && index < s.length() {
|
||||
return makeValueString(string(s.value[index])), nil
|
||||
}
|
||||
return nil, e.Error(fmt.Sprintf("Index %d out of bounds, not within [0, %v)", index, s.length()))
|
||||
}
|
||||
|
||||
func concatStrings(a, b *valueString) *valueString {
|
||||
result := make([]rune, 0, len(a.value)+len(b.value))
|
||||
for _, r := range a.value {
|
||||
result = append(result, r)
|
||||
}
|
||||
for _, r := range b.value {
|
||||
result = append(result, r)
|
||||
}
|
||||
return &valueString{value: result}
|
||||
}
|
||||
|
||||
func stringLessThan(a, b *valueString) bool {
|
||||
var length int
|
||||
if len(a.value) < len(b.value) {
|
||||
length = len(a.value)
|
||||
} else {
|
||||
length = len(b.value)
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
if a.value[i] != b.value[i] {
|
||||
return a.value[i] < b.value[i]
|
||||
}
|
||||
}
|
||||
return len(a.value) < len(b.value)
|
||||
}
|
||||
|
||||
func stringEqual(a, b *valueString) bool {
|
||||
if len(a.value) != len(b.value) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a.value); i++ {
|
||||
if a.value[i] != b.value[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *valueString) length() int {
|
||||
return len(s.value)
|
||||
}
|
||||
|
||||
func (s *valueString) getString() string {
|
||||
return string(s.value)
|
||||
}
|
||||
|
||||
func makeValueString(v string) *valueString {
|
||||
return &valueString{value: v}
|
||||
return &valueString{value: []rune(v)}
|
||||
}
|
||||
|
||||
func (*valueString) typename() string {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user