public static void CreateFunction(string name, FullCellAddr outputCell, FullCellAddr[] inputCells) { name = name.ToUpper(); // If the function exists, with the same input and output cells, keep it. // If it is a placeholder, overwrite its applier; if its input and output // cells have changed, recreate it (including its SdfInfo record). Function oldFunction = Function.Get(name); if (oldFunction != null) { if (!oldFunction.IsPlaceHolder) { return; } } // Registering the function before compilation allows it to call itself recursively SdfInfo sdfInfo = Register(outputCell, inputCells, name); // Console.WriteLine("Compiling {0} as #{1}", name, info.index); Update(sdfInfo, CompileSdf(sdfInfo)); if (oldFunction != null) // ... and is not a placeholder { oldFunction.UpdateApplier(sdfInfo.Apply, sdfInfo.IsVolatile); } else { new Function(name, sdfInfo.Apply, isVolatile: sdfInfo.IsVolatile); } }
// Evaluate cell's expression if necessary and cache its value; // also enqueue supported cells for evaluation if we use support graph public override Value Eval(Sheet sheet, int col, int row) { switch (state) { case CellState.Uptodate: break; case CellState.Computing: FullCellAddr culprit = new FullCellAddr(sheet, col, row); String msg = String.Format("### CYCLE in cell {0} formula {1}", culprit, Show(col, row, workbook.format)); throw new CyclicException(msg, culprit); case CellState.Dirty: case CellState.Enqueued: state = CellState.Computing; v = e.Eval(sheet, col, row); state = CellState.Uptodate; if (workbook.UseSupportSets) { ForEachSupported(EnqueueCellForEvaluation); } break; } return(v); }
public static CGExpr BuildExpression(FullCellAddr addr, Dictionary<FullCellAddr, Variable> addressToVariable) { Cell cell; if (!addr.TryGetCell(out cell)) { return new CGTextConst(TextValue.EMPTY); } else if (cell is NumberCell) { return new CGNumberConst(((NumberCell)cell).value); } else if (cell is TextCell) { return new CGTextConst(((TextCell)cell).value); } else if (cell is QuoteCell) { return new CGTextConst(((QuoteCell)cell).value); } else if (cell is BlankCell) { return new CGError("#FUNERR: Blank cell in function"); } else if (cell is Formula) { // Translate the expr relative to its containing cell at addr CGExpressionBuilder cgBuilder = new CGExpressionBuilder(addressToVariable, addr); Expr expr = ((Formula)cell).Expr; expr.VisitorCall(cgBuilder); return cgBuilder.result; } else if (cell is ArrayFormula) { return new CGError("#FUNERR: Array formula in function"); } else { throw new ImpossibleException("BuildExpression: " + cell); } }
// Partially evaluate the programList with respect to the given static inputs, // producing a new ProgramLines object. public ProgramLines PEval(Value[] args, FullCellAddr[] residualInputs) { PEnv pEnv = new PEnv(); // Map static input cells to their constant values: for (int i = 0; i < args.Length; i++) { pEnv[inputCells[i]] = CGConst.Make(args[i]); } ProgramLines residual = new ProgramLines(outputCell, residualInputs); // PE-time environment PEnv maps each residual input cell address to the delegate argument: for (int i = 0; i < residualInputs.Length; i++) { FullCellAddr input = residualInputs[i]; pEnv[input] = new CGCellRef(input, residual.addressToVariable[input]); } // Process the given function's compute cells in dependency order, output last: foreach (ComputeCell ccell in programList) { ComputeCell rCcell = ccell.PEval(pEnv); if (rCcell != null) { residual.AddComputeCell(ccell.cellAddr, rCcell); } } residual = residual.PruneZeroUseCells(); return(residual); }
private bool HasPrecedentHelper(FullCellAddr node, ICollection <FullCellAddr> transitivePrecedents, HashSet <FullCellAddr> visitedCells) { HashSet <FullCellAddr> precedents; if (nodesPrecedents.TryGetValue(node, out precedents)) { foreach (FullCellAddr addr in transitivePrecedents) { if (precedents.Contains(addr)) { return(true); } } foreach (FullCellAddr addr in precedents) { if (visitedCells.Add(addr)) { if (HasPrecedentHelper(addr, transitivePrecedents, visitedCells)) { return(true); } } } } return(false); }
public override void DependsOn(FullCellAddr here, Action <FullCellAddr> dependsOn) { foreach (Expr e in es) { e.DependsOn(here, dependsOn); } }
/// <summary> /// Find all transitive precedents, that is, cells /// that this cell transitively depends on. /// The cell thisFca is within the function sheet being translated. /// Cells outside the function sheet are not traced. /// </summary> /// <param name="thisFca"></param> /// <exception cref="CyclicException"></exception> private void GetTransitivePrecedents(FullCellAddr thisFca) { ISet <FullCellAddr> precedents = new HashSet <FullCellAddr>(); IDepend node = getNode(thisFca); if (node != null) { node.DependsOn(thisFca, delegate(FullCellAddr fca) { precedents.Add(fca); }); } // Now precedents is the set of cells directly referred from // the Expr in cell thisFca; that is, that cell's direct precedents foreach (FullCellAddr addr in precedents) { // Trace dependencies only from cells on this sheet, // and don't trace precedents of input cells if (addr.sheet == thisFca.sheet) { if (!AddPrecedentDependent(addr, thisFca) && !inputCellSet.Contains(addr)) { GetTransitivePrecedents(addr); } } } }
private bool HasDependentHelper(FullCellAddr node, ICollection <FullCellAddr> transitiveDependents, HashSet <FullCellAddr> visitedCells) { HashSet <FullCellAddr> dependents; if (nodesDependents.TryGetValue(node, out dependents)) { foreach (FullCellAddr addr in transitiveDependents) { if (dependents.Contains(addr)) { return(true); } } foreach (FullCellAddr addr in dependents) { if (visitedCells.Add(addr)) // returns true only first time { if (HasDependentHelper(addr, transitiveDependents, visitedCells)) { return(true); } } } } return(false); }
/// <summary> /// Make "precedent" a precedent of "node", and /// make "node" a dependent of "precedent". /// If the required sets of precedents resp. dependents do not exist, /// create them and associate them with "node" resp. "precedent". /// </summary> /// <exception cref="CyclicException">If a static cycle exists</exception> public bool AddPrecedentDependent(FullCellAddr precedent, FullCellAddr node) { HashSet<FullCellAddr> precedents; if (nodesPrecedents.TryGetValue(node, out precedents)) { try { precedents.Add(precedent); } catch (ArgumentException) { //Happens if the element already exists: there is a cycle throw new CyclicException("Static cycle through cell " + precedent, precedent); } } else { precedents = new HashSet<FullCellAddr>(); precedents.Add(precedent); nodesPrecedents.Add(node, precedents); } HashSet<FullCellAddr> dependents; if (nodesDependents.TryGetValue(precedent, out dependents)) { if (!dependents.Add(node)) { //Happens if the element already exists: there is a cycle throw new CyclicException("Static cycle through cell " + node, node); } else { return true; } } else { dependents = new HashSet<FullCellAddr>(); dependents.Add(node); nodesDependents.Add(precedent, dependents); return false; } }
public void ChangeFocus(FullCellAddr fca) { sheetHolder.SelectTab(fca.sheet.Name); SheetTab sheetTab = (SheetTab)sheetHolder.SelectedTab; sheetTab.ScrollTo(fca); }
// Register, unregister, update and look up the SDF tables /// <summary> /// Allocate an index for a new SDF, but do not bind its SdfDelegate /// </summary> /// <param name="outputCell"></param> /// <param name="inputCells"></param> /// <param name="name"></param> /// <returns></returns> public static SdfInfo Register(FullCellAddr outputCell, FullCellAddr[] inputCells, string name) { name = name.ToUpper(); SdfInfo sdfInfo = GetInfo(name); if (sdfInfo == null) // New SDF, register it { sdfInfo = new SdfInfo(outputCell, inputCells, name, nextIndex++); Debug.Assert(sdfInfo.index == nextIndex - 1); sdfNameToInfo[name] = sdfInfo; if (sdfInfo.index >= sdfDelegates.Length) { Debug.Assert(sdfDelegates.Length == sdfInfos.Length); // Reallocate sdfDelegates array Delegate[] newSdfs = new Delegate[2 * sdfDelegates.Length]; Array.Copy(sdfDelegates, newSdfs, sdfDelegates.Length); sdfDelegates = newSdfs; // Reallocate sdfInfos array SdfInfo[] newSdfInfos = new SdfInfo[2 * sdfInfos.Length]; Array.Copy(sdfInfos, newSdfInfos, sdfInfos.Length); sdfInfos = newSdfInfos; } sdfInfos[sdfInfo.index] = sdfInfo; // Update SDF function listbox if created and visible SdfForm sdfForm = System.Windows.Forms.Application.OpenForms["sdf"] as SdfForm; if (sdfForm != null && sdfForm.Visible) { sdfForm.PopulateFunctionListBox(false); sdfForm.PopulateFunctionListBox(name); sdfForm.Invalidate(); } } return(sdfInfo); }
public CGNormalCellRef(FullCellAddr cellAddr) { if (cellAddr.sheet.IsFunctionSheet) { this.index = -1; // Illegal cell reference } else { this.index = valueTable.GetIndex(cellAddr); } }
public virtual void DependsOn(FullCellAddr here, Action <FullCellAddr> dependsOn) { expr.DependsOn(here, dependsOn); if (evalCond != null) { evalCond.DependsOn(here, dependsOn); } }
public void AddComputeCell(FullCellAddr fca, ComputeCell ccell) { programList.Add(ccell); fcaToComputeCell.Add(fca, ccell); if (ccell.var != null) { addressToVariable.Add(fca, ccell.var); } }
// Set or remove (message=null) cell error mark public void SetCyclicError(String message) { if (Workbook.Cyclic != null) { FullCellAddr culprit = Workbook.Cyclic.culprit; sheetHolder.SelectTab(culprit.sheet.Name); SelectedSheet.SetCellErrorText(culprit.ca, message); } }
public DependencyGraph(FullCellAddr outputCell, FullCellAddr[] inputCells, Func<FullCellAddr, IDepend> getNode) { this.outputCell = outputCell; this.nodesDependents = new Dictionary<FullCellAddr, HashSet<FullCellAddr>>(); this.nodesPrecedents = new Dictionary<FullCellAddr, HashSet<FullCellAddr>>(); this.inputCells = inputCells; this.inputCellSet = new HashSet<FullCellAddr>(); this.inputCellSet.UnionWith(inputCells); this.getNode = getNode; CollectPrecedents(); }
private Variable numberVar; // If non-null, unwrap to this Number variable // If var==null then numberVar==null public ComputeCell(CGExpr expr, Variable var, FullCellAddr cellAddr) { this.expr = expr; this.var = var; // The output cell's expression is in tail position: if (var == null) { this.expr.NoteTailPosition(); } this.cellAddr = cellAddr; this.numberVar = null; }
internal SdfInfo(FullCellAddr outputCell, FullCellAddr[] inputCells, string name, int index) { this.outputCell = outputCell; this.inputCells = inputCells; this.name = name.ToUpper(); this.index = index; this.arity = inputCells.Length; }
private void AddCellToFunction(String info, FullCellAddr addr) { HashSet <String> names; if (!addressToFunctionList.TryGetValue(addr, out names)) { names = new HashSet <String>(); addressToFunctionList[addr] = names; } names.Add(info); }
public DependencyGraph(FullCellAddr outputCell, FullCellAddr[] inputCells, Func <FullCellAddr, IDepend> getNode) { this.outputCell = outputCell; this.nodesDependents = new Dictionary <FullCellAddr, HashSet <FullCellAddr> >(); this.nodesPrecedents = new Dictionary <FullCellAddr, HashSet <FullCellAddr> >(); this.inputCells = inputCells; this.inputCellSet = new HashSet <FullCellAddr>(); this.inputCellSet.UnionWith(inputCells); this.getNode = getNode; CollectPrecedents(); }
public ProgramLines(FullCellAddr outputCell, FullCellAddr[] inputCells) { this.outputCell = outputCell; this.inputCells = inputCells; programList = new List<ComputeCell>(); fcaToComputeCell = new Dictionary<FullCellAddr, ComputeCell>(); unwrapInputCells = new List<UnwrapInputCell>(); addressToVariable = new Dictionary<FullCellAddr, Variable>(); for (short i = 0; i < inputCells.Length; i++) { FullCellAddr addr = inputCells[i]; addressToVariable.Add(addr, new LocalArgument(addr.ToString(), Typ.Value, i)); } }
public ProgramLines(FullCellAddr outputCell, FullCellAddr[] inputCells) { this.outputCell = outputCell; this.inputCells = inputCells; programList = new List <ComputeCell>(); fcaToComputeCell = new Dictionary <FullCellAddr, ComputeCell>(); unwrapInputCells = new List <UnwrapInputCell>(); addressToVariable = new Dictionary <FullCellAddr, Variable>(); for (short i = 0; i < inputCells.Length; i++) { FullCellAddr addr = inputCells[i]; addressToVariable.Add(addr, new LocalArgument(addr.ToString(), Typ.Value, i)); } }
public FullCellAddr[] ResidualInputs(FunctionValue fv) { // The residual input cells are those that have input value NA FullCellAddr[] residualInputs = new FullCellAddr[fv.Arity]; int j = 0; for (int i = 0; i < fv.args.Length; i++) { if (fv.args[i] == ErrorValue.naError) { residualInputs[j++] = inputCells[i]; } } return(residualInputs); }
// Register SDFs (and maybe later: convert DELAY calls to DelayCell). private void RegisterSdfs(Sheet sheet, int col, int row) { Cell cell = sheet[col, row]; if (cell == null || !(cell is Formula)) { return; } Expr e = (cell as Formula).Expr; if (!(e is FunCall)) { return; } FunCall funCall = e as FunCall; Expr[] es = funCall.es; switch (funCall.function.name) { case "DEFINE": if (es.Length >= 2 && es[0] is TextConst && es[1] is CellRef) { String sdfName = ((TextConst)es[0]).value.value; FullCellAddr outputCell = ((CellRef)es[1]).GetAbsoluteAddr(sheet, col, row); FullCellAddr[] inputCells = new FullCellAddr[es.Length - 2]; bool ok = true; for (int i = 2; ok && i < es.Length; i++) { CellRef inputCellRef = es[i] as CellRef; ok = inputCellRef != null; if (ok) { inputCells[i - 2] = inputCellRef.GetAbsoluteAddr(sheet, col, row); } } if (ok) { Funcalc.SdfManager.Register(outputCell, inputCells, sdfName); } } break; case "DELAY": break; } }
/// <summary> /// Count number of references from cell at fromFca to cell address toFca /// </summary> private static int GetCount(FullCellAddr fromFca, FullCellAddr toFca) { int count = 0; Cell fromCell; if (fromFca.TryGetCell(out fromCell)) { fromCell.DependsOn(fromFca, delegate(FullCellAddr fca) { if (toFca.Equals(fca)) { count++; } }); } return(count); }
public static void CreateFunction(string name, FullCellAddr outputCell, FullCellAddr[] inputCells) { name = name.ToUpper(); // If the function exists, with the same input and output cells, keep it. // If it is a placeholder, overwrite its applier; if its input and output // cells have changed, recreate it (including its SdfInfo record). Function oldFunction = Function.Get(name); if (oldFunction != null) if (!oldFunction.IsPlaceHolder) return; // Registering the function before compilation allows it to call itself recursively SdfInfo sdfInfo = Register(outputCell, inputCells, name); // Console.WriteLine("Compiling {0} as #{1}", name, info.index); Update(sdfInfo, CompileSdf(sdfInfo)); if (oldFunction != null) // ... and is not a placeholder oldFunction.UpdateApplier(sdfInfo.Apply, sdfInfo.IsVolatile); else new Function(name, sdfInfo.Apply, isVolatile: sdfInfo.IsVolatile); }
/// <summary> /// Make "precedent" a precedent of "node", and /// make "node" a dependent of "precedent". /// If the required sets of precedents resp. dependents do not exist, /// create them and associate them with "node" resp. "precedent". /// </summary> /// <exception cref="CyclicException">If a static cycle exists</exception> public bool AddPrecedentDependent(FullCellAddr precedent, FullCellAddr node) { HashSet <FullCellAddr> precedents; if (nodesPrecedents.TryGetValue(node, out precedents)) { try { precedents.Add(precedent); } catch (ArgumentException) { //Happens if the element already exists: there is a cycle throw new CyclicException("Static cycle through cell " + precedent, precedent); } } else { precedents = new HashSet <FullCellAddr>(); precedents.Add(precedent); nodesPrecedents.Add(node, precedents); } HashSet <FullCellAddr> dependents; if (nodesDependents.TryGetValue(precedent, out dependents)) { if (!dependents.Add(node)) { //Happens if the element already exists: there is a cycle throw new CyclicException("Static cycle through cell " + node, node); } else { return(true); } } else { dependents = new HashSet <FullCellAddr>(); dependents.Add(node); nodesDependents.Add(precedent, dependents); return(false); } }
public void CallVisitor(CellRef cellRef) { FullCellAddr cellAddr = cellRef.GetAbsoluteAddr(thisFca); if (cellAddr.sheet != thisFca.sheet) { // Reference to other sheet, hopefully a normal sheet result = new CGNormalCellRef(cellAddr); } else if (this.addressToVariable.ContainsKey(cellAddr)) { // Reference to a cell that has already been computed in a local variable result = new CGCellRef(cellAddr, this.addressToVariable[cellAddr]); } else // Inline the cell's formula's expression { result = BuildExpression(cellAddr, addressToVariable); } }
public static CGExpr BuildExpression(FullCellAddr addr, Dictionary <FullCellAddr, Variable> addressToVariable) { Cell cell; if (!addr.TryGetCell(out cell)) { return(new CGTextConst(TextValue.EMPTY)); } else if (cell is NumberCell) { return(new CGNumberConst(((NumberCell)cell).value)); } else if (cell is TextCell) { return(new CGTextConst(((TextCell)cell).value)); } else if (cell is QuoteCell) { return(new CGTextConst(((QuoteCell)cell).value)); } else if (cell is BlankCell) { return(new CGError("#FUNERR: Blank cell in function")); } else if (cell is Formula) { // Translate the expr relative to its containing cell at addr CGExpressionBuilder cgBuilder = new CGExpressionBuilder(addressToVariable, addr); Expr expr = ((Formula)cell).Expr; expr.VisitorCall(cgBuilder); return(cgBuilder.result); } else if (cell is ArrayFormula) { return(new CGError("#FUNERR: Array formula in function")); } else { throw new ImpossibleException("BuildExpression: " + cell); } }
/// <summary> /// Compiles the topologically sorted list of Expr to a list (program) /// of ComputeCells, encapsulating CGExprs. Builds a map from cellAddr to /// local variable ids, for compiling sheet-internal cellrefs to ldloc instructions. /// </summary> public void AddComputeCells(DependencyGraph dpGraph, IList <FullCellAddr> cellList) { Debug.Assert(dpGraph.outputCell == cellList[cellList.Count - 1]); CGExpr outputExpr; if (cellList.Count == 0 || cellList.Count == 1 && dpGraph.inputCellSet.Contains(cellList.Single())) { // The output cell is also an input cell; load it: outputExpr = new CGCellRef(dpGraph.outputCell, addressToVariable[dpGraph.outputCell]); } else { // First process all non-output cells, and ignore all input cells: foreach (FullCellAddr cellAddr in cellList) { if (cellAddr.Equals(dpGraph.outputCell)) { continue; } HashSet <FullCellAddr> dependents = dpGraph.GetDependents(cellAddr); int minUses = dependents.Count; if (minUses == 1) { FullCellAddr fromFca = dependents.First(); minUses = Math.Max(minUses, GetCount(fromFca, cellAddr)); } // Now if minUses==1 then there is at most one use of the cell at cellAddr, // and no local variable is needed. Otherwise, allocate a local variable: if (minUses > 1) { CGExpr newExpr = CGExpressionBuilder.BuildExpression(cellAddr, addressToVariable); Variable var = new LocalVariable(cellAddr.ToString(), newExpr.Type()); AddComputeCell(cellAddr, new ComputeCell(newExpr, var, cellAddr)); } } // Then process the output cell: outputExpr = CGExpressionBuilder.BuildExpression(dpGraph.outputCell, addressToVariable); } // Add the output cell expression last, without a variable to bind it to; hence the null, // also indicating that (only) the output cell is in tail position: AddComputeCell(dpGraph.outputCell, new ComputeCell(outputExpr, null, dpGraph.outputCell)); }
// Evaluate cell's expression if necessary and cache its value; // also enqueue supported cells for evaluation if we use support graph public override Value Eval(Sheet sheet, int col, int row) { switch (state) { case CellState.Uptodate: break; case CellState.Computing: FullCellAddr culprit = new FullCellAddr(sheet, col, row); String msg = String.Format("### CYCLE in cell {0} formula {1}", culprit, Show(col, row, workbook.format)); throw new CyclicException(msg, culprit); case CellState.Dirty: case CellState.Enqueued: state = CellState.Computing; v = e.Eval(sheet, col, row); state = CellState.Uptodate; if (workbook.UseSupportSets) { ForEachSupported(EnqueueCellForEvaluation); } break; } return v; }
/// <summary> /// Insert code to unwrap the computed value of a cell, if the cell /// has type Value but is referred to as a Number more than once. /// Also register the unwrapped version of the variable /// in the NumberVariables dictionary. /// CodeGenerate.Initialize(ilg) must be called first. /// </summary> public void CreateUnwrappedNumberCells() { HashBag <FullCellAddr> numberUses = CountNumberUses(); foreach (KeyValuePair <FullCellAddr, int> numberUseCount in numberUses.ItemMultiplicities()) { FullCellAddr fca = numberUseCount.Key; if (numberUseCount.Value >= 2 && addressToVariable[fca].Type == Typ.Value) { Variable numberVar = new LocalVariable(fca + "_number", Typ.Number); ComputeCell ccell; if (fcaToComputeCell.TryGetValue(fca, out ccell)) // fca is ordinary computed cell { ccell.NumberVar = numberVar; } else // fca is an input cell { unwrapInputCells.Add(new UnwrapInputCell(addressToVariable[fca], numberVar)); } NumberVariables.Add(fca, numberVar); } } }
private void AddNode(HashList <FullCellAddr> sorted, FullCellAddr node) { HashSet <FullCellAddr> precedents; if (GetPrecedents(node, out precedents)) { Cell cell; foreach (FullCellAddr precedent in precedents) { // By including only non-input Formula and ArrayFormula cells, we avoid that // constant cells get stored in local variables. The result will not contain // constants cells (and will contain an input cell only if it is also the // output cell), so must the raw graph to find all cells belonging to an SDF. if (!sorted.Contains(precedent) && precedent.TryGetCell(out cell) && (cell is Formula || cell is ArrayFormula) && !inputCellSet.Contains(precedent)) { AddNode(sorted, precedent); } } } sorted.Add(node); // Last in HashList }
private bool HasPrecedentHelper(FullCellAddr node, ICollection<FullCellAddr> transitivePrecedents, HashSet<FullCellAddr> visitedCells) { HashSet<FullCellAddr> precedents; if (nodesPrecedents.TryGetValue(node, out precedents)) { foreach (FullCellAddr addr in transitivePrecedents) { if (precedents.Contains(addr)) { return true; } } foreach (FullCellAddr addr in precedents) { if (visitedCells.Add(addr)) { if (HasPrecedentHelper(addr, transitivePrecedents, visitedCells)) { return true; } } } } return false; }
/// <summary> /// Tests whether possiblePrecedents contains any cell that node /// transitively depends on. /// </summary> /// <param name="node"></param> /// <param name="transitivePrecedents"></param> /// <returns></returns> public bool HasPrecedent(FullCellAddr node, ICollection<FullCellAddr> transitivePrecedents) { HashSet<FullCellAddr> visitedCells = new HashSet<FullCellAddr>(); return HasPrecedentHelper(node, transitivePrecedents, visitedCells); }
private bool HasDependentHelper(FullCellAddr node, ICollection<FullCellAddr> transitiveDependents, HashSet<FullCellAddr> visitedCells) { HashSet<FullCellAddr> dependents; if (nodesDependents.TryGetValue(node, out dependents)) { foreach (FullCellAddr addr in transitiveDependents) { if (dependents.Contains(addr)) { return true; } } foreach (FullCellAddr addr in dependents) { if (visitedCells.Add(addr)) // returns true only first time { if (HasDependentHelper(addr, transitiveDependents, visitedCells)) { return true; } } } } return false; }
// Call dependsOn(fca) on all cells fca referred from expression, with multiplicity. // Cannot be implemented in terms of VisitRefs, which visits only once. public abstract void DependsOn(FullCellAddr here, Action<FullCellAddr> dependsOn);
private void AddNode(HashList<FullCellAddr> sorted, FullCellAddr node) { HashSet<FullCellAddr> precedents; if (GetPrecedents(node, out precedents)) { Cell cell; foreach (FullCellAddr precedent in precedents) { // By including only non-input Formula and ArrayFormula cells, we avoid that // constant cells get stored in local variables. The result will not contain // constants cells (and will contain an input cell only if it is also the // output cell), so must the raw graph to find all cells belonging to an SDF. if (!sorted.Contains(precedent) && precedent.TryGetCell(out cell) && (cell is Formula || cell is ArrayFormula) && !inputCellSet.Contains(precedent)) { AddNode(sorted, precedent); } } } sorted.Add(node); // Last in HashList }
public HashSet<FullCellAddr> GetDependents(FullCellAddr fca) { return nodesDependents[fca]; }
public override void DependsOn(FullCellAddr here, Action<FullCellAddr> dependsOn) { foreach (CGExpr e in es) { e.DependsOn(here, dependsOn); } }
public CyclicException(String msg, FullCellAddr culprit) : base(msg) { this.culprit = culprit; }
public virtual void DependsOn(FullCellAddr here, Action<FullCellAddr> dependsOn) { expr.DependsOn(here, dependsOn); if (evalCond != null) { evalCond.DependsOn(here, dependsOn); } }
internal bool GetPrecedents(FullCellAddr node, out HashSet<FullCellAddr> precedents) { return nodesPrecedents.TryGetValue(node, out precedents); }
public abstract void DependsOn(FullCellAddr here, Action <FullCellAddr> dependsOn);
/// <summary> /// Find all transitive precedents, that is, cells /// that this cell transitively depends on. /// The cell thisFca is within the function sheet being translated. /// Cells outside the function sheet are not traced. /// </summary> /// <param name="thisFca"></param> /// <exception cref="CyclicException"></exception> private void GetTransitivePrecedents(FullCellAddr thisFca) { ISet<FullCellAddr> precedents = new HashSet<FullCellAddr>(); IDepend node = getNode(thisFca); if (node != null) { node.DependsOn(thisFca, delegate(FullCellAddr fca) { precedents.Add(fca); }); } // Now precedents is the set of cells directly referred from // the Expr in cell thisFca; that is, that cell's direct precedents foreach (FullCellAddr addr in precedents) { // Trace dependencies only from cells on this sheet, // and don't trace precedents of input cells if (addr.sheet == thisFca.sheet) { if (!AddPrecedentDependent(addr, thisFca) && !inputCellSet.Contains(addr)) { GetTransitivePrecedents(addr); } } } }
public void ScrollTo(FullCellAddr fca) { dgv.FirstDisplayedScrollingRowIndex = Math.Max(0, fca.ca.row - 1); }
public override void DependsOn(FullCellAddr here, Action<FullCellAddr> dependsOn) { // We do not track dependencies on cells on ordinary sheets }
// Register SDFs (and maybe later: convert DELAY calls to DelayCell). private void RegisterSdfs(Sheet sheet, int col, int row) { Cell cell = sheet[col, row]; if (cell == null || !(cell is Formula)) return; Expr e = (cell as Formula).Expr; if (!(e is FunCall)) return; FunCall funCall = e as FunCall; Expr[] es = funCall.es; switch (funCall.function.name) { case "DEFINE": if (es.Length >= 2 && es[0] is TextConst && es[1] is CellRef) { String sdfName = (es[0] as TextConst).value.value; FullCellAddr outputCell = (es[1] as CellRef).GetAbsoluteAddr(sheet, col, row); FullCellAddr[] inputCells = new FullCellAddr[es.Length - 2]; bool ok = true; for (int i = 2; ok && i < es.Length; i++) { CellRef inputCellRef = es[i] as CellRef; ok = inputCellRef != null; if (ok) inputCells[i - 2] = inputCellRef.GetAbsoluteAddr(sheet, col, row); } if (ok) Funcalc.SdfManager.Register(outputCell, inputCells, sdfName); } break; case "DELAY": break; default: /* do nothing */ break; } }
private CGExpr result; // The result of the compilation is left here private CGExpressionBuilder(Dictionary<FullCellAddr, Variable> addressToVariable, FullCellAddr addr) { thisFca = addr; this.addressToVariable = addressToVariable; }
public void ChangeCellBackgroundColor(FullCellAddr fca, Color c) { dgv[fca.ca.col, fca.ca.row].Style.BackColor = c; }
public override void DependsOn(FullCellAddr here, Action<FullCellAddr> dependsOn) { e.DependsOn(here, dependsOn); }
public void ChangeCellBackgroundColor(FullCellAddr fca, Color c) { sheetHolder.SelectTab(fca.sheet.Name); SelectedSheet.ChangeCellBackgroundColor(fca, c); }
// Create SDFs private void CreateSdfs(Sheet sheet, int col, int row) { Cell cell = sheet[col, row]; if (cell == null || !(cell is Formula)) { return; } Expr e = (cell as Formula).Expr; if (!(e is FunCall)) { return; } FunCall funCall = e as FunCall; Expr[] es = funCall.es; switch (funCall.function.name) { case "DEFINE": if (es.Length >= 2 && es[0] is TextConst && es[1] is CellRef) { String sdfName = ((TextConst)es[0]).value.value; FullCellAddr outputCell = ((CellRef)es[1]).GetAbsoluteAddr(sheet, col, row); FullCellAddr[] inputCells = new FullCellAddr[es.Length - 2]; bool ok = true; for (int i = 2; ok && i < es.Length; i++) { CellRef inputCellRef = es[i] as CellRef; ok = inputCellRef != null; if (ok) { inputCells[i - 2] = inputCellRef.GetAbsoluteAddr(sheet, col, row); } } if (ok) { Funcalc.SdfManager.CreateFunction(sdfName, outputCell, inputCells); } } break; } }
public override void DependsOn(FullCellAddr here, Action <FullCellAddr> dependsOn) { }
public override void DependsOn(FullCellAddr here, Action<FullCellAddr> dependsOn) { // It seems that this could uselessly be called on every cell // that shares the formula, but this will not happen on function sheets caf.formula.DependsOn(here, dependsOn); }