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); }); }
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."); } }
/// <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); }
public TargetProgram GenerateTargetCode(CobaltProgram cobaltProgram) { TargetProgram program = new TargetProgram("JavaScript"); string js = GenerateProgramCode(cobaltProgram); program.AddFile(new TextFile("index.js", js)); return(program); }
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."); } }
/// <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); }
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); }
/// <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; } }
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); }); }
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()); }
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."); } }
public void Analyze(CobaltProgram program) { AnalyzeCodeBlock(program.Code); }
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."); } }