SquirrelJME/SquirrelJME

View on GitHub
modules/c-source-writer/src/main/java/cc/squirreljme/c/CFile.java

Summary

Maintainability
A
25 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// Multi-Phasic Applications: SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.c;

import cc.squirreljme.c.out.CPivotPoint;
import cc.squirreljme.c.out.CTokenOutput;
import cc.squirreljme.c.std.CFunctionProvider;
import cc.squirreljme.runtime.cldc.debug.Debugging;
import cc.squirreljme.runtime.cldc.util.BooleanArrayList;
import cc.squirreljme.runtime.cldc.util.ByteArrayList;
import cc.squirreljme.runtime.cldc.util.CharacterArrayList;
import cc.squirreljme.runtime.cldc.util.DoubleArrayList;
import cc.squirreljme.runtime.cldc.util.FloatArrayList;
import cc.squirreljme.runtime.cldc.util.IntegerArrayList;
import cc.squirreljme.runtime.cldc.util.LongArrayList;
import cc.squirreljme.runtime.cldc.util.ShortArrayList;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.List;

/**
 * Represents a C file to be written.
 *
 * @since 2023/06/04
 */
public class CFile
    implements Closeable, CSourceWriter
{
    /** The stream to write to. */
    protected final CTokenOutput out;
    
    /** C Block stack. */
    private final Deque<CBlock> _blocks =
        new ArrayDeque<>();
    
    /** Reference to self, for blocks. */
    final Reference<CFile> _fileRef =
        new WeakReference<>(this);
    
    /** Was the last written token a whitespace? */
    private volatile boolean _lastWhitespace;
    
    /** Was the last written token a newline? */
    private volatile boolean _lastNewline;
    
    /**
     * Initializes the C source writer.
     * 
     * @param __out The stream output.
     * @throws NullPointerException On null arguments.
     * @since 2023/05/28
     */
    public CFile(CTokenOutput __out)
        throws NullPointerException
    {
        if (__out == null)
            throw new NullPointerException("NARG");
        
        this.out = __out;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(Object... __values)
        throws IOException
    {
        return this.array(Arrays.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(boolean... __values)
        throws IOException
    {
        return this.array(BooleanArrayList.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(byte... __values)
        throws IOException
    {
        return this.array(ByteArrayList.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(short... __values)
        throws IOException
    {
        return this.array(ShortArrayList.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(char... __values)
        throws IOException
    {
        return this.array(CharacterArrayList.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(int... __values)
        throws IOException
    {
        return this.array(IntegerArrayList.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(long... __values)
        throws IOException
    {
        return this.array(LongArrayList.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(float... __values)
        throws IOException
    {
        return this.array(FloatArrayList.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(double... __values)
        throws IOException
    {
        return this.array(DoubleArrayList.asList(__values));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter array(List<?> __values)
        throws IOException
    {
        CExpressionBuilder.__builder(this)
            .array(__values);
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter character(char __c)
        throws IOException
    {
        // These characters can be input directly
        if (__c == '\t')
            return this.token("\\t");
        else if (__c == '\r')
            return this.token("\\r");
        else if (__c == '\n')
            return this.token("\\n");
        else if (__c == '\'')
            return this.token("\\'");
        else if (__c == '\"')
            return this.token("\\\"");
        else if (__c >= 0x20 && __c < 0x7F)
            return this.token("'" + __c + "'");
        
        // Fallback to number input
        return this.number(CPrimitiveNumberType.UNSIGNED, (int)__c);
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/28
     */
    @Override
    public void close()
        throws IOException
    {
        // Close the output
        this.out.close();
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CBlock curly()
        throws IOException
    {
        // Output open block
        CBlock rv = new CBlock(this, "}");
        this.token("{");
        
        // Setup new block
        this.__pushBlock(rv, true);
        
        return rv;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/04
     */
    @Override
    public CSourceWriter declare(CFunctionType __function)
        throws IOException, NullPointerException
    {
        if (__function == null)
            throw new NullPointerException("NARG");
        
        // Emit
        this.tokens(__function.declareTokens(null), ";");
        
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/19
     */
    @Override
    public CSourceWriter declare(CVariable __var)
        throws IOException, NullPointerException
    {
        if (__var == null)
            throw new NullPointerException("NARG");
        
        this.tokens(__var.declareTokens(), ";");
        
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/19
     */
    @Override
    public <B extends CBlock> B define(Class<B> __blockType, CVariable __var)
        throws IOException, NullPointerException
    {
        if (__blockType == null || __var == null)
            throw new NullPointerException("NARG");
        
        // Get the root type we are using
        CType type = __var.type();
        CType subType = null;
        if (type instanceof CModifiedType)
            subType = ((CModifiedType)type).type;
        else
            subType = type;
        
        // Structure type
        if (subType instanceof CStructType)
        {
            CStructType struct = (CStructType)subType;
            
            // Open struct
            this.tokens(type.declareTokens(null),
                __var.name, "=", "{");
            
            // Setup block
            CStructVariableBlock rv = new CStructVariableBlock(
                this, struct, "};");
            this.__pushBlock(rv, true);
            return __blockType.cast(rv);
        }
        
        // Unknown??
        else
            throw Debugging.todo(__var.getClass());
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/04
     */
    @Override
    public CSourceWriter define(CStructType __what)
        throws IOException, NullPointerException
    {
        if (__what == null)
            throw new NullPointerException("NARG");
        
        // Open struct saying what it is
        this.token(__what.declareTokens(null));
        try (CBlock block = this.curly())
        {
            for (CVariable member : __what.members)
                block.declare(member);
        }
        
        // End semicolon
        this.token(";");
        
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/30
     */
    @Override
    public CFunctionBlock define(CFunctionType __function)
        throws IOException, NullPointerException
    {
        if (__function == null)
            throw new NullPointerException("NARG");
        
        // Open up function
        this.tokens(__function.declareTokens(null), "{");
        
        // Push block for it
        return this.__pushBlock(new CFunctionBlock(this), true);
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/08/12
     */
    @Override
    public CSourceWriter define(CVariable __variable,
        CExpression __expression)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        if (__variable == null || __expression == null)
            throw new NullPointerException("NARG");
        
        return this.tokens(__variable.declareTokens(), "=",
            __expression, ";");
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/24
     */
    @Override
    public CSourceWriter expression(CExpression __expression)
        throws IOException, NullPointerException
    {
        if (__expression == null)
            throw new NullPointerException("NARG");
        
        this.token(__expression);
        return this;
    }
    
    /**
     * Flushes the output.
     * 
     * @throws IOException On flush errors.
     * @since 2023/05/28
     */
    public void flush()
        throws IOException
    {
        if (this.out instanceof OutputStream)
            ((OutputStream)this.out).flush();
        else if (this.out instanceof Writer)
            ((Writer)this.out).flush();
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/28
     */
    @Override
    public CSourceWriter freshLine()
        throws IOException
    {
        // Ignore if there was a newline
        if (this._lastNewline)
            return this;
        
        // Emit newline
        this.out.newLine(true);
        
        // Last was whitespace
        this._lastWhitespace = true;
        this._lastNewline = true;
        
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/31
     */
    @Override
    public CSourceWriter functionCall(
        CFunctionType __function, CExpression... __args)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        if (__function == null)
            throw new NullPointerException("NARG");
        
        // Just forward to expression __builder
        CExpressionBuilder.__builder(this)
            .functionCall(__function, __args)
            .build();
        this.tokens(";");
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/24
     */
    @Override
    public CSourceWriter functionCall(CFunctionProvider __function,
        CExpression... __args)
        throws IOException, NullPointerException
    {
        return this.functionCall(__function.function(), __args);
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/15
     */
    @Override
    public CSourceWriter gotoLabel(String __target)
        throws IOException, NullPointerException
    {
        return this.gotoLabel(CIdentifier.of(__target));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/15
     */
    @Override
    public CSourceWriter gotoLabel(CIdentifier __target)
        throws IOException, NullPointerException
    {
        if (__target == null)
            throw new NullPointerException("NARG");
        
        return this.tokens("goto", __target, ";");
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/08/12
     */
    @Override
    public CPPBlock headerGuard(String __fileName)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        return this.headerGuard(CFileName.of(__fileName));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/08/12
     */
    @Override
    public CPPBlock headerGuard(CFileName __fileName)
        throws IOException, NullPointerException
    {
        if (__fileName == null)
            throw new NullPointerException("NARG");
        
        // Determine how the header guard should be named
        StringBuilder builder = new StringBuilder(__fileName.fileName);
        builder.insert(0, "SJME_HG_");
        for (int i = 0, n = builder.length(); i < n; i++)
        {
            char c = builder.charAt(i);
            
            // Capitalize letters
            if (c >= 'a' && c <= 'z')
                c = (char)('A' + (c - 'a'));
            
            // Turn slashes and dots to underscores
            else if (c == '/' || c == '.')
                c = '_';
            
            // Ignore otherwise
            else
                continue;
            
            // Replace
            builder.setCharAt(i, c);
        }
        
        // Create identifier
        CIdentifier identifier = CIdentifier.of(builder.toString());
        
        // Define the guard to prevent future use of this header
        CPPBlock block = this.preprocessorIf(CExpressionBuilder.builder()
            .not()
            .preprocessorDefined(identifier)
            .build());
        block.preprocessorDefine(identifier, null);
            
        // Return block for writing
        return block;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/15
     */
    @Override
    public CSourceWriter indent(int __by)
        throws IOException
    {
        this.out.indent(__by);
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/15
     */
    @Override
    public CSourceWriter label(String __label)
        throws IOException, NullPointerException
    {
        return this.label(CIdentifier.of(__label));
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/15
     */
    @Override
    public CSourceWriter label(CIdentifier __label)
        throws IOException, NullPointerException
    {
        if (__label == null)
            throw new NullPointerException("NARG");
        
        return this.tokens(__label, ":");
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/19
     */
    @Override
    public CSourceWriter newLine(boolean __force)
        throws IOException
    {
        this.out.newLine(true);
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter number(Number __number)
        throws IOException, NullPointerException
    {
        return this.number(null, __number);
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter number(CNumberType __type, Number __number)
        throws IOException, NullPointerException
    {
        if (__number == null)
            throw new NullPointerException("NARG");
        
        CExpressionBuilder.__builder(this)
            .number(__type, __number)
            .build();
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/22
     */
    @Override
    public CSourceWriter pivot(CPivotPoint __pivot)
        throws IOException, NullPointerException
    {
        if (__pivot == null)
            throw new NullPointerException("NARG");
        
        this.out.pivot(__pivot);
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter preprocessorDefine(CIdentifier __symbol,
        CExpression __expression)
        throws IOException, NullPointerException
    {
        if (__symbol == null)
            throw new NullPointerException("NARG");
        
        if (__expression == null)
            return this.preprocessorLine(CPPDirective.DEFINE, __symbol);
        return this.preprocessorLine(CPPDirective.DEFINE,
            __symbol, __expression);
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CPPBlock preprocessorIf(CExpression __expression)
        throws IOException, NullPointerException
    {
        if (__expression == null)
            throw new NullPointerException("NARG");
        
        // Start the check
        this.preprocessorLine(CPPDirective.IF, __expression);
        
        // Setup new block
        CPPBlock rv = new CPPBlock(this);
        this.__pushBlock(rv, false);
        
        return rv;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter preprocessorInclude(CFileName __fileName)
        throws IOException, NullPointerException
    {
        if (__fileName == null)
            throw new NullPointerException("NARG");
        
        return this.preprocessorLine(CPPDirective.INCLUDE,
            "\"" + __fileName.fileName + "\"");
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/28
     */
    @Override
    public CSourceWriter preprocessorLine(CPPDirective __directive,
        Object... __tokens)
        throws IOException, NullPointerException
    {
        if (__directive == null)
            throw new NullPointerException("NARG");
        
        // Always start this directive on a fresh line
        this.freshLine();
        
        // Write out directive
        this.token("#" + __directive.directive);
        
        // Write tokens for the directive
        if (__tokens != null && __tokens.length > 0)
            this.tokens(__tokens);
        
        // Always end with a fresh line, so we can continue accordingly
        this.freshLine();
        
        // Self
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter preprocessorUndefine(CIdentifier __symbol)
        throws IOException, NullPointerException
    {
        if (__symbol == null)
            throw new NullPointerException("NARG");
        
        return this.preprocessorLine(CPPDirective.UNDEF, __symbol);
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/03
     */
    @Override
    public CSourceWriter returnValue(CExpression __expression)
        throws IOException
    {
        if (__expression == null)
            return this.tokens("return", ";");
        return this.tokens("return", __expression, ";");
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter surround(String __prefix, Object... __tokens)
        throws IOException
    {
        if (__prefix == null)
            return this.tokens("(", __tokens, ")");
        return this.tokens(__prefix, "(", __tokens, ")");
    }
    
    /**
     * {@inheritDoc}
     * @since 3023/05/30
     */
    @Override
    public CSourceWriter surroundDelimited(String __prefix, String __delim,
        Object... __tokens)
        throws IOException, NullPointerException
    {
        if (__delim == null)
            throw new NullPointerException("NARG");
        
        // Open
        if (__prefix != null)
            this.token(__prefix);
        this.token("(");
        
        // Go through each item
        if (__tokens != null && __tokens.length > 0)
            for (int i = 0, n = __tokens.length; i < n; i++)
            {
                if (i > 0)
                    this.token(__delim);
                this.token(__tokens[i]);
            }
        
        // Close
        return this.token(")");
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/23
     */
    @Override
    public CSourceWriter token(CharSequence __token)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        return this.token(__token, false);
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/23
     */
    @Override
    public CSourceWriter token(CharSequence __token, boolean __forceNewline)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        if (__token == null)
            throw new NullPointerException("NARG");
        
        // Do nothing if empty as there is nothing to print
        int n = __token.length();
        if (n == 0)
            return this;
        
        // Forward
        CTokenOutput out = this.out;
        char startChar = __token.charAt(0);
        if (n == 1)
        {
            // Map single character outputs like this to spaces and whatnot
            if (startChar == ' ')
                out.space();
            else if (startChar == '\t')
                out.tab();
            else if (startChar == '\r' || startChar == '\n')
                out.newLine(__forceNewline);
            else
            {
                // Make sure a space is before this token if there was none
                // before...
                if (!this._lastWhitespace)
                    out.space();
                
                out.token(__token, __forceNewline);
            }
        }
        else
        {
            // Make sure a space is before this token if there was none
            // before...
            if (!this._lastWhitespace)
                out.space();
                
            out.token(__token, __forceNewline);
        }
        
        // Ends on newline or forced newline?
        char endChar = __token.charAt(n - 1); 
        if (__forceNewline || endChar == '\r' || endChar == '\n')
        {
            if (endChar != '\r' && endChar != '\n')
                out.newLine(true);
            
            this._lastNewline = true;
            this._lastWhitespace = true;
        }
        
        // Normal whitespace
        else if (endChar == ' ' || endChar == '\t')
        {
            this._lastNewline = false;
            this._lastWhitespace = true;
        }
        
        // Clear these otherwise
        else
        {
            this._lastNewline = false;
            this._lastWhitespace = false;
        }
        
        // Self
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter token(Object __token)
        throws IllegalArgumentException, IOException
    {
        // null
        if (__token == null)
            return this.token("NULL");
        
        // A C Expression
        if (__token instanceof CExpression)
        {
            /* {@squirreljme.error CW36 Cannot output token in such way.} */
            if (__token == CExpression.INVALID_EXPRESSION)
                throw new IllegalArgumentException("CW36");
            
            return this.token(((CExpression)__token).tokens());
        }
            
        // A C identifier
        else if (__token instanceof CIdentifier)
            return this.token(((CIdentifier)__token).identifier);
        
        // Primitive arrays
        else if (__token instanceof boolean[])
            return this.array((boolean[])__token);
        else if (__token instanceof byte[])
            return this.array((byte[])__token);
        else if (__token instanceof short[])
            return this.array((short[])__token);
        else if (__token instanceof int[])
            return this.array((int[])__token);
        else if (__token instanceof long[])
            return this.array((long[])__token);
        else if (__token instanceof float[])
            return this.array((float[])__token);
        else if (__token instanceof double[])
            return this.array((double[])__token);
            
        // Forward writing of arrays
        else if (__token.getClass().isArray())
            return this.tokens((Object[])__token);
            
        // Forward writing of collections
        else if (__token instanceof Collection)
        {
            Collection<?> tokens = (Collection<?>)__token;
            
            return this.tokens(tokens.toArray(new Object[tokens.size()]));
        }
        
        // String or character sequences
        else if (__token instanceof CharSequence)
            return this.token((CharSequence)__token);
        
        // A boolean
        else if (__token instanceof Boolean)
            return this.token(((Boolean)__token) ? "JNI_TRUE" : "JNI_FALSE");
            
        // A character
        else if (__token instanceof Character)
            return this.character((Character)__token);
            
        // A number value
        else if (__token instanceof Number)
            return this.number((Number)__token);
        
        /* {@squirreljme.error CW05 Unknown token type. (The type)} */
        throw new IllegalArgumentException("CW05 " + __token.getClass());
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/05/29
     */
    @Override
    public CSourceWriter tokens(Object... __tokens)
        throws IOException
    {
        if (__tokens == null || __tokens.length == 0)
            return this;
        
        // Write each token
        for (int i = 0, n = __tokens.length; i < n; i++)
            this.token(__tokens[i]);
        
        // Self
        return this;
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/06/19
     */
    @Override
    public CSourceWriter variableSet(CVariable __var, CExpression __value)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        if (__var == null || __value == null)
            throw new NullPointerException("NARG");
        
        return this.variableSet(__var.name, __value);
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/03
     */
    @Override
    public CSourceWriter variableSet(CExpression __var, CExpression __value)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        if (__var == null || __value == null)
            throw new NullPointerException("NARG");
        
        return this.tokens(__var, "=", __value, ";");
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/04
     */
    @Override
    public CSourceWriter variableSetViaFunction(CExpression __var,
        CFunctionType __function, CExpression... __args)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        return this.variableSet(__var, CExpressionBuilder.builder()
                .functionCall(__function, __args)
            .build());
    }
    
    /**
     * {@inheritDoc}
     * @since 2023/07/04
     */
    @Override
    public CSourceWriter variableSetViaFunction(CExpression __var,
        CFunctionProvider __function, CExpression... __args)
        throws IllegalArgumentException, IOException, NullPointerException
    {
        return this.variableSetViaFunction(__var,
            __function.function(), __args);
    }
    
    /**
     * Closes the given block.
     * 
     * @param __cBlock The block to close.
     * @throws IOException On write errors.
     * @throws NullPointerException On null arguments.
     * @since 2023/05/29
     */
    @SuppressWarnings("resource")
    void __close(CBlock __cBlock)
        throws IOException, NullPointerException
    {
        if (__cBlock == null)
            throw new NullPointerException("NARG");
        
        /* {@squirreljme.error CW07 Closing block is not the last opened
        block.} */
        Deque<CBlock> blocks = this._blocks;
        CBlock peek = blocks.peek();
        if (peek != __cBlock)
            throw new IllegalStateException("CW07");
        
        // Remove it
        blocks.pop();
        
        // Write finisher
        __cBlock.__finish();
        
        // Indent down, with any potential extra indentation that was added
        this.out.indent(-(1 + __cBlock.extraIndent));
    }
    
    /**
     * Pushes the block to the stack.
     *
     * @param <B> The type of block to push.
     * @param __block The block to push.
     * @param __indentUp Indentation is upped?
     * @return The block to push.
     * @throws NullPointerException On null arguments.
     * @since 2023/05/31
     */
    <B extends CBlock> B __pushBlock(B __block, boolean __indentUp)
        throws NullPointerException
    {
        if (__block == null)
            throw new NullPointerException("NARG");
        
        // Increase indentation?
        if (__indentUp)
            this.out.indent(1);
        
        // Push block into
        this._blocks.push(__block);
        return __block;
    }
}