예제 #1
0
        public void ShouldFailToAnalyzeStandardOutputStatementWithUnassignedVariable()
        {
            // Arrange
            string        identifier = "test";
            CobaltProgram program    = new CobaltProgram()
            {
                Code = new CodeBlockNode(1)
            };

            program.Code.AddStatement(new VariableDeclarationStatementNode(1)
            {
                Identifier  = new IdentifierNode(1, identifier),
                TypeKeyword = new IntegerTypeKeywordNode(1)
            });
            program.Code.AddStatement(new StandardOutputStatementNode(2)
            {
                Expression = new SingleLeafExpressionNode(2)
                {
                    Leaf = new IdentifierNode(2, identifier)
                }
            });

            // Act / Assert
            Assert.Throws <UninitializedVariableError>(() =>
            {
                Analyzer.Analyze(program);
            });
        }
예제 #2
0
        public void ShouldParseInputStatement(string identifierName)
        {
            // Arrange
            List <Token> tokens = new List <Token>()
            {
                new Token(TokenType.StandardInput, 1, 0),
                new Token(TokenType.Identifier, 0, 6),
                new Token(TokenType.Semicolon, 0, 7 + identifierName.Length),
            };

            tokens.ElementAt(1).SetData(TokenDataKeys.IDENTIFIER_NAME, identifierName);

            // Act
            CobaltProgram program = Parser.Parse(tokens);

            // Assert
            Assert.Single(program.Code.Statements);

            StatementNode statement = program.Code.Statements.First();

            if (statement is StandardInputStatementNode inputStatement)
            {
                Assert.Equal(program.Code, inputStatement.Parent);
                Assert.Equal(identifierName, inputStatement.Identifier.IdentifierName);
            }
            else
            {
                throw new XunitException("Wrong node type.");
            }
        }
예제 #3
0
 /// <summary>
 /// Optimizes the specified program AST using all enabled optimizations.
 /// </summary>
 /// <param name="program">The program to optimize.</param>
 /// <returns>Returns the optimized program</returns>
 public CobaltProgram Optimize(CobaltProgram program)
 {
     if (Optimizations[Optimization.SimplifyNegativeNumbers])
     {
         // TODO: implement
     }
     return(program);
 }
예제 #4
0
        public TargetProgram GenerateTargetCode(CobaltProgram cobaltProgram)
        {
            TargetProgram program = new TargetProgram("JavaScript");
            string        js      = GenerateProgramCode(cobaltProgram);

            program.AddFile(new TextFile("index.js", js));
            return(program);
        }
예제 #5
0
        public void ShouldSuccessfullyAnalyzeSimpleStandardOutputStatement()
        {
            // Arrange
            string        identifier = "test";
            CobaltProgram program    = new CobaltProgram()
            {
                Code = new CodeBlockNode(1)
            };

            program.Code.AddStatement(new VariableDeclarationStatementNode(1)
            {
                Identifier  = new IdentifierNode(1, identifier),
                TypeKeyword = new IntegerTypeKeywordNode(1),
                Expression  = new SingleLeafExpressionNode(1)
                {
                    Leaf = new IntegerValueNode(1, 42)
                }
            });
            program.Code.AddStatement(new StandardOutputStatementNode(2)
            {
                Expression = new SingleLeafExpressionNode(2)
                {
                    Leaf = new IdentifierNode(2, identifier)
                }
            });

            // Act
            Analyzer.Analyze(program);

            // Assert
            if (program.Code.SymbolTable.TryGetSymbol(identifier, out Symbol symbol))
            {
                Assert.Equal(1, symbol.DefinedOnLine);
                Assert.Equal(identifier, symbol.Identifier);
                Assert.True(symbol.Initialized);
                if (symbol.Type is VariableTypeSignature variableType)
                {
                    Assert.Equal(CobaltType.Integer, variableType.CobaltType);
                }
                else
                {
                    throw new XunitException("Bad variable type signature.");
                }
            }
            else
            {
                throw new XunitException("Missing symbol in program scope symbol table.");
            }
        }
예제 #6
0
        /// <summary>
        /// Parses a Cobalt program and returns it as an abstract syntax tree. This is the main parsing entrypoint.
        /// </summary>
        /// <param name="tokens">The tokens representing the program to parse.</param>
        /// <returns>Returns a successfully parsed Cobalt program.</returns>
        /// <exception cref="CobaltSyntaxError">Thrown when the input program contains a syntax error.</exception>
        public CobaltProgram Parse(List <Token> tokens)
        {
            // Validate input
            if (!tokens.Any())
            {
                throw new CobaltSyntaxError("A Cobalt program must contain at least one statement.");
            }

            // Parse program as a code block
            CobaltProgram program = new CobaltProgram();

            program.Code = ParseCodeBlock(tokens, 0, tokens.Count());

            // No error encountered, terminate parsing
            return(program);
        }
