blob: c7912beee05a66d01d42a43f96e0bfd9fb174431 [file] [log] [blame]
// Copyright 2018 The CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cue
import (
"fmt"
"math/big"
"testing"
"github.com/cockroachdb/apd/v2"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/literal"
"cuelang.org/go/cue/token"
)
var testBase = newExpr(&ast.BasicLit{})
func mkInt(a int64) *numLit {
return newInt(testBase, base10).setInt64(a)
}
func mkIntString(a string) *numLit {
return newInt(testBase, base10).setString(a)
}
func mkFloat(a string) *numLit {
return newFloat(testBase, base10).setString(a)
}
var diffOpts = []cmp.Option{
cmp.Comparer(func(x, y big.Rat) bool {
return x.String() == y.String()
}),
cmp.Comparer(func(x, y big.Int) bool {
return x.String() == y.String()
}),
cmp.AllowUnexported(
nullLit{},
boolLit{},
stringLit{},
bytesLit{},
numLit{},
),
cmpopts.IgnoreUnexported(
bottom{},
baseValue{},
baseValue{},
),
}
var (
nullSentinel = &nullLit{}
trueSentinel = &boolLit{b: true}
falseSentinel = &boolLit{b: false}
)
func TestLiterals(t *testing.T) {
t.Skip()
mkMul := func(x int64, m literal.Multiplier, base int) *numLit {
return newInt(testBase, m).setInt64(x)
}
hk := newInt(testBase, 0).setInt64(100000)
testCases := []struct {
lit string
node value
}{
{"0", mkInt(0)},
{"null", nullSentinel},
{"true", trueSentinel},
{"false", falseSentinel},
{"fls", &bottom{}},
{`"foo"`, &stringLit{str: "foo"}},
{`#"foo"#`, &stringLit{str: "foo"}},
{`#""foo"#`, &stringLit{str: `"foo`}},
{`#" ""#`, &stringLit{str: ` "`}},
{`"\"foo\""`, &stringLit{str: `"foo"`}},
{`"foo\u0032"`, &stringLit{str: `foo2`}},
{`"foo\U00000033"`, &stringLit{str: `foo3`}},
{`"foo\U0001f499"`, &stringLit{str: `foo💙`}},
{`"\a\b\f\n\r\t\v"`, &stringLit{str: "\a\b\f\n\r\t\v"}},
{`"""
"""`, &stringLit{str: ""}},
{`"""
abc
"""`, &stringLit{str: "abc"}},
{`"""
abc
def
"""`, &stringLit{str: "abc\ndef"}},
{`"""
abc
def
"""`, &stringLit{str: "abc\n\tdef"}},
{`'\xff'`, &bytesLit{b: []byte("\xff")}},
{"1", mkInt(1)},
{"100_000", hk},
{"1.", mkFloat("1")},
{"0.0", mkFloat("0.0")},
{".0", mkFloat(".0")},
{"012.34", mkFloat("012.34")},
{".01", mkFloat(".01")},
{".01e2", mkFloat("1")},
{"0.", mkFloat("0.")},
{"1K", mkMul(1000, literal.K, 10)},
{".5K", mkMul(500, literal.K, 10)},
{"1Mi", mkMul(1024*1024, literal.Mi, 10)},
{"1.5Mi", mkMul((1024+512)*1024, literal.Mi, 10)},
{"1.3Mi", &bottom{}}, // Cannot be accurately represented.
{"1.3G", mkMul(1300000000, literal.G, 10)},
{"1.3e+20", mkFloat("1.3e+20")},
{"1.3e20", mkFloat("1.3e+20")},
{"1.3e-5", mkFloat("1.3e-5")},
{".3e-1", mkFloat("0.3e-1")},
{"0e-5", mkFloat("0e-5")},
{"0E-5", mkFloat("0e-5")},
{"5e-5", mkFloat("5e-5")},
{"5E-5", mkFloat("5e-5")},
{"0x1234", mkMul(0x1234, 0, 16)},
{"0xABCD", mkMul(0xABCD, 0, 16)},
{"-0xABCD", mkMul(0xABCD, 0, 16)},
{"0b11001000", mkMul(0xc8, 0, 2)},
{"0b1", mkMul(1, 0, 2)},
{"0o755", mkMul(0755, 0, 8)},
{"0755", mkMul(0755, 0, 8)},
}
p := litParser{
ctx: &context{Context: &apd.BaseContext},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("%d/%+q", i, tc.lit), func(t *testing.T) {
got := p.parse(&ast.BasicLit{Value: tc.lit})
if !cmp.Equal(got, tc.node, diffOpts...) {
t.Error(cmp.Diff(got, tc.node, diffOpts...))
t.Errorf("%#v, %#v\n", got, tc.node)
}
})
}
}
func TestLiteralErrors(t *testing.T) {
testCases := []struct {
kind token.Token
lit string
}{
{token.STRING, `"foo\u"`},
{token.STRING, `"foo\u003"`},
{token.STRING, `"foo\U1234567"`},
{token.STRING, `"foo\U12345678"`},
{token.STRING, `"foo\Ug"`},
{token.STRING, `"\xff"`},
// not allowed in string literal, only binary
{token.STRING, `"foo\x00"`},
{token.INT, `0x`},
{token.INT, `0o`},
{token.INT, `0b`},
{token.INT, `0_`},
{token.INT, "0128"},
{token.STRING, ``},
{token.STRING, `"`},
{token.STRING, `"a`},
// wrong indentation
{token.STRING, `"""
abc
def
"""`},
// non-matching quotes
{token.STRING, `"""
abc
'''`},
{token.STRING, `"""
abc
"`},
{token.STRING, `"abc \( foo "`},
}
p := litParser{
ctx: &context{Context: &apd.BaseContext},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%+q", tc.lit), func(t *testing.T) {
got := p.parse(&ast.BasicLit{Kind: tc.kind, Value: tc.lit})
if _, ok := got.(*bottom); !ok {
t.Fatalf("expected error but found none")
}
})
}
}