/** * erlyberly, erlang trace debugger * Copyright (C) 2016 Andy Till * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package erlyberly.format; import java.util.ArrayList; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangBinary; import com.ericsson.otp.erlang.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangPid; import com.ericsson.otp.erlang.OtpErlangString; import com.ericsson.otp.erlang.OtpErlangTuple; import erlyberly.node.OtpUtil; public class ErlangFormatter implements TermFormatter { @Override public StringBuilder appendToString(OtpErlangObject obj, StringBuilder sb) { if(obj instanceof OtpErlangBinary) { sb.append("<<"); Formatting.binaryToString((OtpErlangBinary) obj, ", ", sb); sb.append(">>"); } else if(obj instanceof OtpErlangPid) { sb.append(pidToString((OtpErlangPid) obj)); } else if(OtpUtil.isErlyberlyRecord(obj)) { OtpErlangTuple record = (OtpErlangTuple) obj; OtpErlangAtom recordName = (OtpErlangAtom) record.elementAt(1); OtpErlangList fields = (OtpErlangList) record.elementAt(2); sb.append("{").append(recordName).append(", "); for(int i=0; i < fields.arity(); i++) { if(i != 0) { sb.append(", "); } appendToString(fields.elementAt(i), sb); } sb.append("}"); } else if(OtpUtil.isErlyberlyRecordField(obj)) { OtpErlangObject fieldObj = ((OtpErlangTuple)obj).elementAt(2); appendToString(fieldObj, sb); } else if(obj instanceof OtpErlangTuple || obj instanceof OtpErlangList) { String brackets = bracketsForTerm(obj); OtpErlangObject[] elements = OtpUtil.elementsForTerm(obj); sb.append(brackets.charAt(0)); for(int i=0; i < elements.length; i++) { if(i != 0) { sb.append(", "); } appendToString(elements[i], sb); } if(obj instanceof OtpErlangList && !((OtpErlangList)obj).isProper()) { sb.append(cons()); appendToString(((OtpErlangList)obj).getLastTail(), sb); } sb.append(brackets.charAt(1)); } else if(obj instanceof OtpErlangString) { Formatting.appendString((OtpErlangString) obj, this, "\"", sb); } else { sb.append(obj.toString()); } return sb; } private static String pidToString(OtpErlangPid pid) { return pid.toString(); } public String bracketsForTerm(OtpErlangObject obj) { assert obj != null; if(obj instanceof OtpErlangTuple) return "{}"; else if(obj instanceof OtpErlangList) return "[]"; else throw new RuntimeException("No brackets for type " + obj.getClass()); } /** * Convert an MFA tuple to a string, where the MFA must have the type: * * {Module::atom(), Function::atom(), Args::[any()]}. */ @Override public String modFuncArgsToString(OtpErlangTuple mfa) { StringBuilder sb = new StringBuilder(); sb.append(atomToStringNoQuotes((OtpErlangAtom) mfa.elementAt(0))) .append(":") .append(atomToStringNoQuotes((OtpErlangAtom) mfa.elementAt(1))) .append("("); OtpErlangList args = (OtpErlangList) mfa.elementAt(2); ArrayList<String> stringArgs = new ArrayList<>(); for (OtpErlangObject arg : args) { stringArgs.add(toString(arg)); } sb.append(String.join(", ", stringArgs)); sb.append(")"); return sb.toString(); } private String atomToStringNoQuotes(OtpErlangAtom atom) { return atom.atomValue(); } @Override public String modFuncArityToString(OtpErlangTuple mfa) { StringBuilder sb = new StringBuilder(); OtpErlangList argsList = OtpUtil.toErlangList(mfa.elementAt(2)); sb.append(atomToStringNoQuotes((OtpErlangAtom) mfa.elementAt(0))) .append(":") .append(atomToStringNoQuotes((OtpErlangAtom) mfa.elementAt(1))) .append("/").append(argsList.arity()); return sb.toString(); } @Override public String exceptionToString(OtpErlangAtom errorClass, OtpErlangObject errorReason) { return errorClass + ":" + toString(errorReason); } @Override public String emptyTupleString() { return "{ }"; } @Override public String tupleLeftParen() { return "{"; } @Override public String tupleRightParen() { return "}"; } @Override public String emptyListString() { return "[ ]"; } @Override public String listLeftParen() { return "["; } @Override public String listRightParen() { return "]"; } @Override public String mapLeft(OtpErlangObject obj) { return "#{"; } @Override public Boolean isHiddenField(OtpErlangObject key) {return false;} @Override public String mapRight() { return "}"; } @Override public String cons() { return "|"; } }