523 lines
11 KiB
C++
523 lines
11 KiB
C++
#include <pslang/ast/print.hpp>
|
|
#include <pslang/ast/statement.hpp>
|
|
#include <pslang/ast/type_visitor.hpp>
|
|
#include <pslang/ast/expression_visitor.hpp>
|
|
#include <pslang/ast/statement_visitor.hpp>
|
|
#include <pslang/types/type_visitor.hpp>
|
|
#include <pslang/types/type.hpp>
|
|
|
|
#include <iomanip>
|
|
|
|
namespace pslang::ast
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
print_options as_child(print_options options)
|
|
{
|
|
options.indent_level += 1;
|
|
return options;
|
|
}
|
|
|
|
void put_indent(std::ostream & out, print_options const & options)
|
|
{
|
|
for (std::size_t i = 0; i < options.indent_level; ++i)
|
|
out << options.indent_string;
|
|
}
|
|
|
|
struct raw_type_print_visitor
|
|
: types::const_visitor<raw_type_print_visitor>
|
|
{
|
|
std::ostream & out;
|
|
|
|
using const_visitor::apply;
|
|
|
|
void apply(types::unit_type const & type)
|
|
{
|
|
out << "unit";
|
|
}
|
|
|
|
void apply(types::bool_type const &)
|
|
{
|
|
out << "bool";
|
|
}
|
|
|
|
void apply(types::i8_type const &)
|
|
{
|
|
out << "i8";
|
|
}
|
|
|
|
void apply(types::u8_type const &)
|
|
{
|
|
out << "u8";
|
|
}
|
|
|
|
void apply(types::i16_type const &)
|
|
{
|
|
out << "i16";
|
|
}
|
|
|
|
void apply(types::u16_type const &)
|
|
{
|
|
out << "u16";
|
|
}
|
|
|
|
void apply(types::i32_type const &)
|
|
{
|
|
out << "i32";
|
|
}
|
|
|
|
void apply(types::u32_type const &)
|
|
{
|
|
out << "u32";
|
|
}
|
|
|
|
void apply(types::i64_type const &)
|
|
{
|
|
out << "i64";
|
|
}
|
|
|
|
void apply(types::u64_type const &)
|
|
{
|
|
out << "u64";
|
|
}
|
|
|
|
void apply(types::f16_type const &)
|
|
{
|
|
out << "f16";
|
|
}
|
|
|
|
void apply(types::f32_type const &)
|
|
{
|
|
out << "f32";
|
|
}
|
|
|
|
void apply(types::f64_type const &)
|
|
{
|
|
out << "f64";
|
|
}
|
|
|
|
void apply(types::array_type const & type)
|
|
{
|
|
apply(*type.element_type);
|
|
out << "[" << type.size << "]";
|
|
}
|
|
|
|
void apply(types::function_type const & type)
|
|
{
|
|
if (type.arguments.size() == 1 && !is_function_type(*type.arguments[0]))
|
|
{
|
|
apply(*type.arguments.front());
|
|
out << " -> ";
|
|
apply(*type.result);
|
|
return;
|
|
}
|
|
|
|
out << '(';
|
|
bool first = true;
|
|
for (auto const & argument : type.arguments)
|
|
{
|
|
if (!first) out << ", ";
|
|
first = false;
|
|
apply(*argument);
|
|
}
|
|
out << ") -> ";
|
|
apply(*type.result);
|
|
}
|
|
|
|
void apply(types::struct_type const & type)
|
|
{
|
|
out << type.node->name;
|
|
}
|
|
|
|
void apply(types::pointer_type const & type)
|
|
{
|
|
apply(*type.referenced_type);
|
|
if (type.is_mutable)
|
|
out << " mut";
|
|
out << "*";
|
|
}
|
|
};
|
|
|
|
struct type_print_visitor
|
|
: const_type_visitor<type_print_visitor>
|
|
{
|
|
std::ostream & out;
|
|
|
|
using const_type_visitor::apply;
|
|
|
|
void apply(types::unit_type const &)
|
|
{
|
|
out << "unit";
|
|
}
|
|
|
|
template <typename T>
|
|
void apply(types::primitive_type_base<T> const & type)
|
|
{
|
|
print(out, types::type{types::primitive_type{type}});
|
|
}
|
|
|
|
void apply(array_type const & type)
|
|
{
|
|
apply(*type.element_type);
|
|
out << "[" << type.size << "]";
|
|
}
|
|
|
|
void apply(function_type const & type)
|
|
{
|
|
if (type.arguments.size() == 1 && !std::get_if<function_type>(type.arguments[0].get()))
|
|
{
|
|
apply(*type.arguments.front());
|
|
out << " -> ";
|
|
apply(*type.result);
|
|
return;
|
|
}
|
|
|
|
out << '(';
|
|
bool first = true;
|
|
for (auto const & argument : type.arguments)
|
|
{
|
|
if (!first) out << ", ";
|
|
first = false;
|
|
apply(*argument);
|
|
}
|
|
out << ") -> ";
|
|
apply(*type.result);
|
|
}
|
|
|
|
void apply(pointer_type const & type)
|
|
{
|
|
apply(*type.referenced_type);
|
|
if (type.is_mutable)
|
|
out << " mut";
|
|
out << "*";
|
|
}
|
|
|
|
void apply(type_identifier const & type)
|
|
{
|
|
out << type.name;
|
|
}
|
|
};
|
|
|
|
struct expression_print_visitor
|
|
: const_expression_visitor<expression_print_visitor>
|
|
{
|
|
std::ostream & out;
|
|
print_options options;
|
|
|
|
using const_expression_visitor::apply;
|
|
|
|
template <typename Node>
|
|
void child(Node const & node)
|
|
{
|
|
++options.indent_level;
|
|
apply(node);
|
|
--options.indent_level;
|
|
}
|
|
|
|
void apply(bool_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "bool literal { value = " << (node.value ? "true" : "false") << " }\n";
|
|
}
|
|
|
|
void apply(i8_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "i8 literal { value = " << (std::int32_t)node.value << " }\n";
|
|
}
|
|
|
|
void apply(u8_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "u8 literal { value = " << (std::uint32_t)node.value << " }\n";
|
|
}
|
|
|
|
void apply(i16_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "i16 literal { value = " << node.value << " }\n";
|
|
}
|
|
|
|
void apply(u16_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "u16 literal { value = " << node.value << " }\n";
|
|
}
|
|
|
|
void apply(i32_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "i32 literal { value = " << node.value << " }\n";
|
|
}
|
|
|
|
void apply(u32_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "u32 literal { value = " << node.value << " }\n";
|
|
}
|
|
|
|
void apply(i64_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "i64 literal { value = " << node.value << " }\n";
|
|
}
|
|
|
|
void apply(u64_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "u64 literal { value = " << node.value << " }\n";
|
|
}
|
|
|
|
void apply(f16_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "f32 literal { value = " << std::setprecision(3) << node.value.repr << " }\n";
|
|
}
|
|
|
|
void apply(f32_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "f32 literal { value = " << std::setprecision(7) << node.value << " }\n";
|
|
}
|
|
|
|
void apply(f64_literal const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "f64 literal { value = " << std::setprecision(15) << node.value << " }\n";
|
|
}
|
|
|
|
void apply(identifier const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "identifier { name = \"" << node.name << "\" }\n";
|
|
}
|
|
|
|
void apply(unary_operation const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << node.type << '\n';
|
|
child(*node.arg1);
|
|
}
|
|
|
|
void apply(binary_operation const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << node.type << '\n';
|
|
child(*node.arg1);
|
|
child(*node.arg2);
|
|
}
|
|
|
|
void apply(cast_operation const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "cast as ";
|
|
print(out, *node.type);
|
|
out << '\n';
|
|
child(*node.expression);
|
|
}
|
|
|
|
void apply(function_call const & node)
|
|
{
|
|
put_indent(out, options);
|
|
if (node.function)
|
|
{
|
|
out << "call\n";
|
|
child(*node.function);
|
|
}
|
|
if (node.type)
|
|
{
|
|
out << "constructor { type = ";
|
|
print(out, *node.type);
|
|
out << " }\n";
|
|
}
|
|
for (auto const & argument : node.arguments)
|
|
child(*argument);
|
|
}
|
|
|
|
void apply(array const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "array\n";
|
|
for (auto const & element : node.elements)
|
|
child(*element);
|
|
}
|
|
|
|
void apply(array_access const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "array access\n";
|
|
child(*node.array);
|
|
child(*node.index);
|
|
}
|
|
|
|
void apply(field_access const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "field access { name = \"" << node.field_name << "\" }\n";
|
|
child(*node.object);
|
|
}
|
|
};
|
|
|
|
struct statement_print_visitor
|
|
: const_statement_visitor<statement_print_visitor>
|
|
{
|
|
std::ostream & out;
|
|
print_options options;
|
|
|
|
using const_statement_visitor::apply;
|
|
|
|
template <typename Node>
|
|
void child(Node const & node)
|
|
{
|
|
++options.indent_level;
|
|
apply(node);
|
|
--options.indent_level;
|
|
}
|
|
|
|
void apply(expression_ptr const & node)
|
|
{
|
|
print(out, *node, options);
|
|
}
|
|
|
|
void apply(assignment const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "assignment\n";
|
|
child(node.lhs);
|
|
child(node.rhs);
|
|
}
|
|
|
|
void apply(variable_declaration const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "variable declaration { category = " << node.category << ", name = \"" << node.name << "\"";
|
|
if (node.type)
|
|
{
|
|
out << ", type = ";
|
|
print(out, *node.type);
|
|
}
|
|
out << " }\n";
|
|
child(node.initializer);
|
|
}
|
|
|
|
void apply(if_chain const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "if chain\n";
|
|
++options.indent_level;
|
|
for (auto const & block : node.blocks)
|
|
{
|
|
put_indent(out, options);
|
|
out << "condition\n";
|
|
if (block.condition)
|
|
child(block.condition);
|
|
else
|
|
{
|
|
put_indent(out, as_child(options));
|
|
out << "(none)\n";
|
|
}
|
|
|
|
put_indent(out, options);
|
|
out << "body\n";
|
|
child(*block.statements);
|
|
}
|
|
--options.indent_level;
|
|
}
|
|
|
|
void apply(while_block const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "while\n";
|
|
child(node.condition);
|
|
child(*node.statements);
|
|
}
|
|
|
|
void apply(function_definition const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "function { name = \"" << node.name << "\", return type = ";
|
|
print(out, *node.return_type);
|
|
out << " }\n";
|
|
++options.indent_level;
|
|
for (auto const & arg : node.arguments)
|
|
{
|
|
put_indent(out, options);
|
|
out << "argument { name = \"" << arg.name << "\", type = ";
|
|
print(out, *arg.type);
|
|
out << " }\n";
|
|
}
|
|
put_indent(out, options);
|
|
out << "body\n";
|
|
child(*node.statements);
|
|
--options.indent_level;
|
|
}
|
|
|
|
void apply(foreign_function_declaration const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "foreign function { name = \"" << node.name << "\", return type = ";
|
|
print(out, *node.return_type);
|
|
out << " }\n";
|
|
++options.indent_level;
|
|
for (auto const & arg : node.arguments)
|
|
{
|
|
put_indent(out, options);
|
|
out << "argument { name = \"" << arg.name << "\", type = ";
|
|
print(out, *arg.type);
|
|
out << " }\n";
|
|
}
|
|
--options.indent_level;
|
|
}
|
|
|
|
void apply(return_statement const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "return\n";
|
|
if (node.value)
|
|
child(node.value);
|
|
}
|
|
|
|
void apply(struct_definition const & node)
|
|
{
|
|
put_indent(out, options);
|
|
out << "struct { name = \"" << node.name << "\", size = " << node.layout.size << ", align = " << node.layout.alignment << " }\n";
|
|
for (auto const & field : node.fields)
|
|
{
|
|
put_indent(out, as_child(options));
|
|
out << "field { name = \"" << field.name << "\", type = ";
|
|
print(out, *field.type);
|
|
out << ", offset = " << field.layout.offset << " }\n";
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
void print(std::ostream & out, types::type const & type)
|
|
{
|
|
raw_type_print_visitor{{}, out}.apply(type);
|
|
}
|
|
|
|
void print(std::ostream & out, type const & node)
|
|
{
|
|
type_print_visitor{{}, out}.apply(node);
|
|
}
|
|
|
|
void print(std::ostream & out, expression const & node, print_options const & options)
|
|
{
|
|
expression_print_visitor{{}, out, options}.apply(node);
|
|
}
|
|
|
|
void print(std::ostream & out, statement const & node, print_options const & options)
|
|
{
|
|
statement_print_visitor{{}, out, options}.apply(node);
|
|
}
|
|
|
|
void print(std::ostream & out, statement_list const & node, print_options const & options)
|
|
{
|
|
statement_print_visitor{{}, out, options}.apply(node);
|
|
}
|
|
|
|
}
|