예제 #7
0
        public void CompareTwoIntegers()
        {
            // Arrange
            string source = ReadFromFile("compare_numbers.co");

            // Act
            List <Token>  tokens = Lexer.Tokenize(source);
            CobaltProgram ast    = Parser.Parse(tokens);

            // Assert
            Assert.Equal(5, ast.Code.Statements.Count);
            Assert.True(ast.Code.Statements.ElementAt(0) is VariableDeclarationStatementNode);
            Assert.True(ast.Code.Statements.ElementAt(1) is VariableDeclarationStatementNode);
            Assert.True(ast.Code.Statements.ElementAt(2) is VariableDeclarationStatementNode);
            Assert.True(ast.Code.Statements.ElementAt(3) is VariableAssignmentStatementNode);
            Assert.True(ast.Code.Statements.ElementAt(4) is StandardOutputStatementNode);
        }
예제 #8
0
        /// <summary>
        /// Compiles a Cobalt program.
        /// </summary>
        /// <param name="sourceCode">The input Cobalt code.</param>
        /// <returns>Returns the compiled target program.</returns>
        public TargetProgram Compile(string sourceCode, bool disableOptimization = false)
        {
            try
            {
                // Lexical analysis
                List <Token> tokens = Lexer.Tokenize(sourceCode);

                // Parsing
                CobaltProgram ast = Parser.Parse(tokens);

                // Semantic analysis
                Analyzer.Analyze(ast);

                // Optimization
                if (!disableOptimization)
                {
                    ast = Optimizer.Optimize(ast);
                }

                // Target code generation
                TargetProgram targetProgram = TargetCodeGenerator.GenerateTargetCode(ast);
                return(targetProgram);
            }
            catch (CobaltSyntaxError exception)
            {
                Logger.LogError(exception.Message, exception);
                throw;
            }
            catch (CompilerException exception)
            {
                Logger.LogCritical("A compiler error occured! Please report this error at https://github.com/alex-c/Cobalt.NET/issues.", exception);
                throw;
            }
            catch (Exception exception)
            {
                Logger.LogCritical("An unexpected error occured.", exception);
                throw;
            }
        }
예제 #9
0
        public void ShouldFailToAnalyzeStandardOutputStatementWithUndeclaredVariable()
        {
            // Arrange
            string        identifier = "test";
            CobaltProgram program    = new CobaltProgram()
            {
                Code = new CodeBlockNode(1)
            };

            program.Code.AddStatement(new StandardOutputStatementNode(1)
            {
                Expression = new SingleLeafExpressionNode(1)
                {
                    Leaf = new IdentifierNode(1, identifier)
                }
            });

            // Act / Assert
            Assert.Throws <UndeclaredIdentifierError>(() =>
            {
                Analyzer.Analyze(program);
            });
        }
예제 #10
0
        private string GenerateProgramCode(CobaltProgram cobaltProgram)
        {
            StringBuilder builder = new StringBuilder();

            if (cobaltProgram.Code.Statements.Any(s => s is StandardInputStatementNode _))
            {
                builder.Append("const $cobalt_stdio=require('readline').createInterface({input:process.stdin,output:process.stdout});function $cobalt_stdin(questionText){return new Promise((resolve,reject)=>{$cobalt_stdio.question(questionText,(input)=>resolve(input));});}");
            }
            builder.Append("(async function(){");
            foreach (StatementNode statement in cobaltProgram.Code.Statements)
            {
                switch (statement)
                {
                case VariableDeclarationStatementNode variableDeclaration:
                    GenerateVariableDeclarationCode(builder, variableDeclaration);
                    break;

                case VariableAssignmentStatementNode variableAssignment:
                    GenerateVariableAssignemntCode(builder, variableAssignment);
                    break;

                case StandardInputStatementNode standardInputStatement:
                    GenerateStandardInputStatementCode(builder, standardInputStatement);
                    break;

                case StandardOutputStatementNode standardOutputStatement:
                    GenerateStandardOutputStatementCode(builder, standardOutputStatement);

                    break;

                default:
                    throw new CompilerException($"Code generation for node of type `{statement.GetType()}` not implemented for platform `{Platform}`.");
                }
            }
            builder.Append("process.exit();})();");
            return(builder.ToString());
        }
