//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); }
//Compiles a pattern as lazy by a making a right hand a thunk and then delegating //a job to 'CompileLazyPattern' routine. private void CompileLazyBindingPattern(ElaEquation eq, LabelMap map) { CompileLazyExpression(eq.Right, map, Hints.None); var sys = AddVariable(); PopVar(sys); CompileLazyPattern(sys, eq.Left, false); }
//Used to compile an anonymous function (lambda). This function returns a number of parameters //in compiled lambda. private int CompileLambda(ElaEquation bid) { var fc = new ElaJuxtaposition(); //Lambda is parsed as a an application of a lambda operator, e.g. //expr -> expr, therefore it needs to be transformed in order to be //able to use existing match compilation logic. if (bid.Left.Type == ElaNodeType.Juxtaposition && !bid.Left.Parens) //Parens flag is added if an expression is in parens and //therefore should be qualified as a pattern. { var f = (ElaJuxtaposition)bid.Left; fc.Parameters.Add(f.Target); fc.Parameters.AddRange(f.Parameters); } else { fc.Parameters.Add(bid.Left); } bid.Left = fc; var parLen = fc.Parameters.Count; StartFun(null, parLen); var funSkipLabel = cw.DefineLabel(); var map = new LabelMap(); cw.Emit(Op.Br, funSkipLabel); //We start a real (VM based) lexical scope for a function. StartScope(true, bid.Right.Line, bid.Right.Column); StartSection(); var address = cw.Offset; CompileFunctionMatch(parLen, bid, map, Hints.Scope | Hints.Tail); var funHandle = frame.Layouts.Count; var ss = EndFun(funHandle); frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address)); EndScope(); EndSection(); cw.Emit(Op.Ret); cw.MarkLabel(funSkipLabel); AddLinePragma(bid); //Function is constructed cw.Emit(Op.PushI4, parLen); cw.Emit(Op.Newfun, funHandle); return(parLen); }
//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); }
private void ProcessBinding(ElaEquationSet block, ElaEquation bid, ElaExpression left, ElaExpression right) { bid.Left = left; bid.Right = right; if (bindings.Peek() == unit) { block.Equations.Add(bid); return; } var fName = default(String); if (right != null && left.Type == ElaNodeType.Juxtaposition && !left.Parens) { var fc = (ElaJuxtaposition)left; if (fc.Target.Type == ElaNodeType.NameReference) { fName = fc.Target.GetName(); } } if (fName != null) { var lastB = bindings.Peek(); if (lastB != null && ((ElaJuxtaposition)lastB.Left).Target.GetName() == fName) { lastB.Next = bid; } else { block.Equations.Add(bid); } bindings.Pop(); bindings.Push(bid); } else { block.Equations.Add(bid); } }
//Validate correctness of a binding private void ValidateBinding(ElaEquation s) { //These errors are not critical and allow to continue compilation if (s.Right.Type == ElaNodeType.Builtin && s.Left.Type != ElaNodeType.NameReference) { AddError(ElaCompilerError.InvalidBuiltinBinding, s); } if ((s.VariableFlags & ElaVariableFlags.Private) == ElaVariableFlags.Private && CurrentScope != globalScope) { AddError(ElaCompilerError.PrivateOnlyGlobal, s); } //Bang patterns are only allowed in functions and constructors if (s.Left.Type == ElaNodeType.NameReference && ((ElaNameReference)s.Left).Bang) { AddError(ElaCompilerError.BangPatternNotValid, s, FormatNode(s.Left)); AddHint(ElaCompilerHint.BangsOnlyFunctions, s); } }
//Compiles a binding defined by pattern matching. private void CompileBindingPattern(ElaEquation s, LabelMap map) { //Compile a right hand expression. Currently it is always compiled, event if right-hand //and both left-hand are tuples (however an optimization can be applied in such a case). var sys = AddVariable(); CompileExpression(s.Right, map, Hints.None, s); AddLinePragma(s); PopVar(sys); //Labels needed for pattern compilation var next = cw.DefineLabel(); var exit = cw.DefineLabel(); //Here we compile a pattern a generate a 'handling logic' that raises a MatchFailed exception CompilePattern(sys, s.Left, next, false /*allowBang*/, false /*forceStrict*/); cw.Emit(Op.Br, exit); cw.MarkLabel(next); cw.Emit(Op.Failwith, (Int32)ElaRuntimeError.MatchFailed); cw.MarkLabel(exit); cw.Emit(Op.Nop); }
private void ProcessBinding(ElaEquationSet block, ElaEquation bid, ElaExpression left, ElaExpression right) { bid.Left = left; bid.Right = right; if (bindings.Peek() == unit) { block.Equations.Add(bid); return; } var fName = default(String); if (right != null && left.Type == ElaNodeType.Juxtaposition && !left.Parens) { var fc = (ElaJuxtaposition)left; if (fc.Target.Type == ElaNodeType.NameReference) fName = fc.Target.GetName(); } if (fName != null) { var lastB = bindings.Peek(); if (lastB != null && ((ElaJuxtaposition)lastB.Left).Target.GetName() == fName) lastB.Next = bid; else block.Equations.Add(bid); bindings.Pop(); bindings.Push(bid); } else block.Equations.Add(bid); }
//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); } }
//**********************************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; }
//**********************************TEMPORARY DISABLED //Here we try to check if a right side is a function reference of a partially applied function. //This function might (or might not) set a 'PartiallyApplied' flag. If this flag is set, than a //function is eta expanded during compilation. Normally this flag is not needed when functions //are defined as simple aliases for other functions. private bool IfFunction(ElaEquation eq, ScopeVar sv, int curArgs, bool noPartial) { //This is a partially applied function, therefore we can "count" this as a function. if ((sv.Flags & ElaVariableFlags.Function) == ElaVariableFlags.Function && sv.Data > curArgs) { if (!noPartial) { eq.VariableFlags |= ElaVariableFlags.PartiallyApplied; eq.Arguments = sv.Data - curArgs; } return true; } else if ((sv.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin) { //If this a built-in, we need to use another method to determine number of arguments. var args = BuiltinParams((ElaBuiltinKind)sv.Data); if (args > curArgs) { if (!noPartial) { eq.VariableFlags |= ElaVariableFlags.PartiallyApplied; eq.Arguments = args - curArgs; } return true; } } return false; }
//Main method used to compile functions. Can compile regular named functions, //named functions in-place (FunFlag.Inline) and type constructors (FunFlag.Newtype). //Return 'true' if a function is clean (no no-inits references). private bool CompileFunction(ElaEquation dec, LabelMap oldMap) { var fc = (ElaJuxtaposition)dec.Left; var pars = fc.Parameters.Count; //Target can be null in a case of an anonymous function (lambda) var name = fc.Target != null?fc.Target.GetName() : String.Empty; var sv = name != null?GetVariable(name, dec.Line, dec.Column) : default(ScopeVar); StartFun(name, pars); var funSkipLabel = Label.Empty; var map = new LabelMap(); if ((sv.VariableFlags & ElaVariableFlags.NoWarnings) == ElaVariableFlags.NoWarnings || oldMap.NoWarnings) { map.NoWarnings = true; //Do not generate warning in this function } var startLabel = cw.DefineLabel(); //Functions are always compiled in place, e.g. when met. Therefore a 'goto' //instruction is emitted to skip through function definition. funSkipLabel = cw.DefineLabel(); cw.Emit(Op.Br, funSkipLabel); //FunStart label is needed for tail recursive calls when we emit a 'goto' //instead of an actual function call. map.FunStart = startLabel; //Preserve some information about a function we're currently in. map.FunctionName = name; map.FunctionParameters = pars; map.FunctionScope = CurrentScope; cw.MarkLabel(startLabel); if (dec.Right == null) { AddError(ElaCompilerError.InvalidFunctionDeclaration, dec, dec); return(false); } //We start a real (VM based) lexical scope for a function. StartScope(true, dec.Right.Line, dec.Right.Column); //We add a special 'context' variable; it is not initialized AddVariable("context", dec, ElaVariableFlags.Context | ElaVariableFlags.CompilerGenerated, -1); //StartSection create a real lexical scope. StartSection(); var hints = Hints.Scope | Hints.Tail; AddLinePragma(dec); var address = cw.Offset; cleans.Push(true); CompileFunctionMatch(pars, dec, map, hints); var ret = cleans.Pop(); //This logic creates a function (by finally emitting Newfun). var funHandle = frame.Layouts.Count; var ss = EndFun(funHandle); frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address)); EndScope(); EndSection(); cw.Emit(Op.Ret); cw.MarkLabel(funSkipLabel); AddLinePragma(dec); //Function is constructed cw.Emit(Op.PushI4, pars); cw.Emit(Op.Newfun, funHandle); return(ret); }
void Binding(ElaEquationSet block) { var bid = default(ElaEquation); var left = default(ElaExpression); var right = default(ElaExpression); Expr(out left); bid = new ElaEquation(t); if (la.kind == 23 || la.kind == 51 || la.kind == 57) { if (la.kind == 57) { Get(); Expr(out right); } else if (la.kind == 23) { Get(); Guard(out right); } else { Attribute(ref left); } } if (la.kind == 43) { var cb = default(ElaEquationSet); WhereBinding(out cb); if (left != null && left.Type == ElaNodeType.Header) AddError(ElaParserError.InvalidAttributeWhere); var letb = new ElaLetBinding(); if (cb != null) letb.SetLinePragma(cb.Line, cb.Column); letb.Equations = cb; if (right != null) { letb.Expression = right; right = letb; } else { letb.Expression = left; left = letb; } } ProcessBinding(block, bid, left, right); }
private ElaExpression ValidateDoBlock(ElaExpression exp) { if (exp.Type == ElaNodeType.Juxtaposition && ((ElaJuxtaposition)exp).Parameters[0] == null) { var ext = ((ElaJuxtaposition)exp).Parameters[1]; var ctx = default(ElaContext); if (ext.Type == ElaNodeType.Context) { ctx = (ElaContext)ext; ext = ctx.Expression; } var eqt = new ElaJuxtaposition { Spec = true }; eqt.SetLinePragma(exp.Line, exp.Column); eqt.Target = new ElaNameReference(t) { Name = ">>=" }; eqt.Parameters.Add(ext); var jux = new ElaJuxtaposition(); jux.SetLinePragma(exp.Line, exp.Column); jux.Target = new ElaNameReference { Name = "point" }; jux.Parameters.Add(new ElaUnitLiteral()); eqt.Parameters.Add(new ElaLambda { Left = new ElaPlaceholder(), Right = jux }); if (ctx != null) { ctx.Expression = eqt; exp = ctx; } else exp = eqt; } var root = exp; while (true) { if (exp.Type == ElaNodeType.Juxtaposition) { var juxta = (ElaJuxtaposition)exp; if (juxta.Parameters.Count == 2) { juxta.Parameters[0] = Reduce(juxta.Parameters[0], juxta); juxta.Parameters[1] = Reduce(juxta.Parameters[1], juxta); exp = juxta.Parameters[1]; } else break; } else if (exp.Type == ElaNodeType.LetBinding) { var lb = (ElaLetBinding)exp; lb.Expression = Reduce(lb.Expression, lb); exp = lb.Expression; } else if (exp.Type == ElaNodeType.Lambda) { var lb = (ElaLambda)exp; lb.Right = Reduce(lb.Right, lb); if (lb.Left.Type != ElaNodeType.NameReference && lb.Left.Type != ElaNodeType.Placeholder) { var em = new ElaMatch(); em.SetLinePragma(lb.Left.Line, lb.Left.Column); em.Expression = new ElaNameReference { Name = "$x01" }; em.Entries = new ElaEquationSet(); var eq1 = new ElaEquation(); eq1.SetLinePragma(lb.Left.Line, lb.Left.Column); eq1.Left = lb.Left; eq1.Right = lb.Right; em.Entries.Equations.Add(eq1); var eq2 = new ElaEquation(); eq2.SetLinePragma(lb.Left.Line, lb.Left.Column); eq2.Left = new ElaNameReference { Name = "$x02" }; var errExp = new ElaJuxtaposition(); errExp.SetLinePragma(lb.Left.Line, lb.Left.Column); errExp.Target = new ElaNameReference { Name = "failure" }; errExp.Parameters.Add(new ElaNameReference { Name = "$x02" }); eq2.Right = errExp; em.Entries.Equations.Add(eq2); lb.Left = new ElaNameReference { Name = "$x01" }; lb.Right = em; exp = lb; } else exp = lb.Right; } else break; } var ret = new ElaLazyLiteral { Expression = root }; ret.SetLinePragma(root.Line, root.Column); return ret; }
//Used to compile an anonymous function (lambda). This function returns a number of parameters //in compiled lambda. private int CompileLambda(ElaEquation bid) { var fc = new ElaJuxtaposition(); //Lambda is parsed as a an application of a lambda operator, e.g. //expr -> expr, therefore it needs to be transformed in order to be //able to use existing match compilation logic. if (bid.Left.Type == ElaNodeType.Juxtaposition && !bid.Left.Parens) //Parens flag is added if an expression is in parens and //therefore should be qualified as a pattern. { var f = (ElaJuxtaposition)bid.Left; fc.Parameters.Add(f.Target); fc.Parameters.AddRange(f.Parameters); } else fc.Parameters.Add(bid.Left); bid.Left = fc; var parLen = fc.Parameters.Count; StartFun(null, parLen); var funSkipLabel = cw.DefineLabel(); var map = new LabelMap(); cw.Emit(Op.Br, funSkipLabel); //We start a real (VM based) lexical scope for a function. StartScope(true, bid.Right.Line, bid.Right.Column); StartSection(); var address = cw.Offset; CompileFunctionMatch(parLen, bid, map, Hints.Scope | Hints.Tail); var funHandle = frame.Layouts.Count; var ss = EndFun(funHandle); frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address)); EndScope(); EndSection(); cw.Emit(Op.Ret); cw.MarkLabel(funSkipLabel); AddLinePragma(bid); //Function is constructed cw.Emit(Op.PushI4, parLen); cw.Emit(Op.Newfun, funHandle); return parLen; }
//Main method used to compile functions. Can compile regular named functions, //named functions in-place (FunFlag.Inline) and type constructors (FunFlag.Newtype). //Return 'true' if a function is clean (no no-inits references). private bool CompileFunction(ElaEquation dec) { var fc = (ElaJuxtaposition)dec.Left; var pars = fc.Parameters.Count; //Target can be null in a case of an anonymous function (lambda) var name = fc.Target != null ? fc.Target.GetName() : String.Empty; StartFun(name, pars); var funSkipLabel = Label.Empty; var map = new LabelMap(); var startLabel = cw.DefineLabel(); //Functions are always compiled in place, e.g. when met. Therefore a 'goto' //instruction is emitted to skip through function definition. funSkipLabel = cw.DefineLabel(); cw.Emit(Op.Br, funSkipLabel); //FunStart label is needed for tail recursive calls when we emit a 'goto' //instead of an actual function call. map.FunStart = startLabel; //Preserve some information about a function we're currently in. map.FunctionName = name; map.FunctionParameters = pars; map.FunctionScope = CurrentScope; cw.MarkLabel(startLabel); //We start a real (VM based) lexical scope for a function. StartScope(true, dec.Right.Line, dec.Right.Column); //We add a special 'context' variable; it is not initialized AddVariable("context", dec, ElaVariableFlags.Context, -1); //StartSection create a real lexical scope. StartSection(); var hints = Hints.Scope|Hints.Tail; AddLinePragma(dec); var address = cw.Offset; cleans.Push(true); CompileFunctionMatch(pars, dec, map, hints); var ret = cleans.Pop(); //This logic creates a function (by finally emitting Newfun). var funHandle = frame.Layouts.Count; var ss = EndFun(funHandle); frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address)); EndScope(); EndSection(); cw.Emit(Op.Ret); cw.MarkLabel(funSkipLabel); AddLinePragma(dec); //Function is constructed cw.Emit(Op.PushI4, pars); cw.Emit(Op.Newfun, funHandle); return ret; }
//Checks if a given binding is a recursive binding, e.g. contains a reference to itself //in the right side. private bool IsRecursive(ElaEquation eq) { //For a simple case we don't need to construct a name list and can do the //things much easier. if (eq.Left.Type == ElaNodeType.NameReference && !((ElaNameReference)eq.Left).Uppercase) { var sv = GetVariable(eq.Left.GetName(), CurrentScope, GetFlags.NoError, 0, 0); return IsRecursive(eq.Right, sv.Address, null); } else { //Complex case, a binding contains a pattern in the left hand side. We then need to //extract all names declared in this pattern first, and the build a list of all addresses //that can be referenced by right hand side. var names = new List<String>(); ExtractPatternNames(eq.Left, names); var arr = new int[names.Count]; for (var i = 0; i < names.Count; i++) arr[i] = GetVariable(names[i], CurrentScope, GetFlags.NoError, 0, 0).Address; if (IsRecursive(eq.Right, -1, arr)) return true; return false; } }
//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); } }
//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); } }