Esempio n. 1
0
        public override string PrettyPrint()
        {
            string obj = objInfo == null ? "" : objInfo.PrettyPrint();
            var    doesNotRequireParens = new NodeType[] { NodeType.MemberExpr, NodeType.RegExpLiteral, NodeType.ValueNode, NodeType.ThisExpr, NodeType.IdNode, NodeType.IdRefExpr, NodeType.FunctionCallExpr };

            if ((objInfo.NodeType == NodeType.ValueNode && obj.StartsWith("-")) || !doesNotRequireParens.Contains(objInfo.NodeType))
            {
                obj = "(" + obj + ")";
            }
            string member = "";

            if (IsValidIdentifier(memberInfo.Item1))
            {
                member = "." + memberInfo.Item1;
            }
            else
            {
                // Prettier format for array indexing. ["1"] becomes [1].
                // http://stackoverflow.com/questions/27537677/is-javascript-array-index-a-string-or-integer
                int numericalIndex;
                if (Int32.TryParse(memberInfo.Item1, out numericalIndex))
                {
                    member = "[" + memberInfo.Item1 + "]";
                }
                else
                {
                    member = "[\"" + memberInfo.Item1 + "\"]";
                }
            }
            return(obj + member);
        }
Esempio n. 2
0
        protected override void WorkspaceCallback(Workspace workspace)
        {
            Microsoft.OData.Client.ODataProtocolVersion maxProtocolVersion = Microsoft.OData.Client.ODataProtocolVersion.V4;
            if (workspace.Settings.MaxProtocolVersion.HasValue)
            {
                maxProtocolVersion = workspace.Settings.MaxProtocolVersion.Value;
            }
            
            workspace.GenerateCallOrderInterceptors = true;
            
            // use custom concurrency provider for workspaces that can
            if(workspace.DataLayerProviderKind == DataLayerProviderKind.InMemoryLinq)
            {
                if (workspace.Settings.UpdatableImplementation == UpdatableImplementation.DataServiceUpdateProvider)
                {
                    workspace.ServiceModifications.Interfaces.IServiceProvider.Services[typeof(Microsoft.OData.Service.Providers.IDataServiceUpdateProvider)]
                        = "new UpdateProviderWrapper(this.CurrentDataSource)";
                }
                else
                {
                    workspace.ServiceModifications.Interfaces.IServiceProvider.Services[typeof(Microsoft.OData.Service.IUpdatable)]
                        = "new UpdatableWrapper(this.CurrentDataSource)";
                }
            }
            else if (workspace.DataLayerProviderKind == DataLayerProviderKind.NonClr)
            {
                workspace.Settings.UpdatableImplementation = UpdatableImplementation.DataServiceUpdateProvider;
                workspace.Settings.UseLazyPropertyLoading = false;

                // do this NOW instead of later, so that we can refer to these settings
                (workspace as NonClrWorkspace).DefineClrProperties();
                if (AstoriaTestProperties.UseOpenTypes)
                {
                    OpenTypesUtil.SetupDefaultOpenTypeAttributes(workspace);
                }
            }

            workspace.ServiceContainer.ResourceContainers.Remove("Invoices");

            #region set up etags
            if (workspace.DataLayerProviderKind != DataLayerProviderKind.LinqToSql)
            {
                HashSet<ResourceType> typesWithETags = new HashSet<ResourceType>();
                foreach (ResourceContainer container in workspace.ServiceContainer.ResourceContainers)
                {
                    ResourceType baseType = container.BaseType;
                    if (!typesWithETags.Add(baseType))
                        continue;

                    // set up ETags
                    NodeType[] etagTypes = new NodeType[] { Clr.Types.String, Clr.Types.Int16, Clr.Types.Int32, Clr.Types.Int64, Clr.Types.Guid };
                    List<ResourceProperty> possibleETagProperties = baseType.Properties.OfType<ResourceProperty>()
                        .Where(p => etagTypes.Contains(p.Type) && !p.IsComplexType && !p.IsNavigation && p.PrimaryKey == null
                            && p.Facets.IsDeclaredProperty && !p.Facets.FixedLength && p.ResourceType == baseType
                            && p.Facets.UnderlyingType == UnderlyingType.Same && !p.Facets.IsStoreBlob
                            && (p.Type != Clr.Types.String || (p.Facets.MaxSize.HasValue && p.Facets.MaxSize.Value < 512)))
                        .ToList();

                    if (possibleETagProperties.Any())
                        baseType.Facets.Add(NodeFacet.Attribute(new ConcurrencyAttribute(baseType, possibleETagProperties.Choose(2).Select(p => p.Name).ToArray())));
                }
            }
            #endregion

            #region set up blobs
            if (Versioning.Server.SupportsLiveFeatures && workspace.DataLayerProviderKind != DataLayerProviderKind.LinqToSql)
            {    
                var edmWorkspace = workspace as EdmWorkspace;
                if (edmWorkspace != null)
                {
                    edmWorkspace.CsdlCallbacks.Add(delegate(XmlDocument doc)
                    {
                        doc.InnerXml = doc.InnerXml.Replace(TestUtil.TestNamespaceManager.LookupNamespace("csdl"), TestUtil.TestNamespaceManager.LookupNamespace("csdl2"));
                    });
                    edmWorkspace.SsdlCallbacks.Add(delegate(XmlDocument doc)
                    {
                        doc.InnerXml = doc.InnerXml.Replace("http://schemas.microsoft.com/ado/2006/04/edm/ssdl", "http://schemas.microsoft.com/ado/2009/02/edm/ssdl");
                    });
                    edmWorkspace.MslCallbacks.Add(delegate(XmlDocument doc)
                    {
                        doc.InnerXml = doc.InnerXml.Replace("urn:schemas-microsoft-com:windows:storage:mapping:CS", "http://schemas.microsoft.com/ado/2008/09/mapping/cs");
                    });  
                }

                foreach(ResourceContainer container in workspace.ServiceContainer.ResourceContainers.Where(rc => !(rc is ServiceOperation)))
                {
                    if (workspace.DataLayerProviderKind == DataLayerProviderKind.Edm)
                    {
                        // After named-stream redesign, adding streams to EF is non-trivial
                        continue;
                    }

                    if (container.ResourceTypes.Any(rt => rt.Facets.NamedStreams.Any()))
                    {
                        continue;
                    }

                    // because we don't represent streams as properties, having streams appear on anything other than the absolute base type
                    // makes call order prediction very hard
                    if (container.BaseType.BaseTypes.Any())
                    {
                        continue;
                    }

                    var type = container.BaseType;

                    switch(AstoriaTestProperties.Random.Next(3))
                    {
                        case 0:
                            type.Facets.Add(NodeFacet.Attribute(new NamedStreamResourceAttribute(type, "PhotoStream")));
                            break;

                        case 1:
                            type.Facets.Add(NodeFacet.Attribute(new NamedStreamResourceAttribute(type, "PhotoStream")));
                            type.Facets.Add(NodeFacet.Attribute(new NamedStreamResourceAttribute(type, "ThumbnailStream")));
                            type.Facets.Add(NodeFacet.Attribute(new NamedStreamResourceAttribute(type, "HighResolutionStream")));
                            break;
                    }
                }
            }

            if (workspace.DataLayerProviderKind != DataLayerProviderKind.LinqToSql)
            {
                string streamProviderComparerTypeName = typeof(ReferenceEqualityComparer).FullName;
                if (workspace.DataLayerProviderKind == DataLayerProviderKind.Edm)
                {
                    streamProviderComparerTypeName = workspace.Name + "EqualityComparer";

                    List<string> equalityComparerCode = new List<string>()
                    {
                        "public class " + streamProviderComparerTypeName + " : System.Data.Test.Astoria.KeyBasedEqualityComparerBase",
                        "{",
                        "    protected override string[] GetKeyPropertyNames(Type entityType)",
                        "    {",
                        "        entityType = ObjectContext.GetObjectType(entityType);",
                        "        var fullTypeName = entityType.FullName;",
                    };

                    foreach (var type in workspace.ServiceContainer.ResourceTypes)
                    {
                        equalityComparerCode.Add(string.Join(Environment.NewLine, new string[]
                        {
                            "        if(fullTypeName == \"" + type.FullName + "\")",
                            "        {",
                            "             return new string[] { " + string.Join(", ", type.Properties.Where(p => p.PrimaryKey != null).Select(p => '"' + p.Name + '"').ToArray()) + " };",
                            "        }",
                        }));
                    }

                    equalityComparerCode.Add("        throw new Exception(\"Unrecognized type\");");
                    equalityComparerCode.Add("    }");
                    equalityComparerCode.Add("}");

                    workspace.GlobalAdditionalCode += string.Join(Environment.NewLine, equalityComparerCode);
                }

                #region for inmemory/nonclr, make all int key properties server generated
                if (workspace.DataLayerProviderKind == DataLayerProviderKind.NonClr || workspace.DataLayerProviderKind == DataLayerProviderKind.InMemoryLinq)
                {
                    foreach (ResourceType type in workspace.ServiceContainer.ResourceTypes)
                        foreach (NodeProperty property in type.Key.Properties)
                            if (property.Type == Clr.Types.Int32)
                                property.Facets.ServerGenerated = true;
                }
                #endregion

                #region find possible blob types, and set up stream provider
                List<ResourceType> possibleBlobTypes = new List<ResourceType>();
                foreach (ResourceType type in workspace.ServiceContainer.ResourceContainers.Where(rc => !(rc is ServiceOperation)).Select(rc => rc.BaseType))
                {
                    // need to find one that has only generated keys, all other propeties nullable, and no nav props
                    if (type.Key.Properties.Any(p => !p.Facets.ServerGenerated))
                        continue;
                    if (type.Properties.OfType<ResourceProperty>().Any(p => p.PrimaryKey == null && !p.IsNavigation && !p.Facets.Nullable))
                        continue;
                    if (type.Properties.OfType<ResourceProperty>().Any(p => p.IsNavigation))
                        continue;
                    possibleBlobTypes.Add(type);
                }

                if (possibleBlobTypes.Any())
                {
                    List<ResourceType> blobTypes = new List<ResourceType>();
                    // want one with etags, and one without

                    ResourceType blobType = possibleBlobTypes.Where(rt => rt.Properties.Any(p => p.Facets.ConcurrencyModeFixed)).Choose();
                    if (blobType != null)
                    {
                        blobType.Facets.Add(NodeFacet.Attribute(new BlobsAttribute(blobType)));
                        blobTypes.Add(blobType);
                    }

                    blobType = possibleBlobTypes.Where(rt => !rt.Properties.Any(p => p.Facets.ConcurrencyModeFixed)).Choose();
                    if (blobType != null)
                    {
                        blobType.Facets.Add(NodeFacet.Attribute(new BlobsAttribute(blobType)));
                        blobTypes.Add(blobType);
                    }

                    workspace.ServiceModifications.Interfaces.IServiceProvider.Services[typeof(Microsoft.OData.Service.Providers.IDataServiceStreamProvider)]
                        = "new StreamProviderWrapper(new System.Data.Test.Astoria.InMemoryStreamProvider<" + streamProviderComparerTypeName + ">())";
                    workspace.Settings.StreamProviderImplementation = StreamProviderImplementation.DataServiceStreamProvider;

                    if (Versioning.Server.SupportsLiveFeatures && workspace.DataLayerProviderKind != DataLayerProviderKind.Edm)
                    {
                        workspace.ServiceModifications.Interfaces.IServiceProvider.Services[typeof(Microsoft.OData.Service.Providers.IDataServiceStreamProvider2)]
                            = "new StreamProvider2Wrapper(new System.Data.Test.Astoria.InMemoryStreamProvider<" + streamProviderComparerTypeName + ">())";
                        workspace.Settings.StreamProviderImplementation = StreamProviderImplementation.DataServiceStreamProvider2;

                        var blobTypesWithoutNamedStreams = blobTypes.Where(t => !t.Facets.NamedStreams.Any()).ToList();
                        if (blobTypesWithoutNamedStreams.Count != 0)
                        {
                            var blobTypeWithNamedStreams = blobTypesWithoutNamedStreams.Choose();
                            blobTypeWithNamedStreams.Facets.Add(NodeFacet.Attribute(new NamedStreamResourceAttribute(blobTypeWithNamedStreams, "Thumbnail")));
                        }
                    }

                    List<string> typeResolverLines = new List<string>();
                    foreach (ResourceType rt in blobTypes)
                    {
                        // safe because we know the types are disjoint (either do or do not have etags) and that they were the basetypes of their containers
                        IEnumerable<ResourceContainer> containers = workspace.ServiceContainer.ResourceContainers.Where(rc => rc.BaseType == rt);
                        foreach (ResourceContainer rc in containers)
                        {
                            typeResolverLines.Add("           if(entitySetName == \"" + rc.Name + "\")");
                            typeResolverLines.Add("               return \"" + rt.Namespace + "." + rt.Name + "\";");
                        }
                    }
                    typeResolverLines.Add("           return null;");
                    string typeResolverCode = string.Join(Environment.NewLine, typeResolverLines.ToArray());

                    workspace.RequiredFrameworkSources.Add("InMemoryStreamProvider.cs");
                    workspace.RequiredFrameworkSources.Add("KeyBasedEqualityComparerBase.cs");
                    workspace.RequiredFrameworkSources.Add("ReferenceEqualityComparer.cs");

                    var streamProviderFile = new ConstructedFile("InMemoryStreamProvider.cs");
                    workspace.ServiceModifications.Files.Add(streamProviderFile);
                    streamProviderFile.AddMethod("ResolveType", new NewMethodInfo() 
                    {
                         MethodSignature = "string ResolveType(string entitySetName, DataServiceOperationContext operationContext)",
                         BodyText = typeResolverCode,
                    });
                }
                #endregion
            }
            #endregion

            #region set up service operations
            List<ServiceOperation> serviceOperations = new List<ServiceOperation>();
            foreach(ResourceContainer container in workspace.ServiceContainer.ResourceContainers)
            {
                // no MEST yet for service ops!!
                if (!workspace.ServiceContainer.ResourceContainers.Any(c => c != container && c.BaseType == container.BaseType))
                {
                    foreach (RequestVerb verb in new RequestVerb[] { RequestVerb.Get, RequestVerb.Post })
                    {
                        ServiceOperation serviceOp = Resource.ServiceOperation(verb.ToString() + "_" + container.Name, container, container.BaseType, container.ResourceTypes.ToArray());
                        serviceOp.Verb = verb;

                        string fullTypeName = container.BaseType.Namespace + "." + container.BaseType.Name;
                        
			            if (workspace.DataLayerProviderKind == DataLayerProviderKind.NonClr && !container.BaseType.Facets.IsClrType)
	                        fullTypeName = typeof(NonClr.RowEntityType).FullName;

                        serviceOp.ServiceOpCode = string.Join(Environment.NewLine, new string[]
                        {
                            (verb == RequestVerb.Get ? "[WebGet]" : "[WebInvoke(Method = \"POST\")]"),
                            "public IQueryable<" + fullTypeName + "> " + serviceOp.Name + "()",
                            "{",
                            "     return GetEntitySet<" + fullTypeName + ">(\"" + container.Name + "\");",
                            "}"
                        });
                        serviceOp.ServiceOperationResultKind = Microsoft.OData.Service.Providers.ServiceOperationResultKind.QueryWithMultipleResults;
                        serviceOp.ExpectedTypeName = fullTypeName;
                        serviceOperations.Add(serviceOp);
                    }
                }
            }
            workspace.ServiceContainer.AddNodes(serviceOperations);
            workspace.AddServiceOperationCode();
            #endregion
        }
