From 6fa4754785c5b5ef00f6664850a012540856c7b6 Mon Sep 17 00:00:00 2001 From: italo Date: Tue, 2 Sep 2025 21:15:03 -0300 Subject: [PATCH 1/3] =?UTF-8?q?Adiciona=20testes=20unit=C3=A1rios=20para?= =?UTF-8?q?=20Lexer,=20Token=20e=20AST,=20al=C3=A9m=20de=20melhorias=20na?= =?UTF-8?q?=20estrutura=20de=20tipos=20nativos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dub.json | 6 +- src/frontend/lexer/lexer.d | 119 ++++++++++++++++++++++++++++++++++++ src/frontend/lexer/token.d | 120 ++++++++++++++++++++++++++++++++++--- src/frontend/parser/ast.d | 99 ++++++++++++++++++++++++++++++ src/frontend/values.d | 20 +++++++ 5 files changed, 354 insertions(+), 10 deletions(-) diff --git a/dub.json b/dub.json index 7a5cce8..89a6a20 100644 --- a/dub.json +++ b/dub.json @@ -1,6 +1,7 @@ { "authors": [ - "Fernando Dev" + "Fernando Dev", + "Italo Brito Brandão" ], "copyright": "Copyright © 2025, Fernando Dev", "description": "Compilador Geral Delégua", @@ -32,6 +33,9 @@ "-boundscheck=off" ], "dflags-gdc": ["-O3", "-frelease", "-fbounds-check=off"] + }, + "unittest": { + "dflags-ldc": ["-g", "-O0", "-unittest"] } } } diff --git a/src/frontend/lexer/lexer.d b/src/frontend/lexer/lexer.d index 12af0b7..a9dcf09 100644 --- a/src/frontend/lexer/lexer.d +++ b/src/frontend/lexer/lexer.d @@ -687,3 +687,122 @@ public: } } } + +unittest +{ + writeln("Testando Lexer básico..."); + + auto error = new DiagnosticError(); + auto lexer = new Lexer("test.delegua", "", ".", error); + + assert(lexer !is null); + + writeln("✓ Teste de criação do Lexer passou!"); +} + +unittest +{ + writeln("Testando tokenização básica..."); + + auto error = new DiagnosticError(); + auto lexer = new Lexer("test.delegua", "var x = 42;", ".", error); + auto tokens = lexer.tokenize(); + + assert(tokens.length >= 6); + assert(tokens[0].kind == TokenType.VAR); + assert(tokens[1].kind == TokenType.IDENTIFIER); + assert(tokens[1].value.get!string == "x"); + assert(tokens[2].kind == TokenType.EQUALS); + assert(tokens[3].kind == TokenType.INT); + assert(tokens[3].value.get!long == 42); + assert(tokens[4].kind == TokenType.SEMICOLON); + assert(tokens[$-1].kind == TokenType.EOF); + + writeln("✓ Teste de tokenização básica passou!"); +} + +unittest +{ + writeln("Testando tokenização de strings..."); + + auto error = new DiagnosticError(); + auto lexer = new Lexer("test.delegua", `"hello world"`, ".", error); + auto tokens = lexer.tokenize(); + + assert(tokens.length == 2); + assert(tokens[0].kind == TokenType.STRING); + assert(tokens[0].value.get!string == "hello world"); + assert(tokens[1].kind == TokenType.EOF); + + writeln("✓ Teste de tokenização de strings passou!"); +} + +unittest +{ + writeln("Testando tokenização de números..."); + + auto error = new DiagnosticError(); + + // Teste número inteiro + auto lexer1 = new Lexer("test.delegua", "123", ".", error); + auto tokens1 = lexer1.tokenize(); + assert(tokens1.length == 2); + assert(tokens1[0].kind == TokenType.INT); + assert(tokens1[0].value.get!long == 123); + + // Teste número float - vou verificar primeiro o tipo retornado + auto lexer2 = new Lexer("test.delegua", "12.34", ".", error); + auto tokens2 = lexer2.tokenize(); + assert(tokens2.length == 2); + assert(tokens2[0].kind == TokenType.FLOAT); + // Como pode ser string ou double, vou verificar se é um valor numérico válido + // usando conversão segura + if (tokens2[0].value.type == typeid(string)) { + import std.conv : to; + double val = tokens2[0].value.get!string.to!double; + assert(val == 12.34); + } else { + assert(tokens2[0].value.get!double == 12.34); + } + + writeln("✓ Teste de tokenização de números passou!"); +} + +unittest +{ + writeln("Testando keywords em português..."); + + auto error = new DiagnosticError(); + auto lexer = new Lexer("test.delegua", "se verdadeiro então", ".", error); + auto tokens = lexer.tokenize(); + + assert(tokens.length == 4); + assert(tokens[0].kind == TokenType.SE); + assert(tokens[1].kind == TokenType.TRUE); + assert(tokens[2].kind == TokenType.IDENTIFIER); + assert(tokens[3].kind == TokenType.EOF); + + writeln("✓ Teste de keywords em português passou!"); +} + +unittest +{ + writeln("Testando operadores..."); + + auto error = new DiagnosticError(); + auto lexer = new Lexer("test.delegua", "+ - * / == != >= <=", ".", error); + auto tokens = lexer.tokenize(); + + assert(tokens.length == 9); + assert(tokens[0].kind == TokenType.PLUS); + assert(tokens[1].kind == TokenType.MINUS); + assert(tokens[2].kind == TokenType.ASTERISK); + assert(tokens[3].kind == TokenType.SLASH); + assert(tokens[4].kind == TokenType.EQUALS_EQUALS); + assert(tokens[5].kind == TokenType.NOT_EQUALS); + assert(tokens[6].kind == TokenType.GREATER_THAN_OR_EQUALS); + assert(tokens[7].kind == TokenType.LESS_THAN_OR_EQUALS); + assert(tokens[8].kind == TokenType.EOF); + + writeln("✓ Teste de operadores passou!"); +} diff --git a/src/frontend/lexer/token.d b/src/frontend/lexer/token.d index 02947cc..a9fe088 100644 --- a/src/frontend/lexer/token.d +++ b/src/frontend/lexer/token.d @@ -1,13 +1,14 @@ module frontend.lexer.token; import std.variant; +import std.stdio; // Token type enum TokenType { // Keywords VAR, // var - FALSE, // false + FALSE, // false TRUE, // true SUSTAR, // sustar DO, // faça/faca @@ -78,8 +79,8 @@ enum TokenType NOT_EQUALS, // != GREATER_THAN, // > LESS_THAN, // < - GREATER_THAN_OR_EQUALS, // >= - LESS_THAN_OR_EQUALS, // <= + GREATER_THAN_OR_EQUALS, // >= + LESS_THAN_OR_EQUALS, // <= AND, // && OR, // || COMMA, // , @@ -90,15 +91,15 @@ enum TokenType RPAREN, // ) LBRACE, // { RBRACE, // } - LBRACKET, // [ - RBRACKET, // ] - NOT, // ] + LBRACKET, // [ + RBRACKET, // ] + NOT, // ] RANGE, // .. - BANG, // ! - QUESTION, // ? + BANG, // ! + QUESTION, // ? // Operadores bitwise básicos - BIT_AND, // & + BIT_AND, // & BIT_OR, // | BIT_XOR, // ^ BIT_NOT, // ~ @@ -253,3 +254,104 @@ bool isComplexTypeToken(Token token) return false; } } + +unittest +{ + writeln("Testando Token..."); + + auto loc = Loc("test.d", 1, 1, 5, "."); + auto token = Token(TokenType.IDENTIFIER, Variant("teste"), loc); + assert(token.kind == TokenType.IDENTIFIER); + assert(token.value.get!string == "teste"); + assert(token.loc.line == 1); + assert(token.loc.file == "test.d"); + + auto tokenVar = Token(TokenType.VAR, Variant("var"), loc); + assert(tokenVar.kind == TokenType.VAR); + + writeln("✓ Testes de Token passaram!"); +} + +unittest +{ + writeln("Testando isTypeToken..."); + + auto loc = Loc("test.d", 1, 1, 5, "."); + + auto intToken = Token(TokenType.IDENTIFIER, Variant("int"), loc); + assert(isTypeToken(intToken) == true); + + auto floatToken = Token(TokenType.IDENTIFIER, Variant("float"), loc); + assert(isTypeToken(floatToken) == true); + + auto stringToken = Token(TokenType.IDENTIFIER, Variant("string"), loc); + assert(isTypeToken(stringToken) == true); + + auto boolToken = Token(TokenType.IDENTIFIER, Variant("bool"), loc); + assert(isTypeToken(boolToken) == true); + + auto voidToken = Token(TokenType.IDENTIFIER, Variant("void"), loc); + assert(isTypeToken(voidToken) == true); + + auto vazioToken = Token(TokenType.IDENTIFIER, Variant("vazio"), loc); + assert(isTypeToken(vazioToken) == true); + + auto invalidToken = Token(TokenType.IDENTIFIER, Variant("qualquercoisa"), loc); + assert(isTypeToken(invalidToken) == false); + + auto nonIdToken = Token(TokenType.PLUS, Variant("+"), loc); + assert(isTypeToken(nonIdToken) == false); + + writeln("✓ Testes de isTypeToken passaram!"); +} + +unittest +{ + writeln("Testando isComplexTypeToken..."); + + auto loc = Loc("test.d", 1, 1, 5, "."); + + auto asteriskToken = Token(TokenType.ASTERISK, Variant("*"), loc); + assert(isComplexTypeToken(asteriskToken) == true); + + auto lbracketToken = Token(TokenType.LBRACKET, Variant("["), loc); + assert(isComplexTypeToken(lbracketToken) == true); + + auto rbracketToken = Token(TokenType.RBRACKET, Variant("]"), loc); + assert(isComplexTypeToken(rbracketToken) == true); + + auto plusToken = Token(TokenType.PLUS, Variant("+"), loc); + assert(isComplexTypeToken(plusToken) == false); + + auto identifierToken = Token(TokenType.IDENTIFIER, Variant("teste"), loc); + assert(isComplexTypeToken(identifierToken) == false); + + writeln("✓ Testes de isComplexTypeToken passaram!"); +} + +unittest +{ + writeln("Testando keywords..."); + + assert("var" in keywords); + assert("se" in keywords); + assert("enquanto" in keywords); + assert("para" in keywords); + assert("funcao" in keywords); + assert("função" in keywords); + assert("classe" in keywords); + assert("verdadeiro" in keywords); + assert("falso" in keywords); + + assert(keywords["var"] == TokenType.VAR); + assert(keywords["se"] == TokenType.SE); + assert(keywords["enquanto"] == TokenType.ENQUANTO); + assert(keywords["para"] == TokenType.PARA); + assert(keywords["funcao"] == TokenType.FUNCAO); + assert(keywords["função"] == TokenType.FUNCAO); + assert(keywords["classe"] == TokenType.CLASSE); + assert(keywords["verdadeiro"] == TokenType.TRUE); + assert(keywords["falso"] == TokenType.FALSE); + + writeln("✓ Testes de keywords passaram!"); +} diff --git a/src/frontend/parser/ast.d b/src/frontend/parser/ast.d index 83eb038..8be0b5b 100644 --- a/src/frontend/parser/ast.d +++ b/src/frontend/parser/ast.d @@ -1,5 +1,7 @@ module frontend.parser.ast; +import std.stdio; +import frontend.lexer.token : Loc; import frontend.values; import frontend.lexer.token; import middle.semantic_symbol_info; @@ -825,3 +827,100 @@ class IndexExprAssignment : Stmt this.loc = loc; } } + +unittest +{ + writeln("Testando criação de AST nodes..."); + + auto loc = Loc("test.d", 1, 1, 5, "."); + auto intLit = new IntLiteral(42, loc); + assert(intLit.kind == NodeType.IntLiteral); + assert(intLit.value.get!long == 42); + assert(intLit.loc.line == 1); + assert(intLit.loc.file == "test.d"); + + writeln("✓ Teste de IntLiteral passou!"); +} + +unittest +{ + writeln("Testando criação de BinaryExpr..."); + + auto loc = Loc("test.d", 1, 1, 5, "."); + auto left = new IntLiteral(10, loc); + auto right = new IntLiteral(20, loc); + auto binExpr = new BinaryExpr(left, right, "+", loc); + + assert(binExpr.kind == NodeType.BinaryExpr); + assert(binExpr.left == left); + assert(binExpr.right == right); + assert(binExpr.op == "+"); + assert(binExpr.loc.line == 1); + + writeln("✓ Teste de BinaryExpr passou!"); +} + +unittest +{ + writeln("Testando criação de Identifier..."); + + auto loc = Loc("test.d", 1, 1, 5, "."); + auto ident = new Identifier("variavel", loc); + + assert(ident.kind == NodeType.Identifier); + assert(ident.value.get!string == "variavel"); + assert(ident.loc.line == 1); + assert(ident.loc.start == 1); + + writeln("✓ Teste de Identifier passou!"); +} + +unittest +{ + writeln("Testando criação de Program..."); + + auto loc = Loc("test.d", 1, 1, 5, "."); + auto stmt1 = new IntLiteral(1, loc); + auto stmt2 = new IntLiteral(2, loc); + Stmt[] stmts = [stmt1, stmt2]; + + auto program = new Program(stmts); + + assert(program.kind == NodeType.Program); + assert(program.body.length == 2); + assert(program.body[0] == stmt1); + assert(program.body[1] == stmt2); + + writeln("✓ Teste de Program passou!"); +} + +unittest +{ + writeln("Testando criação de StringLiteral..."); + + auto loc = Loc("test.d", 2, 1, 5, "."); + auto strLit = new StringLiteral("hello", loc); + + assert(strLit.kind == NodeType.StringLiteral); + assert(strLit.value.get!string == "hello"); + assert(strLit.loc.line == 2); + assert(strLit.loc.start == 1); + + writeln("✓ Teste de StringLiteral passou!"); +} + +unittest +{ + writeln("Testando criação de BoolLiteral..."); + + auto loc = Loc("test.d", 1, 1, 5, "."); + auto boolLit = new BoolLiteral(true, loc); + + assert(boolLit.kind == NodeType.BoolLiteral); + assert(boolLit.value.get!bool == true); + + auto boolLit2 = new BoolLiteral(false, loc); + assert(boolLit2.value.get!bool == false); + + writeln("✓ Teste de BoolLiteral passou!"); +} diff --git a/src/frontend/values.d b/src/frontend/values.d index 9b9bd8d..43a1f86 100644 --- a/src/frontend/values.d +++ b/src/frontend/values.d @@ -14,3 +14,23 @@ enum TypesNative : string CLASS = "class", T = "T" } + +unittest +{ + import std.stdio; + writeln("Testando TypesNative..."); + + assert(TypesNative.STRING == "string"); + assert(TypesNative.INT == "long"); + assert(TypesNative.LONG == "long"); + assert(TypesNative.FLOAT == "double"); + assert(TypesNative.BOOL == "bool"); + assert(TypesNative.VOID == "void"); + assert(TypesNative.CHAR == "char"); + assert(TypesNative.NULL == "null"); + assert(TypesNative.ID == "auto"); + assert(TypesNative.CLASS == "class"); + assert(TypesNative.T == "T"); + + writeln("✓ Testes de TypesNative passaram!"); +} From 2921f3b49facebf4baf2b8ac2f0df7cb019bddff Mon Sep 17 00:00:00 2001 From: italo Date: Tue, 2 Sep 2025 21:18:33 -0300 Subject: [PATCH 2/3] =?UTF-8?q?Adiciona=20configura=C3=A7=C3=A3o=20de=20CI?= =?UTF-8?q?=20com=20testes=20automatizados=20para=20o=20projeto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 63 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..23e94b8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,63 @@ +name: CI + +on: + push: + branches: [ master, main, develop ] + pull_request: + branches: [ master, main, develop ] + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + strategy: + matrix: + dc: [dmd-latest, ldc-latest] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install D compiler + uses: dlang-community/setup-dlang@v1 + with: + compiler: ${{ matrix.dc }} + + - name: Print D compiler version + run: | + dmd --version || true + ldc2 --version || true + dub --version + + - name: Cache DUB dependencies + uses: actions/cache@v3 + with: + path: ~/.dub + key: ${{ runner.os }}-dub-${{ hashFiles('**/dub.json', '**/dub.selections.json') }} + restore-keys: | + ${{ runner.os }}-dub- + + - name: Install dependencies + run: dub upgrade + + - name: Build project (debug) + run: dub build --build=debug + + - name: Build project (release) + run: dub build --build=release + + - name: Run unit tests + run: | + echo "=== Executando Unit Tests ===" + dub build --config=unittest --build=unittest + dub run --config=unittest --build=unittest + + - name: Test dub test command + run: dub test + + - name: Build with different compilers (if matrix) + run: | + echo "=== Testing with ${{ matrix.dc }} ===" + dub --version + dub build --compiler=${{ matrix.dc }} || dub build From 5d31ee1f41e3596990b7decebb3f2370fb8bfe77 Mon Sep 17 00:00:00 2001 From: italo Date: Tue, 2 Sep 2025 21:20:13 -0300 Subject: [PATCH 3/3] =?UTF-8?q?Ajusta=20configura=C3=A7=C3=A3o=20do=20CI?= =?UTF-8?q?=20para=20build=20de=20projeto=20em=20modo=20release=20e=20remo?= =?UTF-8?q?ve=20comando=20de=20teste=20do=20DUB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23e94b8..51a095e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: run: dub build --build=debug - name: Build project (release) - run: dub build --build=release + run: dub build --build=release-fast - name: Run unit tests run: | @@ -53,9 +53,6 @@ jobs: dub build --config=unittest --build=unittest dub run --config=unittest --build=unittest - - name: Test dub test command - run: dub test - - name: Build with different compilers (if matrix) run: | echo "=== Testing with ${{ matrix.dc }} ==="