//An entry method for instance compilation private void CompileInstances(ElaClassInstance s, LabelMap map) { var ins = s; //First we need to compile default instances so that //other instances in this class will be able to use them while (ins != null) { if (ins.TypeName == null) { CompileInstanceBody(ins, map); } ins = ins.And; } ins = s; //Now we compile specific instances while (ins != null) { if (ins.TypeName != null) { CompileInstanceBody(ins, map); } ins = ins.And; } }
//Some members may be generated directly by compiler. Here we check if this is the case for this //particular member. private bool TryAutogenerate(ScopeVar var, ElaClassInstance inst) { if ((var.Flags & ElaVariableFlags.Builtin) != ElaVariableFlags.Builtin) { return(false); } switch ((ElaBuiltinKind)var.Data) { //Used to generate Bounded.maxBound constant case ElaBuiltinKind.GenMaxBound: cw.Emit(Op.PushI4, 1); //Obtain type ID, no errors, they are captured elsewhere EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api, (Int32)Api.TypeConsNumber); cw.Emit(Op.Sub); EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api2, (Int32)Api.ConsCodeByIndex); cw.Emit(Op.Api, (Int32)Api.ConsDefault); return(true); //Used to generate Bounded.minBound constant case ElaBuiltinKind.GenMinBound: case ElaBuiltinKind.GenDefault: cw.Emit(Op.PushI4_0); //Obtain type ID, no errors, they are captured elsewhere EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api2, (Int32)Api.ConsCodeByIndex); cw.Emit(Op.Api, (Int32)Api.ConsDefault); return(true); default: return(false); } }
//This function tries to autogenerate an instance (similar to derived in Haskell). //It can fully or partially generate it. A default instance (instance without a type) is //used for such purpose. private List <String> TryGenerateMembers(List <String> members, CodeFrame mod, ElaClassInstance inst) { if (members.Count == 0) { return(members); } var newMem = new List <String>(members); for (var i = 0; i < newMem.Count; i++) { var m = newMem[i]; var btVar = ObtainClassFunction(inst.TypeClassPrefix, inst.TypeClassName, m, inst.Line, inst.Column); //This is less likely but we better check this anyway if (btVar.IsEmpty()) { AddError(ElaCompilerError.MemberInvalid, inst, m, inst.TypeClassName); } var defVar = GetGlobalVariable("$default$" + m, GetFlags.NoError, inst.Line, inst.Column); //We dont' need to generate errors here, errors will be captured later. if (!defVar.IsEmpty()) { var builtin = (btVar.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin; //Check if this member is implemented directly by compiler if (!TryAutogenerate(defVar, inst)) { PushVar(defVar); } if (!builtin) { PushVar(btVar); } else { cw.Emit(Op.PushI4, (Int32)btVar.Data); } EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.UndefinedType); cw.Emit(Op.Addmbr); members.Remove(m); } } return(members); }
//Main method for instance compilation private void CompileInstanceBody(ElaClassInstance s, LabelMap map) { //Obtain type class data var mod = default(CodeFrame); var mbr = default(ClassData); var modId = -1; var classId = ObtainTypeClass(s, out mbr, out modId, out mod); //It's OK if mbr (class members) is null. It is not null only if a class declaration is local. //However mod shouldn't be null in such a case (non-local class), and we will use it to obtain //class members. If mod==null and mbr==null, or mod doesn't have our class, than we really can't find it. var notFound = mbr == null && (mod == null || !mod.InternalClasses.TryGetValue(s.TypeClassName, out mbr)); //Type class not found, nothing else to do if (notFound) { if (!options.IgnoreUndefined) { AddError(ElaCompilerError.UnknownClass, s, s.TypeClassName); } else if (s.TypeName != null) //It is valid if this is a 'default' instance { //Add an instance anyway - the IgnoreUndefine flag is usually set just to gather some module metadata, //so we need to populate it anyway. frame.InternalInstances.Add(new InstanceData(s.TypeName, s.TypeClassName, -1, modId, s.Line, s.Column)); } } else { //Now we need to obtain a local ID of a module where type is defined var typeModuleCode = -1; var typeId = -1; //Type name is null if this is a default instance if (s.TypeName != null) { typeId = ObtainType(s, out typeModuleCode); //Add new instance registration information frame.InternalInstances.Add(new InstanceData(s.TypeName, s.TypeClassName, typeModuleCode, modId, s.Line, s.Column)); } //Fill a list of classMembers, this list is used in this method to validate //whether all members of a class have an implementation var classMembers = new List <String>(mbr.Members.Length); for (var i = 0; i < mbr.Members.Length; i++) { classMembers.Add(mbr.Members[i].Name); } if (s.Where != null) { foreach (var b in s.Where.Equations) { var err = false; //Patterns are now allowed in member bindings if (!b.IsFunction() && b.Left.Type != ElaNodeType.NameReference) { AddError(ElaCompilerError.MemberNoPatterns, b.Left, FormatNode(b.Left)); err = true; } var name = b.GetLeftName(); //Only member functions can be declared inside instance if (!classMembers.Contains(name)) { AddError(ElaCompilerError.MemberInvalid, b, name, s.TypeClassName); err = true; } //Compile member function if (!err) { classMembers.Remove(name); //It's OK if s.TypeName is null, this situation (default instance) is handled in this method CompileInstanceMember(s.TypeClassName, s.TypeClassPrefix, b, map, s.TypePrefix, s.TypeName); } } } //If this is not a default instance, we can try to autogenerate it. if (s.TypeName != null) { classMembers = TryGenerateMembers(classMembers, mod, s); } //Not all of the members are implemented, which is an error if an instance is //not generated by a compiler or this is a 'default' instance. if (s.TypeName != null && !IsBuiltIn(classId, typeId) && classMembers.Count > 0) { if (s.Where != null) { AddError(ElaCompilerError.MemberNotAll, s, s.TypeClassName, s.TypeClassName + " " + s.TypeName); } else { //If a 'where' clause is not specified at all we need to generate a better //error message (most likely this instance appears in 'deriving' clause). AddError(ElaCompilerError.UnableDerive, s, s.TypeClassName, s.TypeName); } } } }
//Here we obtain type information - local ID of a module where a type is defined. private int ObtainType(ElaClassInstance s, out int typeModuleCode) { typeModuleCode = -1; if (s.TypePrefix == null) { //First we check that type is not defined locally if (!frame.InternalTypes.ContainsKey(s.TypeName)) { var sv = GetVariable("$$" + s.TypeName, CurrentScope, GetFlags.NoError, s.Line, s.Column); if (sv.IsEmpty() && !options.IgnoreUndefined) { AddError(ElaCompilerError.UndefinedType, s, s.TypeName); return(-1); } //The trick is - here sv can be only an external name (we do check prior to this //if a type is not local). If it is an external name that first byte contains a //local index of a referenced module - and typeModuleCode is effectly a local ID of a module //where a type is declared typeModuleCode = sv.Address & Byte.MaxValue; return(sv.Data); } else { var sv = GetVariable("$$" + s.TypeName, CurrentScope, GetFlags.NoError, s.Line, s.Column); return(sv.Data); } } else { //TypePrefix is a local name that should correspond to a module alias var sv = GetVariable(s.TypePrefix, s.Line, s.Column); if (sv.IsEmpty()) { return(-1); } //A name exists but this is not a module alias if ((sv.Flags & ElaVariableFlags.Module) != ElaVariableFlags.Module) { AddError(ElaCompilerError.InvalidQualident, s, s.TypePrefix); return(-1); } //Obtain a local ID of a module based on TypePrefix (which is module alias //that should be unique within this module). var frm = frame.References[s.TypePrefix]; typeModuleCode = frm.LogicalHandle; //Now we need to obtain type variable data - it may have the typeId of a type //if this is a built-in type. ScopeVar sv2; if (frm != null && typeModuleCode < refs.Count && typeModuleCode >= 0 && refs[typeModuleCode].GlobalScope.Locals.TryGetValue("$$" + s.TypeName, out sv2)) { return(sv2.Data); } return(-1); } }
//This method returns type class data including: type class metadata (ClassData), module local ID //(modId) and compiled module where class is defined. private int ObtainTypeClass(ElaClassInstance s, out ClassData mbr, out int modId, out CodeFrame mod) { mbr = null; modId = -1; mod = null; //If a type class prefix is not set we need to obtain a type class ID using a special '$$$*' variable //that is initialized during type class compilation if (s.TypeClassPrefix == null) { //We first check if a class definition is non-local if (!frame.InternalClasses.TryGetValue(s.TypeClassName, out mbr)) { var sv = GetVariable("$$$" + s.TypeClassName, CurrentScope, GetFlags.NoError, s.Line, s.Column); if (sv.IsEmpty() && !options.IgnoreUndefined) { AddError(ElaCompilerError.UnknownClass, s, s.TypeClassName); return(-1); } //The trick is - here sv can be only an external name (we do check prior to this //if a class is not local). If it is an external name that first byte contains a //local index of a referenced module - that is exactly what we need here to obtain //a compiled module frame (from refs array). modId = sv.Address & Byte.MaxValue; if (modId < refs.Count && modId >= 0) { mod = refs[modId]; } return(sv.Data); } else { var sv = GetVariable("$$$" + s.TypeClassName, CurrentScope, GetFlags.NoError, s.Line, s.Column); return(sv.Data); } } else { //Type class prefix is set. The prefix itself should be local name (a module alias) var sv = GetVariable(s.TypeClassPrefix, s.Line, s.Column); if (sv.IsEmpty()) { return(-1); } //A name was found but this is not a module alias if ((sv.Flags & ElaVariableFlags.Module) != ElaVariableFlags.Module) { AddError(ElaCompilerError.InvalidQualident, s, s.TypeClassPrefix); return(-1); } //In this case we can look for a reference based on its alias (alias should be unique within //the current module). modId = frame.References[s.TypeClassPrefix].LogicalHandle; if (modId < refs.Count && modId >= 0) { mod = refs[modId]; } //Now we need to obtain 'data' of a type class variable - it might a type class //typeId if this type class is built-in. ScopeVar sv2; if (mod != null && mod.GlobalScope.Locals.TryGetValue("$$$" + s.TypeClassName, out sv2)) { return(sv.Data); } else { return(-1); } } }
void ClassInstance() { var block = default(ElaEquationSet); var ot = t; var list = default(List<ElaClassInstance>); Expect(44); var ci = new ElaClassInstance(t); ci.And = Program.Instances; Program.Instances = ci; ot = t; if (la.kind == 1) { Get(); ci.TypeClassPrefix=t.val; Expect(27); Expect(2); ci.TypeClassName=t.val; } else if (la.kind == 2) { Get(); ci.TypeClassName=t.val; } else SynErr(116); while (la.kind == 1 || la.kind == 2) { if (ci.TypeName != null) { if (list == null) { list = new List<ElaClassInstance>(); list.Add(ci); } ci = new ElaClassInstance { TypeClassPrefix = ci.TypeClassPrefix, TypeClassName = ci.TypeClassName }; ci.SetLinePragma(ot.line, ot.col); ci.And = Program.Instances; Program.Instances = ci; list.Add(ci); } if (la.kind == 1) { Get(); ci.TypePrefix = t.val; Expect(27); Expect(2); ci.TypeName=t.val; } else { Get(); ci.TypeName=t.val; } } if (la.kind == 43) { WhereBinding(out block); if (list == null) ci.Where = block; else { for (var i = 0; i < list.Count; i++) list[i].Where = block; } } }
//This function tries to autogenerate an instance (similar to derived in Haskell). //It can fully or partially generate it. A default instance (instance without a type) is //used for such purpose. private List<String> TryGenerateMembers(List<String> members, CodeFrame mod, ElaClassInstance inst) { if (members.Count == 0) return members; var newMem = new List<String>(members); for (var i = 0; i < newMem.Count; i++) { var m = newMem[i]; var btVar = ObtainClassFunction(inst.TypeClassPrefix, inst.TypeClassName, m, inst.Line, inst.Column); //This is less likely but we better check this anyway if (btVar.IsEmpty()) AddError(ElaCompilerError.MemberInvalid, inst, m, inst.TypeClassName); var defVar = GetGlobalVariable("$default$" + m, GetFlags.NoError, inst.Line, inst.Column); //We dont' need to generate errors here, errors will be captured later. if (!defVar.IsEmpty()) { var builtin = (btVar.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin; //Check if this member is implemented directly by compiler if (!TryAutogenerate(defVar, inst)) PushVar(defVar); if (!builtin) PushVar(btVar); else cw.Emit(Op.PushI4, (Int32)btVar.Data); EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.UndefinedType); cw.Emit(Op.Addmbr); members.Remove(m); } } return members; }
void NewType() { scanner.InjectBlock(); var extends = false; var opened = false; if (la.kind == 45) { Get(); } else if (la.kind == 48) { Get(); extends=true; } else if (la.kind == 49) { Get(); opened=true; } else SynErr(114); var nt = new ElaNewtype(t) { Extends=extends,Opened=opened }; if (la.kind == 1) { Get(); nt.Prefix = t.val; Expect(27); Expect(2); nt.Name = t.val; } else if (la.kind == 2) { Get(); nt.Name = t.val; } else SynErr(115); nt.And = Program.Types; Program.Types = nt; if (la.kind == 51 || la.kind == 57) { if (la.kind == 57) { Get(); if (la.kind == 23) { Get(); } NewTypeConstructor(nt); while (la.kind == 23) { Get(); NewTypeConstructor(nt); } } else { Get(); nt.Header = true; Expect(1); nt.Flags = ProcessAttribute(t.val,nt.Flags); while (la.kind == 1) { Get(); nt.Flags = ProcessAttribute(t.val,nt.Flags); } } } if (la.kind == 65) { Get(); var tt = t; string pf; string nm; while (la.kind == 1 || la.kind == 2) { if (la.kind == 1) { Get(); pf=t.val; Expect(27); Expect(2); nm=t.val; } else { Get(); pf=null;nm=t.val; } var ci = new ElaClassInstance(tt); ci.And = Program.Instances; Program.Instances = ci; ci.TypeName = nt.Name; ci.TypePrefix = nt.Prefix; ci.TypeClassPrefix = pf; ci.TypeClassName = nm; } } EndBlock(); }
//Some members may be generated directly by compiler. Here we check if this is the case for this //particular member. private bool TryAutogenerate(ScopeVar var, ElaClassInstance inst) { if ((var.Flags & ElaVariableFlags.Builtin) != ElaVariableFlags.Builtin) return false; switch ((ElaBuiltinKind)var.Data) { //Used to generate Bounded.maxBound constant case ElaBuiltinKind.GenMaxBound: cw.Emit(Op.PushI4, 1); //Obtain type ID, no errors, they are captured elsewhere EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api, (Int32)Api.TypeConsNumber); cw.Emit(Op.Sub); EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api2, (Int32)Api.ConsCodeByIndex); cw.Emit(Op.Api, (Int32)Api.ConsDefault); return true; //Used to generate Bounded.minBound constant case ElaBuiltinKind.GenMinBound: case ElaBuiltinKind.GenDefault: cw.Emit(Op.PushI4_0); //Obtain type ID, no errors, they are captured elsewhere EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None); cw.Emit(Op.Api2, (Int32)Api.ConsCodeByIndex); cw.Emit(Op.Api, (Int32)Api.ConsDefault); return true; default: return false; } }
//This method returns type class data including: type class metadata (ClassData), module local ID //(modId) and compiled module where class is defined. private int ObtainTypeClass(ElaClassInstance s, out ClassData mbr, out int modId, out CodeFrame mod) { mbr = null; modId = -1; mod = null; //If a type class prefix is not set we need to obtain a type class ID using a special '$$$*' variable //that is initialized during type class compilation if (s.TypeClassPrefix == null) { //We first check if a class definition is non-local if (!frame.InternalClasses.TryGetValue(s.TypeClassName, out mbr)) { var sv = GetVariable("$$$" + s.TypeClassName, CurrentScope, GetFlags.NoError, s.Line, s.Column); if (sv.IsEmpty() && !options.IgnoreUndefined) { AddError(ElaCompilerError.UnknownClass, s, s.TypeClassName); return -1; } //The trick is - here sv can be only an external name (we do check prior to this //if a class is not local). If it is an external name that first byte contains a //local index of a referenced module - that is exactly what we need here to obtain //a compiled module frame (from refs array). modId = sv.Address & Byte.MaxValue; if (modId < refs.Count && modId >= 0) mod = refs[modId]; return sv.Data; } else { var sv = GetVariable("$$$" + s.TypeClassName, CurrentScope, GetFlags.NoError, s.Line, s.Column); return sv.Data; } } else { //Type class prefix is set. The prefix itself should be local name (a module alias) var sv = GetVariable(s.TypeClassPrefix, s.Line, s.Column); if (sv.IsEmpty()) return -1; //A name was found but this is not a module alias if ((sv.Flags & ElaVariableFlags.Module) != ElaVariableFlags.Module) { AddError(ElaCompilerError.InvalidQualident, s, s.TypeClassPrefix); return -1; } //In this case we can look for a reference based on its alias (alias should be unique within //the current module). modId = frame.References[s.TypeClassPrefix].LogicalHandle; if (modId < refs.Count && modId >= 0) mod = refs[modId]; //Now we need to obtain 'data' of a type class variable - it might a type class //typeId if this type class is built-in. ScopeVar sv2; if (mod != null && mod.GlobalScope.Locals.TryGetValue("$$$" + s.TypeClassName, out sv2)) return sv.Data; else return -1; } }
//Here we obtain type information - local ID of a module where a type is defined. private int ObtainType(ElaClassInstance s, out int typeModuleCode) { typeModuleCode = -1; if (s.TypePrefix == null) { //First we check that type is not defined locally if (!frame.InternalTypes.ContainsKey(s.TypeName)) { var sv = GetVariable("$$" + s.TypeName, CurrentScope, GetFlags.NoError, s.Line, s.Column); if (sv.IsEmpty() && !options.IgnoreUndefined) { AddError(ElaCompilerError.UndefinedType, s, s.TypeName); return -1; } //The trick is - here sv can be only an external name (we do check prior to this //if a type is not local). If it is an external name that first byte contains a //local index of a referenced module - and typeModuleCode is effectly a local ID of a module //where a type is declared typeModuleCode = sv.Address & Byte.MaxValue; return sv.Data; } else { var sv = GetVariable("$$" + s.TypeName, CurrentScope, GetFlags.NoError, s.Line, s.Column); return sv.Data; } } else { //TypePrefix is a local name that should correspond to a module alias var sv = GetVariable(s.TypePrefix, s.Line, s.Column); if (sv.IsEmpty()) return -1; //A name exists but this is not a module alias if ((sv.Flags & ElaVariableFlags.Module) != ElaVariableFlags.Module) { AddError(ElaCompilerError.InvalidQualident, s, s.TypePrefix); return -1; } //Obtain a local ID of a module based on TypePrefix (which is module alias //that should be unique within this module). var frm = frame.References[s.TypePrefix]; typeModuleCode = frm.LogicalHandle; //Now we need to obtain type variable data - it may have the typeId of a type //if this is a built-in type. ScopeVar sv2; if (frm != null && typeModuleCode < refs.Count && typeModuleCode >= 0 && refs[typeModuleCode].GlobalScope.Locals.TryGetValue("$$" + s.TypeName, out sv2)) return sv2.Data; return -1; } }
//An entry method for instance compilation private void CompileInstances(ElaClassInstance s, LabelMap map) { var ins = s; //First we need to compile default instances so that //other instances in this class will be able to use them while (ins != null) { if (ins.TypeName == null) CompileInstanceBody(ins, map); ins = ins.And; } ins = s; //Now we compile specific instances while (ins != null) { if (ins.TypeName != null) CompileInstanceBody(ins, map); ins = ins.And; } }
//Main method for instance compilation private void CompileInstanceBody(ElaClassInstance s, LabelMap map) { //Obtain type class data var mod = default(CodeFrame); var mbr = default(ClassData); var modId = -1; var classId = ObtainTypeClass(s, out mbr, out modId, out mod); //It's OK if mbr (class members) is null. It is not null only if a class declaration is local. //However mod shouldn't be null in such a case (non-local class), and we will use it to obtain //class members. If mod==null and mbr==null, or mod doesn't have our class, than we really can't find it. var notFound = mbr == null && (mod == null || !mod.InternalClasses.TryGetValue(s.TypeClassName, out mbr)); //Type class not found, nothing else to do if (notFound) { if (!options.IgnoreUndefined) AddError(ElaCompilerError.UnknownClass, s, s.TypeClassName); else if (s.TypeName != null) //It is valid if this is a 'default' instance { //Add an instance anyway - the IgnoreUndefine flag is usually set just to gather some module metadata, //so we need to populate it anyway. frame.InternalInstances.Add(new InstanceData(s.TypeName, s.TypeClassName, -1, modId, s.Line, s.Column)); } } else { //Now we need to obtain a local ID of a module where type is defined var typeModuleCode = -1; var typeId = -1; //Type name is null if this is a default instance if (s.TypeName != null) { typeId = ObtainType(s, out typeModuleCode); //Add new instance registration information frame.InternalInstances.Add(new InstanceData(s.TypeName, s.TypeClassName, typeModuleCode, modId, s.Line, s.Column)); } //Fill a list of classMembers, this list is used in this method to validate //whether all members of a class have an implementation var classMembers = new List<String>(mbr.Members.Length); for (var i = 0; i < mbr.Members.Length; i++) classMembers.Add(mbr.Members[i].Name); if (s.Where != null) foreach (var b in s.Where.Equations) { var err = false; //Patterns are now allowed in member bindings if (!b.IsFunction() && b.Left.Type != ElaNodeType.NameReference) { AddError(ElaCompilerError.MemberNoPatterns, b.Left, FormatNode(b.Left)); err = true; } var name = b.GetLeftName(); //Only member functions can be declared inside instance if (!classMembers.Contains(name)) { AddError(ElaCompilerError.MemberInvalid, b, name, s.TypeClassName); err = true; } //Compile member function if (!err) { classMembers.Remove(name); //It's OK if s.TypeName is null, this situation (default instance) is handled in this method CompileInstanceMember(s.TypeClassName, s.TypeClassPrefix, b, map, s.TypePrefix, s.TypeName); } } //If this is not a default instance, we can try to autogenerate it. if (s.TypeName != null) classMembers = TryGenerateMembers(classMembers, mod, s); //Not all of the members are implemented, which is an error if an instance is //not generated by a compiler or this is a 'default' instance. if (s.TypeName != null && !IsBuiltIn(classId, typeId) && classMembers.Count > 0) { if (s.Where != null) AddError(ElaCompilerError.MemberNotAll, s, s.TypeClassName, s.TypeClassName + " " + s.TypeName); else { //If a 'where' clause is not specified at all we need to generate a better //error message (most likely this instance appears in 'deriving' clause). AddError(ElaCompilerError.UnableDerive, s, s.TypeClassName, s.TypeName); } } } }