Explicitly mark constructor AST nodes
This commit is contained in:
parent
46a1031a08
commit
dea5c18cfd
8 changed files with 221 additions and 179 deletions
|
|
@ -38,7 +38,10 @@ namespace pslang::ast
|
|||
|
||||
struct function_call
|
||||
{
|
||||
expression_ptr function;
|
||||
// Exactly one of these 2 is non-null
|
||||
expression_ptr function; // function call
|
||||
type_ptr type; // type constructor
|
||||
|
||||
std::vector<expression_ptr> arguments;
|
||||
ast::location location;
|
||||
types::type_ptr inferred_type = nullptr;
|
||||
|
|
|
|||
|
|
@ -193,8 +193,17 @@ namespace pslang::ast
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <pslang/ast/expression_visitor.hpp>
|
||||
#include <pslang/ast/statement_visitor.hpp>
|
||||
#include <pslang/ast/error.hpp>
|
||||
#include <pslang/types/type.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
|
@ -138,11 +139,34 @@ namespace pslang::ast
|
|||
apply(*cast_operation.type);
|
||||
}
|
||||
|
||||
void apply(function_call const & function_call)
|
||||
void apply(function_call & function_call)
|
||||
{
|
||||
if (function_call.function)
|
||||
apply(*function_call.function);
|
||||
if (function_call.type)
|
||||
apply(*function_call.type);
|
||||
for (auto const & argument : function_call.arguments)
|
||||
apply(*argument);
|
||||
|
||||
if (auto id = std::get_if<identifier>(function_call.function.get()))
|
||||
{
|
||||
if (auto type = types::builtin_type(id->name))
|
||||
{
|
||||
if (auto unit_type = std::get_if<types::unit_type>(type.get()))
|
||||
function_call.type = std::make_unique<ast::type>(*unit_type);
|
||||
else if (auto primitive_type = std::get_if<types::primitive_type>(type.get()))
|
||||
function_call.type = std::make_unique<ast::type>(*primitive_type);
|
||||
else
|
||||
throw invalid_ast_error("Unknown built-in type \"" + id->name + "\"", get_location(*function_call.function));
|
||||
|
||||
function_call.function = nullptr;
|
||||
}
|
||||
else if (scopes.at(id->level).structs.contains(id->name))
|
||||
{
|
||||
function_call.type = std::make_unique<ast::type>(type_identifier{.name = id->name, .location = id->location, .level = id->level});
|
||||
function_call.function = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void apply(array const & array)
|
||||
|
|
|
|||
|
|
@ -300,65 +300,19 @@ namespace pslang::ast
|
|||
|
||||
void apply(function_call & node)
|
||||
{
|
||||
if (node.function)
|
||||
apply(*node.function);
|
||||
if (node.type)
|
||||
apply(*node.type);
|
||||
for (auto const & argument : node.arguments)
|
||||
apply(*argument);
|
||||
|
||||
if (node.function)
|
||||
{
|
||||
std::string function_name;
|
||||
|
||||
if (auto identifier = std::get_if<ast::identifier>(node.function.get()))
|
||||
{
|
||||
if (types::type_ptr type = types::builtin_type(identifier->name))
|
||||
{
|
||||
if (node.arguments.empty())
|
||||
{
|
||||
node.inferred_type = type;
|
||||
return;
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Cannot create built-in type ";
|
||||
types::print(os, *type);
|
||||
os << ": expected 0 arguments, but got " << node.arguments.size();
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
|
||||
auto & scope = scopes.at(identifier->level);
|
||||
if (auto it = scope.structs.find(identifier->name); it != scope.structs.end())
|
||||
{
|
||||
if (!node.arguments.empty())
|
||||
{
|
||||
if (node.arguments.size() != it->second.fields.size())
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot create struct " << identifier->name << ": expected " << it->second.fields.size() << " arguments, but got " << node.arguments.size();
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < node.arguments.size(); ++i)
|
||||
{
|
||||
auto arg_type = get_type(*node.arguments[i]);
|
||||
if (!types::equal(*arg_type, *it->second.fields[i].type))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot create struct " << identifier->name << ": argument #" << i << " expected to have type ";
|
||||
types::print(os, *it->second.fields[i].type);
|
||||
os << " but got type ";
|
||||
types::print(os, *arg_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
types::named_type type;
|
||||
type.name = identifier->name;
|
||||
type.level = identifier->level;
|
||||
node.inferred_type = std::make_unique<types::type>(std::move(type));
|
||||
return;
|
||||
}
|
||||
|
||||
function_name = identifier->name + " ";
|
||||
}
|
||||
|
||||
auto function_type = get_type(*node.function);
|
||||
|
||||
|
|
@ -398,6 +352,60 @@ namespace pslang::ast
|
|||
|
||||
node.inferred_type = ftype->result;
|
||||
}
|
||||
else if (node.type)
|
||||
{
|
||||
auto type = get_type(*node.type);
|
||||
|
||||
if (types::is_builtin_type(*type))
|
||||
{
|
||||
if (node.arguments.empty())
|
||||
{
|
||||
node.inferred_type = get_type(*node.type);
|
||||
return;
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Cannot create built-in type ";
|
||||
types::print(os, *type);
|
||||
os << ": expected 0 arguments, but got " << node.arguments.size();
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
else if (auto named_type = std::get_if<types::named_type>(type.get()))
|
||||
{
|
||||
auto const & scope = scopes.at(named_type->level);
|
||||
auto const & data = scope.structs.at(named_type->name);
|
||||
|
||||
if (!node.arguments.empty())
|
||||
{
|
||||
if (node.arguments.size() != data.fields.size())
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot create struct " << named_type->name << ": expected " << data.fields.size() << " arguments, but got " << node.arguments.size();
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < node.arguments.size(); ++i)
|
||||
{
|
||||
auto arg_type = get_type(*node.arguments[i]);
|
||||
if (!types::equal(*arg_type, *data.fields[i].type))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot create struct " << named_type->name << ": argument #" << i << " expected to have type ";
|
||||
types::print(os, *data.fields[i].type);
|
||||
os << " but got type ";
|
||||
types::print(os, *arg_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.inferred_type = type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
throw invalid_ast_error("Function call node has neither function nor type", node.location);
|
||||
}
|
||||
|
||||
void apply(array & node)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -507,65 +507,8 @@ namespace pslang::interpreter
|
|||
|
||||
value eval_impl(context & context, ast::function_call const & function_call)
|
||||
{
|
||||
auto identifier = std::get_if<ast::identifier>(function_call.function.get());
|
||||
if (identifier)
|
||||
if (function_call.function)
|
||||
{
|
||||
if (auto type = types::builtin_type(identifier->name))
|
||||
{
|
||||
if (function_call.arguments.empty())
|
||||
return zero_value(context, *type);
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Cannot create built-in type ";
|
||||
types::print(os, *type);
|
||||
os << ": expected 0 arguments, but got " << function_call.arguments.size();
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
|
||||
for (auto it = context.scope_stack.rbegin(); it != context.scope_stack.rend(); ++it)
|
||||
{
|
||||
if (auto jt = it->structs.find(identifier->name); jt != it->structs.end())
|
||||
{
|
||||
if (function_call.arguments.empty())
|
||||
return zero_value(context, types::named_type{.name = identifier->name, .level = identifier->level});
|
||||
|
||||
if (jt->second.fields.size() != function_call.arguments.size())
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot create struct \"" << identifier->name << "\": expected " << jt->second.fields.size() << " fields, got " << function_call.arguments.size();
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
|
||||
std::vector<value> args;
|
||||
for (auto const & expression : function_call.arguments)
|
||||
args.push_back(eval(context, expression));
|
||||
|
||||
std::unordered_map<std::string, value_ptr> fields;
|
||||
|
||||
for (std::size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
auto actual_type = type_of(args[i]);
|
||||
if (!types::equal(actual_type, *jt->second.fields[i].type))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot create struct \"" << identifier->name << "\": field " << jt->second.fields[i].name << " expects type ";
|
||||
types::print(os, *jt->second.fields[i].type);
|
||||
os << " but actual type is ";
|
||||
types::print(os, actual_type);
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
|
||||
fields[jt->second.fields[i].name] = std::make_unique<value>(std::move(args[i]));
|
||||
}
|
||||
|
||||
return struct_value{
|
||||
.struct_type = std::make_unique<types::type>(types::named_type{.name = identifier->name, .level = identifier->level}),
|
||||
.fields = std::move(fields),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto lvalue = eval(context, function_call.function);
|
||||
auto fvalue = std::get_if<function_value>(&lvalue);
|
||||
if (fvalue)
|
||||
|
|
@ -627,6 +570,50 @@ namespace pslang::interpreter
|
|||
os << ": not a function";
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
else if (function_call.type)
|
||||
{
|
||||
auto type = get_type(*function_call.type);
|
||||
if (types::is_builtin_type(*type))
|
||||
{
|
||||
if (function_call.arguments.empty())
|
||||
return zero_value(context, *type);
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Cannot create built-in type ";
|
||||
types::print(os, *type);
|
||||
os << ": expected 0 arguments, but got " << function_call.arguments.size();
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
else if (auto named_type = std::get_if<types::named_type>(type.get()))
|
||||
{
|
||||
auto const & scope = context.scope_stack.at(named_type->level);
|
||||
auto const & data = scope.structs.at(named_type->name);
|
||||
|
||||
if (function_call.arguments.empty())
|
||||
return zero_value(context, *type);
|
||||
|
||||
std::vector<value> args;
|
||||
for (auto const & expression : function_call.arguments)
|
||||
args.push_back(eval(context, expression));
|
||||
|
||||
std::unordered_map<std::string, value_ptr> fields;
|
||||
|
||||
for (std::size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
fields[data.fields[i].name] = std::make_unique<value>(std::move(args[i]));
|
||||
}
|
||||
|
||||
return struct_value{
|
||||
.struct_type = type,
|
||||
.fields = std::move(fields),
|
||||
};
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Unknown type in constructor");
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Function call node has neither function nor type");
|
||||
}
|
||||
|
||||
value eval_impl(context & context, ast::array const & array)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -289,19 +289,19 @@ postfix_expression
|
|||
: base_expression
|
||||
| postfix_expression lbracket expression rbracket { $$ = ast::array_access{std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$}; }
|
||||
| postfix_expression dot name { $$ = ast::field_access{std::make_unique<ast::expression>($1), $3, @$}; }
|
||||
| postfix_expression lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>($1), $3, @$}; }
|
||||
| unit lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"unit"}), $3, @$}; }
|
||||
| bool lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"bool"}), $3, @$}; }
|
||||
| i8 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"i8"}), $3, @$}; }
|
||||
| u8 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"u8"}), $3, @$}; }
|
||||
| i16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"i16"}), $3, @$}; }
|
||||
| u16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"u16"}), $3, @$}; }
|
||||
| i32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"i32"}), $3, @$}; }
|
||||
| u32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"u32"}), $3, @$}; }
|
||||
| i64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"i64"}), $3, @$}; }
|
||||
| u64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"u64"}), $3, @$}; }
|
||||
| f32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"f32"}), $3, @$}; }
|
||||
| f64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"f64"}), $3, @$}; }
|
||||
| postfix_expression lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>($1), nullptr, $3, @$}; }
|
||||
| unit lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::unit_type{}), $3, @$}; }
|
||||
| bool lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::bool_type{}), $3, @$}; }
|
||||
| i8 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::i8_type{}), $3, @$}; }
|
||||
| u8 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::u8_type{}), $3, @$}; }
|
||||
| i16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::i16_type{}), $3, @$}; }
|
||||
| u16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::u16_type{}), $3, @$}; }
|
||||
| i32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::i32_type{}), $3, @$}; }
|
||||
| u32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::u32_type{}), $3, @$}; }
|
||||
| i64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::i64_type{}), $3, @$}; }
|
||||
| u64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::u64_type{}), $3, @$}; }
|
||||
| f32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::f32_type{}), $3, @$}; }
|
||||
| f64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique<ast::type>(types::f64_type{}), $3, @$}; }
|
||||
;
|
||||
|
||||
base_expression
|
||||
|
|
|
|||
|
|
@ -21,5 +21,6 @@ namespace pslang::types
|
|||
bool is_unsigned_integer_type(type const & type);
|
||||
bool is_floating_point_type(type const & type);
|
||||
bool is_numeric_type(type const & type);
|
||||
bool is_builtin_type(type const & type);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,4 +113,14 @@ namespace pslang::types
|
|||
return false;
|
||||
}
|
||||
|
||||
bool is_builtin_type(type const & type)
|
||||
{
|
||||
if (std::get_if<unit_type>(&type))
|
||||
return true;
|
||||
if (std::get_if<primitive_type>(&type))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue