func TestTypeCheckSameTypedExprsError(t *testing.T) { floatConst := func(s string) Expr { return &NumVal{Value: constant.MakeFromLiteral(s, token.FLOAT, 0), OrigString: s} } floatIntMismatchErr := `expected .* to be of type (float|int), found type (float|int)` paramErr := `could not determine data type of parameter .*` testData := []struct { args MapArgs desired Datum exprs []Expr expectedErr string }{ {nil, nil, []Expr{NewDInt(1), floatConst("1.1")}, floatIntMismatchErr}, {nil, nil, []Expr{NewDInt(1), NewDFloat(1)}, floatIntMismatchErr}, {MapArgs{"a": TypeInt}, nil, []Expr{NewDFloat(1.1), ValArg{"a"}}, floatIntMismatchErr}, {MapArgs{"a": TypeInt}, nil, []Expr{floatConst("1.1"), ValArg{"a"}}, floatIntMismatchErr}, {MapArgs{"a": TypeFloat, "b": TypeInt}, nil, []Expr{ValArg{"b"}, ValArg{"a"}}, floatIntMismatchErr}, {nil, nil, []Expr{ValArg{"b"}, ValArg{"a"}}, paramErr}, } for i, d := range testData { forEachPerm(d.exprs, 0, func(exprs []Expr) { if _, _, err := typeCheckSameTypedExprs(d.args, d.desired, exprs...); !testutils.IsError(err, d.expectedErr) { t.Errorf("%d: expected %s, but found %v", i, d.expectedErr, err) } }) } }
// setConst sets x to the untyped constant for literal lit. func (x *operand) setConst(tok token.Token, lit string) { val := exact.MakeFromLiteral(lit, tok, 0) if val == nil { // TODO(gri) Should we make it an unknown constant instead? x.mode = invalid return } var kind BasicKind switch tok { case token.INT: kind = UntypedInt case token.FLOAT: kind = UntypedFloat case token.IMAG: kind = UntypedComplex case token.CHAR: kind = UntypedRune case token.STRING: kind = UntypedString } x.mode = constant x.typ = Typ[kind] x.val = val }
// number = int_lit [ "p" int_lit ] . // func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) { // mantissa mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0) if mant == nil { panic("invalid mantissa") } if p.lit == "p" { // exponent (base 2) p.next() exp, err := strconv.ParseInt(p.parseInt(), 10, 0) if err != nil { p.error(err) } if exp < 0 { denom := exact.MakeInt64(1) denom = exact.Shift(denom, token.SHL, uint(-exp)) typ = types.Typ[types.UntypedFloat] val = exact.BinaryOp(mant, token.QUO, denom) return } if exp > 0 { mant = exact.Shift(mant, token.SHL, uint(exp)) } typ = types.Typ[types.UntypedFloat] val = mant return } typ = types.Typ[types.UntypedInt] val = mant return }
// setConst sets x to the untyped constant for literal lit. func (x *operand) setConst(tok token.Token, lit string) { var kind BasicKind switch tok { case token.INT: kind = UntypedInt case token.FLOAT: kind = UntypedFloat case token.IMAG: kind = UntypedComplex case token.CHAR: kind = UntypedRune case token.STRING: kind = UntypedString default: unreachable() } x.mode = constant_ x.typ = Typ[kind] x.val = constant.MakeFromLiteral(lit, tok, 0) }
// ConstDecl = "const" ExportedName [ Type ] "=" Literal . // Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . // bool_lit = "true" | "false" . // complex_lit = "(" float_lit "+" float_lit "i" ")" . // rune_lit = "(" int_lit "+" int_lit ")" . // string_lit = `"` { unicode_char } `"` . // func (p *parser) parseConstDecl() { p.expectKeyword("const") pkg, name := p.parseExportedName() var typ0 types.Type if p.tok != '=' { // constant types are never structured - no need for parent type typ0 = p.parseType(nil) } p.expect('=') var typ types.Type var val exact.Value switch p.tok { case scanner.Ident: // bool_lit if p.lit != "true" && p.lit != "false" { p.error("expected true or false") } typ = types.Typ[types.UntypedBool] val = exact.MakeBool(p.lit == "true") p.next() case '-', scanner.Int: // int_lit typ, val = p.parseNumber() case '(': // complex_lit or rune_lit p.next() if p.tok == scanner.Char { p.next() p.expect('+') typ = types.Typ[types.UntypedRune] _, val = p.parseNumber() p.expect(')') break } _, re := p.parseNumber() p.expect('+') _, im := p.parseNumber() p.expectKeyword("i") p.expect(')') typ = types.Typ[types.UntypedComplex] val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im)) case scanner.Char: // rune_lit typ = types.Typ[types.UntypedRune] val = exact.MakeFromLiteral(p.lit, token.CHAR, 0) p.next() case scanner.String: // string_lit typ = types.Typ[types.UntypedString] val = exact.MakeFromLiteral(p.lit, token.STRING, 0) p.next() default: p.errorf("expected literal got %s", scanner.TokenString(p.tok)) } if typ0 == nil { typ0 = typ } pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val)) }
func TestTypeCheckOverloadedExprs(t *testing.T) { intConst := func(s string) Expr { return &NumVal{Value: constant.MakeFromLiteral(s, token.INT, 0), OrigString: s} } decConst := func(s string) Expr { return &NumVal{Value: constant.MakeFromLiteral(s, token.FLOAT, 0), OrigString: s} } strConst := func(s string) Expr { return &StrVal{s: s} } plus := func(left, right Expr) Expr { return &BinaryExpr{Operator: Plus, Left: left, Right: right} } unaryIntFn := makeTestOverload(TypeInt, TypeInt) unaryFloatFn := makeTestOverload(TypeFloat, TypeFloat) unaryDecimalFn := makeTestOverload(TypeDecimal, TypeDecimal) unaryStringFn := makeTestOverload(TypeString, TypeString) unaryIntervalFn := makeTestOverload(TypeInterval, TypeInterval) unaryTimestampFn := makeTestOverload(TypeTimestamp, TypeTimestamp) binaryIntFn := makeTestOverload(TypeInt, TypeInt, TypeInt) binaryFloatFn := makeTestOverload(TypeFloat, TypeFloat, TypeFloat) binaryDecimalFn := makeTestOverload(TypeDecimal, TypeDecimal, TypeDecimal) binaryStringFn := makeTestOverload(TypeString, TypeString, TypeString) binaryTimestampFn := makeTestOverload(TypeTimestamp, TypeTimestamp, TypeTimestamp) binaryStringFloatFn1 := makeTestOverload(TypeInt, TypeString, TypeFloat) binaryStringFloatFn2 := makeTestOverload(TypeFloat, TypeString, TypeFloat) binaryIntDateFn := makeTestOverload(TypeDate, TypeInt, TypeDate) testData := []struct { ptypes PlaceholderTypes desired Datum exprs []Expr overloads []overloadImpl expectedOverload overloadImpl }{ // Unary constants. {nil, nil, []Expr{intConst("1")}, []overloadImpl{unaryIntFn, unaryFloatFn}, unaryIntFn}, {nil, nil, []Expr{decConst("1.0")}, []overloadImpl{unaryIntFn, unaryDecimalFn}, unaryDecimalFn}, {nil, nil, []Expr{decConst("1.0")}, []overloadImpl{unaryIntFn, unaryFloatFn}, nil}, {nil, nil, []Expr{intConst("1")}, []overloadImpl{unaryIntFn, binaryIntFn}, unaryIntFn}, {nil, nil, []Expr{intConst("1")}, []overloadImpl{unaryFloatFn, unaryStringFn}, unaryFloatFn}, {nil, nil, []Expr{intConst("1")}, []overloadImpl{unaryStringFn, binaryIntFn}, nil}, {nil, nil, []Expr{strConst("PT12H2M")}, []overloadImpl{unaryIntervalFn}, unaryIntervalFn}, {nil, nil, []Expr{strConst("PT12H2M")}, []overloadImpl{unaryIntervalFn, unaryStringFn}, unaryStringFn}, {nil, nil, []Expr{strConst("PT12H2M")}, []overloadImpl{unaryIntervalFn, unaryTimestampFn}, nil}, // Limitiation. {nil, nil, []Expr{strConst("PT12H2M")}, []overloadImpl{unaryIntervalFn, unaryIntFn}, nil}, // Limitiation. // Unary unresolved Placeholders. {nil, nil, []Expr{Placeholder{"a"}}, []overloadImpl{unaryStringFn, unaryIntFn}, nil}, {nil, nil, []Expr{Placeholder{"a"}}, []overloadImpl{unaryStringFn, binaryIntFn}, unaryStringFn}, // Unary values (not constants). {nil, nil, []Expr{NewDInt(1)}, []overloadImpl{unaryIntFn, unaryFloatFn}, unaryIntFn}, {nil, nil, []Expr{NewDFloat(1)}, []overloadImpl{unaryIntFn, unaryFloatFn}, unaryFloatFn}, {nil, nil, []Expr{NewDInt(1)}, []overloadImpl{unaryIntFn, binaryIntFn}, unaryIntFn}, {nil, nil, []Expr{NewDInt(1)}, []overloadImpl{unaryFloatFn, unaryStringFn}, nil}, {nil, nil, []Expr{NewDString("a")}, []overloadImpl{unaryIntFn, unaryFloatFn}, nil}, {nil, nil, []Expr{NewDString("a")}, []overloadImpl{unaryIntFn, unaryStringFn}, unaryStringFn}, // Binary constants. {nil, nil, []Expr{intConst("1"), intConst("1")}, []overloadImpl{binaryIntFn, binaryFloatFn, unaryIntFn}, binaryIntFn}, {nil, nil, []Expr{intConst("1"), decConst("1.0")}, []overloadImpl{binaryIntFn, binaryDecimalFn, unaryDecimalFn}, binaryDecimalFn}, {nil, nil, []Expr{strConst("2010-09-28"), strConst("2010-09-29")}, []overloadImpl{binaryTimestampFn}, binaryTimestampFn}, {nil, nil, []Expr{strConst("2010-09-28"), strConst("2010-09-29")}, []overloadImpl{binaryTimestampFn, binaryStringFn}, binaryStringFn}, {nil, nil, []Expr{strConst("2010-09-28"), strConst("2010-09-29")}, []overloadImpl{binaryTimestampFn, binaryIntFn}, nil}, // Limitiation. // Binary unresolved Placeholders. {nil, nil, []Expr{Placeholder{"a"}, Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryFloatFn}, nil}, {nil, nil, []Expr{Placeholder{"a"}, Placeholder{"b"}}, []overloadImpl{binaryIntFn, unaryStringFn}, binaryIntFn}, {nil, nil, []Expr{Placeholder{"a"}, NewDString("a")}, []overloadImpl{binaryIntFn, binaryStringFn}, binaryStringFn}, {nil, nil, []Expr{Placeholder{"a"}, intConst("1")}, []overloadImpl{binaryIntFn, binaryFloatFn}, binaryIntFn}, {nil, nil, []Expr{Placeholder{"a"}, intConst("1")}, []overloadImpl{binaryStringFn, binaryFloatFn}, binaryFloatFn}, // Binary values. {nil, nil, []Expr{NewDString("a"), NewDString("b")}, []overloadImpl{binaryStringFn, binaryFloatFn, unaryFloatFn}, binaryStringFn}, {nil, nil, []Expr{NewDString("a"), intConst("1")}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1}, binaryStringFloatFn1}, {nil, nil, []Expr{NewDString("a"), NewDInt(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1}, nil}, {nil, nil, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1}, binaryStringFloatFn1}, {nil, nil, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn2}, binaryStringFloatFn2}, {nil, nil, []Expr{NewDFloat(1), NewDString("a")}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1}, nil}, {nil, nil, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1, binaryStringFloatFn2}, nil}, // Desired type with ambiguity. {nil, TypeInt, []Expr{intConst("1"), decConst("1.0")}, []overloadImpl{binaryIntFn, binaryDecimalFn, unaryDecimalFn}, binaryIntFn}, {nil, TypeInt, []Expr{intConst("1"), NewDFloat(1)}, []overloadImpl{binaryIntFn, binaryFloatFn, unaryFloatFn}, binaryFloatFn}, {nil, TypeInt, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1, binaryStringFloatFn2}, binaryStringFloatFn1}, {nil, TypeFloat, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1, binaryStringFloatFn2}, binaryStringFloatFn2}, {nil, TypeFloat, []Expr{Placeholder{"a"}, Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryFloatFn}, binaryFloatFn}, // Sub-expressions. {nil, nil, []Expr{decConst("1.0"), plus(intConst("1"), intConst("2"))}, []overloadImpl{binaryIntFn, binaryDecimalFn}, binaryIntFn}, {nil, nil, []Expr{decConst("1.1"), plus(intConst("1"), intConst("2"))}, []overloadImpl{binaryIntFn, binaryDecimalFn}, binaryDecimalFn}, {nil, TypeDecimal, []Expr{decConst("1.0"), plus(intConst("1"), intConst("2"))}, []overloadImpl{binaryIntFn, binaryDecimalFn}, binaryIntFn}, // Limitation. {nil, nil, []Expr{plus(intConst("1"), intConst("2")), plus(decConst("1.1"), decConst("2.2"))}, []overloadImpl{binaryIntFn, binaryDecimalFn}, nil}, // Limitation. {nil, nil, []Expr{plus(decConst("1.1"), decConst("2.2")), plus(intConst("1"), intConst("2"))}, []overloadImpl{binaryIntFn, binaryDecimalFn}, binaryDecimalFn}, // Homogenous preference. {nil, nil, []Expr{NewDInt(1), Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntFn}, {nil, nil, []Expr{NewDFloat(1), Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, nil}, {nil, nil, []Expr{intConst("1"), Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntFn}, {nil, nil, []Expr{decConst("1.0"), Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, nil}, // Limitation. {nil, TypeDate, []Expr{NewDInt(1), Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntDateFn}, {nil, TypeDate, []Expr{NewDFloat(1), Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, nil}, {nil, TypeDate, []Expr{intConst("1"), Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntDateFn}, {nil, TypeDate, []Expr{decConst("1.0"), Placeholder{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntDateFn}, } for i, d := range testData { ctx := MakeSemaContext() ctx.Placeholders.SetTypes(d.ptypes) _, fn, err := typeCheckOverloadedExprs(&ctx, d.desired, d.overloads, d.exprs...) if d.expectedOverload != nil { if err != nil { t.Errorf("%d: unexpected error returned from typeCheckOverloadedExprs: %v", i, err) } else if fn != d.expectedOverload { t.Errorf("%d: expected overload %s to be chosen when type checking %s, found %v", i, d.expectedOverload, d.exprs, fn) } } else if fn != nil { t.Errorf("%d: expected no matching overloads to be found when type checking %s, found %s", i, d.exprs, fn) } } }
func decConst(s string) copyableExpr { return func() Expr { return &NumVal{Value: constant.MakeFromLiteral(s, token.FLOAT, 0), OrigString: s} } }
func (s *scanner) scanNumber(lval *sqlSymType, ch int) { start := s.pos - 1 isHex := false hasDecimal := ch == '.' hasExponent := false for { ch := s.peek() if isHex && isHexDigit(ch) || isDigit(ch) { s.pos++ continue } if ch == 'x' || ch == 'X' { if isHex || s.in[start] != '0' || s.pos != start+1 { lval.id = ERROR lval.str = "invalid hexadecimal literal" return } s.pos++ isHex = true continue } if isHex { break } if ch == '.' { if hasDecimal || hasExponent { break } s.pos++ if s.peek() == '.' { // Found ".." while scanning a number: back up to the end of the // integer. s.pos-- break } hasDecimal = true continue } if ch == 'e' || ch == 'E' { if hasExponent { break } hasExponent = true s.pos++ ch = s.peek() if ch == '-' || ch == '+' { s.pos++ } ch = s.peek() if !isDigit(ch) { lval.id = ERROR lval.str = "invalid floating point literal" return } continue } break } lval.str = s.in[start:s.pos] if hasDecimal || hasExponent { lval.id = FCONST floatConst := constant.MakeFromLiteral(lval.str, token.FLOAT, 0) if floatConst.Kind() == constant.Unknown { panic(fmt.Sprintf("could not make constant float from literal %q", lval.str)) } lval.union.val = &NumVal{Value: floatConst, OrigString: lval.str} } else { if isHex && s.pos == start+2 { lval.id = ERROR lval.str = "invalid hexadecimal literal" return } lval.id = ICONST intConst := constant.MakeFromLiteral(lval.str, token.INT, 0) if intConst.Kind() == constant.Unknown { panic(fmt.Sprintf("could not make constant int from literal %q", lval.str)) } lval.union.val = &NumVal{Value: intConst, OrigString: lval.str} } }
// ConstValue = string | "false" | "true" | ["-"] (int ["'"] | FloatOrComplex) . // FloatOrComplex = float ["i" | ("+"|"-") float "i"] . func (p *parser) parseConstValue() (val constant.Value, typ types.Type) { switch p.tok { case scanner.String: str := p.parseString() val = constant.MakeString(str) typ = types.Typ[types.UntypedString] return case scanner.Ident: b := false switch p.lit { case "false": case "true": b = true default: p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit) } p.next() val = constant.MakeBool(b) typ = types.Typ[types.UntypedBool] return } sign := "" if p.tok == '-' { p.next() sign = "-" } switch p.tok { case scanner.Int: val = constant.MakeFromLiteral(sign+p.lit, token.INT, 0) if val == nil { p.error("could not parse integer literal") } p.next() if p.tok == '\'' { p.next() typ = types.Typ[types.UntypedRune] } else { typ = types.Typ[types.UntypedInt] } case scanner.Float: re := sign + p.lit p.next() var im string switch p.tok { case '+': p.next() im = p.expect(scanner.Float) case '-': p.next() im = "-" + p.expect(scanner.Float) case scanner.Ident: // re is in fact the imaginary component. Expect "i" below. im = re re = "0" default: val = constant.MakeFromLiteral(re, token.FLOAT, 0) if val == nil { p.error("could not parse float literal") } typ = types.Typ[types.UntypedFloat] return } p.expectKeyword("i") reval := constant.MakeFromLiteral(re, token.FLOAT, 0) if reval == nil { p.error("could not parse real component of complex literal") } imval := constant.MakeFromLiteral(im+"i", token.IMAG, 0) if imval == nil { p.error("could not parse imag component of complex literal") } val = constant.BinaryOp(reval, token.ADD, imval) typ = types.Typ[types.UntypedComplex] default: p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit) } return }
// matchExpr reports whether pattern x matches y. // // If tr.allowWildcards, Idents in x that refer to parameters are // treated as wildcards, and match any y that is assignable to the // parameter type; matchExpr records this correspondence in tr.env. // Otherwise, matchExpr simply reports whether the two trees are // equivalent. // // A wildcard appearing more than once in the pattern must // consistently match the same tree. // func (tr *Transformer) matchExpr(x, y ast.Expr) bool { if x == nil && y == nil { return true } if x == nil || y == nil { return false } x = unparen(x) y = unparen(y) // Is x a wildcard? (a reference to a 'before' parameter) if xobj, ok := tr.wildcardObj(x); ok { return tr.matchWildcard(xobj, y) } // Object identifiers (including pkg-qualified ones) // are handled semantically, not syntactically. xobj := isRef(x, tr.info) yobj := isRef(y, tr.info) if xobj != nil { return xobj == yobj } if yobj != nil { return false } // TODO(adonovan): audit: we cannot assume these ast.Exprs // contain non-nil pointers. e.g. ImportSpec.Name may be a // nil *ast.Ident. if reflect.TypeOf(x) != reflect.TypeOf(y) { return false } switch x := x.(type) { case *ast.Ident: log.Fatalf("unexpected Ident: %s", astString(tr.fset, x)) case *ast.BasicLit: y := y.(*ast.BasicLit) xval := exact.MakeFromLiteral(x.Value, x.Kind, 0) yval := exact.MakeFromLiteral(y.Value, y.Kind, 0) return exact.Compare(xval, token.EQL, yval) case *ast.FuncLit: // func literals (and thus statement syntax) never match. return false case *ast.CompositeLit: y := y.(*ast.CompositeLit) return (x.Type == nil) == (y.Type == nil) && (x.Type == nil || tr.matchType(x.Type, y.Type)) && tr.matchExprs(x.Elts, y.Elts) case *ast.SelectorExpr: y := y.(*ast.SelectorExpr) return tr.matchSelectorExpr(x, y) && tr.info.Selections[x].Obj() == tr.info.Selections[y].Obj() case *ast.IndexExpr: y := y.(*ast.IndexExpr) return tr.matchExpr(x.X, y.X) && tr.matchExpr(x.Index, y.Index) case *ast.SliceExpr: y := y.(*ast.SliceExpr) return tr.matchExpr(x.X, y.X) && tr.matchExpr(x.Low, y.Low) && tr.matchExpr(x.High, y.High) && tr.matchExpr(x.Max, y.Max) && x.Slice3 == y.Slice3 case *ast.TypeAssertExpr: y := y.(*ast.TypeAssertExpr) return tr.matchExpr(x.X, y.X) && tr.matchType(x.Type, y.Type) case *ast.CallExpr: y := y.(*ast.CallExpr) match := tr.matchExpr // function call if tr.info.Types[x.Fun].IsType() { match = tr.matchType // type conversion } return x.Ellipsis.IsValid() == y.Ellipsis.IsValid() && match(x.Fun, y.Fun) && tr.matchExprs(x.Args, y.Args) case *ast.StarExpr: y := y.(*ast.StarExpr) return tr.matchExpr(x.X, y.X) case *ast.UnaryExpr: y := y.(*ast.UnaryExpr) return x.Op == y.Op && tr.matchExpr(x.X, y.X) case *ast.BinaryExpr: y := y.(*ast.BinaryExpr) return x.Op == y.Op && tr.matchExpr(x.X, y.X) && tr.matchExpr(x.Y, y.Y) case *ast.KeyValueExpr: y := y.(*ast.KeyValueExpr) return tr.matchExpr(x.Key, y.Key) && tr.matchExpr(x.Value, y.Value) } panic(fmt.Sprintf("unhandled AST node type: %T", x)) }
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) { switch node := t.(type) { case *ast.CallExpr: if len(node.Args) == 1 { v, err := scope.evalTypeCast(node) if err == nil { return v, nil } if err != reader.TypeNotFoundErr { return v, err } } return scope.evalBuiltinCall(node) case *ast.Ident: return scope.evalIdent(node) case *ast.ParenExpr: // otherwise just eval recursively return scope.evalAST(node.X) case *ast.SelectorExpr: // <expression>.<identifier> // try to interpret the selector as a package variable if maybePkg, ok := node.X.(*ast.Ident); ok { if maybePkg.Name == "runtime" && node.Sel.Name == "curg" { return scope.Thread.getGVariable() } else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil { return v, nil } } // if it's not a package variable then it must be a struct member access return scope.evalStructSelector(node) case *ast.TypeAssertExpr: // <expression>.(<type>) return scope.evalTypeAssert(node) case *ast.IndexExpr: return scope.evalIndex(node) case *ast.SliceExpr: if node.Slice3 { return nil, fmt.Errorf("3-index slice expressions not supported") } return scope.evalReslice(node) case *ast.StarExpr: // pointer dereferencing *<expression> return scope.evalPointerDeref(node) case *ast.UnaryExpr: // The unary operators we support are +, - and & (note that unary * is parsed as ast.StarExpr) switch node.Op { case token.AND: return scope.evalAddrOf(node) default: return scope.evalUnary(node) } case *ast.BinaryExpr: return scope.evalBinary(node) case *ast.BasicLit: return newConstant(constant.MakeFromLiteral(node.Value, node.Kind, 0), scope.Thread), nil default: return nil, fmt.Errorf("expression %T not implemented", t) } }
func TestTypeCheckSameTypedExprs(t *testing.T) { intConst := func(s string) Expr { return &NumVal{Value: constant.MakeFromLiteral(s, token.INT, 0), OrigString: s} } floatConst := func(s string) Expr { return &NumVal{Value: constant.MakeFromLiteral(s, token.FLOAT, 0), OrigString: s} } testData := []struct { args MapArgs desired Datum exprs []Expr expectedType Datum }{ // Constants. {nil, nil, []Expr{intConst("1")}, TypeInt}, {nil, nil, []Expr{floatConst("1")}, TypeFloat}, {nil, nil, []Expr{intConst("1"), floatConst("1")}, TypeFloat}, // Resolved exprs. {nil, nil, []Expr{NewDInt(1)}, TypeInt}, {nil, nil, []Expr{NewDFloat(1)}, TypeFloat}, // Mixing constants and resolved exprs. {nil, nil, []Expr{NewDInt(1), intConst("1")}, TypeInt}, {nil, nil, []Expr{NewDInt(1), floatConst("1")}, TypeInt}, // This is what the AST would look like after folding (0.6 + 0.4). {nil, nil, []Expr{NewDInt(1), NewDInt(1)}, TypeInt}, {nil, nil, []Expr{NewDFloat(1), intConst("1")}, TypeFloat}, {nil, nil, []Expr{NewDFloat(1), floatConst("1")}, TypeFloat}, {nil, nil, []Expr{NewDFloat(1), NewDFloat(1)}, TypeFloat}, // Mixing resolved constants and resolved exprs with MapArgs. {MapArgs{"a": TypeFloat}, nil, []Expr{NewDFloat(1), ValArg{"a"}}, TypeFloat}, {MapArgs{"a": TypeFloat}, nil, []Expr{intConst("1"), ValArg{"a"}}, TypeFloat}, {MapArgs{"a": TypeFloat}, nil, []Expr{floatConst("1"), ValArg{"a"}}, TypeFloat}, {MapArgs{"a": TypeInt}, nil, []Expr{intConst("1"), ValArg{"a"}}, TypeInt}, {MapArgs{"a": TypeInt}, nil, []Expr{floatConst("1"), ValArg{"a"}}, TypeInt}, {MapArgs{"a": TypeFloat, "b": TypeFloat}, nil, []Expr{ValArg{"b"}, ValArg{"a"}}, TypeFloat}, // Mixing unresolved constants and resolved exprs with MapArgs. {nil, nil, []Expr{NewDFloat(1), ValArg{"a"}}, TypeFloat}, {nil, nil, []Expr{intConst("1"), ValArg{"a"}}, TypeInt}, {nil, nil, []Expr{floatConst("1"), ValArg{"a"}}, TypeFloat}, // Verify dealing with Null. {nil, nil, []Expr{DNull}, DNull}, {nil, nil, []Expr{DNull, DNull}, DNull}, {nil, nil, []Expr{DNull, intConst("1")}, TypeInt}, {nil, nil, []Expr{DNull, floatConst("1")}, TypeFloat}, {nil, nil, []Expr{DNull, NewDInt(1)}, TypeInt}, {nil, nil, []Expr{DNull, NewDFloat(1)}, TypeFloat}, {nil, nil, []Expr{DNull, NewDFloat(1), intConst("1")}, TypeFloat}, {nil, nil, []Expr{DNull, NewDFloat(1), floatConst("1")}, TypeFloat}, {nil, nil, []Expr{DNull, NewDFloat(1), floatConst("1")}, TypeFloat}, {nil, nil, []Expr{DNull, intConst("1"), floatConst("1")}, TypeFloat}, // Verify desired type when possible. {nil, TypeInt, []Expr{intConst("1")}, TypeInt}, {nil, TypeInt, []Expr{NewDInt(1)}, TypeInt}, {nil, TypeInt, []Expr{floatConst("1")}, TypeInt}, {nil, TypeInt, []Expr{NewDFloat(1)}, TypeFloat}, {nil, TypeFloat, []Expr{intConst("1")}, TypeFloat}, {nil, TypeFloat, []Expr{NewDInt(1)}, TypeInt}, {nil, TypeInt, []Expr{intConst("1"), floatConst("1")}, TypeInt}, {nil, TypeInt, []Expr{intConst("1"), floatConst("1.1")}, TypeFloat}, {nil, TypeFloat, []Expr{intConst("1"), floatConst("1")}, TypeFloat}, // Verify desired type when possible with unresolved constants. {nil, TypeFloat, []Expr{ValArg{"a"}}, TypeFloat}, {nil, TypeFloat, []Expr{intConst("1"), ValArg{"a"}}, TypeFloat}, {nil, TypeFloat, []Expr{floatConst("1"), ValArg{"a"}}, TypeFloat}, } for i, d := range testData { forEachPerm(d.exprs, 0, func(exprs []Expr) { _, typ, err := typeCheckSameTypedExprs(d.args, d.desired, exprs...) if err != nil { t.Errorf("%d: unexpected error returned from typeCheckSameTypedExprs: %v", i, err) } else if !typ.TypeEqual(d.expectedType) { t.Errorf("%d: expected type %s when type checking %s, found %s", i, d.expectedType.Type(), exprs, typ.Type()) } }) } }
// TestNumericConstantVerifyAndResolveAvailableTypes verifies that test NumVals will // all return expected available type sets, and that attempting to resolve the NumVals // as each of these types will all succeed with an expected Datum result. func TestNumericConstantVerifyAndResolveAvailableTypes(t *testing.T) { wantInt := numValAvailIntFloatDec wantDecButCanBeInt := numValAvailDecFloatInt wantDec := numValAvailDecFloat testCases := []struct { str string avail []Type }{ {"1", wantInt}, {"0", wantInt}, {"-1", wantInt}, {"9223372036854775807", wantInt}, {"1.0", wantDecButCanBeInt}, {"-1234.0000", wantDecButCanBeInt}, {"1e10", wantDecButCanBeInt}, {"1E10", wantDecButCanBeInt}, {"1.1", wantDec}, {"1e-10", wantDec}, {"1E-10", wantDec}, {"-1231.131", wantDec}, {"876543234567898765436787654321", wantDec}, } for i, test := range testCases { tok := token.INT if strings.ContainsAny(test.str, ".eE") { tok = token.FLOAT } val := constant.MakeFromLiteral(test.str, tok, 0) if val.Kind() == constant.Unknown { t.Fatalf("%d: could not parse value string %q", i, test.str) } // Check available types. c := &NumVal{Value: val, OrigString: test.str} avail := c.AvailableTypes() if !reflect.DeepEqual(avail, test.avail) { t.Errorf("%d: expected the available type set %v for %v, found %v", i, test.avail, c.Value.ExactString(), avail) } // Make sure it can be resolved as each of those types. for _, availType := range avail { if res, err := c.ResolveAsType(&SemaContext{}, availType); err != nil { t.Errorf("%d: expected resolving %v as available type %s would succeed, found %v", i, c.Value.ExactString(), availType, err) } else { resErr := func(parsed, resolved interface{}) { t.Errorf("%d: expected resolving %v as available type %s would produce a Datum"+ " with the value %v, found %v", i, c, availType, parsed, resolved) } switch typ := res.(type) { case *DInt: var i int64 var err error if tok == token.INT { if i, err = strconv.ParseInt(test.str, 10, 64); err != nil { t.Fatal(err) } } else { var f float64 if f, err = strconv.ParseFloat(test.str, 64); err != nil { t.Fatal(err) } i = int64(f) } if resI := int64(*typ); i != resI { resErr(i, resI) } case *DFloat: f, err := strconv.ParseFloat(test.str, 64) if err != nil { t.Fatal(err) } if resF := float64(*typ); f != resF { resErr(f, resF) } case *DDecimal: d := new(inf.Dec) if !strings.ContainsAny(test.str, "eE") { if _, ok := d.SetString(test.str); !ok { t.Fatalf("could not set %q on decimal", test.str) } } else { f, err := strconv.ParseFloat(test.str, 64) if err != nil { t.Fatal(err) } decimal.SetFromFloat(d, f) } if resD := &typ.Dec; d.Cmp(resD) != 0 { resErr(d, resD) } } } } } }
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) { switch node := t.(type) { case *ast.CallExpr: if fnnode, ok := node.Fun.(*ast.Ident); ok && len(node.Args) == 2 && (fnnode.Name == "complex64" || fnnode.Name == "complex128") { // implement the special case type casts complex64(f1, f2) and complex128(f1, f2) return scope.evalComplexCast(fnnode.Name, node) } // this must be a type cast because we do not support function calls return scope.evalTypeCast(node) case *ast.Ident: return scope.evalIdent(node) case *ast.ParenExpr: // otherwise just eval recursively return scope.evalAST(node.X) case *ast.SelectorExpr: // <expression>.<identifier> // try to interpret the selector as a package variable if maybePkg, ok := node.X.(*ast.Ident); ok { if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil { return v, nil } } // if it's not a package variable then it must be a struct member access return scope.evalStructSelector(node) case *ast.IndexExpr: return scope.evalIndex(node) case *ast.SliceExpr: if node.Slice3 { return nil, fmt.Errorf("3-index slice expressions not supported") } return scope.evalReslice(node) case *ast.StarExpr: // pointer dereferencing *<expression> return scope.evalPointerDeref(node) case *ast.UnaryExpr: // The unary operators we support are +, - and & (note that unary * is parsed as ast.StarExpr) switch node.Op { case token.AND: return scope.evalAddrOf(node) default: return scope.evalUnary(node) } case *ast.BinaryExpr: return scope.evalBinary(node) case *ast.BasicLit: return newConstant(constant.MakeFromLiteral(node.Value, node.Kind, 0), scope.Thread), nil default: return nil, fmt.Errorf("expression %T not implemented", t) } }
func TestTypeCheckOverloadedExprs(t *testing.T) { intConst := func(s string) Expr { return &NumVal{Value: constant.MakeFromLiteral(s, token.INT, 0), OrigString: s} } floatConst := func(s string) Expr { return &NumVal{Value: constant.MakeFromLiteral(s, token.FLOAT, 0), OrigString: s} } plus := func(left, right Expr) Expr { return &BinaryExpr{Operator: Plus, Left: left, Right: right} } unaryIntFn := makeTestOverload(TypeInt, TypeInt) unaryFloatFn := makeTestOverload(TypeFloat, TypeFloat) unaryStringFn := makeTestOverload(TypeString, TypeString) binaryIntFn := makeTestOverload(TypeInt, TypeInt, TypeInt) binaryFloatFn := makeTestOverload(TypeFloat, TypeFloat, TypeFloat) binaryStringFn := makeTestOverload(TypeString, TypeString, TypeString) binaryStringFloatFn1 := makeTestOverload(TypeInt, TypeString, TypeFloat) binaryStringFloatFn2 := makeTestOverload(TypeFloat, TypeString, TypeFloat) binaryIntDateFn := makeTestOverload(TypeDate, TypeInt, TypeDate) testData := []struct { args MapArgs desired Datum exprs []Expr overloads []overloadImpl expectedOverload overloadImpl }{ // Unary constants. {nil, nil, []Expr{intConst("1")}, []overloadImpl{unaryIntFn, unaryFloatFn}, unaryIntFn}, {nil, nil, []Expr{floatConst("1.0")}, []overloadImpl{unaryIntFn, unaryFloatFn}, unaryFloatFn}, {nil, nil, []Expr{intConst("1")}, []overloadImpl{unaryIntFn, binaryIntFn}, unaryIntFn}, {nil, nil, []Expr{intConst("1")}, []overloadImpl{unaryFloatFn, unaryStringFn}, unaryFloatFn}, {nil, nil, []Expr{intConst("1")}, []overloadImpl{unaryStringFn, binaryIntFn}, nil}, // Unary unresolved ValArgs. {nil, nil, []Expr{ValArg{"a"}}, []overloadImpl{unaryStringFn, unaryIntFn}, nil}, {nil, nil, []Expr{ValArg{"a"}}, []overloadImpl{unaryStringFn, binaryIntFn}, unaryStringFn}, // Unary values (not constants). {nil, nil, []Expr{NewDInt(1)}, []overloadImpl{unaryIntFn, unaryFloatFn}, unaryIntFn}, {nil, nil, []Expr{NewDFloat(1)}, []overloadImpl{unaryIntFn, unaryFloatFn}, unaryFloatFn}, {nil, nil, []Expr{NewDInt(1)}, []overloadImpl{unaryIntFn, binaryIntFn}, unaryIntFn}, {nil, nil, []Expr{NewDInt(1)}, []overloadImpl{unaryFloatFn, unaryStringFn}, nil}, {nil, nil, []Expr{NewDString("a")}, []overloadImpl{unaryIntFn, unaryFloatFn}, nil}, {nil, nil, []Expr{NewDString("a")}, []overloadImpl{unaryIntFn, unaryStringFn}, unaryStringFn}, // Binary constants. {nil, nil, []Expr{intConst("1"), intConst("1")}, []overloadImpl{binaryIntFn, binaryFloatFn, unaryIntFn}, binaryIntFn}, {nil, nil, []Expr{intConst("1"), floatConst("1.0")}, []overloadImpl{binaryIntFn, binaryFloatFn, unaryFloatFn}, binaryFloatFn}, // Binary unresolved ValArgs. {nil, nil, []Expr{ValArg{"a"}, ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryFloatFn}, nil}, {nil, nil, []Expr{ValArg{"a"}, ValArg{"b"}}, []overloadImpl{binaryIntFn, unaryStringFn}, binaryIntFn}, {nil, nil, []Expr{ValArg{"a"}, NewDString("a")}, []overloadImpl{binaryIntFn, binaryStringFn}, binaryStringFn}, {nil, nil, []Expr{ValArg{"a"}, intConst("1")}, []overloadImpl{binaryIntFn, binaryFloatFn}, binaryIntFn}, {nil, nil, []Expr{ValArg{"a"}, intConst("1")}, []overloadImpl{binaryStringFn, binaryFloatFn}, binaryFloatFn}, // Binary values. {nil, nil, []Expr{NewDString("a"), NewDString("b")}, []overloadImpl{binaryStringFn, binaryFloatFn, unaryFloatFn}, binaryStringFn}, {nil, nil, []Expr{NewDString("a"), intConst("1")}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1}, binaryStringFloatFn1}, {nil, nil, []Expr{NewDString("a"), NewDInt(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1}, nil}, {nil, nil, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1}, binaryStringFloatFn1}, {nil, nil, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn2}, binaryStringFloatFn2}, {nil, nil, []Expr{NewDFloat(1), NewDString("a")}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1}, nil}, {nil, nil, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1, binaryStringFloatFn2}, nil}, // Desired type with ambiguity. {nil, TypeInt, []Expr{intConst("1"), floatConst("1.0")}, []overloadImpl{binaryIntFn, binaryFloatFn, unaryFloatFn}, binaryIntFn}, {nil, TypeInt, []Expr{intConst("1"), NewDFloat(1)}, []overloadImpl{binaryIntFn, binaryFloatFn, unaryFloatFn}, binaryFloatFn}, {nil, TypeInt, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1, binaryStringFloatFn2}, binaryStringFloatFn1}, {nil, TypeFloat, []Expr{NewDString("a"), NewDFloat(1)}, []overloadImpl{binaryStringFn, binaryFloatFn, binaryStringFloatFn1, binaryStringFloatFn2}, binaryStringFloatFn2}, {nil, TypeFloat, []Expr{ValArg{"a"}, ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryFloatFn}, binaryFloatFn}, // Sub-expressions. {nil, nil, []Expr{floatConst("1.0"), plus(intConst("1"), intConst("2"))}, []overloadImpl{binaryIntFn, binaryFloatFn}, binaryIntFn}, {nil, nil, []Expr{floatConst("1.1"), plus(intConst("1"), intConst("2"))}, []overloadImpl{binaryIntFn, binaryFloatFn}, binaryFloatFn}, {nil, TypeFloat, []Expr{floatConst("1.0"), plus(intConst("1"), intConst("2"))}, []overloadImpl{binaryIntFn, binaryFloatFn}, binaryIntFn}, // Limitation. {nil, nil, []Expr{plus(intConst("1"), intConst("2")), plus(floatConst("1.1"), floatConst("2.2"))}, []overloadImpl{binaryIntFn, binaryFloatFn}, nil}, // Limitation. {nil, nil, []Expr{plus(floatConst("1.1"), floatConst("2.2")), plus(intConst("1"), intConst("2"))}, []overloadImpl{binaryIntFn, binaryFloatFn}, binaryFloatFn}, // Homogenous preference. {nil, nil, []Expr{NewDInt(1), ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntFn}, {nil, nil, []Expr{NewDFloat(1), ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, nil}, {nil, nil, []Expr{intConst("1"), ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntFn}, {nil, nil, []Expr{floatConst("1.0"), ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, nil}, // Limitation. {nil, TypeDate, []Expr{NewDInt(1), ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntDateFn}, {nil, TypeDate, []Expr{NewDFloat(1), ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, nil}, {nil, TypeDate, []Expr{intConst("1"), ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntDateFn}, {nil, TypeDate, []Expr{floatConst("1.0"), ValArg{"b"}}, []overloadImpl{binaryIntFn, binaryIntDateFn}, binaryIntDateFn}, } for i, d := range testData { _, fn, err := typeCheckOverloadedExprs(d.args, d.desired, d.overloads, d.exprs...) if d.expectedOverload != nil { if err != nil { t.Errorf("%d: unexpected error returned from typeCheckOverloadedExprs: %v", i, err) } else if fn != d.expectedOverload { t.Errorf("%d: expected overload %s to be chosen when type checking %s, found %v", i, d.expectedOverload, d.exprs, fn) } } else if fn != nil { t.Errorf("%d: expected no matching overloads to be found when type checking %s, found %s", i, d.exprs, fn) } } }