Esempio n. 3
0
        //---------------------------------------------------------------------
        // Adds concurrency attributes to resource types.
        //---------------------------------------------------------------------
        protected virtual void AddConcurrencyAttributes(Workspace w)
        {
            if (!GetType().Name.Contains("Concurrency"))
                return;

            HashSet<ResourceType> typesWithETags = new HashSet<ResourceType>();
            foreach (ResourceContainer container in w.ServiceContainer.ResourceContainers)
            {
                ResourceType baseType = container.BaseType;
                if (!typesWithETags.Add(baseType))
                    continue;

                // set up ETags
                NodeType[] etagTypes = new NodeType[] { Clr.Types.String, Clr.Types.Int16, Clr.Types.Int32, Clr.Types.Int64, Clr.Types.Guid };
                List<ResourceProperty> possibleETagProperties = baseType.Properties.OfType<ResourceProperty>()
                    .Where(p => etagTypes.Contains(p.Type) && !p.IsComplexType && !p.IsNavigation && p.PrimaryKey == null
                        && p.Facets.IsDeclaredProperty && !p.Facets.FixedLength && p.ResourceType == baseType
                        && p.Facets.UnderlyingType == UnderlyingType.Same && !p.Facets.IsStoreBlob
                        && (p.Type != Clr.Types.String || (p.Facets.MaxSize.HasValue && p.Facets.MaxSize.Value < 512)))
                    .ToList();

                if (possibleETagProperties.Any())
                    baseType.Facets.Add(NodeFacet.Attribute(new ConcurrencyAttribute(baseType, possibleETagProperties.Choose(2).Select(p => p.Name).ToArray())));
            }
        }
        private void Generate(AbstractNode node)
        {
            var debugNodeTypes = new NodeType[]
            {
                /*NodeType.Block,
                 * NodeType.Literal,
                 * NodeType.Identifier,
                 * NodeType.Loop,
                 * NodeType.Operation,*/
            };

            if (debugNodeTypes.Contains(node.Type))
            {
                Debug(node.ToString());
            }
            switch (node.Type)
            {
            case NodeType.Block:
                ((BlockNode)node).Statements.ForEach(Generate);
                break;

            case NodeType.Operation:
                var operation = (OperationNode)node;

                // Operations that require two integer as input
                var integerOperations = new[]
                {
                    OperatorType.BitwiseAnd,
                    OperatorType.AndAlso,
                    OperatorType.BitwiseOr,
                    OperatorType.OrElse
                };

                var outputIntegerOperations = new[]
                {
                    OperatorType.Equals,
                    OperatorType.Greater,
                    OperatorType.NotGreater,
                    OperatorType.Less,
                    OperatorType.NotLess,
                    OperatorType.NotEquals
                };

                /*
                 *  We accept floating only, sometimes we need integer (logical and/or)
                 *  In this case, we cast the floating into an integer and cast back the result into a floating one.
                 */
                var requireInteger         = integerOperations.Contains(operation.Operator);
                var requireOutputTransform = outputIntegerOperations.Contains(operation.Operator);

                Generate(operation.LeftOperand);
                if (requireInteger)
                {
                    Write("ftoi");
                }

                Generate(operation.RightOperand);
                if (requireInteger)
                {
                    Write("ftoi");
                }

                switch (operation.Operator)
                {
                case OperatorType.Add:
                    Write("add.f");
                    break;

                case OperatorType.Sub:
                    Write("sub.f");
                    break;

                case OperatorType.Mul:
                    Write("mul.f");
                    break;

                case OperatorType.Div:
                    Write("div.f");
                    break;

                case OperatorType.BitwiseAnd:
                case OperatorType.AndAlso:
                    Write("and");
                    break;

                case OperatorType.BitwiseOr:
                case OperatorType.OrElse:
                    Write("or");
                    break;

                case OperatorType.Equals:
                    Write("cmpeq.f");
                    break;

                case OperatorType.NotEquals:
                    Write("cmpne.f");
                    break;

                case OperatorType.NotGreater:
                    Write("cmple.f");
                    break;

                case OperatorType.Less:
                    Write("cmplt.f");
                    break;

                case OperatorType.NotLess:
                    Write("cmpge.f");
                    break;

                case OperatorType.Greater:
                    Write("cmpgt.f");
                    break;

                case OperatorType.EuclidianDiv:
                    Write(Drop());
                    Write(Drop());
                    Generate(BuiltinFunctions.EuclidianDiv(operation.LeftOperand, operation.RightOperand));
                    break;

                case OperatorType.Mod:
                    Write(Drop());
                    Write(Drop());
                    Generate(BuiltinFunctions.Mod(operation.LeftOperand, operation.RightOperand));
                    break;

                case OperatorType.Pow:
                    Write(Drop());
                    Write(Drop());
                    Generate(BuiltinFunctions.Pow(operation.LeftOperand, operation.RightOperand));
                    break;
                }
                if (requireInteger || requireOutputTransform)
                {
                    Write("itof");
                }
                break;

            case NodeType.Declaration:
                var dcl = (AddressableDeclarationNode)node;
                switch (dcl.Identifier.Symbol.Type)
                {
                case ObjectType.Floating:
                    Generate(dcl.Expression);
                    Write
                    (
                        Set(dcl.Identifier.Symbol.Pointer)
                    );
                    break;

                case ObjectType.Pointer:
                    var indexExpressions = ((MultiExpressionNode)dcl.Expression).Expressions;
                    indexExpressions.Reverse();
                    AbstractExpressionNode finalExpression = LiteralNode.One;
                    for (var i = 0; i < indexExpressions.Count - 1; i++)
                    {
                        finalExpression = new OperationNode
                                          (
                            OperatorType.Mul,
                            new OperationNode
                            (
                                OperatorType.Add,
                                LiteralNode.One,
                                indexExpressions[i]
                            ),
                            finalExpression
                                          );
                    }
                    finalExpression = new OperationNode
                                      (
                        OperatorType.Mul,
                        indexExpressions.Last(),
                        finalExpression
                                      );
                    Generate(BuiltinFunctions.BorrowMemory(finalExpression));
                    Write(Set(dcl.Identifier.Symbol.Pointer));
                    var tempVar = new AddressableIdentifierNode(new PrimitiveVariableSymbol("", PointerIndex + 1));
                    Write(Pushf());
                    Generate(BuiltinFunctions.BorrowMemory(new LiteralNode(indexExpressions.Count)));
                    Write(Set(tempVar.Symbol.Pointer));
                    indexExpressions.Reverse();
                    for (var i = 0; i < indexExpressions.Count; i++)
                    {
                        Generate
                        (
                            new PointerAssignationNode
                            (
                                new OperationNode
                                (
                                    OperatorType.Add,
                                    tempVar,
                                    new LiteralNode(i)
                                ),
                                indexExpressions[i]
                            )
                        );
                    }

                    /*
                     *      var a = bmem(6);
                     *      var b = bmem(2);
                     *      :b = 2;
                     *      :b + 1 = 2;
                     *      init_array(a, b, 2);
                     */
                    Generate(BuiltinFunctions.InitArray(dcl.Identifier, tempVar, new LiteralNode(indexExpressions.Count)));
                    Write(Get(tempVar.Symbol.Pointer));
                    Generate(BuiltinFunctions.RecoverMemory(tempVar));
                    break;
                }
                break;

            case NodeType.Reference:
                var refnode = (ReferenceNode)node;
                Write
                (
                    Pushf(refnode.Identifier.Symbol.Pointer)
                );
                break;

            case NodeType.Dereference:
                var deref = (DereferenceNode)node;
                Generate(deref.Expression);
                Writes
                (
                    Ftoi(),
                    MemRead(),
                    Itof()
                );
                break;

            case NodeType.Assignation:
                var assign = (AssignationNode)node;
                Generate(assign.ValueExpression);
                Write
                (
                    Set(assign.Identifier.Symbol.Pointer)
                );
                break;

            case NodeType.Drop:
                Write(Drop());
                break;

            case NodeType.PointerAssignation:
                var ptrAssign = (PointerAssignationNode)node;
                Generate(ptrAssign.AddressExpression);
                Write
                (
                    Ftoi()
                );
                Generate(ptrAssign.ValueExpression);
                Writes
                (
                    Ftoi(),
                    MemWrite()
                );
                break;

            case NodeType.Literal:
                var lit = (LiteralNode)node;
                Write(Pushf(lit.Value));
                break;

            case NodeType.Identifier:
                var ident = (AddressableIdentifierNode)node;
                Write
                (
                    Get(ident.Symbol.Pointer)
                );
                break;

            case NodeType.Crash:
                Write("halt");
                break;

            case NodeType.Print:
                if (node is PrintExpressionNode)
                {
                    var printe = (PrintExpressionNode)node;
                    Generate(printe.Expression);
                    Write("out.f");
                }
                else
                {
                    var prints = (PrintStringNode)node;
                    foreach (var c in prints.Content)
                    {
                        Write($"push.i {(int)c}");
                        Write("out.c");
                    }
                }
                Write("push.i 10");
                Write("out.c");
                break;

            case NodeType.Loop:
                var loop = (LoopNode)node;
                {
                    Write(DeclareLabel(BeginLoop(loop.UniqueId)));
                    Generate(loop.Conditional);
                    Write(DeclareLabel(IterationLoop(loop.UniqueId)));
                    Generate(loop.Iteration);
                    Write(Jump(BeginLoop(loop.UniqueId)));
                    Write(DeclareLabel(EndLoop(loop.UniqueId)));
                }
                break;

            case NodeType.Break:
                var breakNode = (BreakNode)node;
                Write(Jump(EndLoop(breakNode.LoopId)));
                break;

            case NodeType.Continue:
                var cont = (ContinueNode)node;
                Write(Jump(EndIf(cont.CondId)));
                break;

            case NodeType.Conditional:
                var cond = (ConditionalNode)node;
                {
                    Generate(cond.Expression);
                    Write(JumpFalse(Else(cond.UniqueId)));
                    Generate(cond.TrueStatement);
                    Write(Jump(EndIf(cond.UniqueId)));
                    Write(DeclareLabel(Else(cond.UniqueId)));
                    Generate(cond.FalseStatement);
                    Write(DeclareLabel(EndIf(cond.UniqueId)));
                }
                break;

            case NodeType.MultiExpression:
                var multi = (MultiExpressionNode)node;
                foreach (var expression in multi.Expressions)
                {
                    Generate(expression);
                }
                break;

            case NodeType.Function:
                var fun = (FunctionNode)node;
                Write(DeclareLabel(fun.Identifier.Symbol.Name));
                PointerIndex = fun.Arguments.Count - 1;
                PreGenerate(fun.Statement);
                Generate(fun.Statement);
                if (fun.Identifier.Symbol.Name != "start")
                {
                    Write(Pushf());
                    Write(Ret());
                }
                else
                {
                    Write(Halt());
                }
                break;

            case NodeType.Return:
                var ret = (ReturnNode)node;
                Generate(ret.Expression);
                Write(Ret());
                break;

            case NodeType.Call:
                var call = (CallNode)node;
                Write(Prep(call.Target.Symbol.Name));
                foreach (var arg in call.Parameters)
                {
                    Generate(arg);
                }
                Write(Call(call.Parameters.Count));
                break;

            case NodeType.Root:
                var root = (RootNode)node;
                foreach (var f in root.Functions)
                {
                    Generate(f);
                }
                break;
            }
        }