public int Execute(Frame frame) { object result = frame.result; if (to.IsAssignableFrom(result)) { return(1); } RedwoodType type; if (result is RedwoodObject rwo) { type = rwo.Type; } else { type = RedwoodType.GetForCSharpType(result.GetType()); } // TODO: this may be repeating existing work that occurs later if (!type.HasImplicitConversion(to)) { throw new NotImplementedException(); } Lambda conversion = RuntimeUtil.GetConversionLambda(type, to); frame.result = conversion.Run(frame.result); return(1); }
internal override IEnumerable <Instruction> Compile() { IEnumerable <Instruction> compiledCondition = Condition.Compile(); Instruction[] compiledPathTrue = PathTrue.Compile().ToArray(); Instruction[] compiledElseStatement = ElseStatement?.Compile().ToArray() ?? new Instruction[0]; List <Instruction> instructions = new List <Instruction>(); instructions.AddRange(compiledCondition); if (RedwoodType.GetForCSharpType(typeof(bool)) != Condition.GetKnownType()) { throw new NotImplementedException(); } // The index of the jump instruction relative to the the else is -1 // (ie. right before the start). When we factor in the jump, we need // to skip 2 additional instructions instructions.Add(new ConditionalJumpInstruction(compiledElseStatement.Length + 2)); instructions.AddRange(compiledElseStatement); instructions.Add(new JumpInstruction(compiledPathTrue.Length + 1)); instructions.AddRange(compiledPathTrue); return(instructions); }
public void CanReturnSimpleValue() { Lambda lambda = Compiler.CompileFunction( new FunctionDefinition { ClassMethod = false, Name = "testFunc", ReturnType = new TypeSyntax { TypeName = new NameExpression { Name = "string" } }, Parameters = new ParameterDefinition[] { }, Body = new BlockStatement { Statements = new Statement[] { new ReturnStatement { Expression = new StringConstant { Value = "Test" } } } } } ); Assert.Equal(RedwoodType.GetForCSharpType(typeof(string)), lambda.ReturnType); Assert.Equal("Test", lambda.Run()); }
internal override void Bind(Binder binder) { Chain.Bind(binder); RedwoodType chainType = Chain.GetKnownType(); if (chainType == null) { return; } if (chainType.CSharpType == null) { KnownType = chainType.slotTypes?[chainType.slotMap[Element.Name]]; } else { PropertyInfo property; FieldInfo field; MemberResolver.TryResolveMember(chainType, Element.Name, false, out property, out field); if (property != null) { KnownType = RedwoodType.GetForCSharpType(property.PropertyType); } if (field != null) { KnownType = RedwoodType.GetForCSharpType(field.FieldType); } } }
public override RedwoodType GetKnownType() { switch (Operator) { case BinaryOperator.LogicalAnd: case BinaryOperator.LogicalOr: return(RedwoodType.GetForCSharpType(typeof(bool))); case BinaryOperator.As: return(Right.EvaluateConstant() as RedwoodType); // TODO! default: break; } if (LambdaType == null || LambdaType.CSharpType == typeof(LambdaGroup)) { return(null); } RedwoodType[] signature = LambdaType.GenericArguments; if (signature == null || signature.Length == 0) { return(null); } return(signature[signature.Length - 1]); }
public override RedwoodType GetKnownType() { if (Value < int.MaxValue && Value > int.MinValue) { return(RedwoodType.GetForCSharpType(typeof(int))); } return(RedwoodType.GetForCSharpType(typeof(BigInteger))); }
private void Init(object target, MethodBase info) { boundTarget = target; this.info = info; ParameterInfo[] parameters = info.GetParameters(); RedwoodType[] expectedArgs = new RedwoodType[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { expectedArgs[i] = RedwoodType.GetForCSharpType(parameters[i].ParameterType); } ExpectedArgs = expectedArgs; }
private IEnumerable <Instruction> CompileLogicalExpression() { List <Instruction> instructions = new List <Instruction>(); instructions.AddRange(Left.Compile()); if (Left.GetKnownType() != RedwoodType.GetForCSharpType(typeof(bool))) { instructions.AddRange( Compiler.CompileImplicitConversion( Left.GetKnownType(), RedwoodType.GetForCSharpType(typeof(bool)) ) ); } List <Instruction> rightInstructions = new List <Instruction>(); rightInstructions.AddRange(Right.Compile()); if (Right.GetKnownType() != RedwoodType.GetForCSharpType(typeof(bool))) { instructions.AddRange( Compiler.CompileImplicitConversion( Right.GetKnownType(), RedwoodType.GetForCSharpType(typeof(bool)) ) ); } // Short circuiting switch (Operator) { case BinaryOperator.LogicalAnd: // If we get a true, then don't skip the second condition instructions.Add(new ConditionalJumpInstruction(2)); instructions.Add(new JumpInstruction(rightInstructions.Count + 1)); break; case BinaryOperator.LogicalOr: instructions.Add(new ConditionalJumpInstruction(rightInstructions.Count + 1)); break; default: // Should not happen throw new NotImplementedException(); } instructions.AddRange(rightInstructions); return(instructions); }
private static List <Variable> GetSpecialMappedVariables() { List <Variable> builtinVariables = new List <Variable>(); foreach (string name in RedwoodType.specialMappedTypes.Keys) { builtinVariables.Add(new Variable { Name = name, DefinedConstant = true, ConstantValue = RedwoodType.specialMappedTypes[name], KnownType = RedwoodType.GetForCSharpType(typeof(RedwoodType)) }); } return(builtinVariables); }
public async Task FunctionCanHoldClosureInformation() { string code = @" function<string> testFunc() { let string varA = ""Test123""; function<string> innerTestFunc() { return varA; } return innerTestFunc(); }"; Lambda lambda = await MakeLambda(code); Assert.Equal(RedwoodType.GetForCSharpType(typeof(string)), lambda.ReturnType); Assert.Equal("Test123", lambda.Run()); }
public async Task FunctionCanBeParsedAndCalled() { string code = @" function<string> testFunc() { function<string> innerTestFunc(string paramA) { return paramA; } return innerTestFunc(""Test""); }"; Lambda lambda = await MakeLambda(code); Assert.Equal(RedwoodType.GetForCSharpType(typeof(string)), lambda.ReturnType); Assert.Equal("Test", lambda.Run()); }
internal override IEnumerable <Instruction> Compile() { List <Instruction> instructions = new List <Instruction>(); instructions.AddRange(Initializer?.Compile() ?? new Instruction[0]); List <Instruction> bodyInstructions = Body.Compile().ToList(); bodyInstructions.AddRange(Incrementor?.Compile() ?? new Instruction[0]); List <Instruction> check; if (Condition == null) { // No condition? We're in a forever loop check = new List <Instruction>(); } else { check = Condition.Compile().ToList(); check.AddRange( Compiler.CompileImplicitConversion( Condition.GetKnownType(), RedwoodType.GetForCSharpType(typeof(bool)) ) ); check.Add(new ConditionalJumpInstruction(2)); // Next instruction (1) + Body + Increment + Jump instruction (1) check.Add(new JumpInstruction(bodyInstructions.Count + 2)); } // Jump back to the beginning of the loop bodyInstructions.Add(new JumpInstruction(-(bodyInstructions.Count + check.Count))); instructions.AddRange(check); instructions.AddRange(bodyInstructions); return(instructions); }
internal override IEnumerable <NameExpression> Walk() { base.Walk(); string typename = CollectName(namespaceWalk); if (TryGetTypeFromAssemblies(typename, out Type cSharpType)) { DeclaredVariable.DefinedConstant = true; DeclaredVariable.KnownType = RedwoodType.GetForCSharpType(typeof(RedwoodType)); DeclaredVariable.ConstantValue = RedwoodType.GetForCSharpType(cSharpType); } else { FreeVar = new NameExpression { Name = typename }; // Let the compiler take it because it needs to go parse // these additional modules return(new NameExpression[] { FreeVar }); } return(new NameExpression[0]); }
internal static List <OverloadGroup> GenerateOverloads( List <FunctionDefinition> functions) { Dictionary <string, List <FunctionDefinition> > functionsByName = new Dictionary <string, List <FunctionDefinition> >(); foreach (FunctionDefinition function in functions) { if (functionsByName.ContainsKey(function.Name)) { functionsByName[function.Name].Add(function); } else { functionsByName.Add(function.Name, new List <FunctionDefinition>( new FunctionDefinition[] { function }) ); } } // TODO: Define a strict ordering for functions in an overload group List <OverloadGroup> overloads = new List <OverloadGroup>(); foreach (List <FunctionDefinition> overload in functionsByName.Values) { Variable variable = overload.Count == 1 ? overload[0].DeclaredVariable : new Variable { Name = overload[0].Name, KnownType = RedwoodType.GetForCSharpType(typeof(LambdaGroup)) }; overloads.Add(new OverloadGroup(overload, variable)); } return(overloads); }
private InternalLambdaDescription CompileInterfaceConversion(RedwoodType type) { int closureId = This.ClosureID; int[] slots = RuntimeUtil.GetSlotMapsToInterface(Type, type); List <Instruction> instructions = new List <Instruction>(); for (int i = 0; i < slots.Length; i++) { // Get the member on our class instructions.Add(new LookupClosureInstruction(closureId, slots[i])); // Assign it as an argument for the closure instructions.Add(new AssignLocalInstruction(i)); } if (type.CSharpType != null) { instructions.Add( new BuildArrayInstruction( Enumerable .Range(0, slots.Length) .ToArray(), typeof(object) ) ); // Save it right past the arguments for the creation of // the array instructions.Add(new AssignLocalInstruction(slots.Length)); } instructions.Add(new LoadConstantInstruction(type)); instructions.Add(new LookupExternalMemberLambdaInstruction( "Constructor", RedwoodType.GetForCSharpType(typeof(RedwoodType)) )); if (type.CSharpType == null) { // We already arranged all of the arguments in order instructions.Add( new InternalCallInstruction( Enumerable .Range(0, slots.Length) .ToArray() ) ); } else { instructions.Add( new ExternalCallInstruction( new int[] { slots.Length } ) ); } instructions.Add(new ReturnInstruction()); return(new InternalLambdaDescription { argTypes = new RedwoodType[0], closureSize = 0, stackSize = slots.Length + 1, returnType = type, instructions = instructions.ToArray() }); }
internal ExternalLambda(object target, MethodInfo info) { Init(target, info); ReturnType = RedwoodType.GetForCSharpType(info.ReturnType); }
internal override IEnumerable <NameExpression> Walk() { Type = RedwoodType.Make(this); base.Walk(); DeclaredVariable.KnownType = RedwoodType.GetForCSharpType(typeof(RedwoodType)); DeclaredVariable.DefinedConstant = true; DeclaredVariable.ConstantValue = Type; List <NameExpression> freeVars = new List <NameExpression>(); List <Variable> declaredVars = new List <Variable>(); InterfaceImplicitConversionVars = new List <Variable>(); int maxConstructorArgs = 0; This = new Variable { Name = "this", KnownType = Type }; declaredVars.Add(This); if (ParameterFields != null) { maxConstructorArgs = ParameterFields.Length; foreach (ParameterDefinition param in ParameterFields) { freeVars.AddRange(param.Walk()); declaredVars.Add(param.DeclaredVariable); } } foreach (TypeSyntax interfaceType in Interfaces) { freeVars.AddRange(interfaceType.Walk()); // TODO: What if we inherit an implicit, or if we // a function that is meant to represent this, or // an implicit declared function? InterfaceImplicitConversionVars.Add( new Variable { Name = RuntimeUtil.GetNameOfConversionToType(interfaceType.TypeName.Name), Closured = true, DefinedConstant = true } ); } foreach (LetDefinition field in InstanceFields) { freeVars.AddRange(field.Walk()); declaredVars.Add(field.DeclaredVariable); } foreach (FunctionDefinition constructor in Constructors) { freeVars.AddRange(constructor.Walk()); maxConstructorArgs = Math.Max(maxConstructorArgs, constructor.Parameters.Length); } TempArgumentVariables = new List <Variable>(); for (int i = 0; i < maxConstructorArgs; i++) { TempArgumentVariables.Add(new Variable { Temporary = true }); } foreach (FunctionDefinition method in Methods) { freeVars.AddRange(method.Walk()); // Closure these variables even though they aren't in the // object's map so that they can be directly accessed method.DeclaredVariable.Closured = true; } Overloads = Compiler.GenerateOverloads(Methods.ToList()); declaredVars.AddRange(Overloads.Select(o => o.variable)); declaredVars.AddRange(InterfaceImplicitConversionVars); MemberVariables = declaredVars; // Make sure that all of our variables end up in the closure that // makes up our RedwoodObject foreach (Variable member in declaredVars) { member.Closured = true; } // Treat the class as a closure that can be populated and then // updated by all methods. Compiler.MatchVariables(freeVars, declaredVars); // When it comes to static methods, we don't want to match to // our own instance variables. foreach (FunctionDefinition method in StaticMethods) { freeVars.AddRange(method.Walk()); } StaticOverloads = Compiler.GenerateOverloads(StaticMethods.ToList()); return(freeVars); }
internal override IEnumerable <NameExpression> Walk() { Type = RedwoodType.Make(this); base.Walk(); DeclaredVariable.KnownType = RedwoodType.GetForCSharpType(typeof(RedwoodType)); DeclaredVariable.DefinedConstant = true; DeclaredVariable.ConstantValue = Type; List <NameExpression> freeVars = new List <NameExpression>(); List <Variable> declaredVars = new List <Variable>(); // The set of variables which must be supplied when creating the interface List <Variable> varsSupplied = new List <Variable>(); This = new Variable { Name = "this", KnownType = null, // Dynamic since the reference isn't necessarily our own type }; declaredVars.Add(This); varsSupplied.Add(This); foreach (FunctionDefinition method in Methods) { // Make sure that the method is a stub if (method.Body != null) { throw new NotImplementedException(); } freeVars.AddRange(method.Walk()); // As in the ClassDefinition, these need to be closured variables method.DeclaredVariable.Closured = true; varsSupplied.Add(method.DeclaredVariable); } // For every raw function and the this variable, // we're going to need to take is as an argument // for building the interface. Each of these should // live on the stack as they are variables. ArgumentVariables = varsSupplied .Select(variable => new Variable { Name = variable.Name } ) .ToList(); SuppliedVariables = varsSupplied; Overloads = Compiler.GenerateOverloads(Methods.ToList()); declaredVars.AddRange(Overloads.Select(o => o.variable)); foreach (Variable variable in declaredVars) { variable.Closured = true; } Variables = declaredVars; // There is no need to match against the fields of the class as // all methods are stubs, and no code is closured against the // interface itself. return(freeVars); }
public override RedwoodType GetKnownType() { return(RedwoodType.GetForCSharpType(typeof(RedwoodType))); }
internal override void Bind(Binder binder) { // We have a series of temporary variables, so we will need // to discard them after the call is made binder.Bookmark(); // We have the process // Eval arg i, assign variable i, eval arg i+1, assign variable i+1 // so if we, for instance, bind every argument, then bind every temp // variable, we are sure to clobber our own arguments for (int i = 0; i < Arguments.Length; i++) { Arguments[i].Bind(binder); binder.BindVariable(ArgumentVariables[i]); } binder.Checkout(); bool fullyResolvedTypes; if (Callee == null) { fullyResolvedTypes = FunctionName.GetKnownType() != null; } else { fullyResolvedTypes = Callee.GetKnownType() != null; } RedwoodType[] argumentTypes = new RedwoodType[Arguments.Length]; for (int i = 0; i < Arguments.Length; i++) { argumentTypes[i] = Arguments[i].GetKnownType(); // Not really sure whether we care about this, but it might be useful // for compiling variable assignments ArgumentVariables[i].KnownType = argumentTypes[i]; fullyResolvedTypes &= argumentTypes[i] != null; } // If we have fully resolved types (ie. all argument types are known // and so is the callee type) we definitely know the return type FullyResolved = fullyResolvedTypes; // We have two options for determining an exact type, either: // 1) The method is fully resolved. One lambda type will come out of // the many, // or // 2) the method is not fully resolved, but the provided arguments // narrow the methods down a single option. if (Callee == null) { LambdaType = FunctionName.GetKnownType(); // If we have a lambda group, we should try to resolve it ResolveLambdaGroupOverload(argumentTypes); } else if (Callee.GetKnownType() == null) { LambdaType = null; } else if (Callee.GetKnownType().CSharpType == null) { LambdaType = Callee .GetKnownType() .GetKnownTypeOfMember(FunctionName.Name); // Make sure that calls to class functions also have the correct // lambda type ResolveLambdaGroupOverload(argumentTypes); } else if (Callee.GetKnownType().CSharpType == typeof(RedwoodType)) { if (!Callee.Constant) { LambdaType = null; } else if (Callee.EvaluateConstant() is RedwoodType calleeType) { if (calleeType.CSharpType == null) { LambdaType = calleeType .GetKnownTypeForStaticMember(FunctionName.Name); ResolveLambdaGroupOverload(argumentTypes); } else { MethodInfo[] infos; bool methodExists = MemberResolver.TryResolveMethod( null, calleeType, FunctionName.Name, true, out infos); if (!methodExists) { throw new NotImplementedException(); } BindExternal(infos); } } else { // This should never happen? throw new NotImplementedException(); } } else { MethodInfo[] infos; bool methodExists = MemberResolver.TryResolveMethod( null, Callee.GetKnownType(), FunctionName.Name, false, out infos); if (!methodExists) { // TODO: Can an object have a lambda field? throw new NotImplementedException(); } BindExternal(infos); } void BindExternal(MethodInfo[] infos) { ExternalMethodGroup = new MethodGroup(infos); ExternalMethodGroup.SelectOverloads(argumentTypes); if (ExternalMethodGroup.infos.Length == 0) { throw new NotImplementedException(); } else if (ExternalMethodGroup.infos.Length == 1) { RedwoodType returnType = RedwoodType.GetForCSharpType(ExternalMethodGroup.infos[0].ReturnType); RedwoodType[] paramTypes = ExternalMethodGroup.infos[0].GetParameters() .Select(param => RedwoodType.GetForCSharpType(param.ParameterType)) .ToArray(); LambdaType = RedwoodType.GetForLambdaArgsTypes( typeof(ExternalLambda), returnType, paramTypes); } } }