From c516afc400b94d0543cddafd42b834de003bd615 Mon Sep 17 00:00:00 2001 From: bronku Date: Sat, 6 Dec 2025 12:35:51 +0100 Subject: [PATCH] assign --- src/ast/operators.hpp | 32 +++++++++++++++++++++++- src/ast/statements.hpp | 19 +++++++++++++++ src/codegen.hpp | 8 ++++++ src/parser.y | 30 +++++++++++++++++++++++ test/ruby/27.in | 2 +- test/ruby/27.out | 2 +- test/ruby/28.in | 55 ++++++++++++++++++++++-------------------- test/ruby/29.in | 46 ++++++++++++----------------------- test/ruby/30.in | 45 ++++++++++++++++++++++------------ test/ruby/31.in | 52 ++++++++++++++++++--------------------- test/ruby/32.in | 51 ++++++++++++++++++++------------------- test/ruby/33.in | 51 ++++++++++++++++++++------------------- test/ruby/34.in | 31 ------------------------ 13 files changed, 240 insertions(+), 184 deletions(-) delete mode 100644 test/ruby/34.in diff --git a/src/ast/operators.hpp b/src/ast/operators.hpp index 8918acf..2512a32 100644 --- a/src/ast/operators.hpp +++ b/src/ast/operators.hpp @@ -24,7 +24,17 @@ struct BinaryOperator LSHIFT, RSHIFT, POWER, - IN + IN, + PLUS_ASSIGN, + MINUS_ASSIGN, + MULTIPLY_ASSIGN, + DIVIDE_ASSIGN, + MODULO_ASSIGN, + BIT_AND_ASSIGN, + BIT_OR_ASSIGN, + BIT_XOR_ASSIGN, + LSHIFT_ASSIGN, + RSHIFT_ASSIGN, }; Kind kind; @@ -75,6 +85,26 @@ struct BinaryOperator return "**"; case IN: return "in"; + case PLUS_ASSIGN: + return "+="; + case MINUS_ASSIGN: + return "-="; + case MULTIPLY_ASSIGN: + return "*="; + case DIVIDE_ASSIGN: + return "/="; + case MODULO_ASSIGN: + return "%="; + case BIT_AND_ASSIGN: + return "&="; + case BIT_OR_ASSIGN: + return "|="; + case BIT_XOR_ASSIGN: + return "^="; + case LSHIFT_ASSIGN: + return "<<="; + case RSHIFT_ASSIGN: + return ">>="; default: return "?"; } diff --git a/src/ast/statements.hpp b/src/ast/statements.hpp index ef6a65d..338a9ed 100644 --- a/src/ast/statements.hpp +++ b/src/ast/statements.hpp @@ -25,6 +25,25 @@ struct Assign : Stmt } }; +struct OpAssign : Stmt +{ + ptr target; + ptr value; + BinaryOperator op; + + OpAssign(ptr t, ptr v, BinaryOperator o) + : target(std::move(t)), value(std::move(v)), op(o) {} + + std::string dump(int indent = 0) const override + { + std::ostringstream oss; + oss << indent_str(indent) << "OpAssign(" << op.symbol() << ")\n"; + oss << target->dump(indent + 1) << "\n"; + oss << value->dump(indent + 1); + return oss.str(); + } +}; + struct ExprStmt : Stmt { ptr expr; diff --git a/src/codegen.hpp b/src/codegen.hpp index dfa1de2..428dfc4 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -118,6 +118,14 @@ private: generate_expr(*assign->value); write("\n"); } + else if (auto *assign = dynamic_cast(&stmt)) + { + write_indent(); + generate_expr(*assign->target); + output << " " << assign->op.symbol() << " "; + generate_expr(*assign->value); + write("\n"); + } else if (auto *if_stmt = dynamic_cast(&stmt)) { write_indent(); diff --git a/src/parser.y b/src/parser.y index 31ee612..6929c06 100644 --- a/src/parser.y +++ b/src/parser.y @@ -82,6 +82,36 @@ stmt: | expr ASSIGN expr NEWLINE { $$ = std::make_unique(std::move($1), std::move($3)); } + | expr PLUS_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::PLUS_ASSIGN)); + } + | expr MINUS_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::MINUS_ASSIGN)); + } + | expr MULTIPLY_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::MULTIPLY_ASSIGN)); + } + | expr DIVIDE_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::DIVIDE_ASSIGN)); + } + | expr MODULO_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::MODULO_ASSIGN)); + } + | expr BIT_AND_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::BIT_AND_ASSIGN)); + } + | expr BIT_OR_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::BIT_OR_ASSIGN)); + } + | expr BIT_XOR_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::BIT_XOR_ASSIGN)); + } + | expr LSHIFT_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::LSHIFT_ASSIGN)); + } + | expr RSHIFT_ASSIGN expr NEWLINE { + $$ = std::make_unique(std::move($1), std::move($3), BinaryOperator(BinaryOperator::RSHIFT_ASSIGN)); + } | KW_IF expr COLON NEWLINE INDENT stmt_list DEDENT elif_chain{ $$ = std::make_unique(std::move($2), std::move($6), std::move($8)); } diff --git a/test/ruby/27.in b/test/ruby/27.in index 32256c6..3898f50 100644 --- a/test/ruby/27.in +++ b/test/ruby/27.in @@ -5,7 +5,7 @@ def count_words(text): for word in words: word = word.strip('.,!?') if word in word_count: - word_count[word] = word_count[word] + 1 + word_count[word] += 1 else: word_count[word] = 1 diff --git a/test/ruby/27.out b/test/ruby/27.out index 7fcb5d8..d2f56fc 100644 --- a/test/ruby/27.out +++ b/test/ruby/27.out @@ -11,7 +11,7 @@ def count_words(text) for word in words word = word.delete('.,!?') if __contains__(word_count, word) - word_count[word] = word_count[word] + 1 + word_count[word] += 1 else word_count[word] = 1 end diff --git a/test/ruby/28.in b/test/ruby/28.in index ea14210..467f0b2 100644 --- a/test/ruby/28.in +++ b/test/ruby/28.in @@ -1,31 +1,34 @@ -def count_words(text): - words = text.lower().split() - word_count = {} +class BankAccount: + def __init__(self, owner, balance=0): + self.owner = owner + self.balance = balance - for word in words: - word = word.strip('.,!?') - if word in word_count: - word_count[word] = word_count[word] + 1 - else: - word_count[word] = 1 + def deposit(self, amount): + if amount > 0: + self.balance += amount + return True + return False - return word_count + def withdraw(self, amount): + if 0 < amount <= self.balance: + self.balance -= amount + return True + return False + + def get_balance(self): + return self.balance + + def __str__(self): + return f"Account({self.owner}, Balance: ${self.balance:.2f})" -def find_most_common(word_count): - most_common = None - max_count = 0 - - for word, count in word_count.items(): - if count > max_count: - most_common = word - max_count = count - - return most_common, max_count +account = BankAccount("Alice", 1000) +print(account) -text = "Hello world hello there world hello" -counts = count_words(text) -common_word, frequency = find_most_common(counts) +print(f"Deposit $500: {account.deposit(500)}") +print(f"Balance: ${account.get_balance()}") -print(f"Text: {text}") -print(f"Word counts: {counts}") -print(f"Most common: '{common_word}' appears {frequency} times") \ No newline at end of file +print(f"Withdraw $200: {account.withdraw(200)}") +print(f"Balance: ${account.get_balance()}") + +print(f"Withdraw $2000: {account.withdraw(2000)}") +print(f"Final balance: ${account.get_balance()}") \ No newline at end of file diff --git a/test/ruby/29.in b/test/ruby/29.in index 467f0b2..25225f9 100644 --- a/test/ruby/29.in +++ b/test/ruby/29.in @@ -1,34 +1,18 @@ -class BankAccount: - def __init__(self, owner, balance=0): - self.owner = owner - self.balance = balance - - def deposit(self, amount): - if amount > 0: - self.balance += amount - return True - return False - - def withdraw(self, amount): - if 0 < amount <= self.balance: - self.balance -= amount - return True - return False - - def get_balance(self): - return self.balance - - def __str__(self): - return f"Account({self.owner}, Balance: ${self.balance:.2f})" +def write_to_file(filename, content): + with open(filename, 'w') as file: + file.write(content) + print(f"Written to {filename}") -account = BankAccount("Alice", 1000) -print(account) +def read_from_file(filename): + try: + with open(filename, 'r') as file: + content = file.read() + return content + except FileNotFoundError: + return "File not found" -print(f"Deposit $500: {account.deposit(500)}") -print(f"Balance: ${account.get_balance()}") +write_to_file("test_output.txt", "Hello, World!\nThis is a test.\n") +content = read_from_file("test_output.txt") +print(f"File content:\n{content}") -print(f"Withdraw $200: {account.withdraw(200)}") -print(f"Balance: ${account.get_balance()}") - -print(f"Withdraw $2000: {account.withdraw(2000)}") -print(f"Final balance: ${account.get_balance()}") \ No newline at end of file +print(read_from_file("nonexistent.txt")) \ No newline at end of file diff --git a/test/ruby/30.in b/test/ruby/30.in index 25225f9..44e9964 100644 --- a/test/ruby/30.in +++ b/test/ruby/30.in @@ -1,18 +1,33 @@ -def write_to_file(filename, content): - with open(filename, 'w') as file: - file.write(content) - print(f"Written to {filename}") +import math -def read_from_file(filename): - try: - with open(filename, 'r') as file: - content = file.read() - return content - except FileNotFoundError: - return "File not found" +def circle_area(radius): + return math.pi * radius ** 2 -write_to_file("test_output.txt", "Hello, World!\nThis is a test.\n") -content = read_from_file("test_output.txt") -print(f"File content:\n{content}") +def circle_circumference(radius): + return 2 * math.pi * radius -print(read_from_file("nonexistent.txt")) \ No newline at end of file +def solve_quadratic(a, b, c): + discriminant = b ** 2 - 4 * a * c + + if discriminant < 0: + return None, None + elif discriminant == 0: + x = -b / (2 * a) + return x, x + else: + x1 = (-b + math.sqrt(discriminant)) / (2 * a) + x2 = (-b - math.sqrt(discriminant)) / (2 * a) + return x1, x2 + +print(f"Circle with radius 5:") +print(f" Area: {circle_area(5):.2f}") +print(f" Circumference: {circle_circumference(5):.2f}") + +print(f"\nQuadratic 2x² + 5x - 3 = 0:") +x1, x2 = solve_quadratic(2, 5, -3) +print(f" Solutions: {x1:.2f}, {x2:.2f}") + +print(f"\nQuadratic x² + 1 = 0:") +x1, x2 = solve_quadratic(1, 0, 1) +if x1 is None: + print(" No real solutions") \ No newline at end of file diff --git a/test/ruby/31.in b/test/ruby/31.in index 44e9964..7f00609 100644 --- a/test/ruby/31.in +++ b/test/ruby/31.in @@ -1,33 +1,29 @@ -import math +def countdown(n): + while n > 0: + print(f"Countdown: {n}") + n -= 1 + print("Blast off!") -def circle_area(radius): - return math.pi * radius ** 2 - -def circle_circumference(radius): - return 2 * math.pi * radius - -def solve_quadratic(a, b, c): - discriminant = b ** 2 - 4 * a * c +def sum_until_negative(): + total = 0 + count = 0 + inputs = [5, 3, 8, -1, 4, 2] - if discriminant < 0: - return None, None - elif discriminant == 0: - x = -b / (2 * a) - return x, x + for value in inputs: + if value < 0: + break + total += value + count += 1 + + if count > 0: + average = total / count + return total, average else: - x1 = (-b + math.sqrt(discriminant)) / (2 * a) - x2 = (-b - math.sqrt(discriminant)) / (2 * a) - return x1, x2 + return 0, 0 -print(f"Circle with radius 5:") -print(f" Area: {circle_area(5):.2f}") -print(f" Circumference: {circle_circumference(5):.2f}") +print("Countdown from 5:") +countdown(5) -print(f"\nQuadratic 2x² + 5x - 3 = 0:") -x1, x2 = solve_quadratic(2, 5, -3) -print(f" Solutions: {x1:.2f}, {x2:.2f}") - -print(f"\nQuadratic x² + 1 = 0:") -x1, x2 = solve_quadratic(1, 0, 1) -if x1 is None: - print(" No real solutions") \ No newline at end of file +print("\nSum until negative:") +total, avg = sum_until_negative() +print(f"Total: {total}, Average: {avg:.2f}") \ No newline at end of file diff --git a/test/ruby/32.in b/test/ruby/32.in index 7f00609..84a0b4c 100644 --- a/test/ruby/32.in +++ b/test/ruby/32.in @@ -1,29 +1,30 @@ -def countdown(n): - while n > 0: - print(f"Countdown: {n}") - n -= 1 - print("Blast off!") - -def sum_until_negative(): - total = 0 - count = 0 - inputs = [5, 3, 8, -1, 4, 2] +def analyze_numbers(numbers): + if not numbers: + return 0, 0, 0 - for value in inputs: - if value < 0: - break - total += value - count += 1 + total = sum(numbers) + average = total / len(numbers) - if count > 0: - average = total / count - return total, average - else: - return 0, 0 + positive_count = 0 + for num in numbers: + if num > 0: + positive_count += 1 + + return total, average, positive_count -print("Countdown from 5:") -countdown(5) +def get_coordinates(): + x = 10 + y = 20 + z = 30 + return x, y, z -print("\nSum until negative:") -total, avg = sum_until_negative() -print(f"Total: {total}, Average: {avg:.2f}") \ No newline at end of file +data = [3, -1, 4, -2, 0, 5, -3] +total, avg, positives = analyze_numbers(data) + +print(f"Numbers: {data}") +print(f"Total: {total}") +print(f"Average: {avg:.2f}") +print(f"Positive numbers: {positives}") + +x, y, z = get_coordinates() +print(f"\nCoordinates: x={x}, y={y}, z={z}") \ No newline at end of file diff --git a/test/ruby/33.in b/test/ruby/33.in index 84a0b4c..ea14210 100644 --- a/test/ruby/33.in +++ b/test/ruby/33.in @@ -1,30 +1,31 @@ -def analyze_numbers(numbers): - if not numbers: - return 0, 0, 0 +def count_words(text): + words = text.lower().split() + word_count = {} - total = sum(numbers) - average = total / len(numbers) + for word in words: + word = word.strip('.,!?') + if word in word_count: + word_count[word] = word_count[word] + 1 + else: + word_count[word] = 1 - positive_count = 0 - for num in numbers: - if num > 0: - positive_count += 1 + return word_count + +def find_most_common(word_count): + most_common = None + max_count = 0 - return total, average, positive_count + for word, count in word_count.items(): + if count > max_count: + most_common = word + max_count = count + + return most_common, max_count -def get_coordinates(): - x = 10 - y = 20 - z = 30 - return x, y, z +text = "Hello world hello there world hello" +counts = count_words(text) +common_word, frequency = find_most_common(counts) -data = [3, -1, 4, -2, 0, 5, -3] -total, avg, positives = analyze_numbers(data) - -print(f"Numbers: {data}") -print(f"Total: {total}") -print(f"Average: {avg:.2f}") -print(f"Positive numbers: {positives}") - -x, y, z = get_coordinates() -print(f"\nCoordinates: x={x}, y={y}, z={z}") \ No newline at end of file +print(f"Text: {text}") +print(f"Word counts: {counts}") +print(f"Most common: '{common_word}' appears {frequency} times") \ No newline at end of file diff --git a/test/ruby/34.in b/test/ruby/34.in deleted file mode 100644 index 0d36327..0000000 --- a/test/ruby/34.in +++ /dev/null @@ -1,31 +0,0 @@ -def count_words(text): - words = text.lower().split() - word_count = {} - - for word in words: - word = word.strip('.,!?') - if word in word_count: - word_count[word] += 1 - else: - word_count[word] = 1 - - return word_count - -def find_most_common(word_count): - most_common = None - max_count = 0 - - for word, count in word_count.items(): - if count > max_count: - most_common = word - max_count = count - - return most_common, max_count - -text = "Hello world hello there world hello" -counts = count_words(text) -common_word, frequency = find_most_common(counts) - -print(f"Text: {text}") -print(f"Word counts: {counts}") -print(f"Most common: '{common_word}' appears {frequency} times") \ No newline at end of file