//Returns a variable from a local scope marked with NoInit flag //If such variable couldn't be found returns -1 private ScopeVar GetNoInitVariable(ElaEquation s, out string name) { ScopeVar var; name = s.Left.GetName(); if (s.IsFunction()) { name = s.GetFunctionName(); } if (CurrentScope.Locals.TryGetValue(name, out var)) { //If it doesn't have a NoInit flag we are not good if ((var.Flags & ElaVariableFlags.NoInit) != ElaVariableFlags.NoInit) { return(ScopeVar.Empty); } else { var.Address = 0 | var.Address << 8; //Aligning it to local scope return(var); } } return(ScopeVar.Empty); }
//Adds a variable with NoInit flag to the current scope //This method also calculates additional flags and metadata for variables. //If a given binding if defined by pattern matching then all variables from //patterns are traversed using AddPatternVariable method. private bool AddNoInitVariable(ElaEquation exp) { var flags = exp.VariableFlags | ElaVariableFlags.NoInit; //This binding is not defined by PM if (exp.IsFunction()) { var name = exp.GetFunctionName(); if (name == null || Char.IsUpper(name[0])) { AddError(ElaCompilerError.InvalidFunctionDeclaration, exp, FormatNode(exp.Left)); return(false); } AddVariable(name, exp.Left, flags | ElaVariableFlags.Function, exp.GetArgumentNumber()); } else if (exp.Left.Type == ElaNodeType.NameReference && !((ElaNameReference)exp.Left).Uppercase) { var data = -1; if (exp.Right.Type == ElaNodeType.Builtin) { //Adding required hints for a built-in data = (Int32)((ElaBuiltin)exp.Right).Kind; flags |= ElaVariableFlags.Builtin; } else if (exp.Right.Type == ElaNodeType.Lambda) { var fun = (ElaLambda)exp.Right; flags |= ElaVariableFlags.Function; data = fun.GetParameterCount(); } else if (exp.Right.IsLiteral()) { flags |= ElaVariableFlags.ObjectLiteral; } AddVariable(exp.Left.GetName(), exp, flags, data); } else { AddPatternVariables(exp.Left); } return(true); }
//Compile an instance member. Argument classPrefix (module alias) is null if a class is //not prefixed, otherwise it should be used to lookup class members. private void CompileInstanceMember(string className, string classPrefix, ElaEquation s, LabelMap map, string currentTypePrefix, string currentType) { //Obtain a 'table' function of a class var name = s.GetLeftName(); var btVar = ObtainClassFunction(classPrefix, className, name, s.Line, s.Column); if (btVar.IsEmpty()) { AddError(ElaCompilerError.MemberInvalid, s, name, className); } var builtin = (btVar.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin; var args = 0; //Here we need to understand how many arguments a class function has. //If our declaration has less arguments (allowed by Ela) we need to do //eta expansion. if (builtin) { args = BuiltinParams((ElaBuiltinKind)btVar.Data); } else { args = btVar.Data; } //First we check if this binding is simply a function reference if (!TryResolveInstanceBinding(args, s.Right, map)) { //Eta expansion should be done if a member is not a function literal //(it can be a partially applied function) or if a number of arguments //doesn't match. if (!s.IsFunction()) { EtaExpand(null, s.Right, map, args); } else if (s.GetArgumentNumber() < args) { EtaExpand(null, s, map, args); } else { CompileFunction(s, map); } } AddLinePragma(s); //It is possible if we are compiling a default instance (that can be //used to automatically generate instances). if (currentType != null) { //Depending whether this is a built-in class or a different approach is //used to add a member function. if (!builtin) { PushVar(btVar); } else { cw.Emit(Op.PushI4, (Int32)btVar.Data); } //We need to obtain a 'real' global ID of the type that we are extending. This //is done using this helper method. EmitSpecName(currentTypePrefix, "$$" + currentType, s, ElaCompilerError.UndefinedType); //Finally adding a member function. cw.Emit(Op.Addmbr); } else { var nm = "$default$" + name; //We are double binding the same default member which is not allowed within //the same module. if (globalScope.Locals.ContainsKey(nm)) { AddError(ElaCompilerError.DefaultMemberAlreadyExist, s, name, className); globalScope.Locals.Remove(name); } var flags = ElaVariableFlags.None; var data = -1; if (s.Right.Type == ElaNodeType.Builtin) { flags = ElaVariableFlags.Builtin; data = (Int32)((ElaBuiltin)s.Right).Kind; } var dv = AddVariable(nm, s, flags, data); PopVar(dv); } }
//Compile all declarations including function bindings, name bindings and bindings //defined by pattern matching. private void CompileDeclaration(ElaEquation s, LabelMap map, Hints hints) { //Check for some errors ValidateBinding(s); var partial = (s.VariableFlags & ElaVariableFlags.PartiallyApplied) == ElaVariableFlags.PartiallyApplied; var fun = partial || s.IsFunction(); if ((s.Left.Type != ElaNodeType.NameReference || ((ElaNameReference)s.Left).Uppercase) && !fun) { if ((hints & Hints.Lazy) == Hints.Lazy) { CompileLazyBindingPattern(s, map); } else { CompileBindingPattern(s, map); } } else { var nm = default(String); var sv = GetNoInitVariable(s, out nm); var addr = sv.Address; if (sv.IsEmpty()) { addr = AddVariable(s.Left.GetName(), s, s.VariableFlags, -1); } else { CurrentScope.AddFlags(nm, ElaVariableFlags.Self); } //Compile expression and write it to a variable if (fun) { //*****CURRENTLY NOT USED! //Here we automatically eta expand functions defined through partial application if (partial) { EtaExpand(s.Left.GetName(), s.Right, map, s.Arguments); } else if (CompileFunction(s, map)) { CurrentScope.AddFlags(nm, ElaVariableFlags.Clean); //A function is clean } } else { map.BindingName = s.Left.GetName(); if ((hints & Hints.Lazy) == Hints.Lazy) { CompileLazyExpression(s.Right, map, Hints.None); } else { var ed = CompileExpression(s.Right, map, Hints.None, s); if (ed.Type == DataKind.Builtin) { CurrentScope.AddFlagsAndData(nm, ElaVariableFlags.Builtin, ed.Data); } } } //Now, when done initialization, when can remove NoInit flags. if (!sv.IsEmpty()) { CurrentScope.RemoveFlags(nm, ElaVariableFlags.NoInit | ElaVariableFlags.Self); } AddLinePragma(s); PopVar(addr); } }
//**********************************TEMPORARY DISABLED //This method checks if a given expression is a regular function definition or a function //defined through partial application of another function. private bool IsFunction(ElaEquation eq) { //A simple case - regular function definition if (eq.IsFunction()) return true; //This may be a function defined through partial application. Here we only //recognize two cases - when the head is an ordinary identifier and if a head is //a fully qualified name. if (eq.Right != null && eq.Right.Type == ElaNodeType.Juxtaposition) { var jx = (ElaJuxtaposition)eq.Right; //A head is an identifier if (jx.Target.Type == ElaNodeType.NameReference) { var sv = GetVariable(jx.Target.GetName(), CurrentScope, GetFlags.NoError, 0, 0); //This is a partially applied function, therefore we can "count" this as a function. return IfFunction(eq, sv, jx.Parameters.Count, false); } else if (jx.Target.Type == ElaNodeType.FieldReference) //This might be a fully qualified name { var tr = (ElaFieldReference)jx.Target; //A target can only be a name reference; otherwise we don't see it as a function. if (tr.TargetObject.Type == ElaNodeType.NameReference) { CodeFrame _; var sv = FindByPrefix(tr.TargetObject.GetName(), tr.FieldName, out _); return IfFunction(eq, sv, jx.Parameters.Count, false); } } } else if (eq.Right != null && eq.Right.Type == ElaNodeType.NameReference) { //This may be a function defined as an alias for another function. var sv = GetVariable(eq.Right.GetName(), CurrentScope, GetFlags.NoError, 0, 0); //This is an alias, we can "count" this as a function. return IfFunction(eq, sv, 0, true); } return false; }
//Compile an instance member. Argument classPrefix (module alias) is null if a class is //not prefixed, otherwise it should be used to lookup class members. private void CompileInstanceMember(string className, string classPrefix, ElaEquation s, LabelMap map, string currentTypePrefix, string currentType) { //Obtain a 'table' function of a class var name = s.GetLeftName(); var btVar = ObtainClassFunction(classPrefix, className, name, s.Line, s.Column); if (btVar.IsEmpty()) AddError(ElaCompilerError.MemberInvalid, s, name, className); var builtin = (btVar.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin; var args = 0; //Here we need to understand how many arguments a class function has. //If our declaration has less arguments (allowed by Ela) we need to do //eta expansion. if (builtin) args = BuiltinParams((ElaBuiltinKind)btVar.Data); else args = btVar.Data; //First we check if this binding is simply a function reference if (!TryResolveInstanceBinding(args, s.Right, map)) { //Eta expansion should be done if a member is not a function literal //(it can be a partially applied function) or if a number of arguments //doesn't match. if (!s.IsFunction()) EtaExpand(null, s.Right, map, args); else if (s.GetArgumentNumber() < args) EtaExpand(null, s, map, args); else CompileFunction(s); } AddLinePragma(s); //It is possible if we are compiling a default instance (that can be //used to automatically generate instances). if (currentType != null) { //Depending whether this is a built-in class or a different approach is //used to add a member function. if (!builtin) PushVar(btVar); else cw.Emit(Op.PushI4, (Int32)btVar.Data); //We need to obtain a 'real' global ID of the type that we are extending. This //is done using this helper method. EmitSpecName(currentTypePrefix, "$$" + currentType, s, ElaCompilerError.UndefinedType); //Finally adding a member function. cw.Emit(Op.Addmbr); } else { var nm = "$default$" + name; //We are double binding the same default member which is not allowed within //the same module. if (globalScope.Locals.ContainsKey(nm)) { AddError(ElaCompilerError.DefaultMemberAlreadyExist, s, name, className); globalScope.Locals.Remove(name); } var flags = ElaVariableFlags.None; var data = -1; if (s.Right.Type == ElaNodeType.Builtin) { flags = ElaVariableFlags.Builtin; data = (Int32)((ElaBuiltin)s.Right).Kind; } var dv = AddVariable(nm, s, flags, data); PopVar(dv); } }