diff --git a/shortcomings.md b/shortcomings.md new file mode 100644 index 0000000..598013d --- /dev/null +++ b/shortcomings.md @@ -0,0 +1,3 @@ +- string processing is very primitive, it just checks for f at the beggining, and then adds #, before { if so +- no elif +- no lambdas \ No newline at end of file diff --git a/src/ast/operators.hpp b/src/ast/operators.hpp index ae7f553..b443a23 100644 --- a/src/ast/operators.hpp +++ b/src/ast/operators.hpp @@ -22,7 +22,8 @@ struct BinaryOperator BIT_OR, BIT_XOR, LSHIFT, - RSHIFT + RSHIFT, + POWER }; Kind kind; @@ -69,6 +70,8 @@ struct BinaryOperator return "<<"; case RSHIFT: return ">>"; + case POWER: + return "**"; default: return "?"; } diff --git a/src/codegen.hpp b/src/codegen.hpp index 773107d..9af7165 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -2,6 +2,27 @@ #include "ast.hpp" #include +// greatly simplified +std::string convert_string(std::string input) +{ + std::ostringstream output; + for (int i = 1; i < input.size(); i++) + { + if (input[i] != '{') + { + output << input[i]; + continue; + } + if (input[i - 1] != '\\') + { + output << "#{"; + continue; + } + output << input[i]; + } + return output.str(); +} + class RubyGenerator { std::ostringstream output; @@ -67,6 +88,7 @@ private: if (!if_stmt->else_body.empty()) { + write_indent(); write("else\n"); indent_level++; for (const auto &s : if_stmt->else_body) @@ -203,11 +225,27 @@ private: } else if (auto *id = dynamic_cast(&expr)) { + std::string ident = id->name; + if (ident == "True") + { + output << "true"; + return; + } + if (ident == "False") + { + output << "false"; + return; + } write(id->name); } else if (auto *str = dynamic_cast(&expr)) { - write(str->value); + std::string value = str->value; + if (value[0] == 'f') + { + value = convert_string(value); + } + output << value; } else if (auto *binop = dynamic_cast(&expr)) { @@ -217,6 +255,117 @@ private: } else if (auto *call = dynamic_cast(&expr)) { + + if (auto *func_id = dynamic_cast(call->func.get())) + { + if (func_id->name == "range") + { + // Handle range() specially for for loops + if (call->args.size() == 1) + { + // range(stop) -> 0...stop + write("0..."); + generate_expr(*call->args[0]); + } + else if (call->args.size() == 2) + { + // range(start, stop) -> start...stop + generate_expr(*call->args[0]); + write("..."); + generate_expr(*call->args[1]); + } + else if (call->args.size() == 3) + { + // range(start, stop, step) - need custom handling + write("("); + generate_expr(*call->args[0]); + write("..."); + generate_expr(*call->args[1]); + write(").step("); + generate_expr(*call->args[2]); + write(")"); + } + else + { + // Fallback to regular call + write("range("); + for (size_t i = 0; i < call->args.size(); i++) + { + if (i > 0) + write(", "); + generate_expr(*call->args[i]); + } + write(")"); + } + return; + } + else if (func_id->name == "int") + { + // int(x) -> Integer(x) + write("Integer("); + if (!call->args.empty()) + { + generate_expr(*call->args[0]); + } + write(")"); + return; + } + else if (func_id->name == "float") + { + // float(x) -> Float(x) + write("Float("); + if (!call->args.empty()) + { + generate_expr(*call->args[0]); + } + write(")"); + return; + } + else if (func_id->name == "str") + { + // str(x) -> x.to_s + if (!call->args.empty()) + { + generate_expr(*call->args[0]); + write(".to_s"); + } + else + { + write("\"\""); + } + return; + } + else if (func_id->name == "len") + { + // len(x) -> x.length or x.size + if (!call->args.empty()) + { + generate_expr(*call->args[0]); + write(".length"); + } + else + { + write("0"); + } + return; + } + else if (func_id->name == "print") + { + write("print("); + if (!call->args.empty()) + { + for (size_t i = 0; i < call->args.size(); i++) + { + if (i > 0) + write(", "); + generate_expr(*call->args[i]); + } + } + write(", \"\\n\")"); + return; + } + } + generate_expr(*call->func); write("("); for (size_t i = 0; i < call->args.size(); i++) diff --git a/src/lexer.l b/src/lexer.l index 56e02db..a569c42 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -72,6 +72,7 @@ continue { return yy::parser::token::TOK_KW_CONTINUE; } \>\>\= { return yy::parser::token::TOK_RSHIFT_ASSIGN; } \+ { return yy::parser::token::TOK_PLUS; } \- { return yy::parser::token::TOK_MINUS; } +\*\* { return yy::parser::token::TOK_POWER; } \* { return yy::parser::token::TOK_MULTIPLY; } \/ { return yy::parser::token::TOK_DIVIDE; } \% { return yy::parser::token::TOK_MODULO; } @@ -100,11 +101,13 @@ continue { return yy::parser::token::TOK_KW_CONTINUE; } \'\'\' { BEGIN(INITIAL); yylval->as() = yytext; return yy::parser::token::TOK_STRING; } . { yymore(); } +f\" { yymore(); BEGIN(STRING_DQ); } \" { yymore(); BEGIN(STRING_DQ); } \\\" { yymore(); } \" { BEGIN(INITIAL); yylval->as() = yytext; return yy::parser::token::TOK_STRING; } . { yymore(); } +f\' { yymore(); BEGIN(STRING_SQ); } \' { yymore(); BEGIN(STRING_SQ); } \\\' { yymore(); } \' { BEGIN(INITIAL); yylval->as() = yytext; return yy::parser::token::TOK_STRING; } diff --git a/src/main.cpp b/src/main.cpp index 7652fb1..9203afd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -90,6 +90,10 @@ int main(int argc, char *argv[]) if (write_ruby) { + if (ast_dump) + { + std::cout << "\n"; + } RubyGenerator generator; std::cout << generator.generate(*ast_root); } diff --git a/src/parser.y b/src/parser.y index 0dc5d40..494870c 100644 --- a/src/parser.y +++ b/src/parser.y @@ -48,6 +48,7 @@ %left PLUS MINUS %left MULTIPLY DIVIDE MODULO +%left POWER %right ASSIGN PLUS_ASSIGN MINUS_ASSIGN MULTIPLY_ASSIGN DIVIDE_ASSIGN MODULO_ASSIGN %right BIT_AND_ASSIGN BIT_OR_ASSIGN BIT_XOR_ASSIGN LSHIFT_ASSIGN RSHIFT_ASSIGN @@ -124,6 +125,11 @@ expr: | expr LT expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::LT) , 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)); } + | expr POWER expr { $$ = std::make_unique( BinaryOperator(BinaryOperator::POWER) , std::move($1), std::move($3)); } + | 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)); } | 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)); } diff --git a/test/ruby/08.out b/test/ruby/08.out index 99ceb54..849c803 100644 --- a/test/ruby/08.out +++ b/test/ruby/08.out @@ -1 +1 @@ -print(x, y) +print(x, y, "\n") diff --git a/test/ruby/11.out b/test/ruby/11.out index 616a975..cd1d32e 100644 --- a/test/ruby/11.out +++ b/test/ruby/11.out @@ -1,3 +1,3 @@ for i in items - print(i) + print(i, "\n") end diff --git a/test/ruby/23.out b/test/ruby/23.out new file mode 100644 index 0000000..08f5d05 --- /dev/null +++ b/test/ruby/23.out @@ -0,0 +1,23 @@ +def factorial(n) + """Calculate factorial recursively""" + if n <= 1 + return 1 + else + return n * factorial(n - 1) + end +end +def is_prime(num) + if num < 2 + return false + end + for i in 2...Integer(num ** 0.5) + 1 + if num % i == 0 + return false + end + end + return true +end +print("Factorial of 5: #{factorial(5)}", "\n") +for n in [2, 3, 4, 17, 21] + print("#{n} is prime: #{is_prime(n)}", "\n") +end diff --git a/test/ruby/24.in b/test/ruby/24.in new file mode 100644 index 0000000..818aec9 --- /dev/null +++ b/test/ruby/24.in @@ -0,0 +1,20 @@ +def calculate_bmi(weight, height): + bmi = weight / (height ** 2) + + if bmi < 18.5: + category = "Underweight" + else: + if bmi < 25: + category = "Normal" + else: + if bmi < 30: + category = "Overweight" + else: + category = "Obese" + + return f"BMI: {bmi}, Category: {category}" + +# Test +print(calculate_bmi(70, 1.75)) +print(calculate_bmi(90, 1.80)) +print(calculate_bmi(50, 1.65)) \ No newline at end of file diff --git a/test/ruby/24.out b/test/ruby/24.out new file mode 100644 index 0000000..ae8e19f --- /dev/null +++ b/test/ruby/24.out @@ -0,0 +1,20 @@ +def calculate_bmi(weight, height) + bmi = weight / height ** 2 + if bmi < 18.5 + category = "Underweight" + else + if bmi < 25 + category = "Normal" + else + if bmi < 30 + category = "Overweight" + else + category = "Obese" + end + end + end + return "BMI: #{bmi}, Category: #{category}" +end +print(calculate_bmi(70, 1.75), "\n") +print(calculate_bmi(90, 1.80), "\n") +print(calculate_bmi(50, 1.65), "\n")