Skip to content

Commit

Permalink
String indexing, change of string representation (google#34)
Browse files Browse the repository at this point in the history
* String indexing, change of string representation

Also std.pow
  • Loading branch information
sbarzowski authored and sparkprime committed Sep 7, 2017
1 parent c26c50c commit 3c94bde
Show file tree
Hide file tree
Showing 41 changed files with 128 additions and 18 deletions.
35 changes: 24 additions & 11 deletions builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"}},
}
13 changes: 8 additions & 5 deletions interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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)))
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions testdata/pow.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1024
1 change: 1 addition & 0 deletions testdata/pow.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.pow(2, 10)
1 change: 1 addition & 0 deletions testdata/pow2.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
100
1 change: 1 addition & 0 deletions testdata/pow2.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.pow(10, 2)
1 change: 1 addition & 0 deletions testdata/pow3.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-1
1 change: 1 addition & 0 deletions testdata/pow3.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.pow(-1, 3)
1 change: 1 addition & 0 deletions testdata/pow4.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUNTIME ERROR: Not a number
1 change: 1 addition & 0 deletions testdata/pow4.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.pow(-1, 0.2)
1 change: 1 addition & 0 deletions testdata/pow5.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.1486983549970351
1 change: 1 addition & 0 deletions testdata/pow5.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.pow(2, 0.2)
1 change: 1 addition & 0 deletions testdata/pow6.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
179754255558423237941456473541041914055900576989066323324790225489927405882855355678287600996768561249516179005117487900995816670389973592784065259754879901383672166545582558984633189841112133404180226485873094649946308637416044832879263264479292996247265876810814717146304544813220293475007274508971205984256
2 changes: 2 additions & 0 deletions testdata/pow6.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// roughly the largest possible double
std.pow(1.1, 7447.081)
1 change: 1 addition & 0 deletions testdata/pow7.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUNTIME ERROR: Overflow
2 changes: 2 additions & 0 deletions testdata/pow7.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// slightly more than the largest possible double
std.pow(1.1, 7447.082)
1 change: 1 addition & 0 deletions testdata/std_substr.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"bc"
1 change: 1 addition & 0 deletions testdata/std_substr.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
std.substr("abcd", 1, 2)
1 change: 1 addition & 0 deletions testdata/string_comparison1.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
1 change: 1 addition & 0 deletions testdata/string_comparison1.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"a" < "b"
1 change: 1 addition & 0 deletions testdata/string_comparison2.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
false
1 change: 1 addition & 0 deletions testdata/string_comparison2.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"a" > "b"
1 change: 1 addition & 0 deletions testdata/string_comparison3.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
false
1 change: 1 addition & 0 deletions testdata/string_comparison3.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"a" == "b"
1 change: 1 addition & 0 deletions testdata/string_comparison4.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
1 change: 1 addition & 0 deletions testdata/string_comparison4.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"a" < "aa"
1 change: 1 addition & 0 deletions testdata/string_comparison5.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
false
1 change: 1 addition & 0 deletions testdata/string_comparison5.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"aa" < "a"
1 change: 1 addition & 0 deletions testdata/string_comparison6.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
1 change: 1 addition & 0 deletions testdata/string_comparison6.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"ą" < "ć"
1 change: 1 addition & 0 deletions testdata/string_comparison7.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
false
1 change: 1 addition & 0 deletions testdata/string_comparison7.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"ą" < "z"
1 change: 1 addition & 0 deletions testdata/string_index.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"a"
1 change: 1 addition & 0 deletions testdata/string_index.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"abcd"[0]
1 change: 1 addition & 0 deletions testdata/string_index2.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"d"
1 change: 1 addition & 0 deletions testdata/string_index2.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"abcd"[3]
1 change: 1 addition & 0 deletions testdata/string_index_negative.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUNTIME ERROR: Index -1 out of bounds, not within [0, 4)
1 change: 1 addition & 0 deletions testdata/string_index_negative.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"abcd"[-1]
1 change: 1 addition & 0 deletions testdata/string_index_out_of_bounds.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
RUNTIME ERROR: Index 4 out of bounds, not within [0, 4)
1 change: 1 addition & 0 deletions testdata/string_index_out_of_bounds.input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"abcd"[4]
58 changes: 56 additions & 2 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 3c94bde

Please sign in to comment.