How can I build parse trees not ASTs?

It's simple. Using the debug event interface, the predefined ParseTreeBuilder class listens for debug events and build a parse tree. Just turn on -debug commandline option and then pass in the ParseTreeBuilder listener. Ask the listener for its tree and, using the standard toStringTree() method for any CommonTree, print it out.

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.runtime.debug.*;

/** Test grammar P; use -debug flag so parser fires
 *  debug events. ParseTreeBuilder used to build parse trees.
 */
public class TestP {
    public static void main(String[] args) throws Exception {
        // create the lexer attached to stdin
        ANTLRInputStream input = new ANTLRInputStream(System.in);
        PLexer lexer = new PLexer(input);
        // create the buffer of tokens between the lexer and parser
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        // create a debug event listener that builds parse trees
        ParseTreeBuilder builder = new ParseTreeBuilder("prog");
        // create the parser attached to the token buffer
        // and tell it which debug event listener to use
        PParser parser = new PParser(tokens, builder);
        // launch the parser starting at rule prog
        parser.prog();
        System.out.println(builder.getTree().toStringTree());
    }
}
grammar P;

prog: decl* stat* ;
 
decl: type ID (',' ID)* ';' ;
    
type: 'int' | 'float' ;
        
stat: 'if' expr 'then' stat ('else' stat)?
    | ID '=' expr 
    ;   
        
expr: INT
    | 'true'
    ;   
        
ID  : 'a'..'z'+ ; 
INT : '0'..'9'+ ;
WS  : (' '|'\n')+ {$channel=HIDDEN;} ;

Sample run:

java TestP
int i;
i=4;
(<grammar prog> (prog (decl (type int) i ;) (stat i = (expr 4))))