From: <jfa...@us...> - 2010-04-24 22:08:23
|
Revision: 5816 http://oorexx.svn.sourceforge.net/oorexx/?rev=5816&view=rev Author: jfaucher Date: 2010-04-24 22:08:17 +0000 (Sat, 24 Apr 2010) Log Message: ----------- Unlock the "define" method to let it work on predefined classes. Allow behavior's update of objects instantiated before the "define" execution. See samples/functional for an example of use (functionality similar to C# extension methods) Modified Paths: -------------- sandbox/jlf/_diary.txt sandbox/jlf/trunk/interpreter/classes/ClassClass.cpp Added Paths: ----------- sandbox/jlf/samples/ sandbox/jlf/samples/functional/ sandbox/jlf/samples/functional/functional-test.rex sandbox/jlf/samples/functional/functional-v1.rex sandbox/jlf/samples/functional/functional-v2.rex Modified: sandbox/jlf/_diary.txt =================================================================== --- sandbox/jlf/_diary.txt 2010-04-24 21:33:53 UTC (rev 5815) +++ sandbox/jlf/_diary.txt 2010-04-24 22:08:17 UTC (rev 5816) @@ -4,6 +4,7 @@ --> added some OutputDebugString (Windows only), now must analyze... Allow extension of the predefined ooRexx classes to get something similar to C# extension methods (but more powerful). +--> done by unlocking the define method : see samples/functional for an example of use. Add the "in" keyword to the DO repetitor : Similar to "over" but calls the method "supplier" instead of "makearray". See if a thread could return more than one result and become a generator when synchronized with : do i in generator ... Added: sandbox/jlf/samples/functional/functional-test.rex =================================================================== --- sandbox/jlf/samples/functional/functional-test.rex (rev 0) +++ sandbox/jlf/samples/functional/functional-test.rex 2010-04-24 22:08:17 UTC (rev 5816) @@ -0,0 +1,141 @@ +/* +This script needs a modified ooRexx interpreter which allows to extend predefined ooRexx classes. +Something similar to C# extension methods : +http://msdn.microsoft.com/en-us/library/bb383977.aspx +http://weblogs.asp.net/scottgu/archive/2007/03/13/new-orcas-language-feature-extension-methods.aspx +*/ + +.Object~define("dump", .methods~ObjectDump) +.Collection~define("dump", .methods~CollectionDump) + +say "-----------------------------------------------------------------" +say "-- Message sending" +say "-----------------------------------------------------------------" + +-- When the source contains a single word, it's a message name +.Array~of(1,2,3,4)~reduce("+")~dump -- sum = 10 +.Array~of(1,2,3,4)~reduce("*")~dump -- product = 24 + +.List~of(1,2,3,4)~reduce("+")~dump -- sum = 10 +.List~of(1,2,3,4)~reduce("*")~dump -- product = 24 + +.Queue~of(1,2,3,4)~reduce("+")~dump -- sum = 10 +.Queue~of(1,2,3,4)~reduce("*")~dump -- product = 24 + +.CircularQueue~of(1,2,3,4)~reduce("+")~dump -- sum = 10 +.CircularQueue~of(1,2,3,4)~reduce("*")~dump -- product = 24 + +123~reduce("min")~dump -- min digit = 1 +123~reduce("max")~dump -- max digit = 3 + +.Array~of(1,2,3,4)~map("-")~dump -- an Array : -1, -2, -3, -4 +.List~of(1,2,3,4)~map("-")~dump -- a List : -1, -2, -3, -4 +.Queue~of(1,2,3,4)~map("-")~dump -- a Queue : -1, -2, -3, -4 +.CircularQueue~of(1,2,3,4)~map("-")~dump -- -1,-2,-3,-4 : -1, -2, -3, -4 (strange : The dump of a circular queue does not show its class, it shows its contents...) + +-- In place mapping +numbers = .Array~of(-1,2,-3,4) +numbers~dump -- an Array : -1, 2, -3, 4 +numbers~map("sign", .true)~dump -- an Array : -1, 1, -1, 1 +numbers~dump -- an Array : -1, 1, -1, 1 + + +say "-----------------------------------------------------------------" +say "-- Routine calling" +say "-----------------------------------------------------------------" + +-- A source with more than one word is a routine source by default +.Array~of(1,2,3,4)~map("return arg(1) * 2")~dump -- an Array : 2, 4, 6, 8 +.Array~of(1,2,3,4)~map("use arg n ; if n == 0 then return 1 ; return n * .context~executable~call(n - 1)")~dump -- an Array : 1, 2, 6, 24 + +.List~of(1,2,3,4)~map("return arg(1) * 2")~dump -- a List : 2, 4, 6, 8 +.List~of(1,2,3,4)~map("use arg n ; if n == 0 then return 1 ; return n * .context~executable~call(n - 1)")~dump -- a List : 1, 2, 6, 24 + +.Queue~of(1,2,3,4)~map("return arg(1) * 2")~dump -- a Queue : 2, 4, 6, 8 +.Queue~of(1,2,3,4)~map("use arg n ; if n == 0 then return 1 ; return n * .context~executable~call(n - 1)")~dump -- a Queue : 1, 2, 6, 24 + +.CircularQueue~of(1,2,3,4)~map("return arg(1) * 2")~dump -- 2,4,6,8 : 2, 4, 6, 8 (strange : the class is not displayed) +.CircularQueue~of(1,2,3,4)~map("use arg n ; if n == 0 then return 1 ; return n * .context~executable~call(n - 1)")~dump -- 1,2,6,24 : 1, 2, 6, 24 + +-- A source can be tagged explicitely as a routine (but you don't need that, because it's the default) +.Array~of(1,2,3,4)~map("::routine return arg(1) * 2")~dump -- an Array : 2, 4, 6, 8 +.Array~of(1,2,3,4)~map("::routine use arg n ; if n == 0 then return 1 ; return n * .context~executable~call(n - 1)")~dump -- an Array : 1, 2, 6, 24 + +-- A routine object can be used directly +.Array~of(1,2,3,4)~map(.context~package~findRoutine("factorial"))~dump -- an Array : 1, 2, 6, 24 + + +say "-----------------------------------------------------------------" +say "-- Method running" +say "-----------------------------------------------------------------" + +colors = .Array~of( , + .Color~new("black", "000000") ,, + .Color~new("blue", "0000FF") ,, + .Color~new("green", "008000") ,, + .Color~new("grey", "BEBEBE") , + ) + +-- A source can be tagged explicitely as a method (you need that, because it's a routine by default) +colors~map('::method return self~rgbInteger "("self~redIntensity", "self~greenIntensity", "self~blueIntensity")"')~dump -- an Array : 0 (0, 0, 0), 255 (0, 0, 255), 32768 (0, 128, 0), 12500670 (190, 190, 190) + +-- A method object can be used directly +-- No need to define the method on the receiver class +colors~map(.methods~entry("decimalColor"))~dump -- an Array : 0 (0, 0, 0), 255 (0, 0, 255), 32768 (0, 128, 0), 12500670 (190, 190, 190) + + +----------------------------------------------------------------- +-- Definitions +----------------------------------------------------------------- + +::method ObjectDump + use strict arg stream = .stdout + stream~lineout(self) + +::method CollectionDump + use strict arg stream = .stdout + stream~charout(self ": ") + previous = .false + do e over self + if previous then stream~charout(", ") + stream~charout(e) + previous = .true + end + stream~lineout("") + + +::routine factorial + use strict arg n + if n == 0 then return 1 + return n * factorial(n - 1) + + +::method factorial + if self == 0 then return 1 + return self * (self - 1)~factorial + + +::method decimalColor + return self~rgbInteger "("self~redIntensity", "self~greenIntensity", "self~blueIntensity")" + + +::class Color +::attribute name +::attribute rgb +::attribute rgbInteger private +::method init + use strict arg name, rgb + self~name = name + self~rgb = rgb + self~rgbInteger = rgb~x2d +::method redIntensity + return self~rgb~substr(1,2)~x2d +::method greenIntensity + return self~rgb~substr(3,2)~x2d +::method blueIntensity + return self~rgb~substr(5,2)~x2d + + +--::requires "functional-v1.rex" +::requires "functional-v2.rex" + Added: sandbox/jlf/samples/functional/functional-v1.rex =================================================================== --- sandbox/jlf/samples/functional/functional-v1.rex (rev 0) +++ sandbox/jlf/samples/functional/functional-v1.rex 2010-04-24 22:08:17 UTC (rev 5816) @@ -0,0 +1,125 @@ +/* +This script needs a modified ooRexx interpreter which allows to extend predefined ooRexx classes. +Something similar to C# extension methods : +http://msdn.microsoft.com/en-us/library/bb383977.aspx +http://weblogs.asp.net/scottgu/archive/2007/03/13/new-orcas-language-feature-extension-methods.aspx + +Initial version v1 : +Use an intermediate class for Routine and Method : RoutineCaller and MethodRunner +See version v2 where Routine and Method are directly doers : RoutineCaller and MethodRunner no longer needed +*/ + + +----------------------------------------------------------------------------- +-- Extends the behavior of predefined classes + +-- A Doer is an object who knows how to execute itself (understands "do") +-- These methods returns a Doer object. +.Routine~define("doer", "return .RoutineCaller~new(self)") +.Method~define("doer", "return .MethodRunner~new(self)") +.String~define("doer", .methods~StringDoer) + +-- Higher-order action "reduce" +.String~define("reduce", .methods~StringReduce) +.Collection~define("reduce", .methods~CollectionReduce) + +-- Higher-order action "map" +-- Can be defined on any collection which supports "first" and "next" +.Array~define("map", .methods~Map) +.List~define("map", .methods~Map) +.Queue~define("map", .methods~Map) +.CircularQueue~define("map", .methods~Map) + + +----------------------------------------------------------------------------- +-- Doer factories + +::method StringDoer + parse var self word1 rest + -- When the source string contains a single word, it's a message name + if rest == "" then return .MessageNameSender~new(self) + if word1~caselessEquals("::method") then do + method = .Method~new("", rest) + return .MethodRunner~new(method) + end + if word1~caselessEquals("::routine") then do + routine = .Routine~new("", rest) + return .RoutineCaller~new(routine) + end + -- Routine by default + routine = .Routine~new("", self) + return .RoutineCaller~new(routine) + + +----------------------------------------------------------------------------- +-- Higher-order actions + +::method CollectionReduce + use strict arg action + doer = action~doer -- parse only once, before iteration + supplier = self~supplier + if \ supplier~available then return .nil + r = supplier~item + supplier~next + do while supplier~available + r = doer~do(r, supplier~item) + supplier~next + end + return r + + +::method StringReduce + use strict arg action + return self~makearray("")~reduce(action) + + +-- Will work with Array, List, Queue, CircularQueue (any collection which supports "first" and "next") +-- I don't use a supplier because it works on a snapshot of the collection and is not done for updating the collection +-- (when inplace == .true the collection is updated in place) +::method Map + use strict arg action, inplace=.false + doer = action~doer -- parse only once, before iteration + r = self + if \inplace then r = self~copy + current = self~first + do while current <> .nil + r[current] = doer~do(self[current]) + current = self~next(current) + end + return r + + +----------------------------------------------------------------------------- +-- Doer classes + +-- Doer for calling a routine +::class RoutineCaller +::attribute routine +::method init + use strict arg routine + self~routine = routine +::method do + return self~routine~callWith(arg(1,"a")) + + +-- Doer for running a method +::class MethodRunner +::attribute method +::method init + use strict arg method + self~method = method +::method do + use strict arg object, ... + return object~run(self~method, "a", arg(2,"a")) + + +-- Doer for sending a message name +::class MessageNameSender +::attribute messageName +::method init + use strict arg messageName + self~messageName = messageName +::method do + use strict arg object, ... + return object~sendWith(self~messageName, arg(2,"a")) + Added: sandbox/jlf/samples/functional/functional-v2.rex =================================================================== --- sandbox/jlf/samples/functional/functional-v2.rex (rev 0) +++ sandbox/jlf/samples/functional/functional-v2.rex 2010-04-24 22:08:17 UTC (rev 5816) @@ -0,0 +1,100 @@ +/* +This script needs a modified ooRexx interpreter which allows to extend predefined ooRexx classes. +Something similar to C# extension methods : +http://msdn.microsoft.com/en-us/library/bb383977.aspx +http://weblogs.asp.net/scottgu/archive/2007/03/13/new-orcas-language-feature-extension-methods.aspx + +Second version v2 : +Routine and Method are directly doers : RoutineCaller and MethodRunner no longer needed +*/ + + +----------------------------------------------------------------------------- +-- Extends the behavior of predefined classes + +-- A Doer is an object who knows how to execute itself (understands "do") +.Routine~define("do", 'return self~callWith(arg(1,"a"))') +.Method~define("do", 'use strict arg object, ... ; return object~run(self, "a", arg(2,"a"))') +-- Note : a Message can't be a Doer directly, must use an intermediate class MessageNameSender + +-- These methods returns a Doer object. +.Routine~define("doer", "return self") +.Method~define("doer", "return self") +.String~define("doer", .methods~StringDoer) + +-- Higher-order action "reduce" +.String~define("reduce", .methods~StringReduce) +.Collection~define("reduce", .methods~CollectionReduce) + +-- Higher-order action "map" +-- Can be defined on any collection which supports "first" and "next" +.Array~define("map", .methods~Map) +.List~define("map", .methods~Map) +.Queue~define("map", .methods~Map) +.CircularQueue~define("map", .methods~Map) + + +----------------------------------------------------------------------------- +-- Doer factories + +::method StringDoer + parse var self word1 rest + -- When the source string contains a single word, it's a message name + if rest == "" then return .MessageNameSender~new(self) + if word1~caselessEquals("::method") then return .Method~new("", rest) + if word1~caselessEquals("::routine") then return .Routine~new("", rest) + -- Routine by default + return .Routine~new("", self) + + +----------------------------------------------------------------------------- +-- Higher-order actions + +::method CollectionReduce + use strict arg action + doer = action~doer -- parse only once, before iteration + supplier = self~supplier + if \ supplier~available then return .nil + r = supplier~item + supplier~next + do while supplier~available + r = doer~do(r, supplier~item) + supplier~next + end + return r + + +::method StringReduce + use strict arg action + return self~makearray("")~reduce(action) + + +-- Will work with Array, List, Queue, CircularQueue (any collection which supports "first" and "next") +-- I don't use a supplier because it works on a snapshot of the collection and is not done for updating the collection +-- (when inplace == .true the collection is updated in place) +::method Map + use strict arg action, inplace=.false + doer = action~doer -- parse only once, before iteration + r = self + if \inplace then r = self~copy + current = self~first + do while current <> .nil + r[current] = doer~do(self[current]) + current = self~next(current) + end + return r + + +----------------------------------------------------------------------------- +-- Doer classes + +-- Doer for sending a message name +::class MessageNameSender +::attribute messageName +::method init + use strict arg messageName + self~messageName = messageName +::method do + use strict arg object, ... + return object~sendWith(self~messageName, arg(2,"a")) + Modified: sandbox/jlf/trunk/interpreter/classes/ClassClass.cpp =================================================================== --- sandbox/jlf/trunk/interpreter/classes/ClassClass.cpp 2010-04-24 21:33:53 UTC (rev 5815) +++ sandbox/jlf/trunk/interpreter/classes/ClassClass.cpp 2010-04-24 22:08:17 UTC (rev 5816) @@ -622,12 +622,14 @@ /* Function: Define an instance method on this class object */ /*****************************************************************************/ { +#if 0 /* check if this is a rexx class */ if ( this->isRexxDefined()) { /* report as a nomethod condition */ reportNomethod(lastMessageName(), this); } +#endif /* make sure there is at least one */ /* parameter */ method_name = stringArgument(method_name, ARG_ONE)->upper(); @@ -663,7 +665,7 @@ /* make a copy of the instance */ /* behaviour so any previous objects */ /* aren't enhanced */ - OrefSet(this, this->instanceBehaviour, (RexxBehaviour *)this->instanceBehaviour->copy()); + //JLF OrefSet(this, this->instanceBehaviour, (RexxBehaviour *)this->instanceBehaviour->copy()); /* add method to the instance mdict */ this->instanceMethodDictionary->stringPut((RexxObject *)method_object, method_name); /* any subclasses that we have need */ @@ -767,7 +769,7 @@ /* make a copy of the instance */ /* behaviour so any previous objects */ /* aren't enhanced */ - OrefSet(this, this->instanceBehaviour, (RexxBehaviour *)this->instanceBehaviour->copy()); + //JLF OrefSet(this, this->instanceBehaviour, (RexxBehaviour *)this->instanceBehaviour->copy()); /* if there is a method to remove */ /* from the instance mdict */ /* remove it */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |