//Builds a list of attribute headers for types private void CompileHeaders(ElaNewtype v) { var t = v; var oldt = default(ElaNewtype); while (t != null) { if (t.Header) { if (oldt == null || t.Extends != oldt.Extends || t.Opened != oldt.Opened) { AddError(ElaCompilerError.TypeHeaderNotConnected, t, t.Name); } else { oldt.Flags = t.Flags; } } else { oldt = t; } t = t.And; } }
//An entry method for type compilation. Ensures that types ('type') are //always compiled before type extensions ('data'). private void CompileTypes(ElaNewtype v, LabelMap map) { CompileHeaders(v); CompileTypeHeaderOnly(v, map); CompileDataHeaderOnly(v, map); CompileTypeBodyOnly(v, map); CompileDataBodyOnly(v, map); }
private void CompileTypeBody(ElaNewtype v, LabelMap map) { if (v.HasBody) { for (var i = 0; i < v.Constructors.Count; i++) { var c = v.Constructors[i]; var cf = v.ConstructorFlags[i]; cf = cf == ElaVariableFlags.None ? v.Flags : cf | v.Flags; CompileConstructor(v.Name, v.ScopeVar, c, cf, v.TypeModuleId); } } }
//This method only compiles types declared through 'type' keyword //and not type extensions ('data' declarations). private void CompileTypeBodyOnly(ElaNewtype nt, LabelMap map) { var v = nt; while (v != null) { if (!v.Extends && !v.Header) { CompileTypeBody(v, map); } v = v.And; } }
//Checks if a constructor is actually a list constructor which implementation can be optimized. private bool IsCons(ElaExpression exp, ElaNewtype t) { if (exp.Type != ElaNodeType.Juxtaposition) { return(false); } var j = (ElaJuxtaposition)exp; //We check that a constructor is in the form 'a :: a' return(j.Parameters.Count == 2 && j.Parameters[0].Type == ElaNodeType.NameReference && j.Parameters[1].Type == ElaNodeType.NameReference && !Char.IsUpper(j.Parameters[0].GetName()[0]) && !Char.IsUpper(j.Parameters[1].GetName()[0])); }
//Main method for type compilation private void CompileTypeHeader(ElaNewtype v, LabelMap map) { //We need to obtain typeId for a type var tc = -1; var sca = -1; var flags = v.Flags; //A body may be null only if this is a built-in type if (!v.HasBody && v.Extends) { AddError(ElaCompilerError.ExtendsNoDefinition, v, v.Name); } else if (!v.HasBody) { tc = (Int32)TCF.GetTypeCode(v.Name); tc = tc == 0 ? -1 : tc; sca = AddVariable("$$" + v.Name, v, flags | ElaVariableFlags.ClosedType | ElaVariableFlags.CompilerGenerated, tc); var sv = GetVariable("$$" + v.Name, v.Line, v.Column); //OK, type is built-in if (tc > 0) { //We add a special variable that contains a global type ID cw.Emit(Op.PushI4, tc); PopVar(sca); } } else { var tf = flags; if (!v.Opened) { tf |= ElaVariableFlags.ClosedType; } sca = v.Extends ? AddVariable() : AddVariable("$$" + v.Name, v, tf | ElaVariableFlags.CompilerGenerated, -1); } //Type is already declared within the same module (types from different //modules can shadow each, so this is perfectly OK). if (!v.Extends && frame.InternalTypes.ContainsKey(v.Name)) { AddError(ElaCompilerError.TypeAlreadyDeclared, v, v.Name); frame.InternalTypes.Remove(v.Name); } if (v.Prefix != null && !v.Extends) { AddError(ElaCompilerError.InvalidTypeDefinition, v); } if (!v.Extends) { frame.InternalTypes.Add(v.Name, tc); } AddLinePragma(v); //-1 mean "this" module (considered by default). var typeModuleId = -1; //Add a type var for a non built-in type with a body if (tc == -1) { //Add a special variable with global type ID which will be calculated at run-time if (v.Extends) { var sv = EmitSpecName(v.Prefix, "$$" + v.Name, v, ElaCompilerError.UndefinedType, out typeModuleId); //We can only extend type that are explicitly declared as open if ((sv.Flags & ElaVariableFlags.ClosedType) == ElaVariableFlags.ClosedType) { AddError(ElaCompilerError.UnableExtendOpenType, v, v.Name); } } else { cw.Emit(Op.Typeid, AddString(v.Name)); } PopVar(sca); v.TypeModuleId = typeModuleId; v.ScopeVar = sca; //if (v.HasBody) //{ // for (var i = 0; i < v.Constructors.Count; i++) // { // var c = v.Constructors[i]; // var cf = v.ConstructorFlags[i]; // cf = cf == ElaVariableFlags.None ? flags : cf|flags; // CompileConstructor(v.Name, sca, c, cf, typeModuleId); // } //} } else { cw.Emit(Op.Nop); } }