private void specStm(Stm e, Env <Spec> env) { C.Nn(e, env); switch (e) { case Declr d: if (d.Exp == null) { env.Declare(d, NotSetSpec.Instance); } else { env.Declare(d, specExp(d.Exp, env, AnySpec.Instance)); if (d.Exp is Ident fromIdent) { d.AssignedFrom.Add(fromIdent); } d.Ident.Spec = d.Exp.Spec; } break; case Assign a: switch (a.To) { case Ident ident: { var orig = env.Get(ident, true).Value; var slot3 = orig == NotSetSpec.Instance ? AnySpec.Instance : orig; var s = specExp(a.Exp, env, slot3); if (orig == NotSetSpec.Instance) { env.Set(ident, s); } if (a.Exp is Ident fromIdent) { a.AssignedFrom.Add(fromIdent); } break; } case MemberAccess ma: { var minObjEnv = env.Create(); var declr = new Let( new Ident(ma.Ident.Name, ma.Ident.TokenType) .CopyInfoFrom(ma.Ident, true), Void.Instance); minObjEnv.Declare(declr, AnySpec.Instance); var minObj = new ObjSpec(minObjEnv); var maExpS = specExp(ma.Exp, env, minObj); if (maExpS is ObjSpec objS) { var orig = objS.Env.Get(ma.Ident, true).Value; var slot3 = orig == NotSetSpec.Instance ? AnySpec.Instance : orig; var s = specExp(a.Exp, env, slot3); if (orig == AnySpec.Instance) { objS.Env.Set(ma.Ident, s); a.AssignedFrom.Add(ma.Ident); } } break; } default: throw new NotSupportedException(); } break; case Return r: var slot2 = returns.Peek(); var retS = specExp(r.Exp, env, slot2 == NotSetSpec.Instance ? AnySpec.Instance : slot2); returns.UpdateTop(retS); break; case Loop l: specSequence(l.Body, env); break; case Break _: break; case Continue _: break; case Toss ts: var _ = specExp(ts.Exception, env, AnySpec.Instance); // TODO requires exp to be of some type? break; case Attempt att: specSequence(att.Body, env); if (att.Grab != null) { var grabEnv = env.Create(); var exDeclr = new Let(new Ident("exception", TokenType.Ident)); grabEnv.Declare(exDeclr, AnySpec.Instance); specSequence(att.Grab, grabEnv); } if (att.AtLast != null) { specSequence(att.AtLast, env); } break; case Import imp: isImportContext = true; var objSpecWithNoMembers = new ObjSpec(env.Create()); var modImpEl = specExp(imp.QualifiedIdent, env, objSpecWithNoMembers); isImportContext = false; if (modImpEl is ObjSpec modImp) { env.AddImport(imp.QualifiedIdent, modImp.Env, (Declr)modImp.Parent); } break; case Sequence seq: specSequence(seq, env); break; default: throw new NotSupportedException(); } }
private Spec specExp2(Exp exp, Env <Spec> env, Spec slot) { C.Nn(exp, env, slot); C.ReturnsNn(); Spec ss; switch (exp) { case Ident i: if (isImportContext) { return(env.GetWithoutImports(i).Value); } else { var orig = env.Get(i).Value; if (orig == NotSetSpec.Instance || orig == AnySpec.Instance) { //prog.RemarkList.AttemptToReadUninitializedVariableWarn(i); setIdentAndAssignedFrom(env, i, slot); return(slot); } if (!isAssignable(orig, slot)) { prog.RemarkList.CannotConvertType(orig, slot, i); } return(orig); } case FnApply fna: { if (fna.Fn is Ident fnI && fnI.Name == "typeof") { if (!(fna.Arguments[0] is Spec)) { fna.Arguments = new FnArguments(new List <Exp> { specExp(fna.Arguments[0], env, AnySpec.Instance) }); } return(VoidSpec.Instance); } var orig = specExp(fna.Fn, env, AnySpec.Instance); if (orig is FnSpec fnS) { if (fnS.ParameterSpec.Count != fna.Arguments.Count) { throw prog.RemarkList.ParameterArgumentCountMismatch(fna, fnS.ParameterSpec.Count); } var argumentSpecs = new List <Spec>(); var ix = 0; foreach (var aa in fna.Arguments) { var pS = fnS.ParameterSpec[ix]; var aS = specExp(aa, env, pS); argumentSpecs.Add(isAssignable(aS, pS) ? aS : pS); ++ix; } if (fna.Arguments.Count != 0 && fnS.Fn?.SpecEnv != null) { var newFn = new Fn(fnS.Fn.Parameters, fnS.Fn.Sequence, fnS.Fn.SpecEnv).CopyInfoFrom(fnS.Fn); var newFnS = specFn(newFn, null, argumentSpecs); fnS = newFnS; } if (!isAssignable(fnS.ReturnSpec, slot)) { prog.RemarkList.CannotConvertType(fnS.ReturnSpec, slot, fna); } return(fnS.ReturnSpec); } var sigS = new List <Spec>(); foreach (var aa in fna.Arguments) { var aS = specExp(aa, env, AnySpec.Instance); sigS.Add(aS); } sigS.Add(slot == NotSetSpec.Instance ? AnySpec.Instance : slot); var newFnSpec = new FnSpec(sigS, null); if (fna.Fn is Ident fnI2) { env.Set(fnI2, newFnSpec); } return(slot); } case Fn f: return(specFn(f, env)); case When w: var _ = specExp(w.Test, env, BoolSpec.Instance); var thenS = specSequenceItem(w.Then, env, slot); if (w.Otherwise == null) { return(VoidSpec.Instance); } else { var otherwiseS = specSequenceItem(w.Otherwise, env, slot); if (!areSame(thenS, otherwiseS)) { prog.RemarkList.CannotConvertType(thenS, otherwiseS, w.Otherwise); } return(thenS); } case ArrConstructor ae: var s = new ArrSpec(commonType(ae.Arguments, env)); var notSetArgs = ae.Arguments.OfType <Ident>().ToList(); foreach (var a in notSetArgs) { var orig2 = env.Get(a).Value; if (isAssignable(s.ItemSpec, orig2)) { setIdentAndAssignedFrom(env, a, s.ItemSpec); } } return(s); case MemberAccess ma: ss = specExp(ma.Exp, env, NotSetSpec.Instance); var mai = ma.Ident; if (ss is NotSetSpec || ss is AnySpec) { var objSEnv = env.Create(); var objS = new ObjSpec(objSEnv) { FromUsage = true }; var declr = new Var(new Ident(mai.Name, mai.TokenType).CopyInfoFrom(mai, true)); objSEnv.Declare(declr, slot); var __ = specExp(mai, objSEnv, slot); ss = objS; if (ma.Exp is Ident i) { setIdentAndAssignedFrom(env, i, objS); } } var objS2 = ss as ObjSpec; if (objS2 == null) { throw prog.RemarkList.OnlyObjectsCanHaveMembers(ma, ss); } if (objS2.FromUsage) { var member = objS2.Env.GetFromThisEnvOnlyOrNull(mai, null); // also use type and var/let? if (member == null) { var declr = new Var(new Ident(mai.Name, mai.TokenType).CopyInfoFrom(mai, true)); objS2.Env.Declare(declr, slot); var __ = specExp(mai, objS2.Env, slot); } } var objSpec = (ObjSpec)ss; ss = objSpec.Env.GetFromThisEnvOnly(mai, null).Value; return(ss); case New n: var objEnv = env.Create(); foreach (var classItem in n.Body) { specStm(classItem, objEnv); } return(new ObjSpec(objEnv) { Parent = n.Parent }); case Text _: return(TextSpec.Instance); case Bool _: return(BoolSpec.Instance); case Builtin bu: return(bu.FixedSpec); case Char _: return(CharSpec.Instance); case Int _: return(IntSpec.Instance); case Void _: return(VoidSpec.Instance); default: throw new NotSupportedException(); } }