package gw.rating.worksheet uses gw.api.extension.ExtensionInterfaces uses gw.api.system.PCLoggerCategory uses gw.lang.reflect.IType uses gw.lang.reflect.java.JavaTypes uses gw.rating.RateFlowLogger uses gw.rating.flow.domain.AssignmentStatement uses gw.rating.flow.domain.CalcStepOperand uses gw.rating.flow.domain.ConstantOperand uses gw.rating.flow.domain.CostDataRoundingStatement uses gw.rating.flow.domain.DateConstantOperand uses gw.rating.flow.domain.GenerationContext uses gw.rating.flow.domain.IfStatement uses gw.rating.flow.domain.InScopeOperand uses gw.rating.flow.domain.LocalVariableOperand uses gw.rating.flow.domain.LoopBodyCompoundStatement uses gw.rating.flow.domain.LoopIterableOperand uses gw.rating.flow.domain.LoopStatement uses gw.rating.flow.domain.LoopVariableOperand uses gw.rating.flow.domain.NegatedValueOperand uses gw.rating.flow.domain.NumericConstantOperand uses gw.rating.flow.domain.RateFunctionArgument uses gw.rating.flow.domain.RateFunctionConstantArgument uses gw.rating.flow.domain.RateFunctionLocalVariableArgument uses gw.rating.flow.domain.RateFunctionOperand uses gw.rating.flow.domain.RateFunctionProductModelPathArgument uses gw.rating.flow.domain.RateTableLookupOperand uses gw.rating.flow.domain.RoundingMethodOperand uses gw.rating.flow.domain.TypelistCollectionOperand uses gw.rating.flow.domain.TypelistConstantOperand uses gw.rating.flow.util.RateflowTypeUtils /** * Helps the rateflow domain classes wrap all Gosu terms and expressions with worksheet-enabled * code * */ class WorksheetCodeGenHelper { static final var _rfLogger = RateFlowLogger.Logger final var _context : GenerationContext final var _operandStack = new Stack() // starting at -1 so the first frame is 0 so we have the most frames before requiring two digits and therefore more memory private var _frameCount = -1 construct(context : GenerationContext) { _context = context } /** Prepare the worksheet specific dependencies if worksheets are turned on * */ function prepareDependencies(){ if(!_context.WorksheetsEnabled) return; prepareUseMap() } /** * Wrap an AssignmentStatement with worksheet-enabled code if worksheets are turned on * * @param statement The statement to wrap * @param code the code to execute * @param isDeclaration whether or not this is a variable declaration * @param variableName name of variable being assigned */ function wrapStatement(statement : AssignmentStatement, code : block(), isDeclaration : boolean, variableName : String) { var frameSequence = nextFrame() doTheWorksheetWrap( \-> { var lhs = statement.Variable if (lhs.VariableFieldName?.HasContent) { _context.append("var ${frameSequence.frameName} = _log.startSet(\"${lhs.VariableName}\", ${lhs.VariableKey}, \"${lhs.VariableFieldName}\")") } else { _context.append("var ${frameSequence.frameName} = _log.startAssignment(\"${statement.Variable.DisplayName}\", ${isDeclaration})") } _context.append("\nvar ${frameSequence.resultName} = ") }, code, \-> { _context.append("\n${frameSequence.frameName}.finishAssignment(${frameSequence.resultName})\n") if (isDeclaration and _context.AddLocalVarDecls) { _context.append("var ") } _context.append("${variableName} = ${frameSequence.resultName}") }) } /** * Wrap a CostDataRoundingStatement with worksheet-enabled code if worksheets are turned on * * @param statement The statement to wrap * @param code the code to execute */ function wrapStatement(statement : CostDataRoundingStatement, code : block()) { var frameSequence = nextFrame() doTheWorksheetWrap( \-> _context.append("\nvar ${frameSequence.frameName} = _log.startCostDataRounding()\ntry {\n"), code, \-> _context.append("\n} finally {\n${frameSequence.frameName}.finish()\n}\n")) } /** * Wrap an IfStatement with worksheet-enabled code if worksheets are turned on * * @param statement The statement to wrap * @param code the code to execute * @param isDeclaration whether this is the initial if statement (as opposed to an else) */ function wrapStatement(statement : IfStatement, code : block(), isInitialIf : boolean) { var frameSequence = nextFrame() doTheWorksheetWrap( \-> { // todo: DAL get rid of useless if _context.append("false) {\n}\nvar ${frameSequence.frameName} = _log.startIf(${isInitialIf})") _context.append("\nvar ${frameSequence.resultName}: boolean") _context.append("\ntry {\n${frameSequence.resultName} = ") }, code, \-> _context.append("\n} finally {\n${frameSequence.frameName}.finishConditional(${frameSequence.resultName})\n}\nif (${frameSequence.resultName}")) } /** * Wrap a LoopStatement with worksheet-enabled code if worksheets are turned on * * @param statement The statement to wrap * @param code the code to execute */ function wrapStatement(loop : LoopStatement, code : block()) { doTheWorksheetWrap( \-> { _context.append("_log.loop(") var operand = loop.IterableOperand _context.appendCommaSeparatedStringLiterals({ operand.DisplayName, operand.ReferenceTypeName, loop.LoopVariable.DisplayName, loop.LoopVariable.ReferenceTypeName, loop.LoopIndexVariableDisplayName }) _context.append(", (${operand.VariableReference})") if (operand.IsArray) { _context.append("?.length") } else { _context.append("?.size()") } _context.append(")\n") }, code, \-> { _context.append("\n_log.endloop()\n") }) } /** * Wrap a LoopBodyCompoundStatement with worksheet-enabled code if worksheets are turned on * * @param statement The statement to wrap * @param code the code to execute */ function wrapStatement(statement : LoopBodyCompoundStatement, code : block()) { doTheWorksheetWrap( \-> { var loopStatement = statement.LoopStatement var iterable = loopStatement.IterableOperand var loopvar = loopStatement.LoopVariable _context.append("_log.iteration(") _context.appendCommaSeparatedStringLiterals({ iterable.DisplayName, iterable.ReferenceTypeName, loopvar.DisplayName, loopvar.ReferenceTypeName, loopStatement.LoopIndexVariableDisplayName }).append(", ") _context.append(loopStatement.LoopIndexVariableReference).append(")\n") }, code, \-> {}) } /** Wrap an operand with worksheet enabled code if worksheets are turned on * * @param operand The operand * @param the code block to wrap * */ function wrapOperand(operand : CalcStepOperand, code : block()){ if (operand typeis RateFunctionOperand){ wrapRateFunctionOperand(operand, code) } else { doTheWorksheetWrap( \-> beginWrapOperand(operand), code, \-> endWrapOperand()) } } function wrapRateFunctionOperand(rateFunction : RateFunctionOperand, code : block()){ var frameSequence = nextFrame() var clazz : IType var functionName : String var returnType : IType if (rateFunction.MethodInfo == null) { functionName = rateFunction.FunctionName } else { returnType = rateFunction.MethodInfo.ReturnType clazz = rateFunction.MethodInfo.Container.OwnersType functionName = rateFunction.MethodInfo.DisplayName } var isVoid = JavaTypes.pVOID().equals(returnType) final var voidFrameID = "voidFrame" + frameSequence.frameName /* Void functions have no return type, but we need to execute them in the context of a frame. Capture the frame in a variable and push before, pop after. In the case of a non-void function, we can simply call the void function in finish call. */ doTheWorksheetWrap( \ -> { _operandStack.push(rateFunction) if (isVoid){ _context.append("var ${voidFrameID} = ${wrapperName()}.startVfn(${clazz.DisplayName}, \"${functionName}\")\ntry {\n") } else { _context.append("${wrapperName()}.startFn(${clazz.DisplayName}, \"${functionName}\").calcValue(") } }, code, \-> { var operand = _operandStack.pop() if (isVoid) { //finish frame takes the return value. Should this be null or something different for a void function? _context.append("\n}\nfinally {\n${voidFrameID}.finishVFn()\n}") } else { _context.append(")") } }) } /** Wrap an argument with worksheet enabled code if worksheets are turned on * * @param arg the argument * @param code the code to wrap * */ function wrapArgument(arg : RateFunctionArgument, code : block()) { if (!_context.WorksheetsEnabled){ code() } else { switch(typeof arg){ case RateFunctionLocalVariableArgument: //Code generated looks like: // _log.argVr("arg", "variable", variable) _context.append("_log.argVar(\"${arg.ArgumentName}\", \"${arg.LocalVarName}\", ${arg.LocalVarName})") break; case RateFunctionProductModelPathArgument: //Code generated looks like: // _log.argVar("arg", "_param.Value", _param.Value) var val = arg.InScopeValue var path = arg.InScopeParam + (val.HasContent ? "." + val : "") _context.append("_log.argVar(\"${arg.ArgumentName}\", \"${path}\", ${arg.generate()})") break; case RateFunctionConstantArgument: default: //RateFunctionLocalMultiFactorFieldVariableArgument and RateFactorConstantArgument are treated the same //RateFunctionLoopVariableArgument... should be treated like a RateFunctionLocalVariableArgument, but it wasn't in the existing code //WorksheetLogger.arg has two versions, one that takes a constant will automatically handle //logging the constant without a creating a new lambda. //Code generated looks like: // _log.arg("arg", 10.0bd) _context.append("_log.arg(\"${arg.ArgumentName}\", ${arg.generate()})") break; } } } /** Wrap a named function argument with worksheet enabled code if worksheets are turned on * * @param name Function name * @param code block * @param operand Optional - the sub operand of the function call */ function wrapFunctionArgument(name : String, code : block()) { var functionArgOperand = new FunctionArgumentOperandWrapper(name) wrapOperand(functionArgOperand, code) } /** Wrap a set of conditional statement(s) * * @param code block containing the conditional statement(s) * */ function wrapConditionals(code : block()) { doTheWorksheetWrap( \ -> _context.append("_log.conditionals(\\ -> {\n"), code, \ -> _context.append("})") ) } /** Log the beginning of an else statement * */ function logElse(){ _context.append("_log.els()") } function beginConditional(idx: int) { _context.append("_log.beginConditional(").append(idx == 0 ? "true" : "false").append(")\n") } function setConditionalResult(varName: String) { _context.append("_log.setConditionalResult(${varName})\n") } function endConditional(isEndif: boolean) { _context.append("_log.endConditional(${isEndif})\n") } /** Generate a worksheet-enabled rate query constructor if worksheets are turned on * * @param op The rate table lookup operand */ function generateRateQueryConstructor(op : RateTableLookupOperand){ if(!_context.WorksheetsEnabled) return; _context.append('"').append(op.OperatorType.Code).append("\", ") if (op.LeftParenthesis > 0 or op.RightParenthesis > 0) { _context.append(op.LeftParenthesis as String).append(", ") _context.append(op.RightParenthesis as String).append(", ") } } /****** Operand wrapping ******/ private function beginWrapOperand(operand : CalcStepOperand){ _operandStack.push(operand) if (operand typeis RateTableLookupOperand) return // these don't need wrapping switch (typeof operand) { case TypelistCollectionOperand: // not sure wrapConstant will work.... case ConstantOperand: case NumericConstantOperand: case DateConstantOperand: beginWrapConstant() break case LocalVariableOperand: beginWrapLocalVariable(operand) break case LoopVariableOperand: beginWrapLoopVariableOperand(operand) break case InScopeOperand: beginWrapInScopeOperand(operand) break case RoundingMethodOperand: beginWrapRoundingMethod() break case TypelistConstantOperand: beginWrapTypelistOperand() break case NegatedValueOperand: beginWrapNegatedValueOperand() break case FunctionArgumentOperandWrapper: beginWrapFunctionArgument(operand) break case LoopIterableOperand: beginWrapLoopIterableOperand(operand) break default: _rfLogger.warn("${typeof operand} type not supported for worksheet operand wrapping") } } private function endWrapOperand(){ var operand = _operandStack.pop() switch(typeof operand){ case InScopeOperand: captureParenthesis(operand, true) if (operand.ParamValue.HasContent and operand.CovTermCode.HasContent) { _context.append(", {\"${operand.CovTermCode}\", \"${operand.ParamTypeName}\"}") } _context.append(')') break case RoundingMethodOperand: case TypelistCollectionOperand: case ConstantOperand: case NumericConstantOperand: case DateConstantOperand: case RateFunctionOperand: case LocalVariableOperand: case LoopVariableOperand: case LoopIterableOperand: case TypelistConstantOperand: case NegatedValueOperand: captureParenthesis(operand) _context.append(')') break case FunctionArgumentOperandWrapper: captureParenthesis(operand) _context.append(')') break } } private function beginWrapConstant(){ _context.append("${wrapperName()}.co(") } private function beginWrapNegatedValueOperand() { _context.append("${wrapperName()}.no(\\ -> ") } private function beginWrapLocalVariable(variable : LocalVariableOperand){ // Escape quotes, because the name can be of the form .get("") var displayName = variable.DisplayName.replaceAll("\"", "\\\\\"") _context.append("${wrapperName()}.vr(\"${displayName}\", ") } private function beginWrapLoopVariableOperand(operand: LoopVariableOperand) { _context.append("${wrapperName()}.vr(\"${operand.DisplayName}\", ") } private function beginWrapLoopIterableOperand(operand: LoopIterableOperand) { _context.append("${wrapperName()}.vr<${operand.ReferenceTypeName}>(\"${operand.DisplayName}\", ") } private function beginWrapInScopeOperand(inScope : InScopeOperand){ var objectDisplayName = inScope.VariableName var objectName = inScope.VariableKey var typeQualification = "" if (inScope.ReferenceTypeName != null and RateflowTypeUtils.isArrayOrIterable(inScope.ReferenceType)) { typeQualification = "<${inScope.ReferenceTypeName}>" } if (inScope.ParamValue.HasContent) { _context.append("${wrapperName()}.get${typeQualification}(\"${objectDisplayName}\", ${objectName}, \"${inScope.ParamValue}\", ") } else { _context.append("${wrapperName()}.vr${typeQualification}(\"${objectDisplayName}\", ") } var type = inScope.ParamName.DisplayName _context.append("\"${type}\", ") } private function beginWrapTypelistOperand(){ _context.append("${wrapperName()}.co(") } private function beginWrapFunctionArgument(argument:FunctionArgumentOperandWrapper){ _context.append("${wrapperName()}.startArg(\"${argument.FunctionArgumentName}\").calcValue(") } private function beginWrapRoundingMethod() { _context.append("${wrapperName()}.startFn(${ExtensionInterfaces.ICalcRoutineRoundingMethodExtensions.RoundingMethodType.DisplayName}, \"setScale\", \"Rounding\").calcValue(") } /****** Other private functions ******/ private function doTheWorksheetWrap( beginWrap : block(), code : block(), endWrap : block()){ if(_context.WorksheetsEnabled) { beginWrap() } code() if(_context.WorksheetsEnabled) { endWrap() } } private function captureParenthesis(operand : CalcStepOperand, forceCapture : boolean = false){ if(forceCapture or operand.LeftParenthesis > 0 or operand.RightParenthesis > 0){ _context.append(", ").append(operand.LeftParenthesis as String).append(", ").append(operand.RightParenthesis as String) } } private function prepareUseMap(){ _context.UseMap.addToDefaultTypeUses("gw.rating.worksheet.*") } private function wrapperName() : String{ switch(_operandStack.peek().OperatorType){ case CalcStepOperatorType.TC_ADDITION: return "_a" case CalcStepOperatorType.TC_MULTIPLICATION: return "_m" case CalcStepOperatorType.TC_SUBTRACTION: return "_s" case CalcStepOperatorType.TC_DIVISION: return "_d" case CalcStepOperatorType.TC_EQUAL: return "_eq" case CalcStepOperatorType.TC_LESSTHAN: return "_lt" case CalcStepOperatorType.TC_GREATERTHAN: return "_gt" case CalcStepOperatorType.TC_LESSTHANOREQUAL: return "_lte" case CalcStepOperatorType.TC_GREATERTHANOREQUAL: return "_gte" case CalcStepOperatorType.TC_OR: return "_or" case CalcStepOperatorType.TC_AND: return "_and" case CalcStepOperatorType.TC_NOTEQUAL: return "_ne" case CalcStepOperatorType.TC_IN: return "_in" case CalcStepOperatorType.TC_NOTIN: return "_ni" default: return "_f" } } private function nextFrame() : FrameSequence{ _frameCount += 1 return new FrameSequence(_frameCount) } private static final class FrameSequence { final var frameName : String final var resultName : String construct(frameNumber : int) { // double $$ to avoid name collissions with loop variables // that start with $ followed by user-defined name that must start with a letter frameName = "$$frame" + frameNumber resultName = "$$result" + frameNumber } } private static class FunctionArgumentOperandWrapper extends CalcStepOperand{ public final var FunctionArgumentName : String construct(s : String){ super(null, 0, 0) FunctionArgumentName = s } override function getDataType(context : GenerationContext) : IType { return null } override property get OperandType() : CalcStepOperandType { return null } override function generateCode(context : GenerationContext) { } } }