armoha / euddraft

System for pluginizing eudplib codes.
Other
29 stars 4 forks source link

Make `StringBuffer` to `object`. #111

Open armoha opened 1 year ago

armoha commented 1 year ago

We need to expand EUDStruct to fully port current behavior of StringBuffer and prevent performance regression.

Highly related to armoha/euddraft#58

Background

constructor and constructor_static

object Obj {};

const staticObj = Obj();  // calls constructor_static and maybe constructor too
const dynamicObj = Obj.alloc();  // calls constructor

var objVar = globalStaticObj;
const globalObjFromCast = Obj.cast(objVar);  // does not call any constructor

function foo(obj: Obj) {}
function afterTriggerExec() {
    foo(objVar);  // does not call any constructor
}

Default implementation of constructor_static calls constructor too, but user is free to override it to make specialized and separate code for static declaration:

class EUDStruct(ut.ExprProxy, metaclass=_EUDStruct_Metaclass):

    # Constructor & Destructor of classes
    def constructor(self):
        """Constructor for individual structures.

        Default constructor accepts no arguments, but derived classes may
        accept additional arguments.

        This function is called when
            - Argument is allocated from pool   (self.isPooled = True)
            - Argument is generated             (self.isPooled = False)

        You may choose to either allocate member from pool or just allocate
        members statically via self.isPooled.
        """
        pass

    def constructor_static(self, *args, **kwargs):
        """Specialized constructor for static variables.

        Static variable may not require allocation for member variables.
        Function may specialize their behavior by overriding this function"""
        self.constructor(*args, **kwargs)

    def destructor(self):
        """Destructor for individual structures.

        Destructor accepts no arguments. Destructor is called when
            - Manually called. (Ex: stack variable)
            - free() is called for object
        """
        pass

Motivation

We want to pass StringBuffer to function but we don't want to make StringBuffer too slow.

StringBuffer.StringIndex, .epd and .capacity should be static

It is breaking change to make var field to const or static field. It is okay to change static field to const or var field.

Field mutability

var field

# eudplib syntax to declare fields
class Obj(EUDStruct):
    _fields_ = [
        "varfield",  # var field without type
        ("untyped_var_field", None),  # equivalent code
        ("typed_var_field", EUDArray),
        # new syntax below
        ("verbose_var_field", None, "var"),
        ("const_field", None, "const"),
        ("static_field", TrgUnit, "static"),
    ]

How to implement

EUDStruct.__init__

image 위에는 객체.cast(인스턴스) 하는거 (_from이 있을 때) 아래는 객체(인자) 정적 선언하는거 (else:)

(maybe we need to use Forward()?)

._initialized

image

Q. allow mutations only within constructor_static?

object ObjectWithStaticField {
    static field_static;
    function constructor_static() {
        this.field_static = $U("Fenix (Zealot)");
        // Q. constructor_static 안에서만 변경을 허용할지?
        this.field_static *= 2;  // 헷갈리니까 막는게 나을 거 같음
    }
};

Q. optimize global object initialization?

object Obj {
    var v;
    const c;
    static s;
    function constructor_static() {
        this.v = 1;
        this.c = 2;
        this.s = 3;
        this.constructor();  // optional
    }
};
const obj_global = Obj();
function beforeTriggerExec() {
    const obj_local = Obj();
}

Q. do we need static if?


// we can write this in eudplib but can't in epScript
object Obj {
    var a, b;
    static s;
    function constructor() {
        if (this.isPooled) {
            // this object is dynamically allocated with Obj.alloc();
        } else {
            // this object is statically allocated with Obj(arg);
        }
    }
    function constructor_static(arg) {
        if (py_isinstance(arg, py_str)) {
            // arg is str
        } else if (py_isinstance(arg, py_int)) {
            // arg is int
        }
        this.constructor();  // optional
    }
};
armoha commented 1 year ago

Actually I started to lose interest in making static field as public API. It complicates the language but does not hold much weight. e.g. ancient version of Rust had immutable struct field but later dropped it.

armoha commented 8 months ago

It's mostly solved in euddraft 0.9.10.2.