Skip to content

Commit 47a3780

Browse files
[CBuilder] Eindeutige Namen für Methoden hinter der API gekapselt. (#105)
1 parent 9ba7e1b commit 47a3780

5 files changed

Lines changed: 70 additions & 55 deletions

File tree

docs/usage_cbuilder.md

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,9 @@ def func1(x):
138138
print(func1(a))
139139
```
140140

141-
Eine Funktion wird über ein Objekt der Klasse `Function` erzeugt. Der Konstruktor hat die
142-
beiden Parameter `funcName` und `uniqueName`, wobei `funcName` der Name der Funktion wie im
143-
Python-Code ist und zum Auflösen über Referenzen innerhalb des Scopes dient. Der Parameter
144-
`uniqueName` ist ein eindeutiger Name für die Funktion und muss identisch zu `funcName`
145-
sein.
141+
Eine Funktion wird über ein Objekt der Klasse `Function` erzeugt. Der Konstruktor hat einen
142+
Parameter `funcName`, der der Name der Funktion wie im Python-Code ist und der zum Auflösen
143+
über Referenzen innerhalb des Scopes dient.
146144

147145
Der Body einer Funktion wird als Liste von Objekten vom Typ `Statement` repräsentiert und im
148146
Konstruktor über den Parameter `body` übergeben.
@@ -172,8 +170,8 @@ List<Argument> parameterArguments = List.of(new Argument[] {new Argument("x", 0)
172170
List<VariableDeclaration> localVariables = List.of(new VariableDeclaration[] {localVarYDecl}); // Liste der lokalen Variablen
173171

174172
// Function erstellen
175-
Function func1 = new Function("func1", "func1", body, parameterArguments, localVariables); // Funktion erzeugen
176-
builder.addFunction(func1); // Function dem CBuilder übergeben
173+
Function func1 = new Function("func1", body, parameterArguments, localVariables); // Funktion erzeugen
174+
builder.addFunction(func1); // Function dem CBuilder übergeben
177175
```
178176

179177
``` java
@@ -326,28 +324,29 @@ Beim Methodenaufruf darf `self` aber nicht in der Parameterliste vorkommen. (Sta
326324
Methoden, die nicht umgesetzt werden müssen, haben kein `self`.)
327325

328326
Methoden haben zwei Methodennamen. Der Parameter `funcName` im `Function`-Konstruktor
329-
entspricht dem Funktionsnamen im Python-Code. Über diesen Namen werden die Methoden im
330-
jeweiligen Scope aufgelöst. Da Methoden vom CBuilder als normale C-Funktion im globalen
331-
Scope angelegt werden, müssen sie noch einen im gesamten Programm eindeutigen (internen)
332-
Namen bekommen, der über den Parameter `uniqueName` im `Function`-Konstruktor festgelegt
333-
wird. (Dennoch können Methoden nicht als normale Funktionen aufgerufen werden, sondern immer
334-
nur über den Kontext ihrer Klasse.) Beim Überschreiben von Methoden muss der `funcName`
335-
entsprechend identisch sein (und `uniqueName` muss variieren).
327+
entspricht dem Funktions-/Methodennamen im Python-Code. Über diesen Namen werden die
328+
Methoden im jeweiligen Scope aufgelöst. Da Methoden vom CBuilder als normale C-Funktion im
329+
globalen Scope angelegt werden, müssen sie noch einen im gesamten Programm eindeutigen
330+
(internen) Namen bekommen, der mit der Methode `Function#createUniqueCName()` festgelegt
331+
wird - diese Methode wird automatisch vom Konstruktor von `MPyClass` aufgerufen. (Dennoch
332+
können Methoden nicht als normale Funktionen aufgerufen werden, sondern immer nur über den
333+
Kontext ihrer Klasse.) Beim Überschreiben von Methoden muss der `funcName` entsprechend
334+
identisch sein.
336335

337336
*Anmerkung*: Der Parameter `classAttributes` im Konstruktor von `MPyClass` wird nur für
338337
statische Attribute verwendet und kann ignoriert werden, da statische Attribute hier nicht
339338
zum geforderten Sprachumfang gehören. Sie können hier einfach ein `Map.of()` übergeben.
340339

341340
``` java
342341
// Methode "__init__(self)" anlegen
343-
Statement simpleSuperCall = new SuperCall(List.of()); // Aufruf von Super
344-
List<Statement> initBody = List.of(new Statement[] { simpleSuperCall }); // Body der Methode: super() kommt als erstes Statement
345-
List<Argument> initParamList = List.of(new Argument[] {new Argument("self", 0)}); // Parameterliste für "__init__" erstellen
346-
Function methodInit = new Function("__init__", "__init_11", initBody, initParamList, List.of()); // Methode "__init__(self)" erstellen; Name im C-Scope: "__init_11" (willkürlich, aber eindeutig im Programm)
342+
Statement simpleSuperCall = new SuperCall(List.of()); // Aufruf von Super
343+
List<Statement> initBody = List.of(new Statement[] { simpleSuperCall }); // Body der Methode: super() kommt als erstes Statement
344+
List<Argument> initParamList = List.of(new Argument[] {new Argument("self", 0)}); // Parameterliste für "__init__" erstellen
345+
Function methodInit = new Function("__init__", initBody, initParamList, List.of()); // Methode "__init__(self)" erstellen
347346

348347
// Methode "foo(self, x)" anlegen
349348
List<Argument> fooParamList = List.of(new Argument[] {new Argument("self", 0), new Argument("x", 1)}); // Parameterliste für "foo"
350-
Function methodFooA = new Function("foo", "foo12", List.of(), fooParamList, List.of()); // Methode "foo" mit leerem Body; Name im C-Scope: "foo12" (willkürlich, aber eindeutig im Programm)
349+
Function methodFooA = new Function("foo", List.of(), fooParamList, List.of()); // Methode "foo" mit leerem Body
351350

352351
// Klasse "A" anlegen: Erbt implizit von __MPyType_Object
353352
List<Function> functionListA = List.of(new Function[] { methodInit, methodFooA }); // Liste der Methoden in A
@@ -382,13 +381,13 @@ Für die Vererbung werden die bereits bekannten Elemente verwendet.
382381
Statement simpleSuperCall = new SuperCall(List.of());
383382
List<Statement> initBody = List.of(new Statement[] { simpleSuperCall });
384383
List<Argument> initParamList = List.of(new Argument[] {new Argument("self", 0)});
385-
Function methodInitB = new Function("__init__", "__init_111", initBody, initParamList, List.of());
384+
Function methodInitB = new Function("__init__", initBody, initParamList, List.of());
386385

387386
// "foo(self, x)"
388387
Statement fooPrint = new Call(printRef, List.of(new Expression[] { new Reference("x")}));
389388
List<Argument> fooParamList = List.of(new Argument[] {new Argument("self", 0), new Argument("x", 1)});
390389
List<Statement> fooBody = List.of(new Statement[]{ fooPrint });
391-
Function methodFooB = new Function("foo", "foo122", fooBody, fooParamList, List.of());
390+
Function methodFooB = new Function("foo", fooBody, fooParamList, List.of());
392391

393392
// Klasse "B"
394393
List<Function> functionListB = List.of(new Function[]{ methodInitB, methodFooB });
@@ -410,8 +409,8 @@ builder.addStatement(callFoo);
410409

411410
Ausblick: Für den Aufruf von Methoden auf Objekten erzeugen Sie wieder einen `Call`, der als
412411
Referenz eine `AttributeReference` erhält. Diese `AttributeReference` gruppiert den
413-
Methodennamen (den `funcName`, nicht den `uniqueName`!) und eine Referenz auf das Objekt.
414-
Weitere Details [siehe unten](usage_cbuilder.md#methoden-auf-objekten-aufrufen).
412+
Methodennamen (`funcName`) und eine Referenz auf das Objekt. Weitere Details [siehe
413+
unten](usage_cbuilder.md#methoden-auf-objekten-aufrufen).
415414

416415
### Verwendung von `self`
417416

@@ -441,12 +440,12 @@ Statement returnX = new ReturnStatement(getSelfX);
441440
// "__init__(self, y)"
442441
List<Statement> initBodyWithSelfAssign = List.of(new Statement[] { simpleSuperCall, assignSelfX });
443442
List<Argument> initParamListWithX = List.of(new Argument[] {new Argument("self", 0), new Argument("y", 1)});
444-
Function methodInitWithSelf = new Function("__init__", "__init_13", initBodyWithSelfAssign, initParamListWithX, List.of());
443+
Function methodInitWithSelf = new Function("__init__", initBodyWithSelfAssign, initParamListWithX, List.of());
445444

446445
// "getX(self)"
447446
List<Statement> getXBody = List.of(new Statement[] { returnX });
448447
List<Argument> paramListGetX = List.of(new Argument[] {new Argument("self", 0)});
449-
Function getX = new Function("getX", "getX14", getXBody , paramListGetX, List.of());
448+
Function getX = new Function("getX", getXBody, paramListGetX, List.of());
450449

451450
// Class "C"
452451
List<Function> functionListC = List.of(new Function[] { methodInitWithSelf, getX });
@@ -466,9 +465,9 @@ print(objectC.getX())
466465
Das Instanziieren einer Klasse erfolgt wie der Aufruf einer Funktion.
467466

468467
Der Aufruf von Methoden wird über ein Objekt der Klasse `AttributeReference` und einen
469-
`Call` realisiert. Diese `AttributeReference` gruppiert den Methodennamen (den `funcName`,
470-
nicht den `uniqueName`!) und eine Referenz auf das Objekt. Der Parameter `self` darf beim
471-
Methodenaufruf nicht übergeben werden.
468+
`Call` realisiert. Diese `AttributeReference` gruppiert den Methodennamen (`funcName`) und
469+
eine Referenz auf das Objekt. Der Parameter `self` darf beim Methodenaufruf nicht übergeben
470+
werden.
472471

473472
``` java
474473
// Variable "objectC"

src/main/java/CBuilder/ManualTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.nio.file.Path;
1111
import java.util.HashMap;
1212
import java.util.List;
13-
import java.util.Map;
1413

1514
/**
1615
* Allows to test the Builder without involving the ast or anything.
@@ -113,7 +112,7 @@ static void generateProgram(Path output) {
113112
new Reference("type")
114113
})));
115114

116-
builder.addFunction(new Function("printA", "printA_global", List.of(new Statement[]{
115+
builder.addFunction(new Function("printA", List.of(new Statement[]{
117116
new Call(new Reference("print"),
118117
List.of(new Expression[]{
119118
new Reference("a")
@@ -127,15 +126,15 @@ static void generateProgram(Path output) {
127126
})));
128127

129128
builder.addClass(new MPyClass("B", new Reference("__MPyType_Object"), List.of(new Function[]{
130-
new Function("print", "printA_TypeB", List.of(new Statement[]{
129+
new Function("print", List.of(new Statement[]{
131130
new Call(new Reference("print"),
132131
List.of(new Expression[]{
133132
new Reference("self")
134133
}))
135134
}), List.of(new Argument[]{
136135
new Argument("self", 0)
137136
}), List.of()),
138-
new Function("__init__", "__init___TypeB", List.of(new Statement[] {
137+
new Function("__init__", List.of(new Statement[] {
139138
new SuperCall(List.of()),
140139
new AttributeAssignment(new AttributeReference("b", new Reference("self")), new IntLiteral(100))
141140
}), List.of(new Argument[]{
@@ -144,7 +143,7 @@ static void generateProgram(Path output) {
144143
}), new HashMap<>()));
145144

146145
builder.addClass(new MPyClass("C", new Reference("B"), List.of(new Function[]{
147-
new Function("__init__", "__init___TypeC", List.of(new Statement[] {
146+
new Function("__init__", List.of(new Statement[] {
148147
new SuperCall(List.of())
149148
}), List.of(new Argument[]{
150149
new Argument("self", 0)

src/main/java/CBuilder/manualTests/ClassTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ static void generateProgram(Path output) {
2828
Reference object = new Reference("__MPyType_Object");
2929

3030
MPyClass clazz = new MPyClass("A", object, List.of(new Function[]{
31-
new Function("foo", "foo1", List.of(new Expression[]{
31+
new Function("foo", List.of(new Expression[]{
3232
new Call(funcPrint, List.of(new Expression[]{
3333
new StringLiteral("foo")
3434
}))
3535
}), List.of(new Argument[]{new Argument("self", 0)}), List.of()),
36-
new Function("__init__", "__init1_", List.of(new Expression[]{
36+
new Function("__init__", List.of(new Expression[]{
3737
new SuperCall(List.of())
3838
}), List.of(new Argument[]{new Argument("self", 0)}), List.of())
3939
}), Map.of());

src/main/java/CBuilder/objects/MPyClass.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public MPyClass(String name, Reference parent, List<Function> functions, Map<Ref
4141
this.parent = parent;
4242
this.functions = functions;
4343
this.classAttributes = classAttributes;
44+
this.functions.forEach(func -> func.createUniqueCName(this.name + "_"));
4445
}
4546

4647
/**

src/main/java/CBuilder/objects/functions/Function.java

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99

1010
/**
1111
* Mini-Python function declaration.
12+
*
1213
* Implements the Reference interface to allow using instances for referring to the declaration too.
1314
*/
1415
public class Function extends Reference {
1516

16-
private final String uniqueName;
17+
private String cName;
1718
private final List<Statement> body;
1819
private final List<Argument> positionalArgs; // (a, b, c)
1920
// TODO default arguments (a=1)
@@ -24,27 +25,23 @@ public class Function extends Reference {
2425

2526

2627
/**
27-
* Create a new function.
28+
* Create a new function (or method).
2829
*
29-
* Receives both funcName and unique name to fulfill requirements of both mini-python's duck typing
30+
* Receives a funcName to fulfill requirements of both mini-python's duck typing
3031
* and the declaration of the native C function containing the functions' statements.
31-
* Since C does not have namespaces, for declaring the function itself a globally unique name is needed.
32-
* But to make duck typing work, function names that were equal in the original mini-python code
33-
* need to be the same here too.
34-
*
35-
* By setting receivesPackedKeywordArgs or receivesPackedPositionalArgs to true,
36-
* the functions arguments handling make 'args' and/or 'kwargs' variables available in the scope of the function.
32+
* Since there are no namespaces in C, a globally unique name is required for the
33+
* declaration of the function itself. For functions, funcName is used for this purpose;
34+
* for methods, the class name is automatically added as a prefix to funcName once the
35+
* class is created (MPyClass).
3736
*
3837
* @param funcName The mini-python name of the function.
39-
* @param uniqueName The C name for the function.
4038
* @param body The body of the function.
4139
* @param positionalArgs The arguments of the function.
4240
* @param localVariables Variables declared inside the function.
4341
*/
44-
public Function(String funcName, String uniqueName, List<Statement> body, List<Argument> positionalArgs, List<VariableDeclaration> localVariables) {
42+
public Function(String funcName, List<Statement> body, List<Argument> positionalArgs, List<VariableDeclaration> localVariables) {
4543
super(funcName);
46-
47-
this.uniqueName = uniqueName;
44+
this.cName = funcName;
4845
this.body = body;
4946
this.positionalArgs = positionalArgs;
5047
this.receivesPackedKeywordArgs = false;
@@ -53,16 +50,35 @@ public Function(String funcName, String uniqueName, List<Statement> body, List<A
5350
}
5451

5552
/**
56-
* Create the c-code representation for the function.
53+
* Creates a unique method name used in emitted C code (auxiliary method, called by MPyClass)
54+
*
55+
* A method is a function that can only be called in connection with its class. However,
56+
* since a method is emitted just as a normal function in the C code, the method name must
57+
* be extended with a prefix at C level and thus be made unique. The constructor of MPyClass
58+
* handles this by calling this auxiliary method.
59+
*
60+
* <p> To call a method, use its name (funcName) and not its unique C name.
61+
*
62+
* <p>Warning: Do not use this helper method for functions, as functions are not allowed to
63+
* have a different C name.
64+
*
65+
* @param prefix The prefix string which should be adde to the C name.
66+
*/
67+
public void createUniqueCName(String prefix) {
68+
cName = prefix + name;
69+
}
70+
71+
/**
72+
* Create the C code representation for the function.
5773
*
58-
* @return The c-code of the function implementing this mini-python function.
74+
* @return The C code of the function implementing this mini-python function.
5975
*/
6076
public String buildCFunction() {
6177
StringBuilder declaration = new StringBuilder();
6278

6379
// FIXME args and kwargs probably breaks code that does not use the same names
6480
// for accessing packed arguments
65-
declaration.append("__MPyObj* func_" + uniqueName + "(__MPyObj *args, __MPyObj *kwargs) {\n");
81+
declaration.append("__MPyObj* func_" + cName + "(__MPyObj *args, __MPyObj *kwargs) {\n");
6682

6783
StringBuilder body = new StringBuilder();
6884

@@ -116,7 +132,7 @@ public String buildCFunction() {
116132
}
117133

118134
/**
119-
* Create the c-code for object declaration of this function.
135+
* Create the C code for object declaration of this function.
120136
*
121137
* @return A mini-python object declaration for this function.
122138
*/
@@ -125,17 +141,17 @@ public String buildFuncObjectDeclaration() {
125141
}
126142

127143
/**
128-
* Create the c-code for initialization of the function's mini-python object.
144+
* Create the C code for initialization of the function's mini-python object.
129145
*
130146
* @return Initialisation code for this function's mini-python object.
131147
*/
132148
public String buildInitialisation() {
133-
return name + " = __mpy_obj_init_func(&func_" + uniqueName + ");\n" +
149+
return name + " = __mpy_obj_init_func(&func_" + cName + ");\n" +
134150
"__mpy_obj_ref_inc(" + name + ");\n";
135151
}
136152

137153
/**
138-
* Create the c-code for cleaning up the function object.
154+
* Create the C code for cleaning up the function object.
139155
*
140156
* @return Cleanup code for collecting this function's mini-python object.
141157
*/

0 commit comments

Comments
 (0)