// GenPySwift.fu - Python/Swift code generator
//
// Copyright (C) 2020-2025 Piotr Fusik
//
// This file is part of Fusion Transpiler,
// see https://github.com/fusionlanguage/fut
//
// Fusion Transpiler is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Fusion Transpiler is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Fusion Transpiler. If not, see http://www.gnu.org/licenses/
public abstract class GenPySwift : GenBase
{
protected override void WriteDocPara!(FuDocPara para, bool many)
{
if (many) {
WriteNewLine();
StartDocLine();
WriteNewLine();
StartDocLine();
}
foreach (FuDocInline inline in para.Children) {
switch (inline) {
case FuDocText text:
Write(text.Text);
break;
case FuDocCode code:
WriteChar('`');
WriteDocCode(code.Text);
WriteChar('`');
break;
case FuDocLine:
WriteNewLine();
StartDocLine();
break;
default:
assert false;
}
}
}
protected abstract string GetDocBullet();
protected override void WriteDocList!(FuDocList list)
{
WriteNewLine();
foreach (FuDocPara item in list.Items) {
Write(GetDocBullet());
WriteDocPara(item, false);
WriteNewLine();
}
StartDocLine();
}
protected override void WriteLocalName!(FuSymbol symbol, FuPriority parent)
{
if (symbol is FuMember member) {
if (member.IsStatic())
WriteName(this.CurrentMethod.Parent);
else
Write("self");
WriteChar('.');
}
WriteName(symbol);
}
internal override void VisitAggregateInitializer!(FuAggregateInitializer expr)
{
Write("[ ");
WriteCoercedLiterals(expr.Type.AsClassType().GetElementType(), expr.Items);
Write(" ]");
}
internal override void VisitPrefixExpr!(FuPrefixExpr expr, FuPriority parent)
{
switch (expr.Op) {
case FuToken.Increment:
case FuToken.Decrement:
expr.Inner.Accept(this, parent);
break;
default:
base.VisitPrefixExpr(expr, parent);
break;
}
}
internal override void VisitPostfixExpr!(FuPostfixExpr expr, FuPriority parent)
{
switch (expr.Op) {
case FuToken.Increment:
case FuToken.Decrement:
expr.Inner.Accept(this, parent);
break;
default:
base.VisitPostfixExpr(expr, parent);
break;
}
}
static bool IsPtr(FuExpr expr) => expr.Type is FuClassType klass && klass.Class.Id != FuId.StringClass && !(klass is FuStorageType);
protected abstract string GetReferenceEqOp(bool not);
protected override void WriteEqual!(FuExpr left, FuExpr right, FuPriority parent, bool not)
{
if (IsPtr(left) || IsPtr(right))
WriteEqualExpr(left, right, parent, GetReferenceEqOp(not));
else
base.WriteEqual(left, right, parent, not);
}
protected virtual void WriteExpr!(FuExpr expr, FuPriority parent)
{
expr.Accept(this, parent);
}
protected virtual void WriteElementCoerced!(FuType type, FuExpr value)
{
WriteCoerced(type, value, FuPriority.Argument);
}
protected void WriteListAppend!(FuExpr obj, List<FuExpr#> args)
{
WritePostfix(obj, ".append(");
FuType elementType = obj.Type.AsClassType().GetElementType();
if (args.Count == 0)
WriteNewStorage(elementType);
else
WriteElementCoerced(elementType, args[0]);
WriteChar(')');
}
protected virtual bool VisitPreCall!(FuCallExpr call) => false;
protected bool VisitXcrement!(FuExpr expr, bool postfix, bool write)
{
bool seen;
switch (expr) {
case FuVar def:
return def.Value != null && VisitXcrement(def.Value, postfix, write);
case FuAggregateInitializer:
case FuLiteral:
case FuLambdaExpr:
return false;
case FuInterpolatedString interp:
seen = false;
foreach (FuInterpolatedPart part in interp.Parts)
seen |= VisitXcrement(part.Argument, postfix, write);
return seen;
case FuSymbolReference symbol:
return symbol.Left != null && VisitXcrement(symbol.Left, postfix, write);
case FuUnaryExpr unary:
if (unary.Inner == null) // new C()
return false;
seen = VisitXcrement(unary.Inner, postfix, write);
if ((unary.Op == FuToken.Increment || unary.Op == FuToken.Decrement) && postfix == unary is FuPostfixExpr) {
if (write) {
WriteExpr(unary.Inner, FuPriority.Assign);
WriteLine(unary.Op == FuToken.Increment ? " += 1" : " -= 1");
}
seen = true;
}
return seen;
case FuBinaryExpr binary:
seen = VisitXcrement(binary.Left, postfix, write);
if (binary.Op == FuToken.Is)
return seen;
if (binary.Op == FuToken.CondAnd || binary.Op == FuToken.CondOr)
assert !VisitXcrement(binary.Right, postfix, false);
else
seen |= VisitXcrement(binary.Right, postfix, write);
return seen;
case FuSelectExpr select:
seen = VisitXcrement(select.Cond, postfix, write);
assert !VisitXcrement(select.OnTrue, postfix, false);
assert !VisitXcrement(select.OnFalse, postfix, false);
return seen;
case FuCallExpr call:
seen = VisitXcrement(call.Method, postfix, write);
foreach (FuExpr arg in call.Arguments)
seen |= VisitXcrement(arg, postfix, write);
if (!postfix)
seen |= VisitPreCall(call);
return seen;
default:
assert false;
}
}
internal override void VisitExpr!(FuExpr statement)
{
VisitXcrement(statement, false, true);
if (!(statement is FuUnaryExpr unary) || (unary.Op != FuToken.Increment && unary.Op != FuToken.Decrement)) {
WriteExpr(statement, FuPriority.Statement);
WriteNewLine();
if (statement is FuVar def)
WriteInitCode(def);
}
VisitXcrement(statement, true, true);
CleanupTemporaries();
}
protected override void EndStatement!()
{
WriteNewLine();
}
protected abstract void OpenChild!();
protected abstract void CloseChild!();
protected override void WriteChild!(FuStatement! statement)
{
OpenChild();
statement.AcceptStatement(this);
CloseChild();
}
internal override void VisitBlock!(FuBlock statement)
{
WriteStatements(statement.Statements);
}
bool OpenCond!(string statement, FuExpr cond, FuPriority parent)
{
VisitXcrement(cond, false, true);
Write(statement);
WriteExpr(cond, parent);
OpenChild();
return VisitXcrement(cond, true, true);
}
protected virtual void WriteContinueDoWhile!(FuExpr cond)
{
OpenCond("if ", cond, FuPriority.Argument);
WriteLine("continue");
CloseChild();
VisitXcrement(cond, true, true);
WriteLine("break");
}
protected virtual bool NeedCondXcrement!(FuLoop loop) => loop.Cond != null;
void EndBody!(FuLoop loop)
{
if (loop is FuFor forLoop) {
if (forLoop.IsRange)
return;
VisitOptionalStatement(forLoop.Advance);
}
if (NeedCondXcrement(loop))
VisitXcrement(loop.Cond, false, true);
}
internal override void VisitContinue!(FuContinue statement)
{
if (statement.Loop is FuDoWhile doWhile)
WriteContinueDoWhile(doWhile.Cond);
else {
EndBody(statement.Loop);
WriteLine("continue");
}
}
void OpenWhileTrue!()
{
Write("while ");
VisitLiteralTrue();
OpenChild();
}
protected abstract string GetIfNot();
internal override void VisitDoWhile!(FuDoWhile statement)
{
OpenWhileTrue();
statement.Body.AcceptStatement(this);
if (statement.Body.CompletesNormally()) {
OpenCond(GetIfNot(), statement.Cond, FuPriority.Primary);
WriteLine("break");
CloseChild();
VisitXcrement(statement.Cond, true, true);
}
CloseChild();
}
protected virtual void OpenWhile!(FuLoop loop)
{
OpenCond("while ", loop.Cond, FuPriority.Argument);
}
void CloseWhile!(FuLoop loop)
{
loop.Body.AcceptStatement(this);
if (loop.Body.CompletesNormally())
EndBody(loop);
CloseChild();
if (NeedCondXcrement(loop)) {
if (loop.HasBreak && VisitXcrement(loop.Cond, true, false)) {
Write("else");
OpenChild();
VisitXcrement(loop.Cond, true, true);
CloseChild();
}
else
VisitXcrement(loop.Cond, true, true);
}
}
protected abstract void WriteForRange!(FuVar iter, FuBinaryExpr cond, long rangeStep);
internal override void VisitFor!(FuFor statement)
{
if (statement.IsRange) {
assert statement.Init is FuVar indVar;
Write("for ");
if (statement.IsIndVarUsed)
WriteName(indVar);
else
WriteChar('_');
Write(" in ");
assert statement.Cond is FuBinaryExpr cond;
WriteForRange(indVar, cond, statement.RangeStep);
WriteChild(statement.Body);
}
else {
VisitOptionalStatement(statement.Init);
if (statement.Cond != null)
OpenWhile(statement);
else
OpenWhileTrue();
CloseWhile(statement);
}
}
protected abstract void WriteElseIf!();
internal override void VisitIf!(FuIf statement)
{
if (TryWriteIfTryParse(statement))
return;
bool condPostXcrement = OpenCond("if ", statement.Cond, FuPriority.Argument);
statement.OnTrue.AcceptStatement(this);
CloseChild();
if (statement.OnFalse == null && condPostXcrement && !statement.OnTrue.CompletesNormally())
VisitXcrement(statement.Cond, true, true);
else if (statement.OnFalse != null || condPostXcrement) {
if (!condPostXcrement && statement.OnFalse is FuIf! childIf && !VisitXcrement(childIf.Cond, false, false)) {
WriteElseIf();
VisitIf(childIf);
}
else {
Write("else");
OpenChild();
VisitXcrement(statement.Cond, true, true);
VisitOptionalStatement(statement.OnFalse);
CloseChild();
}
}
}
protected abstract void WriteResultVar!();
internal override void VisitReturn!(FuReturn statement)
{
if (statement.Value == null)
WriteLine("return");
else {
VisitXcrement(statement.Value, false, true);
WriteTemporaries(statement.Value);
if (VisitXcrement(statement.Value, true, false)) {
WriteResultVar(); // FIXME: name clash? only matters if return ... result++, unlikely
Write(" = ");
WriteCoercedExpr(this.CurrentMethod.Type, statement.Value);
WriteNewLine();
VisitXcrement(statement.Value, true, true);
WriteLine("return result");
}
else {
Write("return ");
WriteCoercedExpr(this.CurrentMethod.Type, statement.Value);
WriteNewLine();
}
CleanupTemporaries();
}
}
internal override void VisitWhile!(FuWhile statement)
{
OpenWhile(statement);
CloseWhile(statement);
}
}