예제 #11
0
        public void CalculateMeanOfThreeNumbers()
        {
            // Arrange
            string source = ReadFromFile("mean.co");

            // Act
            List <Token>  tokens = Lexer.Tokenize(source);
            CobaltProgram ast    = Parser.Parse(tokens);

            // Assert
            Assert.Equal(6, ast.Code.Statements.Count);
            Assert.True(ast.Code.Statements.ElementAt(0) is VariableDeclarationStatementNode);
            Assert.True(ast.Code.Statements.ElementAt(1) is VariableDeclarationStatementNode);
            Assert.True(ast.Code.Statements.ElementAt(2) is VariableDeclarationStatementNode);
            Assert.True(ast.Code.Statements.ElementAt(3) is VariableAssignmentStatementNode);
            Assert.True(ast.Code.Statements.ElementAt(5) is StandardOutputStatementNode);

            StatementNode meanStatement = ast.Code.Statements.ElementAt(ast.Code.Statements.Count - 2);

            if (meanStatement is VariableDeclarationStatementNode variableDeclaration)
            {
                Assert.Equal("mean", variableDeclaration.Identifier.IdentifierName);
                ExpressionNode expression = variableDeclaration.Expression;
                if (expression is DivisionNode division)
                {
                    if (division.LeftOperand is AdditionNode addition)
                    {
                        if (addition.LeftOperand is IdentifierNode identifier)
                        {
                            Assert.Equal("a", identifier.IdentifierName);
                        }
                        else
                        {
                            throw new XunitException("Wrong operand type.");
                        }
                        if (addition.RightOperand is AdditionNode additionNode)
                        {
                            Assert.True(additionNode.LeftOperand is IdentifierNode);
                            Assert.True(additionNode.RightOperand is IdentifierNode);
                        }
                        else
                        {
                            throw new XunitException("Wrong expressin type.");
                        }
                    }
                    else
                    {
                        throw new XunitException("Wrong expressin type.");
                    }
                    if (division.RightOperand is IntegerValueNode integer)
                    {
                        Assert.Equal(3, integer.Value);
                    }
                    else
                    {
                        throw new XunitException("Wrong operand type.");
                    }
                }
                else
                {
                    throw new XunitException("Wrong expression type.");
                }
            }
            else
            {
                throw new XunitException("Wrong statement type.");
            }
        }
예제 #12
0
 public void Analyze(CobaltProgram program)
 {
     AnalyzeCodeBlock(program.Code);
 }
예제 #13
0
        public void ShouldParseOutputStatementWithSingleValue(object value, CobaltType type)
        {
            // Arrange
            List <Token> tokens = new List <Token>()
            {
                new Token(TokenType.StandardOutput, 0, 0),
                new Token(TokenType.LiteralValue, 0, 0),
                new Token(TokenType.Semicolon, 0, 0),
            };

            tokens.ElementAt(1).SetData(TokenDataKeys.LITERAL_VALUE, value);
            tokens.ElementAt(1).SetData(TokenDataKeys.COBALT_TYPE, type);

            // Act
            CobaltProgram program = Parser.Parse(tokens);

            // Assert
            Assert.Single(program.Code.Statements);

            StatementNode statement = program.Code.Statements.First();

            if (statement is StandardOutputStatementNode outputStatement)
            {
                Assert.Equal(program.Code, outputStatement.Parent);
                ExpressionNode expression = outputStatement.Expression;
                if (expression is SingleLeafExpressionNode singleLeafExpression)
                {
                    switch (type)
                    {
                    case CobaltType.Boolean:
                        if (singleLeafExpression.Leaf is BooleanValueNode booleanValue)
                        {
                            Assert.Equal((bool)value, booleanValue.Value);
                        }
                        else
                        {
                            throw new XunitException("Wrong Cobalt type.");
                        }
                        break;

                    case CobaltType.Float:
                        if (singleLeafExpression.Leaf is FloatValueNode floatValue)
                        {
                            Assert.Equal((float)value, floatValue.Value);
                        }
                        else
                        {
                            throw new XunitException("Wrong Cobalt type.");
                        }
                        break;

                    case CobaltType.Integer:
                        if (singleLeafExpression.Leaf is IntegerValueNode integerValue)
                        {
                            Assert.Equal((int)value, integerValue.Value);
                        }
                        else
                        {
                            throw new XunitException("Wrong Cobalt type.");
                        }
                        break;

                    default:
                        throw new XunitException("No test implemented for this type!");
                    }
                }
                else
                {
                    throw new XunitException("Wrong expression type.");
                }
            }
            else
            {
                throw new XunitException("Wrong node type.");
            }
        }