//
// Copyright (c) 2025, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 29 May 2025 Brian Frank Split out from CodePrinter
//
using build
using compiler
using util
**
** CodePrinter routing for Expr nodes
**
mixin ExprPrinter : CodePrinter
{
virtual This expr(Expr x)
{
m.exprStack.push(x)
doExpr(x)
m.exprStack.pop
return this
}
virtual This doExpr(Expr x)
{
switch (x.id)
{
// literals
case ExprId.nullLiteral: return nullLiteral(x)
case ExprId.trueLiteral: return trueLiteral(x)
case ExprId.falseLiteral: return falseLiteral(x)
case ExprId.intLiteral: return intLiteral(x)
case ExprId.floatLiteral: return floatLiteral(x)
case ExprId.decimalLiteral: return decimalLiteral(x)
case ExprId.strLiteral: return strLiteral(x)
case ExprId.durationLiteral: return durationLiteral(x)
case ExprId.uriLiteral: return uriLiteral(x)
case ExprId.typeLiteral: return typeLiteral(x)
case ExprId.slotLiteral: return slotLiteral(x)
case ExprId.rangeLiteral: return rangeLiteral(x)
case ExprId.listLiteral: return listLiteral(x)
case ExprId.mapLiteral: return mapLiteral(x)
// comparison / type checking
case ExprId.boolNot: return notExpr(x)
case ExprId.cmpNull: return compareNullExpr(x)
case ExprId.cmpNotNull: return compareNotNullExpr(x)
case ExprId.same: return sameExpr(x)
case ExprId.notSame: return notSameExpr(x)
case ExprId.boolOr: return orExpr(x)
case ExprId.boolAnd: return andExpr(x)
case ExprId.isExpr: return isExpr(x)
case ExprId.isnotExpr: return isnotExpr(x)
case ExprId.asExpr: return asExpr(x)
case ExprId.elvis: return elvisExpr(x)
case ExprId.coerce: return coerceExpr(x)
// local var handling
case ExprId.localVar: return localExpr(x)
case ExprId.thisExpr: return thisExpr(x)
case ExprId.superExpr: return superExpr(x)
case ExprId.itExpr: return itExpr(x)
// complicated stuff
case ExprId.call: return callExpr(x)
case ExprId.staticTarget: return staticTargetExpr(x)
case ExprId.construction: return ctorExpr(x)
case ExprId.shortcut: return shortcutExpr(x)
case ExprId.field: return fieldExpr(x)
case ExprId.assign: return assignExpr(x)
case ExprId.closure: return closureExpr(x)
case ExprId.ternary: return ternaryExpr(x)
case ExprId.throwExpr: return throwExpr(x)
default: throw Err("${x.id}: ${x.toStr}")
}
}
// literals
abstract This nullLiteral(LiteralExpr x)
abstract This trueLiteral(LiteralExpr x)
abstract This falseLiteral(LiteralExpr x)
abstract This intLiteral(LiteralExpr x)
abstract This floatLiteral(LiteralExpr x)
abstract This decimalLiteral(LiteralExpr x)
abstract This strLiteral(LiteralExpr x)
abstract This durationLiteral(LiteralExpr x)
abstract This uriLiteral(LiteralExpr x)
abstract This typeLiteral(LiteralExpr x)
abstract This slotLiteral(SlotLiteralExpr x)
abstract This rangeLiteral(RangeLiteralExpr x)
abstract This listLiteral(ListLiteralExpr x)
abstract This mapLiteral(MapLiteralExpr x)
// logic, type checking, comparisons
abstract This compareExpr(Expr lhs, Token op, Expr rhs)
abstract This compareNullExpr(UnaryExpr x)
abstract This compareNotNullExpr(UnaryExpr x)
abstract This notExpr(UnaryExpr x)
abstract This elvisExpr(BinaryExpr x)
abstract This sameExpr(BinaryExpr x)
abstract This notSameExpr(BinaryExpr x)
abstract This orExpr(CondExpr x)
abstract This andExpr(CondExpr x)
abstract This isExpr(TypeCheckExpr x)
abstract This isnotExpr(TypeCheckExpr x)
abstract This asExpr(TypeCheckExpr x)
abstract This coerceExpr(TypeCheckExpr x)
// local vars
abstract This localExpr(LocalVarExpr x)
abstract This thisExpr(LocalVarExpr x)
abstract This superExpr(LocalVarExpr x)
abstract This itExpr(LocalVarExpr x)
// misc stuff
abstract This staticTargetExpr(StaticTargetExpr x)
abstract This fieldExpr(FieldExpr x)
abstract This closureExpr(ClosureExpr x)
abstract This ternaryExpr(TernaryExpr x)
abstract This throwExpr(ThrowExpr x)
abstract This assignExpr(BinaryExpr x)
//////////////////////////////////////////////////////////////////////////
// Calls
//////////////////////////////////////////////////////////////////////////
** Call expression handling
virtual This callExpr(CallExpr x)
{
if (x.isDynamic) return trapExpr(x)
if (x.isSafe) return safeCallExpr(x)
if (x.target != null && !x.synthetic)
{
if (x.args.size == 0)
{
op := unaryOperator(x.method.qname)
if (op != null) return unaryExpr(op, x.target)
}
if (x.args.size == 1)
{
op := binaryOperator(x.method.qname)
if (op != null) return binaryExpr(x.target, op, x.args.first)
}
}
return callMethodExpr(x)
}
** Shortcut special handling for comparison, otherwise route to callExpr
virtual This shortcutExpr(ShortcutExpr x)
{
if (x.isCompare) return compareExpr(x.target, x.opToken, x.args.first)
if (x.isPostfixLeave) return postfixLeaveExpr(x)
if (x.isAssign) return shortcutAssignExpr(x)
return callExpr(x)
}
** Write unary expression with optional grouping parens
virtual This unaryExpr(Str op, Expr operand)
{
w("(").w(op).w("(").expr(operand).w("))")
}
** Write binary expression with optional grouping parens
virtual This binaryExpr(Expr lhs, Str op, Expr rhs)
{
w("(").expr(lhs).w(")").sp.w(op).sp.w("(").expr(rhs).w(")")
}
** Write list of cond with operator with optional grouping parens
virtual This condExpr(Str op, Expr[] operands)
{
oparen
operands.each |x, i|
{
if (i > 0) sp.w(op).sp
expr(x)
}
return cparen
}
** Return operator if given method qname maps to unary operator or null
virtual Str? unaryOperator(Str qname) { null }
** Return operator if given method qname maps to binary operator or null
virtual Str? binaryOperator(Str qname) { null }
** Normal call method
abstract This callMethodExpr(CallExpr x)
** Trap call expression handling
abstract This trapExpr(CallExpr x)
** Null safe call expression handling
abstract This safeCallExpr(CallExpr x)
** Constructor call expression
abstract This ctorExpr(CallExpr x)
** Handle special assign+math operators: '+= -= *= /='
abstract This shortcutAssignExpr(ShortcutExpr x)
** Handle postfix ++ or --
abstract This postfixLeaveExpr(ShortcutExpr x)
//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////
** Top expr - if false might need to wrap some exprs in parens
Bool isTopExpr()
{
// if only one expr in stack
if (m.exprStack.size <= 1) return true
// if this is a call arg, don't need parens
peek := m.exprStack[-2]
call := peek as CallExpr
if (call != null && call.target !== m.exprStack.last) return true
// adds for list/map literal
switch (peek.id)
{
case ExprId.listLiteral:
case ExprId.mapLiteral: return true
}
return false
}
** Open paren only if not top expr
This oparen() { isTopExpr ? this : w("(") }
** Close paren only if not top expr
This cparen() { isTopExpr ? this : w(")") }
}