This document explains how Object-Oriented Programming (OOP) works in this project from parsing to runtime execution.
Scope is the current minimal OOP implementation (v1):
- class definition
- object instantiation
- instance and class attributes
- methods
- constructor via
__بناء__
Not implemented in v1:
- inheritance
- access modifiers
- overriding rules
Daad executes directly from AST (no bytecode).
OOP flow:
- Lexer emits tokens like
CLASS,DOT,NAME,ASSIGN. - Parser builds AST nodes such as
ClassDefStmt,Attribute,Call,Assign. - Interpreter evaluates statements and expressions.
- Runtime values are stored in
Envand represented byClassValueandObjectValue.
Defined in internals/ast/nodes.go:
ClassDefStmt: class declaration- fields:
Name,Body
- fields:
Attribute: attribute access expression- models
obj.attr - fields:
Value(object/class expression),Attr(attribute name)
- models
Assign: assignment expression- target can be
NameorAttribute
- target can be
Call: function or method invocation- used for
ClassName(...)andobj.method(...)
- used for
Important detail: method calls are represented as a call whose function expression is an attribute:
obj.method()becomesCall{Func: Attribute{Value: Name("obj"), Attr: "method"}}
Implemented in internals/parser/parser.go.
parseClassDef parses:
صنف شخص:
...
into:
ClassDefStmt{Name: "شخص", Body: classSuite}
Class body supports (v1):
- method declarations (
FunctionDefStmt) - class-level assignments (as
ExprStmtcontainingAssign)
parsePrimary repeatedly extends atoms with postfix operations:
(... )for calls[...]for subscripts.namefor attributes
That means chained forms are naturally supported by AST construction order, such as:
a.b.c()obj.method(arg)
Defined in internals/interpreter/types.go.
Represents a class at runtime:
Name stringAttributes map[string]Value
The attributes map stores both:
- class fields
- methods (stored as
*FunctionValue)
Represents an object instance:
Class *ClassValueAttributes map[string]Value
The instance has a pointer to its class plus its own attribute map.
Environment is implemented in internals/interpreter/env.go:
Env.valuesstores name -> valueEnv.parentforms lexical scope chain
OOP-related scope behavior:
- When class methods are created in
execClassDefStmt, each methodFunctionValuecaptures current interpreter env (Env: i.env). - When any function/method is called,
callFunctioncreates a child env from the function closure env:funcEnv := NewEnv(fn.Env)- parameters are bound inside
funcEnv
So methods use lexical scoping like normal functions.
Implemented in execClassDefStmt in internals/interpreter/execStmt.go.
Execution steps:
- Create empty
classAttributesmap. - Iterate class body statements.
- For each
FunctionDefStmt:- create
FunctionValue - store in class attributes by method name
- create
- For class-level assignment expressions:
- only
Nameassignment target is accepted - evaluate value and store in class attributes
- only
- Register class in current env:
env[className] = &ClassValue{...}
Implemented in execCallExpr in internals/interpreter/execExpr.go when callee is *ClassValue.
When ClassName(args...) is executed:
- Create object:
obj := &ObjectValue{Class: class, Attributes: map[string]Value{}}
- Copy non-function class attributes into object attributes.
- If class has
__بناء__and it is a function:- call it automatically
- pass object as first argument (
ذاتي) - append user-provided constructor args after it
- Return object.
Constructor convention in v1:
- Name must be exactly
__بناء__.
Implemented in execAttributeExpr in internals/interpreter/execExpr.go.
Lookup order for obj.attr:
obj.Attributes[attr](instance first)obj.Class.Attributes[attr](class fallback)
If class attribute is a function (*FunctionValue), interpreter returns a bound callable:
- wraps method in
BuiltinValue - prepends instance (
obj) to call args - then calls
callFunction
This is how obj.method(x) automatically receives ذاتي as first parameter.
For ClassName.attr, lookup is direct in class attributes map.
Implemented in execAssignExpr in internals/interpreter/execExpr.go.
Supported targets:
Name-> normal variable assignment in envAttribute-> attribute assignment
For attribute assignment:
- if container evaluates to
*ObjectValue, write to instance attributes - if container evaluates to
*ClassValue, write to class attributes - otherwise runtime error
So both are valid:
ذاتي.الاسم = اسم
شخص.نوع = "إنسان"
Source:
صنف شخص:
ن = "يسيسي"
دالة __بناء__(ذاتي, اسم):
ذاتي.الاسم = اسم
دالة عرف_نفسك(ذاتي):
اطبع("أنا " + ذاتي.الاسم)
م = شخص("ssss")
م.عرف_نفسك()
اطبع(م.ن)
Runtime behavior:
- Class
شخصis created with:- class field
ن - methods
__بناء__andعرف_نفسك
- class field
شخص("ssss")creates objectم, copies fieldن, then calls constructor.- Constructor writes
الاسمinto instance attributes. م.عرف_نفسك()resolves method from class, bindsمas first arg, executes method.م.نsucceeds via instance/class attribute resolution.
- No inheritance hierarchy.
- No explicit visibility (
private/public). - No metaclass behavior.
- Constructor invocation is convention-based (
__بناء__). - Instance receives a copy of non-function class attributes during instantiation.
- AST nodes:
internals/ast/nodes.go - Parser:
internals/parser/parser.go - Statement execution:
internals/interpreter/execStmt.go - Expression execution:
internals/interpreter/execExpr.go - Runtime types:
internals/interpreter/types.go - Environment:
internals/interpreter/env.go - OOP AST test:
tests/ast/oop_ast_test.go - OOP interpreter test:
tests/interpreter/interpreter_test.go - OOP example:
tests/examples/oop_class.daad