From a514ceaa6a1b234c8d714439dc249fdbc144d13a Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Mon, 18 Dec 2023 00:34:53 +0100 Subject: [PATCH 1/4] Added a draft on programmatic code manipulation --- General/ProgrammaticCodeManipulation.md | 75 +++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 General/ProgrammaticCodeManipulation.md diff --git a/General/ProgrammaticCodeManipulation.md b/General/ProgrammaticCodeManipulation.md new file mode 100644 index 0000000..a48749f --- /dev/null +++ b/General/ProgrammaticCodeManipulation.md @@ -0,0 +1,75 @@ +# Programmatic Code Manipulation + +In this article, we will present some code snippets that demonstrate how we can manipulate the source code (packages, classes, methods, variables, traits, etc.) programmatically, without using the SystemBrowser. Those techniques can be very useful for reflective operations, code generation, testing, etc. + +This article explains only those operations that **modify** the code. We will write a separate article on **querrying code** (looking for method senders, class references, pragma usages, etc.). + +## Packages + +### Creating a new package + +### Removing a package + +### Creating a package without installing it + +_(how to create an instance of a package without installing it into the image? - e.g., for testing)_ + +## Classes + +### Creating a new class + +To create a new class, you can use the same code that appears in SystemBrowser. Here is an example with **Fluid** class definition. + +```st +builder := Object << #MyClass slots: { }; package: 'MyPackage'. class := builder install. +``` + +The first statement will return an instance of `FluidClassBuilder`. The second statement will build the class, install it into the image, and assign it to the variable. If `MyPackage` does not exist, it will be created. + +### Creating an anonymous class + +### Removing a class + +## Methods + +### Adding a method to a class + +To add a new method to a class, you can simply write its source code (including method name and arguments) in a string and send it as argument of a `compile:` method of your class. Here is an example of adding the method `printOn:` to `MyClass` (this is a silly implementation that will print a class name followed by a random number). + +```st +sourceCode := 'printOn: aStream "Print the object of this class" aStream nextPutAll: self class name; space; nextPutAll: (Random new nextInteger: 100) asString'. + +MyClass compile: sourceCode. +``` + +You can also specify a protocol: + +```st +MyClass compile: sourceCode classified: 'printing'. +``` + +Finally, to avoid the error when generating a code in a fresh image, you should specify the author name. It can be anything, so in this case, we use `'Dummy Name'` + +```st +Author + useAuthor: 'Dummy Name' + during: [ MyClass compile: sourceCode ]. +``` + +### Removing a method from a class + +## Variables + +## Traits + +### Adding trait to a class + +### Removing trait from a class + +### Creating a new trait + +### Adding methods to a trait + +## Useful links + +- A toolkit to generate Pharo code: \ No newline at end of file From fac2407063601bb5985b858e59ab3deeb9232bfe Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Tue, 19 Dec 2023 10:30:58 +0100 Subject: [PATCH 2/4] Added more examples for local class creation --- General/ProgrammaticCodeManipulation.md | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/General/ProgrammaticCodeManipulation.md b/General/ProgrammaticCodeManipulation.md index a48749f..5d7f9ba 100644 --- a/General/ProgrammaticCodeManipulation.md +++ b/General/ProgrammaticCodeManipulation.md @@ -26,6 +26,41 @@ builder := Object << #MyClass slots: { }; package: 'MyPackage'. class := bui The first statement will return an instance of `FluidClassBuilder`. The second statement will build the class, install it into the image, and assign it to the variable. If `MyPackage` does not exist, it will be created. +### Creating a new class without installing it + +Here is an example of how you can create a new class without installing it into the system. You can do that by sending `build` message instead of `install` as in the previous example. + +```st +builder := OrderedCollection << #MyCollection. myCollectionClass := builder build. myCollectionClass compile: 'generate: aNumber "Generate aNumber random integers" | rand | rand := Random new. aNumber timesRepeat: [ self add: (rand nextInteger: 100) ]'. myCollection := myCollectionClass new. myCollection generate: 4. "a MyCollection(60 5 40 11)" +``` + +This way, we can create two classes that have a same name but do different things. In the example below, we create two classes named `Dog`. One class defines method `bark` to return the string `'Woof, woof!'` (English barking), the other one - `'Ouaf, ouaf !'` (French barking). As you can see below, the instances of those classes do different things. The classes are different but the class names are the same. + +```st +dogClass1 := (Object << #Dog) build. dogClass2 := (Object << #Dog) build. dogClass1 compile: 'bark "Bark in English" ^ ''Woof, woof!'''. dogClass2 compile: 'bark "Bark in French" ^ ''Ouaf, ouaf !'''. dog1 := dogClass1 new. dog2 := dogClass2 new. dog1 bark. "'Woof, woof!'" dog2 bark. "'Ouaf, ouaf !'" + +dog1 class = dog2 class. "false" dog1 class name = dog2 class name. "true" +``` + +**Be careful!** In Pharo 11, if the class named `Dog` already exists in the system, your new classes will also have the methods that the system class defines. However, the new methods will override the system ones in your new class. + +For example, if there already was a class Dog in your image defined as follows: + +```st +Object << #Dog slots: {}; package: 'MyPackage' + +Dog >> printOn: aStream aStream nextPutAll: 'I am a dog!'. + +Dog >> bark "Bark in Ukrainian" ^ 'Гав, гав!' +``` + +You can still define the local class as in the example above. In this case, your new class will understand the method `printOn:` from the system class and will define a new method `bark`: + +```st +dog1. "I am a dog" +dog1 bark. "Woof, woof!" +``` + ### Creating an anonymous class ### Removing a class From b1453c5adce2a3d7622e3fea357175b1863c1119 Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Tue, 19 Dec 2023 13:26:11 +0100 Subject: [PATCH 3/4] Added instructions on how to remove packages, classes, and methods --- General/ProgrammaticCodeManipulation.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/General/ProgrammaticCodeManipulation.md b/General/ProgrammaticCodeManipulation.md index 5d7f9ba..2fbb4ea 100644 --- a/General/ProgrammaticCodeManipulation.md +++ b/General/ProgrammaticCodeManipulation.md @@ -1,4 +1,4 @@ -# Programmatic Code Manipulation +# Programmatic Code Manipulation (Pharo 11) In this article, we will present some code snippets that demonstrate how we can manipulate the source code (packages, classes, methods, variables, traits, etc.) programmatically, without using the SystemBrowser. Those techniques can be very useful for reflective operations, code generation, testing, etc. @@ -10,6 +10,12 @@ This article explains only those operations that **modify** the code. We will wr ### Removing a package +```st +package removeFromSystem. +``` + +**Be careful!** In Pharo 11 and earlier, this will not properly remove classes from the system. So make sure to remove all classes before you remove the package. + ### Creating a package without installing it _(how to create an instance of a package without installing it into the image? - e.g., for testing)_ @@ -65,6 +71,12 @@ dog1 bark. "Woof, woof!" ### Removing a class +This will also remove all methods of the class. + +```st +class removeFromSystem. +``` + ## Methods ### Adding a method to a class @@ -93,6 +105,10 @@ Author ### Removing a method from a class +```st +method removeFromSystem. +``` + ## Variables ## Traits From e76fab850b35959f460f13805bd02b16f0e6ceb2 Mon Sep 17 00:00:00 2001 From: Oleksandr Zaitsev Date: Tue, 19 Dec 2023 13:32:05 +0100 Subject: [PATCH 4/4] Added instruction on how to create a new package --- General/ProgrammaticCodeManipulation.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/General/ProgrammaticCodeManipulation.md b/General/ProgrammaticCodeManipulation.md index 2fbb4ea..2124989 100644 --- a/General/ProgrammaticCodeManipulation.md +++ b/General/ProgrammaticCodeManipulation.md @@ -8,6 +8,16 @@ This article explains only those operations that **modify** the code. We will wr ### Creating a new package +```st +package := self packageOrganizer ensurePackage: 'PackageName'. +``` + +In Playground, you can use any object or class instead of `self`: + +```st +package := Object packageOrganizer ensurePackage: 'PackageName'. +``` + ### Removing a package ```st