Пример #1
0
        public List <Diagnostic> Check()
        {
            // NOTE, TODO: Becase we are using a List<AstNode> at the
            // top level, we have to individually call checks on the top
            // level nodes and collect the results.
            // At some point we should probably go to a root-node ast
            // instead.
            var results = new List <Diagnostic>();
            var ctx     = new CheckerContext();

            foreach (var node in ast)
            {
                results.AddRange(Check(node, ctx));
            }

            return(results);
        }
Пример #2
0
        private List <Diagnostic> Check(
            AstNode node, CheckerContext ctx)
        {
            var diagnostics = new List <Diagnostic>();

            #region "VariableAssignment"
            if (node is VariableAssignment va)
            {
                diagnostics.AddRange(Check(va.Parameter, ctx));
                if (va.Identifier.NodeType.Equals(ParserContext.NativeTypes.Implicit))
                {
                    va.Identifier.NodeType = va.Parameter.NodeType;
                }
                diagnostics.AddRange(Check(va.Identifier, ctx));

                var identifierType = va.Identifier.NodeType;
                var parameterType  = va.Parameter.NodeType;

                if (!identifierType.Equals(parameterType))
                {
                    diagnostics.Add(new Diagnostic(
                                        va.File,
                                        va.Line,
                                        va.Column,
                                        $"Unable to convert type '{parameterType?.GetName()}' to '{identifierType?.GetName()}'",
                                        DiagnosticCode.InvalidTypes));
                }
            }
            #endregion
            #region "FunctionCall"
            if (node is FunctionCall f)
            {
                AstNode function;
                if (f.Name is Identifier)
                {
                    function = ctx.Variables.GetFunction((f.Name as Identifier).Name)?.Node as AstNode;
                    if (function is null)
                    {
                        function = ctx.Variables.GetVariable((f.Name as Identifier).Name)?.Node;
                    }
                }
                else
                {
                    diagnostics.AddRange(Check(f.Name, ctx));
                    function = f.Name;
                }
                if (function is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        node.File,
                                        node.Line,
                                        node.Column,
                                        length: f.Name.Length,
                                        $"Invalid function: {f.Name}",
                                        DiagnosticCode.UnknownFunction));
                }
                else if (function != null)
                {
                    if (f.Parameters.Count != function.NodeType.SubTypes?.Count - 1)
                    {
                        diagnostics.Add(new Diagnostic(
                                            node.File,
                                            node.Line,
                                            node.Column,
                                            length: f.Name.Length,
                                            $"Invalid parameters for function '{f.Name}'",
                                            DiagnosticCode.InvalidParameters));
                    }
                    foreach (var parameter in f.Parameters)
                    {
                        diagnostics.AddRange(Check(parameter, ctx));
                    }
                    var minCount = Math.Min(f.Parameters.Count, function.NodeType.SubTypes.Count - 1);
                    for (var i = 0; i < minCount; i++)
                    {
                        var callParam     = f.Parameters[i];
                        var callParamType = callParam.NodeType;
                        var declParamType = function.NodeType.SubTypes[i];
                        if (!callParamType.Equals(declParamType))
                        {
                            diagnostics.Add(new Diagnostic(
                                                callParam.File,
                                                callParam.Line,
                                                callParam.Column,
                                                length: callParam.Length,
                                                $"Unable to convert type '{callParamType?.GetName()}' to  '{declParamType?.GetName()}'",
                                                DiagnosticCode.InvalidTypes));
                        }
                    }
                    f.NodeType = function.NodeType.SubTypes.LastOrDefault();
                }
            }
            #endregion
            #region "FunctionDeclaration"
            if (node is FunctionDeclaration fd)
            {
                ctx.Variables.AddFunction(new CheckerFunction
                {
                    Node = fd
                });

                ctx.Variables.Push();
                foreach (var parameter in fd.Parameters)
                {
                    ctx.Variables.AddVariable(
                        new CheckerVariable
                    {
                        Node = parameter as VariableDeclaration
                    });
                }
                foreach (var expressionNode in fd.Expression)
                {
                    diagnostics.AddRange(Check(expressionNode, ctx));
                }
                var controlFlowChecker = new ReturnControlFlowChecker(fd);
                if (!controlFlowChecker.Check())
                {
                    diagnostics.Add(new Diagnostic(
                                        fd.File,
                                        fd.Line,
                                        fd.Column,
                                        length: 8, // The word 'function' is 8 chars long
                                        $"Not all code paths return value of type {fd.NodeType.SubTypes.LastOrDefault()?.GetName()}",
                                        DiagnosticCode.ControlFlowError
                                        ));
                }
                ctx.Variables.Pop();
            }
            #endregion
            #region "VariableDeclaration and PointerDeclaration"
            if (node is VariableDeclaration vd)
            {
                if (vd.NodeType.Equals(ParserContext.NativeTypes.Implicit))
                {
                    diagnostics.Add(new Diagnostic(
                                        vd.File,
                                        vd.Line,
                                        vd.Column,
                                        "Cannot implicitly type a variable declaration without an assignment",
                                        DiagnosticCode.ImplicitTypingError));
                }
                ctx.Variables.AddVariable(new CheckerVariable
                {
                    Node = vd
                });
            }
            #endregion
            #region "Identifier"
            if (node is Identifier idNode)
            {
                var variable = ctx.Variables.GetVariable(idNode.Name);
                if (variable is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        idNode.File,
                                        idNode.Line,
                                        idNode.Column,
                                        length: idNode.Name.Length,
                                        $"Unknown variable '{idNode.Name}'",
                                        DiagnosticCode.UnknownVariable));
                    return(diagnostics);
                }
                idNode.NodeType = variable.Node.NodeType;
            }
            #endregion
            #region "For"
            // TODO: Check for various parts
            if (node is For forNode)
            {
                foreach (var exNode in forNode.Init)
                {
                    diagnostics.AddRange(Check(exNode, ctx));
                }
                foreach (var exNode in forNode.Condition)
                {
                    diagnostics.AddRange(Check(exNode, ctx));
                }
                foreach (var exNode in forNode.Incrementor)
                {
                    diagnostics.AddRange(Check(exNode, ctx));
                }
                foreach (var exNode in forNode.Expression)
                {
                    diagnostics.AddRange(Check(exNode, ctx));
                }
            }
            #endregion
            #region "If"
            // TODO: Type check args
            if (node is If ifNode)
            {
                foreach (var conditionnode in ifNode.Condition)
                {
                    diagnostics.AddRange(Check(conditionnode, ctx));
                }
                foreach (var exNode in ifNode.Expression)
                {
                    diagnostics.AddRange(Check(exNode, ctx));
                }
            }
            #endregion
            #region "While"
            if (node is While whileNode)
            {
                foreach (var cNode in whileNode.Condition)
                {
                    diagnostics.AddRange(Check(cNode, ctx));
                }
                foreach (var exNode in whileNode.Expression)
                {
                    diagnostics.AddRange(Check(exNode, ctx));
                }
            }
            #endregion
            #region "Dot"
            if (node is Dot dot)
            {
                // Need to set up the types on the Dot
                var variableName = dot.Left as Identifier;
                if (variableName is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        dot.Left.File,
                                        dot.Left.Line,
                                        dot.Left.Column,
                                        "Unknown variable",
                                        DiagnosticCode.UnknownVariable));
                    return(diagnostics);
                }
                var variable = ctx.Variables.GetVariable(variableName.Name);
                if (variable is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        dot.Left.File,
                                        dot.Left.Line,
                                        dot.Left.Column,
                                        length: variableName.Name.Length,
                                        "Unknown variable",
                                        DiagnosticCode.UnknownVariable));
                    return(diagnostics);
                }
                // var type = ctx.Types.Where(tp => tp.Name == variableName.Name).FirstOrDefault();
                var type = variable.Node.NodeType;
                if (type is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        dot.Left.File,
                                        dot.Left.Line,
                                        dot.Left.Column,
                                        $"Unknown type: {variableName.Name}",
                                        DiagnosticCode.UnknownType));
                    return(diagnostics);
                }
                var fieldName = dot.Right as Identifier;
                // if (right is null) throw new ParseException(i, tokens, nodes, "Right node is not an identifier");
                var field = type.Body?.Where(fld => fld.Identifier == fieldName.Name).FirstOrDefault();
                if (field is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        dot.Right.File,
                                        dot.Right.Line,
                                        dot.Right.Column,
                                        length: fieldName.Length,
                                        $"Unknown field: {fieldName.Name}",
                                        DiagnosticCode.UnknownField));
                    return(diagnostics);
                }
                dot.NodeType = field.NodeType;
            }
            #endregion
            #region "DerefArrow"
            if (node is DerefArrow derefArrowNode)
            {
                diagnostics.AddRange(Check(derefArrowNode.Left, ctx));
                // Need to set up the types on the DerefArrow
                // var variableName = derefArrowNode.Left as Identifier;
                // if (variableName is null)
                // {
                //     diagnostics.Add(new Diagnostic(
                //         derefArrowNode.Left.File,
                //         derefArrowNode.Left.Line,
                //         derefArrowNode.Left.Column,
                //         "Unknown variable",
                //         DiagnosticCode.UnknownVariable));
                //     return diagnostics;
                // }
                // var variable = ctx.Variables.GetVariable(variableName.Name);
                // if (variable is null)
                // {
                //     diagnostics.Add(new Diagnostic(
                //         derefArrowNode.Left.File,
                //         derefArrowNode.Left.Line,
                //         derefArrowNode.Left.Column,
                //         length: variableName.Name.Length,
                //         "Unknown variable",
                //         DiagnosticCode.UnknownVariable));
                //     return diagnostics;
                // }
                if (derefArrowNode.Left.NodeType.Name != ParserContext.NativeTypes.Pointer.Name)
                {
                    diagnostics.Add(new Diagnostic(
                                        derefArrowNode.File,
                                        derefArrowNode.Line,
                                        derefArrowNode.Column,
                                        2,
                                        "Type must be a ptr to use a Dereferencing Arrow",
                                        DiagnosticCode.InvalidTypes
                                        ));
                    return(diagnostics);
                }
                // var type = ctx.Types.Where(tp => tp.Name == variableName.Name).FirstOrDefault();
                var type = derefArrowNode.Left.NodeType.SubTypes.FirstOrDefault();
                if (type is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        derefArrowNode.Left.File,
                                        derefArrowNode.Left.Line,
                                        derefArrowNode.Left.Column,
                                        $"Unknown type",
                                        DiagnosticCode.UnknownType));
                    return(diagnostics);
                }
                var fieldName = derefArrowNode.Right as Identifier;
                var field     = type.Body.Where(fld => fld.Identifier == fieldName.Name).FirstOrDefault();
                if (field is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        derefArrowNode.Right.File,
                                        derefArrowNode.Right.Line,
                                        derefArrowNode.Right.Column,
                                        length: fieldName.Name.Length,
                                        $"Unknown field: {fieldName.Name}",
                                        DiagnosticCode.UnknownField));
                    return(diagnostics);
                }
                derefArrowNode.NodeType = field.NodeType;
            }
            #endregion
            #region "ParenGroup"
            if (node is ParenGroup pGroup)
            {
                foreach (var childNode in pGroup.Nodes)
                {
                    diagnostics.AddRange(Check(childNode, ctx));
                }
                if (pGroup.Nodes.Count > 0)
                {
                    pGroup.NodeType = pGroup.Nodes.LastOrDefault().NodeType;
                }
            }
            #endregion
            #region "Incr"
            if (node is Incr incrNode)
            {
                diagnostics.AddRange(Check(incrNode.Parameter, ctx));
                // Not sure if we want to disallow doing this on non ints yet:
                // if (!incrNode.Parameter.NodeType.Equals(ParserContext.NativeTypes.LangInt))
                // {
                //     var length = 0;
                //     if (incrNode.Parameter is Identifier incrId)
                //     {
                //         length = incrId.Name.Length;
                //     }
                //     diagnostics.Add(new Diagnostic(
                //         incrNode.File,
                //         incrNode.Line,
                //         incrNode.Column,
                //         length,
                //         $"Cannot perform incr op on type {incrNode.Parameter.NodeType.GetName()}",
                //         DiagnosticCode.InvalidTypes
                //     ));
                //     return diagnostics;
                // }
            }
            #endregion
            #region "Decr"
            if (node is Decr decrNode)
            {
                diagnostics.AddRange(Check(decrNode.Parameter, ctx));
                // Not sure if we want to disallow doing this on non ints yet:
                // if (!decrNode.Parameter.NodeType.Equals(ParserContext.NativeTypes.LangInt))
                // {
                //     var length = 1;
                //     if (decrNode.Parameter is Identifier decrId)
                //     {
                //         length = decrId.Name.Length;
                //     }
                //     diagnostics.Add(new Diagnostic(
                //         decrNode.File,
                //         decrNode.Line,
                //         decrNode.Column,
                //         length,
                //         $"Cannot perform incr op on type {decrNode.Parameter.NodeType.GetName()}",
                //         DiagnosticCode.InvalidTypes
                //     ));
                //     return diagnostics;
                // }
            }
            #endregion
            #region "AddressOf"
            if (node is AddressOf addressOfNode)
            {
                // Get the type on the parameter
                diagnostics.AddRange(Check(addressOfNode.Parameter, ctx));
                addressOfNode.NodeType = new LangType("ptr", null, null, 0, 0)
                {
                    SubTypes = new List <LangType> {
                        addressOfNode.Parameter.NodeType
                    }
                };
            }
            #endregion
            #region "Deref"
            if (node is Deref derefNode)
            {
                var parameter = derefNode.Parameter;
                if (parameter is null)
                {
                    diagnostics.Add(new Diagnostic(
                                        parameter.File,
                                        parameter.Line,
                                        parameter.Column,
                                        "No parameter for deref op",
                                        DiagnosticCode.SyntaxError
                                        ));
                    return(diagnostics);
                }
                // TODO: Improve
                // if (!(parameter is Identifier identifier))
                // {
                //     diagnostics.Add(new Diagnostic(
                //         parameter.File,
                //         parameter.Line,
                //         parameter.Column,
                //         $"Invalid token type. Expected {typeof(Identifier)}, got {parameter.GetType()}",
                //         DiagnosticCode.SyntaxError
                //     ));
                //     return diagnostics;
                // }
                // diagnostics.AddRange(Check(identifier, ctx));
                // derefNode.NodeType = identifier.NodeType.SubTypes.FirstOrDefault();
                diagnostics.AddRange(Check(parameter, ctx));
                if (parameter.NodeType.Name == "ptr")
                {
                    derefNode.NodeType = parameter.NodeType.SubTypes.FirstOrDefault();
                }
            }
            #endregion
            #region "LangType"
            if (node is LangType ltNode)
            {
                if (!(ltNode.Body is null) && ltNode.Body.Count() > 0)
                {
                    foreach (var fieldNode in ltNode.Body)
                    {
                        diagnostics.AddRange(Check(fieldNode, ctx));
                    }
                    // Warn about non-ptr types in custom types
                    foreach (var warnNode in ltNode.Body.Where(fieldNode => fieldNode.NodeType.Body?.Count() > 0 && fieldNode.LangType.Name != "ptr"))
                    {
                        diagnostics.Add(new Diagnostic(
                                            warnNode.File,
                                            warnNode.Line,
                                            warnNode.Column,
                                            "You may wish to use ptr<T> types in custom types",
                                            DiagnosticCode.Warning
                                            )
                        {
                            Level = DiagnosticCodeLevel.Warning
                        });
                    }
                }
            }
            #endregion
            #region "Return"
            if (node is Return returnNode)
            {
                if (!(returnNode.Parameter is null))
                {
                    diagnostics.AddRange(Check(returnNode.Parameter, ctx));
                    returnNode.NodeType = returnNode.Parameter.NodeType;
                }
            }
            #endregion
            #region "As"
            if (node is As asNode)
            {
                diagnostics.AddRange(Check(asNode.From, ctx));
            }
            #endregion
            #region "Equal"
            if (node is Equal equalNode)
            {
                diagnostics.AddRange(Check(equalNode.Left, ctx));
                diagnostics.AddRange(Check(equalNode.Right, ctx));
            }
            #endregion
            #region "Ternary"
            if (node is Ternary ternary)
            {
                diagnostics.AddRange(Check(ternary.Condition, ctx));
                diagnostics.AddRange(Check(ternary.Left, ctx));
                diagnostics.AddRange(Check(ternary.Right, ctx));
                if (!ternary.Left.NodeType.Equals(ternary.Right.NodeType))
                {
                    diagnostics.Add(new Diagnostic(
                                        ternary.File,
                                        ternary.Line,
                                        ternary.Column,
                                        $"Result types '{ternary.Left.NodeType.GetName()}' and '{ternary.Right.NodeType.GetName()}' are not the same",
                                        DiagnosticCode.InvalidTypes));
                    return(diagnostics);
                }
                ternary.NodeType = ternary.Left.NodeType;
            }
            #endregion
            #region "BitwiseNot"
            if (node is BitwiseNot bitwiseNot)
            {
                diagnostics.AddRange(Check(bitwiseNot.Parameter, ctx));
                bitwiseNot.NodeType = bitwiseNot.Parameter.NodeType;
            }
            #endregion
            #region "BitwiseAnd"
            if (node is BitwiseAnd bitwiseAnd)
            {
                diagnostics.AddRange(Check(bitwiseAnd.Left, ctx));
                diagnostics.AddRange(Check(bitwiseAnd.Right, ctx));
                bitwiseAnd.NodeType = bitwiseAnd.Left.NodeType;
            }
            #endregion
            #region "BitwiseOr"
            if (node is BitwiseOr bitwiseOr)
            {
                diagnostics.AddRange(Check(bitwiseOr.Left, ctx));
                diagnostics.AddRange(Check(bitwiseOr.Right, ctx));
                bitwiseOr.NodeType = bitwiseOr.Left.NodeType;
            }
            #endregion
            return(diagnostics);
        }