diff options
author | Miguel de Icaza <miguel@gnome.org> | 2001-08-23 03:32:44 +0400 |
---|---|---|
committer | Miguel de Icaza <miguel@gnome.org> | 2001-08-23 03:32:44 +0400 |
commit | 17228003d38d0727e6e205738a0a63171f443c15 (patch) | |
tree | 9d33ec503507201125582b613ba17728b0731de1 | |
parent | a026988bb83e90ca31865965233e22a2f7e4e6dd (diff) |
2001-08-22 Miguel de Icaza <miguel@ximian.com>
* codegen.cs (EmitCode): New function, will emit the code for a
Block of code given a TypeContainer and its ILGenerator.
* statement.cs (Block): Standard public readonly optimization.
(Block::Block constructors): Link children.
(Block::Child): Child Linker.
(Block::EmitVariables): Emits IL variable declarations.
* class.cs: Drop support for MethodGroups here, delay until
Semantic Analysis.
(Method::): Applied the same simplification that I did before, and
move from Properties to public readonly fields.
(Method::ParameterTypes): Returns the parameter types for the
function, and implements a cache that will be useful later when I
do error checking and the semantic analysis on the methods is
performed.
(Constructor::GetCallingConvention): Renamed from CallingConvetion
and made a method, optional argument tells whether this is a class
or a structure to apply the `has-this' bit.
(Method::GetCallingConvention): Implement, returns the calling
convention.
(Method::Define): Defines the type, a second pass is performed
later to populate the methods.
(Constructor::ParameterTypes): implement a cache similar to the
one on Method::ParameterTypes, useful later when we do semantic
analysis.
(TypeContainer::EmitMethod): New method. Emits methods.
* expression.cs: Removed MethodGroup class from here.
* parameter.cs (Parameters::GetCallingConvention): new method.
svn path=/trunk/mcs/; revision=573
-rwxr-xr-x | mcs/mcs/ChangeLog | 36 | ||||
-rwxr-xr-x | mcs/mcs/cil-codegen.cs | 48 | ||||
-rwxr-xr-x | mcs/mcs/class.cs | 244 | ||||
-rwxr-xr-x | mcs/mcs/codegen.cs | 58 | ||||
-rwxr-xr-x | mcs/mcs/compiler.csproj.user | 4 | ||||
-rwxr-xr-x | mcs/mcs/cs-parser.jay | 2 | ||||
-rwxr-xr-x | mcs/mcs/driver.cs | 3 | ||||
-rwxr-xr-x | mcs/mcs/expression.cs | 34 | ||||
-rwxr-xr-x | mcs/mcs/gen-treedump.cs | 21 | ||||
-rwxr-xr-x | mcs/mcs/makefile | 2 | ||||
-rwxr-xr-x | mcs/mcs/parameter.cs | 8 | ||||
-rwxr-xr-x | mcs/mcs/rootcontext.cs | 17 | ||||
-rwxr-xr-x | mcs/mcs/statement.cs | 148 |
13 files changed, 395 insertions, 230 deletions
diff --git a/mcs/mcs/ChangeLog b/mcs/mcs/ChangeLog index 274e9390b54..c1d44645a25 100755 --- a/mcs/mcs/ChangeLog +++ b/mcs/mcs/ChangeLog @@ -1,3 +1,39 @@ +2001-08-22 Miguel de Icaza <miguel@ximian.com> + + * codegen.cs (EmitCode): New function, will emit the code for a + Block of code given a TypeContainer and its ILGenerator. + + * statement.cs (Block): Standard public readonly optimization. + (Block::Block constructors): Link children. + (Block::Child): Child Linker. + (Block::EmitVariables): Emits IL variable declarations. + + * class.cs: Drop support for MethodGroups here, delay until + Semantic Analysis. + (Method::): Applied the same simplification that I did before, and + move from Properties to public readonly fields. + (Method::ParameterTypes): Returns the parameter types for the + function, and implements a cache that will be useful later when I + do error checking and the semantic analysis on the methods is + performed. + (Constructor::GetCallingConvention): Renamed from CallingConvetion + and made a method, optional argument tells whether this is a class + or a structure to apply the `has-this' bit. + (Method::GetCallingConvention): Implement, returns the calling + convention. + (Method::Define): Defines the type, a second pass is performed + later to populate the methods. + + (Constructor::ParameterTypes): implement a cache similar to the + one on Method::ParameterTypes, useful later when we do semantic + analysis. + + (TypeContainer::EmitMethod): New method. Emits methods. + + * expression.cs: Removed MethodGroup class from here. + + * parameter.cs (Parameters::GetCallingConvention): new method. + 2001-08-21 Miguel de Icaza <miguel@ximian.com> * class.cs (TypeContainer::Populate): Drop RootContext from the diff --git a/mcs/mcs/cil-codegen.cs b/mcs/mcs/cil-codegen.cs deleted file mode 100755 index 5a729be9edf..00000000000 --- a/mcs/mcs/cil-codegen.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// cil-codegen.cs: The CIL code generation interface -// -// Author: -// Miguel de Icaza (miguel@ximian.com) -// -// (C) 2001 Ximian, Inc. -// - -using System; -using System.Reflection; -using System.Reflection.Emit; - -public class CilCodeGen { - AppDomain current_domain; - AssemblyBuilder assembly_builder; - ModuleBuilder module_builder; - - public CilCodeGen (string name, string output) - { - AssemblyName an; - - an = new AssemblyName (); - an.Name = "AssemblyName"; - current_domain = AppDomain.CurrentDomain; - assembly_builder = current_domain.DefineDynamicAssembly ( - an, AssemblyBuilderAccess.RunAndSave); - - module_builder = assembly_builder.DefineDynamicModule (name, output); - } - - public AssemblyBuilder AssemblyBuilder { - get { - return assembly_builder; - } - } - - public ModuleBuilder ModuleBuilder { - get { - return module_builder; - } - } - - public void Save (string name) - { - assembly_builder.Save (name); - } -} diff --git a/mcs/mcs/class.cs b/mcs/mcs/class.cs index 8a40fcbb930..05724bc5146 100755 --- a/mcs/mcs/class.cs +++ b/mcs/mcs/class.cs @@ -46,8 +46,8 @@ namespace CIR { // Holds the list of ArrayList interfaces; - // Holds the method groups. - Hashtable method_groups; + // Holds the methods. + ArrayList methods; // // Pointers to the default constructor and the default static constructor @@ -167,26 +167,15 @@ namespace CIR { string name = method.Name; Object value = defined_names [name]; - if (value != null && (!(value is MethodGroup))) + if (value != null && (!(value is Method))) return AdditionResult.NameExists; - if (method_groups == null) - method_groups = new Hashtable (); + if (methods == null) + methods = new ArrayList (); - MethodGroup mg = (MethodGroup) method_groups [name]; - if (mg == null){ - mg = new MethodGroup (name); + methods.Add (method); + DefineName (name, method); - mg.Add (method); - method_groups.Add (name, mg); - - return AdditionResult.Success; - } - mg.Add (method); - - if (value == null) - DefineName (name, mg); - return AdditionResult.Success; } @@ -296,9 +285,9 @@ namespace CIR { } } - public Hashtable MethodGroups { + public ArrayList Methods { get { - return method_groups; + return methods; } } @@ -463,26 +452,14 @@ namespace CIR { // void EmitConstructor (Constructor c) { - ConstructorBuilder cb; - MethodAttributes ca = (MethodAttributes.RTSpecialName | - MethodAttributes.SpecialName); - bool is_static = (c.ModFlags & Modifiers.STATIC) != 0; - - if (is_static) - ca |= MethodAttributes.Static; - - ca |= Modifiers.MethodAttr (c.ModFlags); - - cb = TypeBuilder.DefineConstructor ( - ca, c.CallingConvention, c.ParameterTypes (this)); - - if (is_static){ + if ((c.ModFlags & Modifiers.STATIC) != 0){ if (initialized_static_fields != null) - EmitStaticFieldInitializers (cb); + EmitStaticFieldInitializers (c.ConstructorBuilder); } else { if (initialized_fields != null) - EmitFieldInitializers (cb); + EmitFieldInitializers (c.ConstructorBuilder); } + } // @@ -532,16 +509,36 @@ namespace CIR { if (Constructors != null){ foreach (Constructor c in Constructors) - EmitConstructor (c); + c.Define (this); } + if (Methods != null){ + foreach (Method m in Methods) + m.Define (this); + } + } + + // + // Emits the code, this step is performed after all + // the types, enumerations, constructors + // + public void Emit () + { if (default_constructor == null) EmitDefaultConstructor (false); if (initialized_static_fields != null && default_static_constructor == null) EmitDefaultConstructor (true); - } + if (Constructors != null) + foreach (Constructor c in Constructors) + c.Emit (); + + if (Methods != null) + foreach (Method m in Methods) + m.Emit (this); + } + public delegate void ExamineType (TypeContainer container, object cback_data); void WalkTypesAt (TypeContainer root, ExamineType visit, object cback_data) @@ -645,19 +642,21 @@ namespace CIR { } public class Method { - Parameters parameters; - string return_type; - string name; - int modifiers; - Block block; - + public readonly Parameters Parameters; + public readonly string ReturnType; + public readonly string Name; + public readonly int ModFlags; + public MethodBuilder MethodBuilder; + + Block block; + // return_type can be "null" for VOID values. public Method (string return_type, int mod, string name, Parameters parameters) { - this.return_type = return_type; - this.name = name; - this.parameters = parameters; - this.modifiers = Modifiers.Check (AllowedModifiers, mod, Modifiers.PRIVATE); + Name = name; + ReturnType = return_type; + Parameters = parameters; + ModFlags = Modifiers.Check (AllowedModifiers, mod, Modifiers.PRIVATE); } // <summary> @@ -686,34 +685,89 @@ namespace CIR { } } - public string Name { - get { - return name; - } + // + // Returns the `System.Type' for the ReturnType of this + // function. Provides a nice cache. (used between semantic analysis + // and actual code generation + // + Type type_return_type; + public Type GetReturnType (TypeContainer parent) + { + if (type_return_type == null) + type_return_type = parent.LookupType (ReturnType, false); + + return type_return_type; } - public int ModFlags { - get { - return modifiers; - } + // + // Returns the System.Type array for the parameters of this method + // + Type [] parameter_types; + public Type [] ParameterTypes (TypeContainer parent) + { + if (Parameters == null) + return null; + + if (parameter_types == null) + parameter_types = Parameters.GetParameterInfo (parent); + + return parameter_types; } - public Parameters Parameters { - get { - return parameters; - } + public CallingConventions GetCallingConvention (bool is_class) + { + CallingConventions cc = 0; + + cc = Parameters.GetCallingConvention (); + + if (is_class) + if ((ModFlags & Modifiers.STATIC) != 0) + cc |= CallingConventions.HasThis; + + return cc; } - public string ReturnType { - get { - return return_type; + // + // Creates the type + // + public void Define (TypeContainer parent) + { + Type ret_type = GetReturnType (parent); + Type [] parameters = ParameterTypes (parent); + + // + // Create the method + // + MethodBuilder = parent.TypeBuilder.DefineMethod ( + Name, Modifiers.MethodAttr (ModFlags), + GetCallingConvention (parent is Class), + ret_type, parameters); + + // + // Define each type attribute (in/out/ref) and + // the argument names. + // + Parameter [] p = Parameters.FixedParameters; + if (p != null){ + int i; + + for (i = 0; i < p.Length; i++) + MethodBuilder.DefineParameter ( + i + 1, p [i].Attributes, p [i].Name); + + if (i != parameters.Length) + Console.WriteLine ("Implement the type definition for params"); } } - public string ArgumentSignature { - get { - return ""; // TYPEFIX: Type.MakeParameterSignature (name, parameters); - } + // + // Emits the code + // + public void Emit (TypeContainer parent) + { + ILGenerator ig = MethodBuilder.GetILGenerator (); + + CodeGen.EmitCode (parent, ig, block); } } @@ -772,6 +826,7 @@ namespace CIR { } public class Constructor { + public ConstructorBuilder ConstructorBuilder; public readonly ConstructorInitializer Initializer; public readonly Parameters Parameters; public readonly string Name; @@ -829,27 +884,60 @@ namespace CIR { } } - public CallingConventions CallingConvention { - get { - CallingConventions cc = 0; - - if (Parameters.ArrayParameter != null) - cc |= CallingConventions.VarArgs; - else - cc |= CallingConventions.Standard; + public CallingConventions GetCallingConvention (bool parent_is_class) + { + CallingConventions cc = 0; + + if (Parameters.ArrayParameter != null) + cc |= CallingConventions.VarArgs; + else + cc |= CallingConventions.Standard; + if (parent_is_class) if ((ModFlags & Modifiers.STATIC) != 0) cc |= CallingConventions.HasThis; // FIXME: How is `ExplicitThis' used in C#? - return cc; - } + return cc; } + // + // Cached representation + /// + Type [] parameter_types; public Type [] ParameterTypes (TypeContainer tc) { - return Parameters.GetParameterInfo (tc); + if (Parameters == null) + return null; + + if (parameter_types == null) + parameter_types = Parameters.GetParameterInfo (tc); + + return parameter_types; + } + + // + // Creates the ConstructorBuilder + // + public void Define (TypeContainer parent) + { + MethodAttributes ca = (MethodAttributes.RTSpecialName | + MethodAttributes.SpecialName); + + if ((ModFlags & Modifiers.STATIC) != 0) + ca |= MethodAttributes.Static; + + ConstructorBuilder = parent.TypeBuilder.DefineConstructor ( + ca, GetCallingConvention (parent is Class), + ParameterTypes (parent)); + } + + // + // Emits the code + // + public void Emit () + { } } diff --git a/mcs/mcs/codegen.cs b/mcs/mcs/codegen.cs new file mode 100755 index 00000000000..1f36bde8c6d --- /dev/null +++ b/mcs/mcs/codegen.cs @@ -0,0 +1,58 @@ +// +// codegen.cs: The code generator +// +// Author: +// Miguel de Icaza (miguel@ximian.com) +// +// (C) 2001 Ximian, Inc. +// + +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace CIR { + + public class CodeGen { + AppDomain current_domain; + AssemblyBuilder assembly_builder; + ModuleBuilder module_builder; + + public CodeGen (string name, string output) + { + AssemblyName an; + + an = new AssemblyName (); + an.Name = "AssemblyName"; + current_domain = AppDomain.CurrentDomain; + assembly_builder = current_domain.DefineDynamicAssembly ( + an, AssemblyBuilderAccess.RunAndSave); + + module_builder = assembly_builder.DefineDynamicModule (name, output); + } + + public AssemblyBuilder AssemblyBuilder { + get { + return assembly_builder; + } + } + + public ModuleBuilder ModuleBuilder { + get { + return module_builder; + } + } + + public void Save (string name) + { + assembly_builder.Save (name); + } + + public static void EmitCode (TypeContainer parent, ILGenerator ig, Block block) + { + block.EmitVariables (parent, ig); + + ig.Emit (OpCodes.Ret); + } + } +} diff --git a/mcs/mcs/compiler.csproj.user b/mcs/mcs/compiler.csproj.user index 1357db4344b..0c76164add8 100755 --- a/mcs/mcs/compiler.csproj.user +++ b/mcs/mcs/compiler.csproj.user @@ -9,11 +9,11 @@ EnableUnmanagedDebugging = "false"
EnableSQLServerDebugging = "false"
StartAction = "Project"
- StartArguments = ""
+ StartArguments = "i-undefined.cs"
StartPage = ""
StartProgram = ""
StartURL = ""
- StartWorkingDirectory = ""
+ StartWorkingDirectory = "c:\mono\mcs\tests"
StartWithIE = "false"
/>
<Config
diff --git a/mcs/mcs/cs-parser.jay b/mcs/mcs/cs-parser.jay index a536c9ec12b..31cfc646fa9 100755 --- a/mcs/mcs/cs-parser.jay +++ b/mcs/mcs/cs-parser.jay @@ -664,7 +664,7 @@ method_header member_name
OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS
{
- Method method = new Method ("void", (int) $2, (string) $4, (Parameters) $6);
+ Method method = new Method ("System.Void", (int) $2, (string) $4, (Parameters) $6);
current_local_parameters = (Parameters) $6;
$$ = method;
diff --git a/mcs/mcs/driver.cs b/mcs/mcs/driver.cs index e06b56007fe..5a9bfc0e72d 100755 --- a/mcs/mcs/driver.cs +++ b/mcs/mcs/driver.cs @@ -286,13 +286,14 @@ namespace CIR if (output_file == null)
output_file = "a.exe";
- context.CodeGen = new CilCodeGen (output_file, output_file);
+ context.CodeGen = new CodeGen (output_file, output_file);
//
// The second pass of the compiler
//
context.ResolveTree ();
context.PopulateTypes ();
+ context.EmitCode ();
if (context.Report.Errors > 0){
error ("Compilation failed");
diff --git a/mcs/mcs/expression.cs b/mcs/mcs/expression.cs index 52e0b17ad83..05f15941b07 100755 --- a/mcs/mcs/expression.cs +++ b/mcs/mcs/expression.cs @@ -457,40 +457,6 @@ namespace CIR { } } } - - public class MethodGroup : Expression { - Hashtable signatures; - string name; - - public MethodGroup (string name) - { - signatures = new Hashtable (); - this.name = name; - } - - public bool Add (Method method) - { - string sig = method.ArgumentSignature; - - if (signatures.Contains (sig)) - return false; - - signatures.Add (sig, method); - return true; - } - - public string Name { - get { - return name; - } - } - - public Hashtable Methods { - get { - return signatures; - } - } - } } diff --git a/mcs/mcs/gen-treedump.cs b/mcs/mcs/gen-treedump.cs index 9121a2322d9..5f0a38f290c 100755 --- a/mcs/mcs/gen-treedump.cs +++ b/mcs/mcs/gen-treedump.cs @@ -631,9 +631,11 @@ namespace Generator { if (b.Variables != null){ foreach (DictionaryEntry entry in b.Variables){ + VariableInfo vi = (VariableInfo) entry.Value; + space (); output_newline ( - (string) entry.Value + " " + + vi.Type + " " + (string) entry.Key + ";"); } newline (); @@ -664,15 +666,6 @@ namespace Generator { newline (); } - void GenerateMethodGroup (MethodGroup mg) - { - foreach (DictionaryEntry de in mg.Methods){ - Method m = (Method) de.Value; - - GenerateMethod (m); - } - } - void GenerateInterfaceMethod (InterfaceMethod imethod) { space (); @@ -883,11 +876,9 @@ namespace Generator { GenerateFromTypes (tc); - if (tc.MethodGroups != null){ - foreach (DictionaryEntry de in tc.MethodGroups){ - MethodGroup mg = (MethodGroup) de.Value; - - GenerateMethodGroup (mg); + if (tc.Methods != null){ + foreach (Method m in tc.Methods){ + GenerateMethod (m); } } } diff --git a/mcs/mcs/makefile b/mcs/mcs/makefile index 6c8c246e2c5..0694b75a3fb 100755 --- a/mcs/mcs/makefile +++ b/mcs/mcs/makefile @@ -10,7 +10,7 @@ COMPILER_SOURCES = \ assign.cs \ driver.cs $(COMMON_SOURCES) \ class.cs \ - cil-codegen.cs \ + codegen.cs \ constant.cs \ decl.cs \ enum.cs \ diff --git a/mcs/mcs/parameter.cs b/mcs/mcs/parameter.cs index 2c3a6b2b1ae..8e3d1811825 100755 --- a/mcs/mcs/parameter.cs +++ b/mcs/mcs/parameter.cs @@ -166,6 +166,14 @@ namespace CIR { return types; } + + public CallingConventions GetCallingConvention () + { + if (ArrayParameter != null) + return CallingConventions.VarArgs; + else + return CallingConventions.Standard; + } } } diff --git a/mcs/mcs/rootcontext.cs b/mcs/mcs/rootcontext.cs index 6174555e386..0fa5a5a4117 100755 --- a/mcs/mcs/rootcontext.cs +++ b/mcs/mcs/rootcontext.cs @@ -30,7 +30,7 @@ namespace CIR { // // The System.Reflection.Emit CodeGenerator // - CilCodeGen cg; + CodeGen cg; // // The module builder pointer @@ -78,7 +78,7 @@ namespace CIR { } } - public CilCodeGen CodeGen { + public CodeGen CodeGen { get { return cg; } @@ -583,6 +583,19 @@ namespace CIR { } } + public void EmitCode () + { + Hashtable classes; + + if ((classes = tree.Classes) != null){ + foreach (DictionaryEntry de in classes){ + TypeContainer tc = (TypeContainer) de.Value; + + tc.Emit (); + } + } + } + // <summary> // Compiling against Standard Libraries property. // </summary> diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs index d6d5842f931..d8a5bbd6df0 100755 --- a/mcs/mcs/statement.cs +++ b/mcs/mcs/statement.cs @@ -8,6 +8,8 @@ // using System; +using System.Reflection; +using System.Reflection.Emit; namespace CIR { @@ -213,58 +215,91 @@ namespace CIR { } } + public struct VariableInfo { + public readonly string Type; + public LocalBuilder LocalBuilder ; + + public VariableInfo (string type) + { + Type = type; + LocalBuilder = null; + } + } + // <summary> // Used for Label management // </summary> // public class Block : Statement { - Block parent; + public readonly Block Parent; + public readonly bool Implicit; + public readonly string Label; + + // + // The statements in this block + // StatementCollection statements; + + // + // Labels. (label, block) pairs. + // Hashtable labels; - Hashtable labels_referenced; - bool implicit_block; + + // + // Keeps track of (name, type) pairs + // Hashtable variables; - string label; - int internal_id_serial; - bool used = false; + + // + // Maps variable names to ILGenerator.LocalBuilders + // + Hashtable local_builders; + + // + // If we have a child, here it is + // + Block child; + bool used = false; + public Block (Block parent) { - this.parent = parent; - this.implicit_block = false; + if (parent != null) + parent.Child = this; + + this.Parent = parent; + this.Implicit = false; } public Block (Block parent, bool implicit_block) { - this.parent = parent; - this.implicit_block = true; + if (parent != null) + parent.Child = this; + + this.Parent = parent; + this.Implicit = true; } public Block (Block parent, string labeled) { - this.parent = parent; - this.implicit_block = true; - label = labeled; + if (parent != null) + parent.Child = this; + + this.Parent = parent; + this.Implicit = true; + Label = labeled; } - public Block Parent { + public Block Child { get { - return parent; + return child; } - } - public bool Implicit { - get { - return implicit_block; + set { + child = value; } } - public string Label { - get { - return label; - } - } - // <summary> // Adds a label to the current block. // </summary> @@ -292,8 +327,10 @@ namespace CIR { if (GetVariableType (name) != null) return false; + + VariableInfo vi = new VariableInfo (type); - variables.Add (name, type); + variables.Add (name, vi); return true; } @@ -311,8 +348,8 @@ namespace CIR { type = (string) variables [name]; if (type != null) return type; - else if (parent != null) - return parent.GetVariableType (name); + else if (Parent != null) + return Parent.GetVariableType (name); return null; } @@ -334,31 +371,13 @@ namespace CIR { } } - // <summary> - // Call this to label the label as used in a block - // </summary> - public void Reference (string name) - { - if (labels_referenced == null) - labels_referenced = new Hashtable (); - labels_referenced.Add (name, null); - } - // <returns> // A list of labels that were not used within this block // </returns> public string [] GetUnreferenced () { - ArrayList unrefs = new ArrayList (); - - foreach (string s in (string []) labels.Keys) { - if (labels_referenced [s] == null) - unrefs.Add (s); - } - - string [] result = new string [unrefs.Count]; - unrefs.CopyTo (result); - return result; + // FIXME: Implement me + return null; } public StatementCollection Statements { @@ -390,12 +409,45 @@ namespace CIR { // used to create temporary variables that should not // be seen by the application // </summary + int internal_id_serial; public string MakeInternalID () { string ret = internal_id_serial.ToString (); internal_id_serial++; return "0_" + ret; } + + // <summary> + // Emits the variable declarations + // </summary> + + public void EmitVariables (TypeContainer tc, ILGenerator ig) + { + // + // Process this block variables + // + if (variables != null){ + local_builders = new Hashtable (); + + foreach (DictionaryEntry de in variables){ + string name = (string) de.Key; + VariableInfo vi = (VariableInfo) de.Value; + Type t; + + t = tc.LookupType (vi.Type, false); + if (t == null) + continue; + + vi.LocalBuilder = ig.DeclareLocal (t); + } + } + + // + // Now, handle the children + // + if (Child != null) + Child.EmitVariables (tc, ig); + } } public class SwitchLabel { |