Skip to content

Commit 18292b5

Browse files
authored
Merge pull request #680 from glu-lang/feature/printer/code
Feature: add CodePrinter
2 parents 1918cd9 + 226bfaa commit 18292b5

15 files changed

Lines changed: 326 additions & 44 deletions

include/AST/ASTNode.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ class ASTNode {
7474
/// @param out The output stream to print to.
7575
void print(llvm::raw_ostream &out);
7676

77+
/// @brief This attempts to reconstruct the original source code from the
78+
/// AST node.
79+
/// @param out The output stream to print to.
80+
void printInterface(llvm::raw_ostream &out);
81+
7782
/// @brief Print a human-readable representation of this node to
7883
/// standard output, for debugging purposes.
7984
void print();

include/AST/TypePrinter.hpp

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,23 @@ class TypePrinter : public glu::types::TypeVisitor<TypePrinter, std::string> {
6565

6666
std::string visitIntTy(glu::types::IntTy *type)
6767
{
68-
std::string result;
69-
if (type->isSigned()) {
70-
result = "i";
71-
} else {
72-
result = "u";
73-
}
74-
result += std::to_string(type->getBitWidth());
75-
return result;
68+
auto prefix = type->isSigned() ? "Int" : "UInt";
69+
return prefix + std::to_string(type->getBitWidth());
7670
}
7771

7872
std::string visitFloatTy(glu::types::FloatTy *type)
7973
{
80-
switch (type->getBitWidth()) {
81-
case glu::types::FloatTy::HALF: return "f16";
82-
case glu::types::FloatTy::FLOAT: return "f32";
83-
case glu::types::FloatTy::DOUBLE: return "f64";
84-
case glu::types::FloatTy::INTEL_LONG_DOUBLE: return "f80";
85-
default: return "f" + std::to_string(type->getBitWidth());
74+
unsigned bitWidth = type->getBitWidth();
75+
76+
// Map special bit widths to their actual sizes
77+
switch (bitWidth) {
78+
case glu::types::FloatTy::HALF: bitWidth = 16; break;
79+
case glu::types::FloatTy::FLOAT: bitWidth = 32; break;
80+
case glu::types::FloatTy::DOUBLE: bitWidth = 64; break;
81+
case glu::types::FloatTy::INTEL_LONG_DOUBLE: bitWidth = 80; break;
8682
}
83+
84+
return "Float" + std::to_string(bitWidth);
8785
}
8886

8987
// Composite types
@@ -158,7 +156,6 @@ class TypePrinter : public glu::types::TypeVisitor<TypePrinter, std::string> {
158156
visitTypeVariableTy([[maybe_unused]] glu::types::TypeVariableTy *type)
159157
{
160158
if (_enableTypeVariableNames && type) {
161-
162159
// Get or assign an ID for this type variable
163160
auto it = _typeVarIds.find(type);
164161
if (it == _typeVarIds.end()) {

lib/ASTPrinter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ target_link_libraries(ASTPrinter
1313
target_sources(ASTPrinter
1414
PRIVATE
1515
ASTPrinter.cpp
16+
CodePrinter.cpp
1617
)

lib/ASTPrinter/CodePrinter.cpp

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
#include "AST/Exprs.hpp"
2+
#include "AST/Types.hpp"
3+
#include "ASTVisitor.hpp"
4+
#include "TypePrinter.hpp"
5+
6+
#include <llvm/Support/raw_ostream.h>
7+
#include <string>
8+
9+
namespace glu::ast {
10+
11+
/// @brief CodePrinter is a visitor that converts AST nodes back to Glu source
12+
/// code.
13+
///
14+
/// This class generates valid Glu source code from AST nodes, primarily for
15+
/// decompilation of function prototypes and type declarations. It supports:
16+
/// - FunctionDecl (without body)
17+
/// - StructDecl
18+
/// - EnumDecl
19+
///
20+
/// The generated code should be syntactically valid Glu code that can be
21+
/// used as interface declarations.
22+
class CodePrinter : public ASTVisitor<CodePrinter> {
23+
llvm::raw_ostream &_out; ///< The output stream to write the code to
24+
TypePrinter _typePrinter; ///< Type printer for formatting types
25+
size_t _indent = 0; ///< Current indentation level
26+
27+
public:
28+
/// @brief Constructs a CodePrinter object.
29+
/// @param out The output stream to write the generated code to.
30+
CodePrinter(llvm::raw_ostream &out = llvm::outs())
31+
: _out(out), _typePrinter(true /* enable type variable names */)
32+
{
33+
}
34+
35+
/// @brief Visit a ModuleDecl and print its contents as Glu code
36+
/// @param node The ModuleDecl node to print
37+
void visitModuleDecl(ModuleDecl *node)
38+
{
39+
// Print each top-level declaration in the module
40+
for (auto *decl : node->getDecls()) {
41+
visit(decl);
42+
_out << "\n";
43+
}
44+
}
45+
46+
/// @brief Visit a FunctionDecl and print its signature (without body)
47+
/// @param node The FunctionDecl node to print
48+
void visitFunctionDecl(FunctionDecl *node)
49+
{
50+
printDeclPrefix(node);
51+
52+
_out << "func " << node->getName();
53+
54+
printFunctionParameters(node->getParams());
55+
56+
if (auto *funcType = node->getType()) {
57+
auto *returnType = funcType->getReturnType();
58+
if (!llvm::isa<glu::types::VoidTy>(returnType)) {
59+
_out << " -> ";
60+
printType(returnType);
61+
}
62+
}
63+
64+
_out << ";";
65+
}
66+
67+
/// @brief Visit a StructDecl and print its definition
68+
/// @param node The StructDecl node to print
69+
void visitStructDecl(StructDecl *node)
70+
{
71+
printDeclPrefix(node);
72+
73+
_out << "struct " << node->getName() << " {\n";
74+
75+
_indent += 4;
76+
77+
for (auto *field : node->getFields()) {
78+
visit(field);
79+
_out << "\n";
80+
}
81+
82+
_indent -= 4;
83+
84+
printIndent();
85+
86+
_out << "}";
87+
}
88+
89+
/// @brief Visit an EnumDecl and print its definition
90+
/// @param node The EnumDecl node to print
91+
void visitEnumDecl(EnumDecl *node)
92+
{
93+
printDeclPrefix(node);
94+
95+
_out << "enum " << node->getName() << " {\n";
96+
97+
_indent += 4;
98+
99+
for (auto *field : node->getFields()) {
100+
visit(field);
101+
_out << "\n";
102+
}
103+
104+
_indent -= 4;
105+
106+
printIndent();
107+
108+
_out << "}";
109+
}
110+
111+
/// @brief Visit a FieldDecl and print its declaration
112+
/// @param node The FieldDecl node to print
113+
void visitFieldDecl(FieldDecl *node)
114+
{
115+
printDeclPrefix(node);
116+
117+
_out << node->getName();
118+
119+
// For enum fields, we don't print the type (just the name)
120+
// For struct fields, we print "name: type"
121+
if (!llvm::isa_and_nonnull<EnumDecl>(node->getParent())) {
122+
_out << ": ";
123+
printType(node->getType());
124+
}
125+
126+
_out << ",";
127+
}
128+
129+
/// @brief Visit a ParamDecl and print its declaration
130+
/// @param node The ParamDecl node to print
131+
void visitParamDecl(ParamDecl *node)
132+
{
133+
visit(node->getAttributes());
134+
_out << node->getName() << ": ";
135+
printType(node->getType());
136+
}
137+
138+
/// @brief Visit a LiteralExpr and print its value for attribute parameters
139+
/// @param node The LiteralExpr node to print
140+
void visitLiteralExpr(LiteralExpr *node)
141+
{
142+
std::visit(
143+
[this](auto &&val) {
144+
using T = std::decay_t<decltype(val)>;
145+
if constexpr (std::is_same_v<T, llvm::APInt>) {
146+
_out << val;
147+
} else if constexpr (std::is_same_v<T, llvm::APFloat>) {
148+
_out << val.convertToDouble();
149+
} else if constexpr (std::is_same_v<T, llvm::StringRef>) {
150+
_out << "\"" << val.str() << "\"";
151+
} else if constexpr (std::is_same_v<T, bool>) {
152+
_out << (val ? "true" : "false");
153+
} else if constexpr (std::is_same_v<T, std::nullptr_t>) {
154+
_out << "null";
155+
}
156+
},
157+
node->getValue()
158+
);
159+
}
160+
161+
void visitAttribute(Attribute *node)
162+
{
163+
_out << "@" << node->getAttributeKindSpelling();
164+
165+
if (node->getParameter()) {
166+
_out << "(";
167+
visit(node->getParameter());
168+
_out << ")";
169+
}
170+
}
171+
172+
void visitAttributeList(AttributeList *node)
173+
{
174+
for (auto *attr : node->getAttributes()) {
175+
visit(attr);
176+
_out << " ";
177+
}
178+
}
179+
180+
private:
181+
/// @brief Print indentation
182+
void printIndent() { _out.indent(_indent); }
183+
184+
/// @brief Print a type using the enhanced type printer
185+
/// @param type The type to print
186+
void printType(glu::types::TypeBase *type)
187+
{
188+
if (type) {
189+
_out << _typePrinter.visit(type);
190+
} else {
191+
_out << "void";
192+
}
193+
}
194+
195+
/// @brief Print attributes and visibility prefix for declarations
196+
/// @param decl The declaration to print prefix for
197+
void printDeclPrefix(DeclBase *decl)
198+
{
199+
printIndent();
200+
visit(decl->getAttributes());
201+
printVisibility(decl->getVisibility());
202+
}
203+
204+
/// @brief Print function parameters
205+
/// @param params The parameter list
206+
void printFunctionParameters(llvm::ArrayRef<ParamDecl *> params)
207+
{
208+
_out << "(";
209+
for (size_t i = 0; i < params.size(); ++i) {
210+
if (i > 0) {
211+
_out << ", ";
212+
}
213+
visit(params[i]);
214+
}
215+
_out << ")";
216+
}
217+
218+
/// @brief Print visibility modifier if present
219+
/// @param visibility The visibility to print
220+
void printVisibility(Visibility visibility)
221+
{
222+
switch (visibility) {
223+
case Visibility::Public: _out << "public "; break;
224+
case Visibility::Private: _out << "private "; break;
225+
}
226+
}
227+
};
228+
229+
void ASTNode::printInterface(llvm::raw_ostream &out)
230+
{
231+
CodePrinter(out).visit(this);
232+
}
233+
234+
} // namespace glu::ast

test/GIL/GILPrinter.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ TEST_F(GILPrinterTest, SimpleFunction)
4343
printer.visit(fn);
4444
EXPECT_EQ(str, R"(gil @test : $() -> Void {
4545
entry:
46-
%0 = integer_literal $i32, 42
46+
%0 = integer_literal $Int32, 42
4747
}
4848
4949
)");
@@ -67,10 +67,10 @@ TEST_F(GILPrinterTest, FunctionWithArguments)
6767
)
6868
);
6969
printer.visit(fn);
70-
EXPECT_EQ(str, R"(gil @test : $(f64) -> f64 {
71-
bb0(%0 : $f64):
72-
%1 = float_literal $f64, 42.5
73-
%2 = call @test, %0 : $f64, %1 : $f64
70+
EXPECT_EQ(str, R"(gil @test : $(Float64) -> Float64 {
71+
bb0(%0 : $Float64):
72+
%1 = float_literal $Float64, 42.5
73+
%2 = call @test, %0 : $Float64, %1 : $Float64
7474
}
7575
7676
)");
@@ -109,8 +109,8 @@ TEST_F(GILPrinterTest, DebugInstTest)
109109

110110
EXPECT_EQ(str, R"(gil @test : $() -> Void {
111111
bb0:
112-
%0 = integer_literal $i32, 10, loc "main.glu":2:1
113-
debug %0 : $i32, let "x", loc "main.glu":2:1
112+
%0 = integer_literal $Int32, 10, loc "main.glu":2:1
113+
debug %0 : $Int32, let "x", loc "main.glu":2:1
114114
}
115115
116116
)");

test/functional/ASTPrinter/struct.glu

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
// CHECK: -->Fields:
1010
// CHECK-NEXT: FieldDecl {{0x[0-9a-f]+}} <line:17:5>
1111
// CHECK-NEXT: -->Name: a
12-
// CHECK-NEXT: -->Type: i32
12+
// CHECK-NEXT: -->Type: Int32
1313
// CHECK: FieldDecl {{0x[0-9a-f]+}} <line:18:5>
1414
// CHECK-NEXT: -->Name: b
15-
// CHECK-NEXT: -->Type: i32
15+
// CHECK-NEXT: -->Type: Int32
1616
struct S {
1717
a: Int,
1818
b: Int,
@@ -25,5 +25,5 @@ typealias Point = S;
2525

2626
// CHECK: TypeAliasDecl {{0x[0-9a-f]+}} <line:29:11>
2727
// CHECK-NEXT: -->Name: MyAlias
28-
// CHECK-NEXT: -->Type: i32
28+
// CHECK-NEXT: -->Type: Int32
2929
typealias MyAlias = Int;

test/functional/GILPrinter/func.glu

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
// RUN: gluc --print-gil %s | FileCheck -v %s
33
//
44

5-
// CHECK:gil @main : $() -> i32 {
5+
// CHECK:gil @main : $() -> Int32 {
66
func main() {
77

88
// CHECK-NEXT:entry:
9-
// CHECK-NEXT: %0 = integer_literal $i32, 0
10-
// CHECK-NEXT: return %0 : $i32
9+
// CHECK-NEXT: %0 = integer_literal $Int32, 0
10+
// CHECK-NEXT: return %0 : $Int32
1111

1212
// CHECK-NEXT:}
1313
}

test/functional/Sema/ambiguous_overload_candidates.glu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ func main() -> Int {
1717
// without additional type context
1818
let x = getValue(5);
1919
// CHECK: 18:13: error: Ambiguous type variable mapping found: multiple valid solutions exist; Consider adding explicit type annotations to resolve the ambiguity
20-
// CHECK: 10:1: note: Candidate of type: (i32) -> f32
21-
// CHECK: 6:1: note: Candidate of type: (i32) -> i32
20+
// CHECK: 10:1: note: Candidate of type: (Int32) -> Float32
21+
// CHECK: 6:1: note: Candidate of type: (Int32) -> Int32
2222

2323
return 0;
2424
}

0 commit comments

Comments
 (0)