public static ExprOp FindDefinition(ExprOp op, Scope scope, ExprOp locus, int depth, int recursionDepth = 0) { // If the object we are apply call notation to is a FUNCTION value // or a O_LAMBDA expression, then this is the object we want to // call. if (op.IsFunction || op.Kind == OpKindEnum.O_LAMBDA) { return(op); } if (recursionDepth > 256) { throw new ValueError(ValueError.ValueFunctionRecursionDepthTooDeep); } // If it's an identifier, look up its definition and see if it's a // function. if (op.IsIdent) { return(FindDefinition(LookupIdent(op, scope), scope, locus, depth, recursionDepth + 1)); } // Value objects might be callable if they contain an expression. if (op.IsValue) { Value def = op.AsValue; if (Expr.IsExpr(def)) { return(FindDefinition(Expr.AsExpr(def), scope, locus, depth, recursionDepth + 1)); } else { throw new ValueError(String.Format(ValueError.CannotCallSmthAsFunction, def)); } } // Resolve ordinary expressions. return(FindDefinition(WrapValue(op.Calc(scope, locus, depth + 1)), scope, locus, depth + 1, recursionDepth + 1)); }
/// <summary> /// Ported from value_t expr_t::op_t::calc /// </summary> public Value Calc(Scope scope, ExprOp locus = null, int depth = 0) { try { Value result = Value.Empty; Logger.Current.Debug("expr.calc", () => String.Format("{0}{1} => ...", new String('.', depth), ErrorContext.OpContext(this))); switch (Kind) { case OpKindEnum.VALUE: result = AsValue; break; case OpKindEnum.O_DEFINE: result = Value.Empty; break; case OpKindEnum.IDENT: ExprOp definition = LookupIdent(this, scope); if (definition != null) { // Evaluating an identifier is the same as calling its definition // directly result = definition.Calc(scope, locus, depth + 1); CheckTypeContext(scope, result); } break; case OpKindEnum.FUNCTION: // Evaluating a FUNCTION is the same as calling it directly; this // happens when certain functions-that-look-like-variables (such as // "amount") are resolved. CallScope callArgs = new CallScope(scope, locus, depth + 1); result = AsFunction(callArgs); CheckTypeContext(scope, result); break; case OpKindEnum.SCOPE: // assert(! is_scope_unset()); - does not make sense here if (IsScopeUnset) { SymbolScope subscope = new SymbolScope(scope); result = Left.Calc(subscope, locus, depth + 1); } else { BindScope boundScope = new BindScope(scope, AsScope); result = Left.Calc(boundScope, locus, depth + 1); } break; case OpKindEnum.O_LOOKUP: ContextScope contextScope = new ContextScope(scope, ValueTypeEnum.Scope); bool scopeError = true; Value obj = Left.Calc(contextScope, locus, depth + 1); if (obj != null) { if (obj.Type == ValueTypeEnum.Scope && obj.AsScope != null) { BindScope boundScope = new BindScope(scope, obj.AsScope); result = Right.Calc(boundScope, locus, depth + 1); scopeError = false; } } if (scopeError) { throw new CalcError(CalcError.ErrorMessageLeftOperandDoesNotEvaluateToObject); } break; case OpKindEnum.O_CALL: result = CalcCall(scope, locus, depth); CheckTypeContext(scope, result); break; case OpKindEnum.O_LAMBDA: result = Value.Get(this); break; case OpKindEnum.O_MATCH: result = Value.Get(Right.Calc(scope, locus, depth + 1).AsMask.Match(Left.Calc(scope, locus, depth + 1).ToString())); break; case OpKindEnum.O_EQ: result = Value.Get(Left.Calc(scope, locus, depth + 1).IsEqualTo(Right.Calc(scope, locus, depth + 1))); break; case OpKindEnum.O_LT: result = Value.Get(Left.Calc(scope, locus, depth + 1).IsLessThan(Right.Calc(scope, locus, depth + 1))); break; case OpKindEnum.O_LTE: result = Value.Get(!Left.Calc(scope, locus, depth + 1).IsGreaterThan(Right.Calc(scope, locus, depth + 1))); break; case OpKindEnum.O_GT: result = Value.Get(Left.Calc(scope, locus, depth + 1).IsGreaterThan(Right.Calc(scope, locus, depth + 1))); break; case OpKindEnum.O_GTE: result = Value.Get(!Left.Calc(scope, locus, depth + 1).IsLessThan(Right.Calc(scope, locus, depth + 1))); break; case OpKindEnum.O_ADD: result = Left.Calc(scope, locus, depth + 1) + Right.Calc(scope, locus, depth + 1); break; case OpKindEnum.O_SUB: result = Left.Calc(scope, locus, depth + 1) - Right.Calc(scope, locus, depth + 1); break; case OpKindEnum.O_MUL: result = Left.Calc(scope, locus, depth + 1) * Right.Calc(scope, locus, depth + 1); break; case OpKindEnum.O_DIV: result = Left.Calc(scope, locus, depth + 1) / Right.Calc(scope, locus, depth + 1); break; case OpKindEnum.O_NEG: result = Left.Calc(scope, locus, depth + 1).Negated(); break; case OpKindEnum.O_NOT: result = Value.Get(!Left.Calc(scope, locus, depth + 1).Bool); break; case OpKindEnum.O_AND: if (Left.Calc(scope, locus, depth + 1).Bool) { result = Right.Calc(scope, locus, depth + 1); } else { result = Value.Get(false); } break; case OpKindEnum.O_OR: Value temp_O_OR = Left.Calc(scope, locus, depth + 1); if (temp_O_OR.Bool) { result = temp_O_OR; } else { result = Right.Calc(scope, locus, depth + 1); } break; case OpKindEnum.O_QUERY: if (Right == null || Right.Kind != OpKindEnum.O_COLON) { throw new InvalidOperationException("O_QUERY"); } Value temp = Left.Calc(scope, locus, depth + 1); if (temp.Bool) { result = Right.Left.Calc(scope, locus, depth + 1); } else { result = Right.Right.Calc(scope, locus, depth + 1); } break; case OpKindEnum.O_COLON: throw new InvalidOperationException("We should never calculate an O_COLON operator"); case OpKindEnum.O_CONS: result = CalcCons(scope, locus, depth); break; case OpKindEnum.O_SEQ: result = CalcSeq(scope, locus, depth); break; default: throw new CalcError(String.Format(CalcError.ErrorMessageUnexpectedExprNode, this)); } Logger.Current.Debug("expr.calc", () => String.Format("{0}{1} => {2}", new String('.', depth), ErrorContext.OpContext(this), result.Dump(true))); return(result); } catch { if (locus == null) { locus = this; } throw; } }
/// <summary> /// Ported from expr_t::ptr_op_t expr_t::op_t::compile /// </summary> public ExprOp Compile(Scope scope, int depth = 0, Scope paramScope = null) { ExprOp result = null; Scope boundScope; Logger.Current.Debug("expr.compile", () => new String('.', depth)); if (Kind >= OpKindEnum.LAST) { throw new InvalidOperationException(); } if (IsIdent) { Logger.Current.Debug("expr.compile", () => String.Format("Lookup: {0} in {1}", AsIdent, scope)); ExprOp def = null; if (paramScope != null) { def = paramScope.Lookup(SymbolKindEnum.FUNCTION, AsIdent); } if (def == null) { def = scope.Lookup(SymbolKindEnum.FUNCTION, AsIdent); } if (def != null) { // Identifier references are first looked up at the point of // definition, and then at the point of every use if they could // not be found there. Logger.Current.Debug("expr.compile", () => String.Format("Found definition:{0}", def.Dump())); result = Copy(def); } else if (Left != null) { result = Copy(); } else { result = this; } } else if (IsScope) { Scope subScope = new SymbolScope(Scope.EmptyScope); AsScope = subScope; boundScope = new BindScope(scope, subScope); scope = boundScope; } else if (Kind < OpKindEnum.TERMINALS) { result = this; } else if (Kind == OpKindEnum.O_DEFINE) { switch (Left.Kind) { case OpKindEnum.IDENT: { ExprOp node = Right.Compile(scope, depth + 1, paramScope); Logger.Current.Debug("expr.compile", () => String.Format("Defining {0} in {1}", Left.AsIdent, scope)); scope.Define(SymbolKindEnum.FUNCTION, Left.AsIdent, node); } break; case OpKindEnum.O_CALL: if (Left.Left.IsIdent) { ExprOp node = new ExprOp(OpKindEnum.O_LAMBDA); node.Left = Left.Right; node.Right = Right; node.Compile(scope, depth + 1, paramScope); Logger.Current.Debug("expr.compile", () => String.Format("Defining {0} in {1}", Left.Left.AsIdent, scope)); scope.Define(SymbolKindEnum.FUNCTION, Left.Left.AsIdent, node); break; } throw new CompileError(CompileError.ErrorMessageInvalidFunctionDefinition); default: throw new CompileError(CompileError.ErrorMessageInvalidFunctionDefinition); } result = WrapValue(Value.Empty); } else if (Kind == OpKindEnum.O_LAMBDA) { SymbolScope parms = new SymbolScope(paramScope ?? Scope.EmptyScope); for (ExprOp sym = Left; sym != null; sym = sym.HasRight ? sym.Right : null) { ExprOp varName = sym.Kind == OpKindEnum.O_CONS ? sym.Left : sym; if (!varName.IsIdent) { string buf = varName.Dump(); throw new CalcError(String.Format(CalcError.ErrorMessageInvalidFunctionOrLambdaParameter, buf)); } else { Logger.Current.Debug("expr.compile", () => String.Format("Defining function parameter {0}", varName.AsIdent)); parms.Define(SymbolKindEnum.FUNCTION, varName.AsIdent, new ExprOp(OpKindEnum.PLUG)); } } ExprOp rhs = Right.Compile(scope, depth + 1, parms); if (rhs == Right) { result = this; } else { result = Copy(Left, rhs); } } if (result == null) { if (Left == null) { throw new CalcError(CalcError.ErrorMessageSyntaxError); } ExprOp lhs = Left.Compile(scope, depth + 1, paramScope); ExprOp rhs = Kind > OpKindEnum.UNARY_OPERATORS && HasRight ? (Kind == OpKindEnum.O_LOOKUP ? Right : Right.Compile(scope, depth + 1, paramScope)) : null; if (lhs == Left && (rhs == null || rhs == Right)) { result = this; } else { ExprOp intermediate = Copy(lhs, rhs); // Reduce constants immediately if possible if ((lhs == null || lhs.IsValue) && (rhs == null || rhs.IsValue)) { result = WrapValue(intermediate.Calc(scope, null, depth + 1)); } else { result = intermediate; } } } Logger.Current.Debug("expr.compile", () => new String('.', depth)); return(result); }