internal override Value Eval(DelimiterNode prev, DelimiterNode next, IScope paramScope, IScope bodyScope, INodeRequestor nodes, ILineRequestor requestor) { // if we need prev or next params but they're not there, simply return the function if ((m_usePrevious || m_useNext) && prev == null && next == null) return this; // scope we'll add prev/next/body params to ScopeChain localScope = new ScopeChain(); if (m_usePrevious) { if (prev == null) { if (m_useNext) return new PartialFunctionIn(this, null, next); throw new Loki3Exception().AddMissingValue(true/*bPrevious*/); } Value value1 = ComputeParam(Metadata[keyPreviousPattern], prev, paramScope, nodes, requestor); Value match, leftover; if (!PatternChecker.Do(value1, Metadata[keyPreviousPattern], false/*bShortPat*/, out match, out leftover)) throw new Loki3Exception().AddWrongPattern(Metadata[keyPreviousPattern], value1); if (leftover != null) { if (m_useNext) // currently can't do partials for infix throw new Loki3Exception().AddWrongPattern(Metadata[keyPreviousPattern], value1); // create a partial function that starts w/ match & still needs leftover return new UserFunction(this, match, leftover, null); } Utility.AddToScope(m_pattern1, match, localScope); } if (m_useNext) { if (next == null) { if (m_usePrevious) return new PartialFunctionIn(this, prev, null); throw new Loki3Exception().AddMissingValue(false/*bPrevious*/); } Value value2 = ComputeParam(Metadata[keyNextPattern], next, paramScope, nodes, requestor); Value match, leftover; if (!PatternChecker.Do(value2, Metadata[keyNextPattern], false/*bShortPat*/, out match, out leftover)) throw new Loki3Exception().AddWrongPattern(Metadata[keyNextPattern], value2); // if we created a function that needs a body, add it if present ValueFunction matchFunc = match as ValueFunction; if (matchFunc != null && matchFunc.RequiresBody() && requestor != null) match = EvalList.DoAddBody(matchFunc, bodyScope, requestor); if (leftover != null) { if (m_usePrevious) // currently can't do partials for infix throw new Loki3Exception().AddWrongPattern(Metadata[keyNextPattern], value2); // create a partial function that starts w/ match & still needs leftover return new UserFunction(this, match, null, leftover); } Utility.AddToScope(m_pattern2, match, localScope); } // tack on body if requested if (Metadata.GetOptionalT<bool>("body?", false)) { bool foundBody = false; // if there's a following node, use it DelimiterNode possibleBody = nodes.GetNext(); if (possibleBody != null) { localScope.SetValue("body", UseNodeAsBody(possibleBody)); foundBody = true; } // if no body, use the following lines if (!foundBody && requestor != null) { List<DelimiterList> body = EvalList.DoGetBody(bodyScope, requestor); if (body.Count != 0) { localScope.SetValue("body", new ValueLine(body, bodyScope)); foundBody = true; } } if (!foundBody) // create a partial function that only needs a body return new UserFunction(this, localScope); } // create a new scope and add passed in arguments... ScopeChain scope = (ShouldCreateScope ? new ScopeChain(bodyScope) : bodyScope as ScopeChain); scope.Function = this; if (m_fullPattern != null && m_passed != null) Utility.AddToScope(m_fullPattern, m_passed, scope); // ...and the prev/next/body params we just extracted Utility.AddToScope(localScope, scope); if (m_passedScope != null) Utility.AddToScope(m_passedScope, scope); // lazily parse EnsureParsed(bodyScope); // eval each line using current scope try { Value retval = EvalBody.Do(m_parsedLines, scope); scope.Exit(); return retval; } catch (PopStackException pop) { // if we're supposed to pop back to here then return, else keep throwing up the stack if (pop.ScopeName == scope.Name) return pop.Return; throw pop; } catch (Loki3Exception e) { // if this scope catches exceptions, stop here if (Loki3Exception.catchScopeName == scope.Name) { if (scope.Parent != null) scope.Parent.SetValue(Loki3Exception.exceptionKey, new ValueMap(e.Errors)); return ValueNil.Nil; } throw e; } }
/// <summary> /// Evaluates a token, possibly requesting the previous and next values. /// Returns a value. /// </summary> /// <param name="token">token representing a function or variable</param> /// <param name="scope">used to request functions, variables, and delimiters if there's no scope attached to the node</param> /// <param name="nodes">used to request previous and next nodes</param> internal static Value Do(DelimiterNode node, IScope scope, INodeRequestor nodes, ILineRequestor requestor) { if (node.Value != null) { // node has already been evaluated return node.Value; } else if (node.Token != null) { // function/variable or built-in Token token = node.Token; Value value = scope.GetValue(token); if (value is ValueFunction && nodes != null) { ValueFunction function = value as ValueFunction; // get previous & next nodes if needed DelimiterNode previous = (function.ConsumesPrevious ? nodes.GetPrevious() : null); DelimiterNode next = (function.ConsumesNext ? nodes.GetNext() : null); scope.FunctionName = token.Value; // evaluate try { return function.Eval(previous, next, scope, scope, nodes, requestor); } catch (Loki3Exception e) { // this function is the correct context if there isn't already one there if (!e.Errors.ContainsKey(Loki3Exception.keyFunction)) e.AddFunction(token.Value); if (!e.Errors.ContainsKey(Loki3Exception.keyScope)) e.AddScope(scope); throw e; } } else if (value != null) { return value; } else { return EvalBuiltin.Do(token); } } else if (node.List != null) { // delimited list of nodes Value value = null; DelimiterList list = node.List; IScope listScope = (list.Scope != null ? list.Scope : scope); DelimiterType type = list.Delimiter.DelimiterType; // get contents as a Value switch (type) { case DelimiterType.AsString: value = new ValueString(list.Original); break; case DelimiterType.AsValue: value = EvalList.Do(list.Nodes, listScope); break; case DelimiterType.AsArray: List<Value> values = new List<Value>(list.Nodes.Count); foreach (DelimiterNode subnode in list.Nodes) { // note: 'nodes' is null so functions don't get evaled Value subvalue = Do(subnode, listScope, null, requestor); values.Add(subvalue); } value = new ValueArray(values); break; case DelimiterType.AsEvaledArray: value = EvalList.DoEvaledArray(list.Nodes, listScope); break; case DelimiterType.AsRaw: value = new ValueRaw(list, listScope); break; case DelimiterType.AsArrayOfRaw: List<Value> rawvalues = new List<Value>(list.Nodes.Count); foreach (DelimiterNode subnode in list.Nodes) rawvalues.Add(new ValueRaw(subnode, listScope)); value = new ValueArray(rawvalues); break; } // run contents through a function if specified ValueFunction function = list.Delimiter.Function; if (function == null) return value; DelimiterNode next = new DelimiterNodeValue(value); return function.Eval(null, next, scope, scope, nodes, requestor); } return new ValueNil(); }
/// <summary>Evaluate this node, possibly consuming adjacent nodes</summary> internal void Eval(IScope scope, INodeRequestor nodes, ILineRequestor requestor) { if (m_state != NodeState.Node && m_state != NodeState.Function) return; // get new value if (m_state == NodeState.Node) { // hasn't been evaled at all m_value = EvalNode.Do(m_node, scope, nodes, requestor); } else if (m_state == NodeState.Function) { // previously resolved to a function DelimiterNode previous = (m_func.ConsumesPrevious ? previous = nodes.GetPrevious() : null); DelimiterNode next = (m_func.ConsumesNext ? next = nodes.GetNext() : null); if ((m_func.ConsumesPrevious || m_func.ConsumesNext) && (previous == null && next == null)) { // no prev/next parameters passed, perhaps it can use body? if (m_func.RequiresBody() && requestor != null) { // tack on body if present m_value = EvalList.DoAddBody(m_func, scope, requestor); } else { // function can't be evaled further m_state = NodeState.Value; return; } } else { if (!m_func.ConsumesPrevious && !m_func.ConsumesNext && !m_func.RequiresBody() && !m_func.ForceEval) { // function can't be evaled further m_state = NodeState.Value; return; } m_value = m_func.Eval(previous, next, scope, scope, nodes, requestor); } } if (m_value == null) { // e.g. because node was a comment m_state = NodeState.Empty; return; } // store new info about this node m_node = new DelimiterNodeValue(m_value); m_order = (int)m_value.Order; if (m_value.Type == ValueType.Function) { m_state = NodeState.Function; m_func = m_value as ValueFunction; } else { m_state = NodeState.Value; } }