some progress

This commit is contained in:
bronku 2025-12-05 11:27:45 +01:00
parent 5c9abff73f
commit f92f5ef7f6
11 changed files with 235 additions and 4 deletions

3
shortcomings.md Normal file
View file

@ -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

View file

@ -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 "?";
}

View file

@ -2,6 +2,27 @@
#include "ast.hpp"
#include <sstream>
// 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<const Identifier *>(&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<const String *>(&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<const BinOp *>(&expr))
{
@ -217,6 +255,117 @@ private:
}
else if (auto *call = dynamic_cast<const Call *>(&expr))
{
if (auto *func_id = dynamic_cast<const Identifier *>(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++)

View file

@ -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; }
<STRING_TSQ>\'\'\' { BEGIN(INITIAL); yylval->as<std::string>() = yytext; return yy::parser::token::TOK_STRING; }
<STRING_TSQ>. { yymore(); }
f\" { yymore(); BEGIN(STRING_DQ); }
\" { yymore(); BEGIN(STRING_DQ); }
<STRING_DQ>\\\" { yymore(); }
<STRING_DQ>\" { BEGIN(INITIAL); yylval->as<std::string>() = yytext; return yy::parser::token::TOK_STRING; }
<STRING_DQ>. { yymore(); }
f\' { yymore(); BEGIN(STRING_SQ); }
\' { yymore(); BEGIN(STRING_SQ); }
<STRING_SQ>\\\' { yymore(); }
<STRING_SQ>\' { BEGIN(INITIAL); yylval->as<std::string>() = yytext; return yy::parser::token::TOK_STRING; }

View file

@ -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);
}

View file

@ -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<BinOp>( BinaryOperator(BinaryOperator::LT) , std::move($1), std::move($3)); }
| expr KW_AND expr { $$ = std::make_unique<BinOp>( BinaryOperator(BinaryOperator::AND) , std::move($1), std::move($3)); }
| expr KW_OR expr { $$ = std::make_unique<BinOp>( BinaryOperator(BinaryOperator::OR) , std::move($1), std::move($3)); }
| expr LTE expr { $$ = std::make_unique<BinOp>( BinaryOperator(BinaryOperator::LTE) , std::move($1), std::move($3)); }
| expr POWER expr { $$ = std::make_unique<BinOp>( BinaryOperator(BinaryOperator::POWER) , std::move($1), std::move($3)); }
| expr MODULO expr { $$ = std::make_unique<BinOp>( BinaryOperator(BinaryOperator::MOD) , std::move($1), std::move($3)); }
| expr EQ expr { $$ = std::make_unique<BinOp>( BinaryOperator(BinaryOperator::EQ) , std::move($1), std::move($3)); }
| expr DIVIDE expr { $$ = std::make_unique<BinOp>( BinaryOperator(BinaryOperator::DIV) , std::move($1), std::move($3)); }
| KW_NOT expr { $$ = std::make_unique<UnaryOpExpr>( UnaryOperator(UnaryOperator::NOT) , std::move($2)); }
| expr L_BRACKET expr R_BRACKET { $$ = std::make_unique<Subscript>(std::move($1), std::move($3));}
| expr L_BRACKET slice R_BRACKET{ $$ = std::make_unique<SliceSubscript>(std::move($1), std::move($3)); }

View file

@ -1 +1 @@
print(x, y)
print(x, y, "\n")

View file

@ -1,3 +1,3 @@
for i in items
print(i)
print(i, "\n")
end

23
test/ruby/23.out Normal file
View file

@ -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

20
test/ruby/24.in Normal file
View file

@ -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))

20
test/ruby/24.out Normal file
View file

@ -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")