How my ECS implementation looks Still a single macro. Although it does emit the stub getter and setter methods, which the runtime patches with the native code.
The macro looks simple, but implementing it in C/C++ will require many thousands of error-prone lines of code.
That is in addtion to all the table related code.
So ECS is anything but easy to implemnt properly (outside of the basic structure of arrays).
/*******************************************************************************
Column-oriented Struct (aka CLasSifier)
Usage:
cls tname.parent(@ParentArgs)! @`prefix` @Params @Args
:
@Fields
=
@InitCode
! - Provide `.tname` method
@`prefix` - Picks custom prefix for the database columns,
associated with this type. Default is `tname_`
Can be ``
@Params: - Params to the cls
\nocvt - Dont init the `cls` column, which allows
converting the unknown `id` entities to this type
\clearable - Provides the No.cls_clear_tname method,
which clears all tables associated with this type
@Args:
UpperCaseName - Normal argument name, usable Fields values and Initcode
lowerCaseName - Creates a field and initalizes with that arg
Name!Value - Keyword argument with a default value
@Fields:
name - Unitialized field, which defaults to No
name!!Default - Unitialized field, which defaults to Default
name!Value - Initialized field. Value could reference @Args
'name'!Value - Creates a column prefix_name, but doesn't generate
the cls.name method.
Symta's version of C++ private data fields
*******************************************************************************/
cls Name @Fs =
OName Name
NeedsTypePredicate Name(0:['!' &Name]) //wants a type predicate?
Parent \id
ParentArgs:
Name(:['()' &Name @&ParentArgs])
Name(:['.' &Name &Parent])
Sink No
case Parent:
[`$` Part] =
Sink = Part
Parent = \id
Name(:['.' &Name &Parent])
Pfx "[Name]_" //part prefix
case Fs [`@` &Pfx],@R: Fs = R //cls Name@UserPfx @Fs
Params nocvt!0 clearable!0
while 1:
case Fs:
[`\\` Param],@_ = Params.Param = 1
[`\\` [`()` Param [@Args]]],@_ = Params.Param = Args
Else = done
Fs = Fs.tail
Body:
AFs: //auto initialized fields
case Fs [@FsP [[`|` @L [`=` [] []] @R ]]]: Fs =: @FsP [`=` L.j [`|` @R]]
case Fs [@FsP [`=` &AFs &Body]]: Fs = FsP
case Fs [@FsP &Body<[`|` @_]]: Fs = FsP
//normalize into [Name Value PrefixedName]
// If Name is `_`, dont produce a database column, only a method
AFs = AFs{['!' ['@' ['.' PN N]]] V =: N V PN
;['!' ['@' PN]] V =: _ V PN
;['!' ['!' N]] V =: N ['!' V] "[Pfx][N]"
;['!' N] V =: N V "[Pfx][N]"
; N =: N No "[Pfx][N]"}
if NeedsTypePredicate: push [_ 1 Name] AFs //if Object.widget: ...
//push [_ Name cls] AFs
FNs: //field names
PFNs: //typenanme-prefixed field names
KVs: //keyworded args default value
As: //arguments
FAs: //field arguments
till Fs.end:
KV No //keyword value
case Fs [[`!` _] [`!` _]@_]+[[`!` _]]: Fs =: Fs.head No @Fs.tail
case Fs [[`!` F] D @R]:
KV =: D
Fs =: F @R
push KV KVs
AN /Fs
A AN.title
push A As
if AN.is_keyword:
push AN FNs
push A FAs
PAN "[Pfx][AN]"
Fs(:[`@` &PAN],@&Fs)
push PAN PFNs
FNs,FAs,As,KVs,PFNs [FNs FAs As KVs PFNs]{f}
KAs [As KVs].zip{A,[D] =: ['!' A] D; A,_=:A}.j //args with keywords
Fields: @[FNs PFNs].zip{?0,?1,No} @AFs.skip(?0><_){?0,?2,?1}
ParentRig []
ParentDel []
Me \Me
Id form: gid_ Me
if Parent<>id:
PR form: Me.$("rig_[Parent]") $@ParentArgs
push PR ParentRig
PD form: Me.$("del_[Parent]")
push PD ParentDel
InitedAFs AFs.keep(?1(1:No+['!' V]=0))
InitCls if Params.nocvt: form (Me = $init_cls_nocvt)
else form (Me = $init_cls Name)
Hdr1 form: `|` (cls_set_ Id cls Name)
$@([PFNs FAs].zip{PFN,A = form: $rig PFN,A})
$@(InitedAFs{N,V,PN = form: $rig PN,V})
Hdr form: InitCls
$@ParentRig
$@([PFNs FAs].zip{PFN,A = form: $rig PFN,A})
$@(InitedAFs{N,V,PN = form: $rig PN,V})
Type form: type Name.Parent $@(KAs): = `|` $@Hdr Body
Rig1 form: id.$("rig1_[Name]") $@(KAs) = $@Hdr1 Body Me
Rig form: id.$("rig_[Name]") $@(KAs) = `|` $@Hdr Body Me
IdS "Id".rand
Del1F form: `|` (IdS Id) $@(Fields{FN,PFN,V = form: $"T_[PFN]_".del IdS})
Del1 form: id.$("del1_[Name]") = Del1F
//Del1 form: id.$("del1_[Name]") = $del $@(Fields{?1})
Del form: id.$("del_[Name]") = `|` $$("del1_[Name]") $@ParentDel
AsText form: Name.ser_ = "${[\Name] [Me.ub.t.as_text.drop^2]}"
//`cls`-types don't have the `__` method
//Instead they explicitly define accessors for the used fields.
FAs:
TblNos:
for [FN PFN V] Fields:
SetPrologue 0
GetPrologue 0
case V:
['!' X] =
case X [['$' SE] @&X]: //user want
SetPrologue = SE
case X [['$' GE] @&X]: GetPrologue = GE
X = X.~
V =: '!' X
if X^is_constant:
case X ['$' E]: X = E
push PFN,X TblNos
V = No
FG case V: //field getter: replacing missing elements by the default value
['!' X] =
case GetPrologue:
['=' [] PBody] =
form: Name.FN = `|` PBody (gid_get_ $"T_[PFN]_" Me).@X
Else = form: Name.FN = (gid_get_ $"T_[PFN]_" Me).@X
Else = case GetPrologue:
['=' [] PBody] =
form: Name.FN = `|` PBody (gid_get_ $"T_[PFN]_" Me)
Else = form: Name.FN = cls_get_stub_ Me $"T_[PFN]_"
push FG FAs
FSN "=[FN]"
FS case V: //field setter: assigning default value removes the element
['!' V] =
case SetPrologue:
['=' [Var] PBody] =
form: Name.FSN Var =
PBody
if Var><V: ($"T_[PFN]_").del Id
else gid_set_ $"T_[PFN]_" Me Var
Else =
form: Name.FSN ~V =
if ~V><V: ($"T_[PFN]_").del Id
else gid_set_ $"T_[PFN]_" Me ~V
Else =
case SetPrologue:
['=' [Var] PBody] =
form: Name.FSN Var = `|` PBody (gid_set_ $"T_[PFN]_" Me Var)
Else = form: Name.FSN ~V = cls_set_stub_ Me ~V $"T_[PFN]_"
push FS FAs
if got Sink:
S form: Name.__ ~Method ~Args = |~Args.0 = $(\Me).Sink; ~Args.apply_method ~Method
push S FAs
AsType form: id.$("[Name]_") = ref_ (tag_ (_data Name)) Id
SetCvt form: No.set_cls_cvt_ Name: ~Id => ref_ (tag_ (_data Name)) ~Id
push AsType FAs
push SetCvt FAs
FTbls map [FN PFN V] Fields: //field tables
form: $"T_[PFN]_" cls_tbl_ PFN
SetNos map PFN,NoVal TblNos: //set default value for the tables
form: $"T_[PFN]_".setNo NoVal
Clearer:
if Params.clearable:
CTbls map [FN PFN V] Fields: form: $"T_[PFN]_".clear
Clear form: no.$("cls_clear_[Name]") = `;` $@CTbls
Clearer =: Clear
form @(`|` $@FTbls $@SetNos Type Rig1 Rig Del1 Del AsText $@FAs $@Clearer)
Current Mood: accomplished