String indexing, change of string representation (#34)

* String indexing, change of string representation

Also std.pow
This commit is contained in:
Stanisław Barzowski 2017-09-07 11:56:11 -04:00 committed by Dave Cunningham
parent c26c50c04b
commit 3c94bde4dc
41 changed files with 128 additions and 18 deletions

View File

@ -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"}},
}

View File

@ -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
View File

@ -0,0 +1 @@
1024

1
testdata/pow.input vendored Normal file
View File

@ -0,0 +1 @@
std.pow(2, 10)

1
testdata/pow2.golden vendored Normal file
View File

@ -0,0 +1 @@
100

1
testdata/pow2.input vendored Normal file
View File

@ -0,0 +1 @@
std.pow(10, 2)

1
testdata/pow3.golden vendored Normal file
View File

@ -0,0 +1 @@
-1

1
testdata/pow3.input vendored Normal file
View File

@ -0,0 +1 @@
std.pow(-1, 3)

1
testdata/pow4.golden vendored Normal file
View File

@ -0,0 +1 @@
RUNTIME ERROR: Not a number

1
testdata/pow4.input vendored Normal file
View File

@ -0,0 +1 @@
std.pow(-1, 0.2)

1
testdata/pow5.golden vendored Normal file
View File

@ -0,0 +1 @@
1.1486983549970351

1
testdata/pow5.input vendored Normal file
View File

@ -0,0 +1 @@
std.pow(2, 0.2)

1
testdata/pow6.golden vendored Normal file
View File

@ -0,0 +1 @@
179754255558423237941456473541041914055900576989066323324790225489927405882855355678287600996768561249516179005117487900995816670389973592784065259754879901383672166545582558984633189841112133404180226485873094649946308637416044832879263264479292996247265876810814717146304544813220293475007274508971205984256

2
testdata/pow6.input vendored Normal file
View File

@ -0,0 +1,2 @@
// roughly the largest possible double
std.pow(1.1, 7447.081)

1
testdata/pow7.golden vendored Normal file
View File

@ -0,0 +1 @@
RUNTIME ERROR: Overflow

2
testdata/pow7.input vendored Normal file
View 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
View File

@ -0,0 +1 @@
"bc"

1
testdata/std_substr.input vendored Normal file
View File

@ -0,0 +1 @@
std.substr("abcd", 1, 2)

1
testdata/string_comparison1.golden vendored Normal file
View File

@ -0,0 +1 @@
true

1
testdata/string_comparison1.input vendored Normal file
View File

@ -0,0 +1 @@
"a" < "b"

1
testdata/string_comparison2.golden vendored Normal file
View File

@ -0,0 +1 @@
false

1
testdata/string_comparison2.input vendored Normal file
View File

@ -0,0 +1 @@
"a" > "b"

1
testdata/string_comparison3.golden vendored Normal file
View File

@ -0,0 +1 @@
false

1
testdata/string_comparison3.input vendored Normal file
View File

@ -0,0 +1 @@
"a" == "b"

1
testdata/string_comparison4.golden vendored Normal file
View File

@ -0,0 +1 @@
true

1
testdata/string_comparison4.input vendored Normal file
View File

@ -0,0 +1 @@
"a" < "aa"

1
testdata/string_comparison5.golden vendored Normal file
View File

@ -0,0 +1 @@
false

1
testdata/string_comparison5.input vendored Normal file
View File

@ -0,0 +1 @@
"aa" < "a"

1
testdata/string_comparison6.golden vendored Normal file
View File

@ -0,0 +1 @@
true

1
testdata/string_comparison6.input vendored Normal file
View File

@ -0,0 +1 @@
"ą" < "ć"

1
testdata/string_comparison7.golden vendored Normal file
View File

@ -0,0 +1 @@
false

1
testdata/string_comparison7.input vendored Normal file
View File

@ -0,0 +1 @@
"ą" < "z"

1
testdata/string_index.golden vendored Normal file
View File

@ -0,0 +1 @@
"a"

1
testdata/string_index.input vendored Normal file
View File

@ -0,0 +1 @@
"abcd"[0]

1
testdata/string_index2.golden vendored Normal file
View File

@ -0,0 +1 @@
"d"

1
testdata/string_index2.input vendored Normal file
View File

@ -0,0 +1 @@
"abcd"[3]

1
testdata/string_index_negative.golden vendored Normal file
View File

@ -0,0 +1 @@
RUNTIME ERROR: Index -1 out of bounds, not within [0, 4)

1
testdata/string_index_negative.input vendored Normal file
View File

@ -0,0 +1 @@
"abcd"[-1]

View File

@ -0,0 +1 @@
RUNTIME ERROR: Index 4 out of bounds, not within [0, 4)

View File

@ -0,0 +1 @@
"abcd"[4]

View File

@ -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 {