Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function types in AST; loop(…), repeat(…) #1085

Merged
merged 8 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion compiler_v4/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,30 @@ pub struct AstTypeParameter {
// Types

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct AstType {
pub enum AstType {
Named(AstNamedType),
Function(AstFunctionType),
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct AstNamedType {
pub name: AstResult<AstString>,
pub type_arguments: Option<AstTypeArguments>,
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct AstFunctionType {
pub parameter_types: Vec<AstFunctionTypeParameterType>,
pub closing_parenthesis_error: Option<AstError>,
pub return_type: AstResult<Box<AstType>>,
pub span: Range<Offset>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct AstFunctionTypeParameterType {
pub type_: Box<AstType>,
pub comma_error: Option<AstError>,
}

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct AstTypeArguments {
pub span: Range<Offset>,
Expand Down Expand Up @@ -496,11 +515,31 @@ impl CollectAstErrors for AstTypeParameter {
}

impl CollectAstErrors for AstType {
fn collect_errors_to(&self, errors: &mut Vec<CompilerError>) {
match &self {
Self::Named(named) => named.collect_errors_to(errors),
Self::Function(function) => function.collect_errors_to(errors),
}
}
}
impl CollectAstErrors for AstNamedType {
fn collect_errors_to(&self, errors: &mut Vec<CompilerError>) {
self.name.collect_errors_to(errors);
self.type_arguments.collect_errors_to(errors);
}
}
impl CollectAstErrors for AstFunctionType {
fn collect_errors_to(&self, errors: &mut Vec<CompilerError>) {
self.parameter_types.collect_errors_to(errors);
self.return_type.collect_errors_to(errors);
}
}
impl CollectAstErrors for AstFunctionTypeParameterType {
fn collect_errors_to(&self, errors: &mut Vec<CompilerError>) {
self.type_.collect_errors_to(errors);
self.comma_error.collect_errors_to(errors);
}
}
impl CollectAstErrors for AstTypeArguments {
fn collect_errors_to(&self, errors: &mut Vec<CompilerError>) {
self.arguments.collect_errors_to(errors);
Expand Down
26 changes: 25 additions & 1 deletion compiler_v4/src/ast_to_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,13 @@ impl<'a> Context<'a> {
return hir::Err;
};

let type_ = match type_ {
AstType::Named(type_) => type_,
AstType::Function(function_type) => {
self.add_error(function_type.span.clone(), "Function type is not a trait");
return hir::Err;
}
};
let Some(name) = type_.name.value() else {
return hir::Err;
};
Expand Down Expand Up @@ -873,6 +880,23 @@ impl<'a> Context<'a> {
return Type::Error;
};

let type_ = match type_ {
AstType::Named(type_) => type_,
AstType::Function(type_) => {
return Type::Function(FunctionType {
parameter_types: type_
.parameter_types
.iter()
.map(|it| self.lower_type(type_parameters, self_base_type, &*it.type_))
.collect(),
return_type: Box::new(self.lower_type(
type_parameters,
self_base_type,
type_.return_type.value().map(AsRef::as_ref),
)),
})
}
};
let Some(name) = type_.name.value() else {
return Type::Error;
};
Expand Down Expand Up @@ -1883,7 +1907,7 @@ impl<'c, 'a> BodyBuilder<'c, 'a> {
Ok(substitutions) => {
assert!(substitutions.is_empty());
self.push_lowered(
name.string.clone(),
None,
ExpressionKind::Call {
function: id,
substitutions,
Expand Down
3 changes: 2 additions & 1 deletion compiler_v4/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ fn debug(options: DebugOptions) -> ProgramResult {
DebugOptions::Hir(options) => {
let source = fs::read_to_string(&options.path).unwrap();
let (hir, errors) = compile_hir(&options.path, &source);
println!("{}", hir.to_text(true));

if !errors.is_empty() {
for error in errors {
error!("{}", error.to_string_with_location(&source));
}
return Err(Exit::CodeContainsErrors);
}
println!("{}", hir.to_text(true));
}
DebugOptions::Mono(options) => {
let source = fs::read_to_string(&options.path).unwrap();
Expand Down
10 changes: 8 additions & 2 deletions compiler_v4/src/mono_to_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,9 @@ impl<'h> Context<'h> {
}

self.lower_expression(declaration_name, *id, expression);
if &*expression.type_ == "Never" {
self.push("// Returns `Never`\n");
}
self.push("\n\n");
}
}
Expand Down Expand Up @@ -810,8 +813,11 @@ impl<'h> Context<'h> {
}

self.lower_body_expressions(declaration_name, &case.body);

self.push(format!("{id} = {};\n", case.body.return_value_id()));
if case.body.return_type() == "Never" {
self.push("// Returns `Never`\n");
} else {
self.push(format!("{id} = {};\n", case.body.return_value_id()));
}

self.push("break;");
}
Expand Down
78 changes: 75 additions & 3 deletions compiler_v4/src/string_to_ast/type_.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,97 @@
use super::{
literal::{closing_bracket, comma, opening_bracket},
literal::{closing_bracket, closing_parenthesis, comma, opening_bracket, opening_parenthesis},
parser::{OptionOfParser, OptionOfParserWithValue, Parser},
whitespace::{AndTrailingWhitespace, ValueAndTrailingWhitespace},
word::raw_identifier,
};
use crate::ast::{AstError, AstString, AstType, AstTypeArgument, AstTypeArguments};
use crate::ast::{
AstError, AstFunctionType, AstFunctionTypeParameterType, AstNamedType, AstString, AstType,
AstTypeArgument, AstTypeArguments,
};
use tracing::instrument;

#[instrument(level = "trace")]
pub fn type_(parser: Parser) -> Option<(Parser, AstType)> {
None.or_else(|| named_type(parser).map(|(parser, it)| (parser, AstType::Named(it))))
.or_else(|| function_type(parser).map(|(parser, it)| (parser, AstType::Function(it))))
}

#[instrument(level = "trace")]
pub fn named_type(parser: Parser) -> Option<(Parser, AstNamedType)> {
let (parser, name) = raw_identifier(parser)?.and_trailing_whitespace();
let (parser, type_arguments) = type_arguments(parser).optional(parser);
Some((
parser,
AstType {
AstNamedType {
name,
type_arguments,
},
))
}

#[instrument(level = "trace")]
pub fn function_type(parser: Parser) -> Option<(Parser, AstFunctionType)> {
let start_offset = parser.offset();
let mut parser = opening_parenthesis(parser)?.and_trailing_whitespace();

// TODO: error on duplicate type parameter names
let mut parameter_types: Vec<AstFunctionTypeParameterType> = vec![];
let mut parser_for_missing_comma_error: Option<Parser> = None;
while let Some((new_parser, parameter_type, new_parser_for_missing_comma_error)) =
function_type_parameter_type(parser.and_trailing_whitespace())
{
if let Some(parser_for_missing_comma_error) = parser_for_missing_comma_error {
parameter_types.last_mut().unwrap().comma_error = Some(
parser_for_missing_comma_error
.error_at_current_offset("This parameter type is missing a comma."),
);
}

parser = new_parser;
parameter_types.push(parameter_type);
parser_for_missing_comma_error = new_parser_for_missing_comma_error;
}
let parser = parser.and_trailing_whitespace();

let (parser, closing_parenthesis_error) = closing_parenthesis(parser)
.unwrap_or_ast_error(
parser,
"These parameter types are missing a closing parenthesis.",
)
.and_trailing_whitespace();

let (parser, return_type) =
type_(parser).unwrap_or_ast_error(parser, "This function type is missing a return type.");

Some((
parser,
AstFunctionType {
parameter_types,
closing_parenthesis_error,
return_type: return_type.map(Box::new),
span: start_offset..parser.offset(),
},
))
}
#[instrument(level = "trace")]
fn function_type_parameter_type<'a>(
parser: Parser,
) -> Option<(Parser, AstFunctionTypeParameterType, Option<Parser>)> {
let (parser, type_) = type_(parser)?.and_trailing_whitespace();

let (parser, parser_for_missing_comma_error) =
comma(parser).map_or((parser, Some(parser)), |parser| (parser, None));

Some((
parser,
AstFunctionTypeParameterType {
type_: Box::new(type_),
comma_error: None,
},
parser_for_missing_comma_error,
))
}

#[instrument(level = "trace")]
pub fn type_arguments<'s>(parser: Parser<'s>) -> Option<(Parser, AstTypeArguments)> {
let start_offset = parser.offset();
Expand Down
20 changes: 19 additions & 1 deletion packages_v5/example.candy
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,21 @@ fun needs(condition: Bool, message: Text) {
}
}

fun loop(body: () Nothing) {
body()
loop(body)
}
fun repeat(times: Int, body: () Nothing) {
needs(times.isNonNegative())
switch times.isGreaterThan(0) {
false => {},
true => {
body()
repeat(times.subtract(1), body)
},
}
}

struct List[T] = builtin
fun listFilled[T](length: Int, item: T) List[T] {
builtinListFilled(length, item)
Expand Down Expand Up @@ -538,11 +553,14 @@ fun main() Int {
print("Length: {list.length().toText()}")
print("[{list.get(0).toText()}, {list.get(1).toText()}, {list.get(2).toText()}]")

print("orld!".endsWith("World!").toText().isEmpty())

let foo = 123
let addCaptured = (x: Int) { x.add(foo) }
print("addCaptured(1) = {addCaptured(1).toText()}")

print("orld!".endsWith("World!").toText().isEmpty())
repeat(3, () { print("Hello, World!") })

0
}

Expand Down
Loading