diff --git a/shortcomings.md b/shortcomings.md index 6b59a1a..98c2b52 100644 --- a/shortcomings.md +++ b/shortcomings.md @@ -1,5 +1,6 @@ - string processing is very primitive, it just checks for f at the beggining, and then adds #, before { if so -- no elif +- no elif parsed as if else if else if else .... insted of if elif elif else, semantically the same - no lambdas - tuples need to be in () -- converts tuples into lists \ No newline at end of file +- converts tuples into lists +- no += -= *= /= ... \ No newline at end of file diff --git a/src/ast/operators.hpp b/src/ast/operators.hpp index b443a23..8918acf 100644 --- a/src/ast/operators.hpp +++ b/src/ast/operators.hpp @@ -23,7 +23,8 @@ struct BinaryOperator BIT_XOR, LSHIFT, RSHIFT, - POWER + POWER, + IN }; Kind kind; @@ -72,6 +73,8 @@ struct BinaryOperator return ">>"; case POWER: return "**"; + case IN: + return "in"; default: return "?"; } diff --git a/src/ast/statements.hpp b/src/ast/statements.hpp index 533d5a7..ef6a65d 100644 --- a/src/ast/statements.hpp +++ b/src/ast/statements.hpp @@ -83,7 +83,6 @@ struct Continue : Stmt } }; -// #todo elif struct If : Stmt { ptr condition; diff --git a/src/codegen.hpp b/src/codegen.hpp index 69f7074..dfa1de2 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -27,6 +27,7 @@ class RubyGenerator { std::ostringstream output; int indent_level = 0; + bool write_contains = false; void write_indent() { @@ -52,6 +53,29 @@ public: generate_stmt(*stmt); } + std::string tmp = output.str(); + output.str(""); + output.clear(); + + if (write_contains) + { + output << "def __contains__(container, item)" << "\n" + << " if container.is_a?(Hash)" + << "\n" + << " container.key?(item)" + << "\n" + << " else" + << "\n" + << " container.include?(item)" + << "\n" + << " end" + << "\n" + << "end" + << "\n"; + } + + output << tmp; + return output.str(); } @@ -271,9 +295,22 @@ private: } else if (auto *binop = dynamic_cast(&expr)) { - generate_expr(*binop->left); - write(" " + binop->op.symbol() + " "); - generate_expr(*binop->right); + if (binop->op.kind == BinaryOperator::IN) + { + write("__contains__("); + generate_expr(*binop->right); + write(", "); + generate_expr(*binop->left); + write(")"); + write_contains = true; + return; + } + else + { + generate_expr(*binop->left); + write(" " + binop->op.symbol() + " "); + generate_expr(*binop->right); + } } else if (auto *call = dynamic_cast(&expr)) { @@ -416,7 +453,18 @@ private: } generate_expr(*attr->value); - output << "." << attr->attr; + if (attr->attr == "lower") + { + output << "." << "downcase"; + } + else if (attr->attr == "strip") + { + output << "." << "delete"; + } + else + { + output << "." << attr->attr; + } } else if (auto *subscript = dynamic_cast(&expr)) { diff --git a/src/parser.y b/src/parser.y index e52f5d9..31ee612 100644 --- a/src/parser.y +++ b/src/parser.y @@ -62,6 +62,7 @@ %type >> stmt_list %type > params %type >> elif_chain +%type , std::unique_ptr>>> dict_pairs %% @@ -122,7 +123,8 @@ expr: | expr MINUS expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::SUB), std::move($1), std::move($3)); } | expr LT expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::LT) , std::move($1), std::move($3)); } | expr GT expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::GT) , std::move($1), std::move($3)); } - | expr GTE expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::GTE) , std::move($1), std::move($3)); } + | expr GTE expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::GTE) , std::move($1), std::move($3)); } + | expr NE expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::NE) , std::move($1), std::move($3)); } | expr KW_AND expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::AND) , std::move($1), std::move($3)); } | expr KW_OR expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::OR) , std::move($1), std::move($3)); } | expr LTE expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::LTE) , std::move($1), std::move($3)); } @@ -130,6 +132,7 @@ expr: | expr MODULO expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::MOD) , std::move($1), std::move($3)); } | expr EQ expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::EQ) , std::move($1), std::move($3)); } | expr DIVIDE expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::DIV) , std::move($1), std::move($3)); } + | expr KW_IN expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::IN) , std::move($1), std::move($3)); } | KW_NOT expr { $$ = std::make_unique( UnaryOperator(UnaryOperator::NOT) , std::move($2)); } | expr L_BRACKET expr R_BRACKET { $$ = std::make_unique(std::move($1), std::move($3));} | expr L_BRACKET slice R_BRACKET{ $$ = std::make_unique(std::move($1), std::move($3)); } @@ -139,6 +142,8 @@ expr: | L_BRACKET args R_BRACKET { $$ = std::make_unique(std::move($2));} | L_PAREN tuple R_PAREN { $$ = std::make_unique(std::move($2));} | MINUS expr { $$ = std::make_unique(UnaryOperator(UnaryOperator::NEG), std::move($2));} + | L_BRACE dict_pairs R_BRACE { $$ = std::make_unique(std::move($2)); } + | L_BRACE R_BRACE { $$ = std::make_unique(std::vector, std::unique_ptr>>()); } ; slice: @@ -180,6 +185,19 @@ elif_chain: $$ = std::move($5); } ; + +dict_pairs: + expr COLON expr + { + $$ = std::vector, std::unique_ptr>>(); + $$.push_back(std::make_pair(std::move($1), std::move($3))); + } + | dict_pairs COMMA expr COLON expr + { + $1.push_back(std::make_pair(std::move($3), std::move($5))); + $$ = std::move($1); + } + ; %% namespace yy { void parser::error(const std::string& msg) diff --git a/test/ruby/27.in b/test/ruby/27.in index 0d36327..32256c6 100644 --- a/test/ruby/27.in +++ b/test/ruby/27.in @@ -5,27 +5,14 @@ def count_words(text): for word in words: word = word.strip('.,!?') if word in word_count: - word_count[word] += 1 + word_count[word] = 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 +print(f"Word counts: {counts}") \ No newline at end of file diff --git a/test/ruby/27.out b/test/ruby/27.out new file mode 100644 index 0000000..7fcb5d8 --- /dev/null +++ b/test/ruby/27.out @@ -0,0 +1,24 @@ +def __contains__(container, item) + if container.is_a?(Hash) + container.key?(item) + else + container.include?(item) + end +end +def count_words(text) + words = text.downcase().split() + word_count = {} + for word in words + word = word.delete('.,!?') + if __contains__(word_count, word) + word_count[word] = word_count[word] + 1 + else + word_count[word] = 1 + end + end + return word_count +end +text = "Hello world hello there world hello" +counts = count_words(text) +print("Text: #{text}", "\n") +print("Word counts: #{counts}", "\n") diff --git a/test/ruby/28.in b/test/ruby/28.in index 0d36327..ea14210 100644 --- a/test/ruby/28.in +++ b/test/ruby/28.in @@ -5,7 +5,7 @@ def count_words(text): for word in words: word = word.strip('.,!?') if word in word_count: - word_count[word] += 1 + word_count[word] = word_count[word] + 1 else: word_count[word] = 1 diff --git a/test/ruby/34.in b/test/ruby/34.in new file mode 100644 index 0000000..0d36327 --- /dev/null +++ b/test/ruby/34.in @@ -0,0 +1,31 @@ +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