Skip to content

Commit

Permalink
[cpp] implementing lexing, parsing and semal for return statements.
Browse files Browse the repository at this point in the history
  • Loading branch information
harrand committed May 14, 2024
1 parent 479c17b commit f9a0ea4
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 5 deletions.
16 changes: 14 additions & 2 deletions cpp/src/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ struct ast
bool operator==(const function_call& rhs) const = default;
};

struct return_statement
{
std::optional<boxed_expression> expr;
constexpr std::string to_string() const
{
return std::format("return({})", expr.has_value() ? expr.value()->to_string() : "");
}
bool operator==(const return_statement& rhs) const = default;
};


struct expression
{
std::variant
Expand All @@ -104,7 +115,8 @@ struct ast
ast::decimal_literal,
ast::identifier,
ast::variable_declaration,
ast::function_call
ast::function_call,
ast::return_statement
> expr;
bool capped = false;
constexpr std::string to_string() const
Expand Down Expand Up @@ -160,7 +172,7 @@ struct ast

struct node
{
using payload_t = std::variant<std::monostate, integer_literal, decimal_literal, identifier, function_call, expression, variable_declaration, function_definition, block, meta_region>;
using payload_t = std::variant<std::monostate, integer_literal, decimal_literal, identifier, function_call, expression, return_statement, variable_declaration, function_definition, block, meta_region>;
payload_t payload = std::monostate{};
srcloc meta = {};
std::vector<node> children = {};
Expand Down
5 changes: 5 additions & 0 deletions cpp/src/lex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@ namespace lex
{
return token{.t = type::question_mark};
}
else if(data.starts_with("return"))
{
state.advance(5);
return token{.t = type::return_statement, .lexeme = "return"};
}
else if(data.starts_with("$"))
{
return token{.t = type::dollar_sign};
Expand Down
1 change: 1 addition & 0 deletions cpp/src/lex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace lex
operator_ref,
operator_deref,
question_mark,
return_statement,
initialiser,
dollar_sign,
_count,
Expand Down
39 changes: 38 additions & 1 deletion cpp/src/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ namespace parse
bool reduce_from_block(std::size_t offset);
// given an ast::meta_region subtree at the offset, try to reduce its surrounding tokens/atoms into something bigger. returns true on success, false otherwise.
bool reduce_from_meta_region(std::size_t offset);
// given an ast::return_statement subtree at the offset, try to reduce its surrounding tokens/atoms into something bigger. returns true on success, false otherwise.
bool reduce_from_return_statement(std::size_t offset);
// given a non-ast token at the offset, try to reduce it and its surrounding tokens/atoms into something bigger. returns true on success, false otherwise.
bool reduce_from_token(std::size_t offset);

Expand Down Expand Up @@ -287,7 +289,6 @@ namespace parse
retriever retr{*this, offset};
srcloc meta;
auto value = retr.must_retrieve<ast::identifier>(&meta);
bool check = value.iden == "putchar" && (offset + 4 < this->subtrees.size());

if(!retr.avail()){return false;}
auto colon1 = retr.retrieve<lex::token>();
Expand Down Expand Up @@ -664,6 +665,15 @@ namespace parse
return false;
}

bool parser_state::reduce_from_return_statement(std::size_t offset)
{
retriever retr{*this, offset};
srcloc meta;
auto value = retr.must_retrieve<ast::return_statement>(&meta);
retr.reduce_to(ast::expression{.expr = value, .capped = true}, meta);
return true;
}

bool parser_state::reduce_from_token(std::size_t offset)
{
retriever retr{*this, offset};
Expand Down Expand Up @@ -801,6 +811,29 @@ namespace parse
return true;
}
break;
case lex::type::return_statement:
{
if(!retr.avail()){return false;}
auto expr = retr.retrieve<ast::expression>();
if(!expr.has_value())
{
retr.undo();
}
// if we dont have an expression, we must have a semicolon.
// if we do have an expression, we need a semicolon if its not already capped.
if(!expr.has_value() || (expr.has_value() && !expr->capped))
{
if(!retr.avail()){return false;}
auto semicolon = retr.retrieve<lex::token>();
if(!semicolon.has_value() || semicolon->t != lex::type::semicolon)
{
return false;
}
}
retr.reduce_to(ast::return_statement{.expr = expr}, meta);
return true;
}
break;
default: break;
}
if(token_is_unary_operator(value))
Expand Down Expand Up @@ -876,6 +909,10 @@ namespace parse
{
ret = this->reduce_from_function_call(i);
},
[&](ast::return_statement arg)
{
ret = this->reduce_from_return_statement(i);
},
[&](ast::expression arg)
{
ret = this->reduce_from_expression(i);
Expand Down
17 changes: 16 additions & 1 deletion cpp/src/semal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,21 @@ namespace semal
return maybe_function->return_ty;
}

type return_statement(const data& d, const ast::return_statement& payload)
{
const function_t* maybe_parent = d.state.try_find_parent_function(d.tree, d.path);
d.assert_that(maybe_parent != nullptr, "detected return statement outside of a function. return statements are only valid within function implementation blocks.");
if(payload.expr.has_value())
{
type ret = expression(d, *payload.expr.value());
d.assert_that(!maybe_parent->return_ty.is_void(), std::format("return statement was an expression of type \"{}\", but the enclosing function \"{}\" returns {}. do not return an expression.", ret.name(), maybe_parent->name, maybe_parent->return_ty.name()));
d.assert_that(ret == maybe_parent->return_ty, std::format("type of `return` statement \"{}\" does not match the return-type of the enclosing function \"{}\", which is a \"{}\"", ret.name(), maybe_parent->name, maybe_parent->return_ty.name()));
return ret;
}
d.assert_that(maybe_parent->return_ty.is_void(), std::format("detected empty `return` statement within function \"{}\" which doesn't return u0. `return;` is only valid in functions that return `u0`.", maybe_parent->name));
return type::from_primitive(primitive_type::u0);
}

type variable_declaration(const data& d, const ast::variable_declaration& payload)
{
if(d.path.size() <= 1)
Expand Down Expand Up @@ -696,11 +711,11 @@ namespace semal
{
ret = for_statement(d, forst);
},
*/
[&](ast::return_statement returnst)
{
ret = return_statement(d, returnst);
},
*/
[&](ast::variable_declaration decl)
{
ret = variable_declaration(d, decl);
Expand Down
6 changes: 5 additions & 1 deletion samples/scratchpad.psy
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ complicated :: (par1 : i64, par : i8, par3 : u32) -> i64
}

poggers : i64;
morb :: (par0 : i64) -> u0
morb :: (par0 : i64) -> f64
{
poggers1 : i64;
poggers2 : i64;
poggers3 : i64;
//morb2 :: (par0 : i64) -> u0 := extern;
complicated(poggers, dub(5), 3);
complicated(--complicated(1, 2, 3), dub(5), 690);

retval : f64;
return retval;
//return morb(5 * complicated(dub(5)));
}
morb(65);

Expand Down

0 comments on commit f9a0ea4

Please sign in to comment.