Commit 5e6d896e authored by Roman Alifanov's avatar Roman Alifanov

Add obj.field = value syntax for object field assignment

Support assigning to object fields from outside the class using obj.field = value syntax with all compound operators (=, +=, -=, *=, /=, ..=). Also support typed parameters in functions to enable field assignment on passed objects.
parent 27136f0b
......@@ -237,7 +237,7 @@ logger.log ("Starting app")
### Операции с полями объектов
Поля объектов поддерживают составные операторы присваивания:
Поля объектов можно изменять как внутри класса через `this.field`, так и снаружи через `obj.field`:
```
class Counter {
......@@ -245,7 +245,7 @@ class Counter {
name = ""
func increment (n = 1) {
this.value += n # арифметика
this.value += n # арифметика внутри класса
}
func decrement (n = 1) {
......@@ -256,6 +256,25 @@ class Counter {
this.name ..= suffix # конкатенация строк
}
}
# Присваивание полей снаружи
counter = new Counter ()
counter.value = 100
counter.name = "main"
counter.value += 50 # counter.value = 150
```
**Присваивание в функциях:** при передаче объекта в функцию требуется типизация параметра:
```
func resetCounter (c: Counter) {
c.value = 0
}
counter = new Counter ()
counter.value = 42
resetCounter (counter)
print (counter.value) # 0
```
Поддерживаемые операторы: `=`, `+=`, `-=`, `*=`, `/=`, `..=`
......
......@@ -8,7 +8,7 @@
- **Clean syntax** — Python-like readability with Go/Vala influences
- **Optional static typing** — TypeScript-style type annotations: `name: string`, `func greet(x: int): string`
- **Classes & inheritance** — OOP with constructors and method calls
- **Classes & inheritance** — OOP with constructors, method calls, field assignment
- **Lambdas**`x => x * 2`, `(a, b) => a + b`, multiline blocks
- **Decorators**`@retry`, `@log`, `@cache`, `@validate`, `@awk`, `@test`, user decorators `@obj.method()`
- **Callbacks** — pass functions/lambdas as arguments
......@@ -117,6 +117,10 @@ class Logger {
logger = Logger ("DEBUG")
logger.log ("Starting...")
# Field assignment
logger.level = "WARN"
logger.log ("Changed level")
```
### Pipe Operator
......
......@@ -8,7 +8,7 @@
- **Чистый синтаксис** — читаемость Python с влиянием Go/Vala
- **Опциональная статическая типизация** — аннотации типов в стиле TypeScript: `name: string`, `func greet(x: int): string`
- **Классы и наследование** — ООП с конструкторами и вызовами методов
- **Классы и наследование** — ООП с конструкторами, вызовами методов, присваиванием полей
- **Лямбды**`x => x * 2`, `(a, b) => a + b`, многострочные блоки
- **Декораторы**`@retry`, `@log`, `@cache`, `@validate`, `@awk`, `@test`, пользовательские `@obj.method()`
- **Колбеки** — передача функций/лямбд как аргументов
......@@ -117,6 +117,10 @@ class Logger {
logger = Logger ("DEBUG")
logger.log ("Запуск...")
# Присваивание полей
logger.level = "WARN"
logger.log ("Уровень изменён")
```
### Pipe-оператор
......
......@@ -521,8 +521,11 @@ class ClassMixin:
self.in_function = True
old_local_vars = self.local_vars
self.local_vars = set()
old_object_vars = self.object_vars.copy()
for param in func.params:
self.local_vars.add(param.name)
if param_types.get(param.name) == "object":
self.object_vars.add(param.name)
for stmt in func.body.statements:
self.generate_statement(stmt)
......@@ -535,6 +538,7 @@ class ClassMixin:
self.deferred_calls = old_deferred
self.in_function = old_in_function
self.local_vars = old_local_vars
self.object_vars = old_object_vars
self.param_name_map = old_param_name_map
self.emit("}")
self.emit()
......
......@@ -173,6 +173,11 @@ class DispatchMixin:
value = self.generate_expr(stmt.value)
self.emit(f'export {var_name}="{value}"')
return
if isinstance(stmt.target.object, Identifier):
obj_name = stmt.target.object.name
if obj_name in self.object_vars:
self._generate_obj_field_assignment(stmt, obj_name)
return
target = self.generate_lvalue(stmt.target)
......@@ -370,6 +375,25 @@ class DispatchMixin:
else:
self.emit(f'__CT_OBJ["$this.{field}"]="{value}"')
def _generate_obj_field_assignment(self, stmt: Assignment, obj_name: str):
"""Generate obj.field = value assignment for object variables."""
field = stmt.target.member
value = self.generate_expr(stmt.value)
if stmt.operator == "=":
self.emit(f'__CT_OBJ["${{{obj_name}}}.{field}"]="{value}"')
elif stmt.operator == "+=":
self.emit(f'__CT_OBJ["${{{obj_name}}}.{field}"]="$(( ${{__CT_OBJ["${{{obj_name}}}.{field}"]}} + {value} ))"')
elif stmt.operator == "-=":
self.emit(f'__CT_OBJ["${{{obj_name}}}.{field}"]="$(( ${{__CT_OBJ["${{{obj_name}}}.{field}"]}} - {value} ))"')
elif stmt.operator == "*=":
self.emit(f'__CT_OBJ["${{{obj_name}}}.{field}"]="$(( ${{__CT_OBJ["${{{obj_name}}}.{field}"]}} * {value} ))"')
elif stmt.operator == "/=":
self.emit(f'__CT_OBJ["${{{obj_name}}}.{field}"]="$(( ${{__CT_OBJ["${{{obj_name}}}.{field}"]}} / {value} ))"')
elif stmt.operator == "..=":
self.emit(f'__CT_OBJ["${{{obj_name}}}.{field}"]="${{__CT_OBJ["${{{obj_name}}}.{field}"]}}{value}"')
else:
self.emit(f'__CT_OBJ["${{{obj_name}}}.{field}"]="{value}"')
def _generate_method_call_assignment(self, stmt: Assignment, target: str) -> bool:
"""Generate method call assignment. Returns True if handled."""
callee = stmt.value.callee
......
......@@ -1841,3 +1841,124 @@ config: dict = {"key": "value"}
''')
assert code == 0
assert "Type mismatch" not in stderr
class TestObjectFieldAssignment:
def test_simple_field_assignment(self):
code, stdout, stderr = run_ct('''
class User {
name = "default"
age = 0
}
user = new User()
user.name = "Alice"
user.age = 25
print("{user.name} is {user.age}")
''')
assert code == 0
assert "Alice is 25" in stdout
def test_compound_assignment_plus(self):
code, stdout, stderr = run_ct('''
class Counter {
value = 10
}
c = new Counter()
c.value += 5
print(c.value)
''')
assert code == 0
assert "15" in stdout
def test_compound_assignment_minus(self):
code, stdout, stderr = run_ct('''
class Counter {
value = 20
}
c = new Counter()
c.value -= 7
print(c.value)
''')
assert code == 0
assert "13" in stdout
def test_compound_assignment_multiply(self):
code, stdout, stderr = run_ct('''
class Counter {
value = 5
}
c = new Counter()
c.value *= 3
print(c.value)
''')
assert code == 0
assert "15" in stdout
def test_compound_assignment_divide(self):
code, stdout, stderr = run_ct('''
class Counter {
value = 20
}
c = new Counter()
c.value /= 4
print(c.value)
''')
assert code == 0
assert "5" in stdout
def test_multiple_objects_independent(self):
code, stdout, stderr = run_ct('''
class Point {
x = 0
y = 0
}
p1 = new Point()
p2 = new Point()
p1.x = 10
p1.y = 20
p2.x = 100
p2.y = 200
print("{p1.x},{p1.y} {p2.x},{p2.y}")
''')
assert code == 0
assert "10,20 100,200" in stdout
def test_field_assignment_in_function(self):
code, stdout, stderr = run_ct('''
class Box {
value = 0
}
func setBox(b: Box, v) {
b.value = v
}
box = new Box()
setBox(box, 42)
print(box.value)
''')
assert code == 0
assert "42" in stdout
def test_chained_assignments(self):
code, stdout, stderr = run_ct('''
class Counter {
value = 0
}
c = new Counter()
c.value = 10
c.value += 5
c.value *= 2
c.value -= 10
print(c.value)
''')
assert code == 0
assert "20" in stdout
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment