| /*=============================================================================# |
| # Copyright (c) 2012, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.rtm.base.core; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| import org.eclipse.emf.ecore.EObject; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| |
| import org.eclipse.statet.rtm.rtdata.types.RTypedExpr; |
| |
| |
| public abstract class AbstractRCodeGenerator { |
| |
| |
| protected static final RTypedExpr R_NUM_ZERO_EXPR= new RTypedExpr(RTypedExpr.R, "0"); //$NON-NLS-1$ |
| |
| |
| protected static String quoteChar(final String s) { |
| final StringBuilder result= new StringBuilder(s.length()); |
| result.append('"'); |
| for (int i= 0; i < s.length(); ) { |
| final char c= s.charAt(i++); |
| switch (c) { |
| case '\\': |
| case '\'': |
| case '"': |
| result.append('\\'); |
| result.append(c); |
| continue; |
| default: |
| result.append(c); |
| continue; |
| } |
| } |
| result.append('"'); |
| return result.toString(); |
| } |
| |
| |
| protected static interface IRExprProcessor { |
| |
| String getValue(RTypedExpr expr); |
| |
| } |
| |
| protected static final IRExprProcessor DIRECT_PROCESSOR= new IRExprProcessor() { |
| |
| @Override |
| public String getValue(final RTypedExpr expr) { |
| return expr.getExpr(); |
| } |
| |
| }; |
| |
| protected static final IRExprProcessor TEXT_PROCESSOR= new IRExprProcessor() { |
| |
| @Override |
| public String getValue(final RTypedExpr expr) { |
| return quoteChar(expr.getExpr()); |
| } |
| |
| }; |
| |
| protected static final IRExprProcessor QUOTE_PROCESSOR= new IRExprProcessor() { |
| |
| @Override |
| public String getValue(final RTypedExpr expr) { |
| return quoteChar(expr.getExpr()); |
| } |
| |
| }; |
| |
| |
| protected class FunBuilder { |
| |
| |
| private final int offset; |
| private final int emtpyOffset; |
| |
| |
| public FunBuilder(final int offset, final String funName) { |
| this.offset= offset; |
| AbstractRCodeGenerator.this.builder.append(funName); |
| AbstractRCodeGenerator.this.builder.append('('); |
| this.emtpyOffset= AbstractRCodeGenerator.this.builder.length(); |
| } |
| |
| public boolean isEmpty() { |
| return (AbstractRCodeGenerator.this.builder.length() == this.emtpyOffset); |
| } |
| |
| public boolean append(final String argName, final String argValue) { |
| if (argValue == null) { |
| return false; |
| } |
| doAppendArg(argName, argValue); |
| return true; |
| } |
| |
| public boolean append(final String argName, final String argValue, final boolean appendEmpty) { |
| if (argValue == null && !appendEmpty) { |
| return false; |
| } |
| doAppendArg(argName, argValue); |
| return true; |
| } |
| |
| public boolean appendEmpty(final String argName) { |
| doAppendArg(argName, null); |
| return true; |
| } |
| |
| public boolean appendExpr(final String argName, final RTypedExpr argValue) { |
| if (argValue == null) { |
| return false; |
| } |
| doAppendArg(argName, getRExprPRocessor(argValue.getTypeKey()).getValue(argValue)); |
| return true; |
| } |
| |
| public boolean appendExpr(final String argName, final RTypedExpr argValue, |
| final String requiredType) { |
| if (argValue == null || argValue.getTypeKey() != requiredType) { |
| return false; |
| } |
| doAppendArg(argName, getRExprPRocessor(argValue.getTypeKey()).getValue(argValue)); |
| return true; |
| } |
| |
| public boolean appendExpr(final String argName, final RTypedExpr argValue, |
| final Collection<String> requiredTypes) { |
| if (argValue == null || !requiredTypes.contains(argValue.getTypeKey())) { |
| return false; |
| } |
| doAppendArg(argName, getRExprPRocessor(argValue.getTypeKey()).getValue(argValue)); |
| return true; |
| } |
| |
| public FunBuilder appendFun(final String argName, final String funName) { |
| final int offset= AbstractRCodeGenerator.this.builder.length(); |
| doAppendArg(argName, null); |
| return new FunBuilder(offset, funName); |
| } |
| |
| private void doAppendArg(final String argName, final String argValue) { |
| if (!isEmpty()) { |
| AbstractRCodeGenerator.this.builder.append(", "); //$NON-NLS-1$ |
| } |
| if (argName != null) { |
| AbstractRCodeGenerator.this.builder.append(argName); |
| AbstractRCodeGenerator.this.builder.append(AbstractRCodeGenerator.this.argAssign); |
| } |
| if (argValue != null) { |
| AbstractRCodeGenerator.this.builder.append(argValue); |
| } |
| } |
| |
| public void close() { |
| AbstractRCodeGenerator.this.builder.append(")"); //$NON-NLS-1$ |
| } |
| |
| public void closeOrRemove() { |
| if (isEmpty()) { |
| AbstractRCodeGenerator.this.builder.delete(this.offset, AbstractRCodeGenerator.this.builder.length()); |
| return; |
| } |
| AbstractRCodeGenerator.this.builder.append(")"); //$NON-NLS-1$ |
| } |
| |
| } |
| |
| |
| protected final List<String> requiredPkgs= new ArrayList<>(); |
| protected final StringBuilder builder= new StringBuilder(); |
| |
| private final String newLine= "\n"; |
| private final String mainAssign= " <- "; |
| private final String argAssign= " = "; |
| private final int indent= 0; |
| |
| |
| public AbstractRCodeGenerator() { |
| } |
| |
| |
| protected void reset() { |
| this.requiredPkgs.clear(); |
| this.builder.setLength(0); |
| } |
| |
| protected void addRequirePackage(final String pkgName) { |
| if (!this.requiredPkgs.contains(pkgName)) { |
| this.requiredPkgs.add(pkgName); |
| |
| final FunBuilder fun= appendFun("library"); //$NON-NLS-1$ |
| fun.append(null, pkgName); |
| fun.close(); |
| appendNewLine(); |
| } |
| } |
| |
| protected void appendNewLine() { |
| this.builder.append(this.newLine); |
| } |
| |
| protected void appendAssign(final String to) { |
| this.builder.append(to); |
| this.builder.append(this.mainAssign); |
| } |
| |
| protected FunBuilder appendFun(final String funName) { |
| return new FunBuilder(this.builder.length(), funName); |
| } |
| |
| protected void appendExprList(final List<? extends RTypedExpr> list, |
| final String op, final String empty) { |
| appendExprList(list, DIRECT_PROCESSOR, op, empty); |
| } |
| |
| protected void appendExprList(final List<? extends RTypedExpr> list, |
| final IRExprProcessor processor, final String op, final String empty) { |
| final int offset= this.builder.length(); |
| for (int i= 0; i < list.size(); i++) { |
| final RTypedExpr expr= list.get(i); |
| if (expr.getTypeKey() == RTypedExpr.MAPPED) { |
| if (this.builder.length() > offset) { |
| this.builder.append(op); |
| } |
| this.builder.append(processor.getValue(list.get(i))); |
| } |
| } |
| if (this.builder.length() == offset && empty != null) { |
| this.builder.append(empty); |
| } |
| } |
| |
| protected void appendExprsC(final List<? extends RTypedExpr> list, |
| final IRExprProcessor processor) { |
| this.builder.append("c("); //$NON-NLS-1$ |
| appendExprList(list, processor, ", ", null); //$NON-NLS-1$ |
| this.builder.append(")"); //$NON-NLS-1$ |
| } |
| |
| protected IRExprProcessor getRExprPRocessor(final String typeKey) { |
| if (typeKey == RTypedExpr.CHAR) { |
| return TEXT_PROCESSOR; |
| } |
| return DIRECT_PROCESSOR; |
| } |
| |
| |
| public abstract void generate(EObject root); |
| |
| |
| public List<String> getRequiredPkgs() { |
| return ImCollections.toList(this.requiredPkgs); |
| } |
| |
| public String getRCode() { |
| return this.builder.toString(); |
| } |
| |
| } |