-
Notifications
You must be signed in to change notification settings - Fork 86
arithmetic comparison and boolean operators
Sympl supports addition, subtraction, multiplication, division, less than, greater than, equals, and not equal as the obvious corresponding Expression Tree nodes. It support and, or, and not logical operators (see section for their semantics). Here are example Sympl expressions:
(+ 3 5)
(+ (* 10 x) (/ y 2))
(< x max-fixnum)
(or arg default-value)
(= (not (and a b))
(or (not a) (not b)))
All these operators are all keyword forms that AnalyzeBinaryExpr in etgen.cs analyzes. It picks off and and or to handle specially since they are conditionally evaluating and need to support Sympl's truth value semantics (see section on IF expressions). Otherwise, AnalyzeBinaryExpr just emits a BinaryOperation DynamicExpression using the operator stored in the SymplBinaryExpr AST node.
Here's the code for AnalyzeBinaryExpr, which is discussed further below:
public static Expression AnalyzeBinaryExpr(SymplBinaryExpr expr,
AnalysisScope scope) {
if (expr.Operation == ExpressionType.And) {
return AnalyzeIfExpr(
new SymplIfExpr(
expr.Left, expr.Right, null),
scope);
} else if (expr.Operation == ExpressionType.Or) {
// (Let* (tmp1 x)
// (If tmp1 tmp1
// (Let* (tmp2 y) (If tmp2 tmp2))))
IdOrKeywordToken tmp2 = new IdOrKeywordToken(
"__tmpLetVariable2");
var tmpExpr2 = new SymplIdExpr(tmp2);
var binding2 = new LetBinding(tmp2, expr.Right); ;
var ifExpr2 = new SymplIfExpr(
tmpExpr2, tmpExpr2, null);
var letExpr2 = new SymplLetStarExpr(
new[] { binding2 },
new[] { ifExpr2 });
// Build outer let*
IdOrKeywordToken tmp1 = new IdOrKeywordToken(
"__tmpLetVariable1");
var tmpExpr1 = new SymplIdExpr(tmp1);
LetBinding binding1 = new LetBinding(tmp1, expr.Left); ;
SymplExpr ifExpr1 = new SymplIfExpr(
tmpExpr1, tmpExpr1, letExpr2);
return AnalyzeLetStarExpr(
new SymplLetStarExpr(
new[] { binding1 },
new[] { ifExpr1 }
),
scope
);
}
return Expression.Dynamic(
scope.GetRuntime().GetBinaryOperationBinder(expr.Operation),
typeof(object),
AnalyzeExpr(expr.Left, scope),
AnalyzeExpr(expr.Right, scope));
Because and and or have equivalent semantic to IF (with some temporary bindings for or), the code above creates ASTs for IF and re-uses the AnalyzeIfExpr and AnalyzeLetStarExpr. Sympl could also have "open coded" the equivalent Expression Tree code generation here.
If the operation is other than and and or, AnalyzeBinaryExpr emits a DynamicExpression with a SymplBinaryOperationBinder. See section for a discussion of why this method calls GetBinaryOperationBinder rather than just calling the constructor. The reason Sympl uses a DynamicExpression when it only supports the static built-in semantics of Expression Trees is for interoperability with dynamic languages from other languages or libraries that might flow though a Sympl program.
The only unary operation Sympl supports is logical negation. Here's the code for AnalyzeUnaryExpr in etgen.cs:
public static Expression AnalyzeUnaryExpr(SymplUnaryExpr expr,
AnalysisScope scope) {
if (expr.Operation == ExpressionType.Not) {
return Expression.Not(WrapBooleanTest(
AnalyzeExpr(expr.Operand,
scope)));
}
return Expression.Dynamic(
scope.GetRuntime()
.GetUnaryOperationBinder(expr.Operation),
typeof(object),
AnalyzeExpr(expr.Operand, scope));
Execution never reaches the DynamicExpression result. This is there as plumbing and an example should Sympl support other unary operations, such as binary or arithmetic negation.
Sympl's logical not translates directly to an Expression Tree Not node as long as the operand expression has a Boolean type. Because of Sympl's truth semantics (see section on IF expressions), AnalyzeUnaryExpr calls WrapBooleanTest which results in an Expression with Type bool.
Binding binary operations is pretty easy in Sympl. Here's the code for the binder's FallbackBinaryOperation in runtime.cs:
public override DynamicMetaObject FallbackBinaryOperation(
DynamicMetaObject target, DynamicMetaObject arg,
DynamicMetaObject errorSuggestion) {
var restrictions = target.Restrictions.Merge(arg.Restrictions)
.Merge(BindingRestrictions.GetTypeRestriction(
target.Expression, target.LimitType))
.Merge(BindingRestrictions.GetTypeRestriction(
arg.Expression, arg.LimitType));
return new DynamicMetaObject(
RuntimeHelpers.EnsureObjectResult(
Expression.MakeBinary(
this.Operation,
Expression.Convert(target.Expression, target.LimitType),
Expression.Convert(arg.Expression, arg.LimitType))),
restrictions);
This function gathers all the restrictions for the arguments and merges them with the target's restrictions. Then it also merges in restrictions to ensure the arguments are the same LimitType as they have during this pass through the CallSite; the rule produced is only good for those types.
Then FallbackBinaryOperation returns a DynamicMetaObject with a BinaryExpression. The arguments are wrapped in ConvertExpressions to ensure they have the strict typing required for the BinaryExpression node returned. Recall that the argument's expression type may be more general than the actual LimitType they have at run time. The result BinaryExpression also passes through EnsureObjectResult in case it needs to be wrapped to ensure it is strictly typed as assignable to object. For more information, see section 3.2.4.
As stated above, Sympl never really uses its UnaryOperationBinder. It exists a plumbing for future unary features and as an example. It is exactly like FallbackBinaryOperation except that it only has one argument to process.
Here's the code form runtime.cs.
public override DynamicMetaObject FallbackUnaryOperation(
DynamicMetaObject target,
DynamicMetaObject errorSuggestion) {
return new DynamicMetaObject(
RuntimeHelpers.EnsureObjectResult(
Expression.MakeUnary(
this.Operation,
Expression.Convert(target.Expression, target.LimitType),
target.LimitType)),
target.Restrictions.Merge(
BindingRestrictions.GetTypeRestriction(
target.Expression, target.LimitType)));
Frontmatter
1 Introduction
1.1 Sources
1.2 Walkthrough Organization
2 Quick Language Overview
3 Walkthrough of Hello World
3.1 Quick Code Overview
3.2 Hosting, Globals, and .NET Namespaces Access
3.2.1 DLR Dynamic Binding and Interoperability -- a Very Quick Description
3.2.2 DynamicObjectHelpers
3.2.3 TypeModels and TypeModelMetaObjects
3.2.4 TypeModelMetaObject's BindInvokeMember -- Finding a Binding
3.2.5 TypeModelMetaObject.BindInvokeMember -- Restrictions and Conversions
3.3 Import Code Generation and File Module Scopes
3.4 Function Call and Dotted Expression Code Generation
3.4.1 Analyzing Function and Member Invocations
3.4.2 Analyzing Dotted Expressions
3.4.3 What Hello World Needs
3.5 Identifier and File Globals Code Generation
3.6 Sympl.ExecuteFile and Finally Running Code
4 Assignment to Globals and Locals
5 Function Definition and Dynamic Invocations
5.1 Defining Functions
5.2 SymplInvokeBinder and Binding Function Calls
6 CreateThrow Runtime Binding Helper
7 A Few Easy, Direct Translations to Expression Trees
7.1 Let* Binding
7.2 Lambda Expressions and Closures
7.3 Conditional (IF) Expressions
7.4 Eq Expressions
7.5 Loop Expressions
8 Literal Expressions
8.1 Integers and Strings
8.2 Keyword Constants
8.3 Quoted Lists and Symbols
8.3.1 AnalyzeQuoteExpr -- Code Generation
8.3.2 Cons and List Keyword Forms and Runtime Support
9 Importing Sympl Libraries and Accessing and Invoking Their Globals
10 Type instantiation
10.1 New Keyword Form Code Generation
10.2 Binding CreateInstance Operations in TypeModelMetaObject
10.3 Binding CreateInstance Operations in FallbackCreateInstance
10.4 Instantiating Arrays and GetRuntimeTypeMoFromModel
11 SymplGetMemberBinder and Binding .NET Instance Members
12 ErrorSuggestion Arguments to Binder FallbackX Methods
13 SymplSetMemberBinder and Binding .NET Instance Members
14 SymplInvokeMemberBinder and Binding .NET Member Invocations
14.1 FallbackInvokeMember
14.2 FallbackInvoke
15 Indexing Expressions: GetIndex and SetIndex
15.1 SymplGetIndexBinder's FallbackGetIndex
15.2 GetIndexingExpression
15.3 SymplSetIndexBinder's FallbackSetIndex
16 Generic Type Instantiation
17 Arithmetic, Comparison, and Boolean Operators
17.1 Analysis and Code Generation for Binary Operations
17.2 Analysis and Code Generation for Unary Operations
17.3 SymplBinaryOperationBinder
17.4 SymplUnaryOperationBinder
18 Canonical Binders or L2 Cache Sharing
19 Binding COM Objects
20 Using Defer When MetaObjects Have No Value
21 SymPL Language Description
21.1 High-level
21.2 Lexical Aspects
21.3 Built-in Types
21.4 Control Flow
21.4.1 Function Call
21.4.2 Conditionals
21.4.3 Loops
21.4.4 Try/Catch/Finally and Throw
21.5 Built-in Operations
21.6 Globals, Scopes, and Import
21.6.1 File Scopes and Import
21.6.2 Lexical Scoping
21.6.3 Closures
21.7 Why No Classes
21.8 Keywords
21.9 Example Code (mostly from test.sympl)
22 Runtime and Hosting
22.1 Class Summary
23 Appendixes
23.1 Supporting the DLR Hosting APIs
23.1.1 Main and Example Host Consumer
23.1.2 Runtime.cs Changes
23.1.3 Sympl.cs Changes
23.1.4 Why Not Show Using ScriptRuntime.Globals Namespace Reflection
23.1.5 The New DlrHosting.cs File
23.2 Using the Codeplex.com DefaultBinder for rich .NET interop
23.3 Using Codeplex.com Namespace/Type Trackers instead of ExpandoObjects
23.4 Using Codeplex.com GeneratorFunctionExpression
Other documents:
Dynamic Language Runtime
DLR Hostirng Spec
Expression Trees v2 Spec
Getting Started with the DLR as a Library Author
Sites, Binders, and Dynamic Object Interop Spec