|
@@ -0,0 +1,181 @@
|
|
|
+package compiler
|
|
|
+
|
|
|
+import (
|
|
|
+ "kumachan/lang/source"
|
|
|
+ "kumachan/lang/typsys"
|
|
|
+ "kumachan/interpreter/program"
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+func assign(expected typsys.Type, expr *program.Expr, ctx *exprContext, s0 *typsys.InferringState) (*program.Expr, *typsys.InferringState, *source.Error) {
|
|
|
+ var loc = expr.Info.Location
|
|
|
+ var t = expr.Type.Type
|
|
|
+ // direct
|
|
|
+ if ok, s1 := typsys.Match(expected, t, s0); ok {
|
|
|
+ return expr, s1, nil
|
|
|
+ }
|
|
|
+ // convert to interface
|
|
|
+ if I, Id, Ia, ok := getInterface(expected, ctx); ok {
|
|
|
+ var d, a, ok = getTypeDefArgs(t, ctx)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ if _, is_interface := d.Content.(typsys.Interface); is_interface {
|
|
|
+ // interface -> interface
|
|
|
+ var ok, s1 = typsys.MatchAll(Ia, a, s0)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ { var It, ok = typsys.GetCertainOrInferred(expected, s1)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ if up, ok := getInterfacePath(d, Id, ctx, nil); ok {
|
|
|
+ var converted = &program.Expr {
|
|
|
+ Type: It,
|
|
|
+ Info: expr.Info,
|
|
|
+ Content: program.InterfaceTransformUpward {
|
|
|
+ Arg: expr,
|
|
|
+ Path: up,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ return converted, s1, nil
|
|
|
+ } else {
|
|
|
+ goto NG
|
|
|
+ } }
|
|
|
+ } else {
|
|
|
+ // concrete -> interface
|
|
|
+ var table, ok = ctx.resolveDispatchTable(d, Id)
|
|
|
+ if !(ok) {
|
|
|
+ if isSamInterface(I, Id) {
|
|
|
+ var field = I.FieldList[0]
|
|
|
+ var field_t = inflateFieldType(field, Id, Ia)
|
|
|
+ var ok, s1 = typsys.Match(field_t, t, s0)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ { var It, ok = typsys.GetCertainOrInferred(expected, s1)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ var converted = &program.Expr {
|
|
|
+ Type: It,
|
|
|
+ Info: expr.Info,
|
|
|
+ Content: program.InterfaceFromSingleMethod {
|
|
|
+ MethodValue: expr,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ return converted, s1, nil }
|
|
|
+ }
|
|
|
+ goto NG
|
|
|
+ }
|
|
|
+ { var ok, s1 = typsys.MatchAll(Ia, a, s0)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ { var It, ok = typsys.GetCertainOrInferred(expected, s1)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ var converted = &program.Expr {
|
|
|
+ Type: It,
|
|
|
+ Info: expr.Info,
|
|
|
+ Content: program.Interface {
|
|
|
+ ConcreteValue: expr,
|
|
|
+ DispatchTable: table,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ return converted, s1, nil } }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // convert to union
|
|
|
+ if U, Ud, Ua, ok := getUnion(expected, ctx); ok {
|
|
|
+ var converted *program.Expr
|
|
|
+ var s2 *typsys.InferringState
|
|
|
+ var key string
|
|
|
+ var found = false
|
|
|
+ for i, field := range U.FieldList {
|
|
|
+ var index = uint(i)
|
|
|
+ var field_t = inflateFieldType(field, Ud, Ua)
|
|
|
+ var ok, s1 = typsys.Match(field_t, t, s0)
|
|
|
+ if ok {
|
|
|
+ var certain, ok = typsys.GetCertainOrInferred(expected, s1)
|
|
|
+ if ok {
|
|
|
+ if found {
|
|
|
+ var this_key = field.Name
|
|
|
+ return nil, nil, source.MakeError(loc,
|
|
|
+ E_AmbiguousAssignmentToUnion {
|
|
|
+ Union: Ud.Ref.String(),
|
|
|
+ Key1: key,
|
|
|
+ Key2: this_key,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ found = true
|
|
|
+ key = field.Name
|
|
|
+ s2 = s1
|
|
|
+ converted = &program.Expr {
|
|
|
+ Type: certain,
|
|
|
+ Info: expr.Info,
|
|
|
+ Content: program.Union {
|
|
|
+ Index: int(index),
|
|
|
+ Value: expr,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if found {
|
|
|
+ return converted, s2, nil
|
|
|
+ } else {
|
|
|
+ goto NG
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // convert to Int
|
|
|
+ if program.T_Int_(expected) {
|
|
|
+ if _, _, ok := getEnum(t, ctx); ok {
|
|
|
+ var converted = &program.Expr {
|
|
|
+ Type: typsys.CertainType { Type: program.T_Int() },
|
|
|
+ Info: expr.Info,
|
|
|
+ Content: program.EnumToInt { EnumValue: expr },
|
|
|
+ }
|
|
|
+ return converted, s0, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ NG:
|
|
|
+ return nil, nil, source.MakeError(loc,
|
|
|
+ E_NotAssignable {
|
|
|
+ From: typsys.Describe(t),
|
|
|
+ To: typsys.DescribeWithInferringState(expected, s0),
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+func getUnion(t typsys.Type, ctx *exprContext) (typsys.Union, *typsys.TypeDef, ([] typsys.Type), bool) {
|
|
|
+ var def, args, ok = getTypeDefArgs(t, ctx)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ { var union, ok = def.Content.(typsys.Union)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ return union, def, args, true }
|
|
|
+ NG:
|
|
|
+ return typsys.Union {}, nil, nil, false
|
|
|
+}
|
|
|
+func getEnum(t typsys.Type, ctx *exprContext) (typsys.Enum, *typsys.TypeDef, bool) {
|
|
|
+ var def, _, ok = getTypeDefArgs(t, ctx)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ { var enum, ok = def.Content.(typsys.Enum)
|
|
|
+ if !(ok) { goto NG }
|
|
|
+ return enum, def, true }
|
|
|
+ NG:
|
|
|
+ return typsys.Enum {}, nil, false
|
|
|
+}
|
|
|
+func getTypeDefArgs(t typsys.Type, ctx *exprContext) (*typsys.TypeDef, ([] typsys.Type), bool) {
|
|
|
+ if ref_type, ok := t.(typsys.RefType); ok {
|
|
|
+ var def = ctx.mustHaveType(ref_type.Def)
|
|
|
+ var args = ref_type.Args
|
|
|
+ return def, args, true
|
|
|
+ } else {
|
|
|
+ return nil, nil, false
|
|
|
+ }
|
|
|
+}
|
|
|
+func getInterfacePath(root *typsys.TypeDef, descendant *typsys.TypeDef, ctx *exprContext, base ([] int)) ([] int, bool) {
|
|
|
+ for i, child_ref := range root.Interfaces {
|
|
|
+ var child = ctx.mustHaveType(child_ref)
|
|
|
+ var current = append(base, i)
|
|
|
+ if child == descendant {
|
|
|
+ return current, true
|
|
|
+ } else {
|
|
|
+ var path, ok = getInterfacePath(child, descendant, ctx, current)
|
|
|
+ if ok {
|
|
|
+ return path, true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil, false
|
|
|
+}
|
|
|
+
|
|
|
+
|