diff --git a/compiler_v4/src/ast_to_hir.rs b/compiler_v4/src/ast_to_hir.rs index 76bf90a09..d01632b13 100644 --- a/compiler_v4/src/ast_to_hir.rs +++ b/compiler_v4/src/ast_to_hir.rs @@ -412,7 +412,12 @@ impl<'a> Context<'a> { }, ); self.global_identifiers - .force_insert(signature.name, Named::Functions(vec![id])); + .entry(signature.name) + .and_modify(|it| match it { + Named::Assignment(_) => panic!(), + Named::Functions(function_ids) => function_ids.push(id), + }) + .or_insert_with(|| Named::Functions(vec![id])); } } diff --git a/compiler_v4/src/hir.rs b/compiler_v4/src/hir.rs index 41c4e5ca0..0a181544c 100644 --- a/compiler_v4/src/hir.rs +++ b/compiler_v4/src/hir.rs @@ -332,28 +332,32 @@ impl NamedType { // Builtin types #[must_use] - pub fn array(t: impl Into) -> Self { - Self::new("Array", [t.into()]) - } - #[must_use] pub fn int() -> Self { Self::new("Int", []) } #[must_use] + pub fn list(t: impl Into) -> Self { + Self::new("List", [t.into()]) + } + #[must_use] pub fn text() -> Self { Self::new("Text", []) } // Standard library types #[must_use] - pub fn nothing() -> Self { - Self::new("Nothing", []) + pub fn maybe(t: impl Into) -> Self { + Self::new("Maybe", [t.into()]) } #[must_use] pub fn never() -> Self { Self::new("Never", []) } #[must_use] + pub fn nothing() -> Self { + Self::new("Nothing", []) + } + #[must_use] pub fn ordering() -> Self { Self::new("Ordering", []) } @@ -418,8 +422,10 @@ impl Type { #[must_use] pub fn equals_lenient(&self, other: &Self) -> bool { + #[allow(clippy::redundant_guards)] match (self, other) { (Self::Error, _) | (_, Self::Error) => true, + (Self::Named(NamedType { box name, .. }), _) if name == "Never" => true, (Self::Named(from), Self::Named(to)) => { from.name == to.name && from @@ -710,12 +716,22 @@ pub struct SwitchCase { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, VariantArray)] #[strum(serialize_all = "camelCase")] pub enum BuiltinFunction { - ArrayFilled, - ArrayLength, IntAdd, IntCompareTo, IntSubtract, IntToText, + ListFilled, + ListGet, + ListInsert, + ListLength, + ListOf0, + ListOf1, + ListOf2, + ListOf3, + ListOf4, + ListOf5, + ListRemoveAt, + ListReplace, Panic, Print, TextConcat, @@ -729,26 +745,6 @@ impl BuiltinFunction { #[must_use] pub fn signature(self) -> BuiltinFunctionSignature { match self { - Self::ArrayFilled => BuiltinFunctionSignature { - name: "builtinArrayFilled".into(), - type_parameters: ["T".into()].into(), - parameters: [ - ("length".into(), NamedType::int().into()), - ("item".into(), ParameterType::new("T").into()), - ] - .into(), - return_type: NamedType::array(ParameterType::new("T")).into(), - }, - Self::ArrayLength => BuiltinFunctionSignature { - name: "builtinArrayLength".into(), - type_parameters: ["T".into()].into(), - parameters: [( - "array".into(), - NamedType::array(ParameterType::new("T")).into(), - )] - .into(), - return_type: NamedType::int().into(), - }, Self::IntAdd => BuiltinFunctionSignature { name: "builtinIntAdd".into(), type_parameters: Box::default(), @@ -785,6 +781,138 @@ impl BuiltinFunction { parameters: [("int".into(), NamedType::int().into())].into(), return_type: NamedType::text().into(), }, + Self::ListFilled => BuiltinFunctionSignature { + name: "builtinListFilled".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ("length".into(), NamedType::int().into()), + ("item".into(), ParameterType::new("T").into()), + ] + .into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListGet => BuiltinFunctionSignature { + name: "builtinListGet".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ( + "list".into(), + NamedType::list(ParameterType::new("T")).into(), + ), + ("index".into(), NamedType::int().into()), + ] + .into(), + return_type: NamedType::maybe(ParameterType::new("T")).into(), + }, + Self::ListInsert => BuiltinFunctionSignature { + name: "builtinListInsert".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ( + "list".into(), + NamedType::list(ParameterType::new("T")).into(), + ), + ("index".into(), NamedType::int().into()), + ("item".into(), ParameterType::new("T").into()), + ] + .into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListLength => BuiltinFunctionSignature { + name: "builtinListLength".into(), + type_parameters: ["T".into()].into(), + parameters: [( + "list".into(), + NamedType::list(ParameterType::new("T")).into(), + )] + .into(), + return_type: NamedType::int().into(), + }, + Self::ListOf0 => BuiltinFunctionSignature { + name: "builtinListOf".into(), + type_parameters: ["T".into()].into(), + parameters: Box::default(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListOf1 => BuiltinFunctionSignature { + name: "builtinListOf".into(), + type_parameters: ["T".into()].into(), + parameters: [("item0".into(), ParameterType::new("T").into())].into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListOf2 => BuiltinFunctionSignature { + name: "builtinListOf".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ("item0".into(), ParameterType::new("T").into()), + ("item1".into(), ParameterType::new("T").into()), + ] + .into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListOf3 => BuiltinFunctionSignature { + name: "builtinListOf".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ("item0".into(), ParameterType::new("T").into()), + ("item1".into(), ParameterType::new("T").into()), + ("item2".into(), ParameterType::new("T").into()), + ] + .into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListOf4 => BuiltinFunctionSignature { + name: "builtinListOf".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ("item0".into(), ParameterType::new("T").into()), + ("item1".into(), ParameterType::new("T").into()), + ("item2".into(), ParameterType::new("T").into()), + ("item3".into(), ParameterType::new("T").into()), + ] + .into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListOf5 => BuiltinFunctionSignature { + name: "builtinListOf".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ("item0".into(), ParameterType::new("T").into()), + ("item1".into(), ParameterType::new("T").into()), + ("item2".into(), ParameterType::new("T").into()), + ("item3".into(), ParameterType::new("T").into()), + ("item4".into(), ParameterType::new("T").into()), + ] + .into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListRemoveAt => BuiltinFunctionSignature { + name: "builtinListRemoveAt".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ( + "list".into(), + NamedType::list(ParameterType::new("T")).into(), + ), + ("index".into(), NamedType::int().into()), + ] + .into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, + Self::ListReplace => BuiltinFunctionSignature { + name: "builtinListReplace".into(), + type_parameters: ["T".into()].into(), + parameters: [ + ( + "list".into(), + NamedType::list(ParameterType::new("T")).into(), + ), + ("index".into(), NamedType::int().into()), + ("newItem".into(), ParameterType::new("T").into()), + ] + .into(), + return_type: NamedType::list(ParameterType::new("T")).into(), + }, Self::Panic => BuiltinFunctionSignature { name: "builtinPanic".into(), type_parameters: Box::default(), diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index 9710c9df8..6df0d61b5 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -1,6 +1,6 @@ use crate::{ ast_to_hir::TypeUnifier, - hir::{self, Hir, NamedType, ParameterType, Type}, + hir::{self, BuiltinFunction, Hir, NamedType, ParameterType, Type}, id::IdGenerator, mono::{self, Mono}, type_solver::{goals::SolverSolution, values::SolverVariable}, @@ -32,6 +32,7 @@ impl<'h> Context<'h> { functions: FxHashMap::default(), }; let main_function = context.lower_function(hir.main_function_id, &FxHashMap::default()); + context.lower_function(BuiltinFunction::Panic.id(), &FxHashMap::default()); Mono { type_declarations: context .type_declarations @@ -56,7 +57,11 @@ impl<'h> Context<'h> { } fn lower_assignment(&mut self, id: hir::Id) -> Box { - let assignment = &self.hir.assignments[&id]; + let assignment = &self + .hir + .assignments + .get(&id) + .unwrap_or_else(|| panic!("Unknown assignment: {id}")); let name = assignment.name.to_string().into_boxed_str(); match self.assignments.entry(name.clone()) { Entry::Occupied(_) => return name.clone(), @@ -82,12 +87,16 @@ impl<'h> Context<'h> { let function = self.hir.functions.get(&id).unwrap_or_else(|| { let impl_ = self.find_impl_for(id, &substitutions); let function = &impl_.functions[&id]; - if let Type::Parameter(parameter_type) = &impl_.type_ { - let self_type = substitutions[&ParameterType::self_type()].clone(); - substitutions - .to_mut() - .force_insert(parameter_type.clone(), self_type); + + let mut unifier = TypeUnifier::new(&impl_.type_parameters); + assert!(unifier + .unify(&substitutions[&ParameterType::self_type()], &impl_.type_) + .unwrap()); + let substitutions = substitutions.to_mut(); + for (parameter_type, type_) in unifier.finish().unwrap() { + substitutions.force_insert(parameter_type, type_); } + function }); @@ -123,7 +132,18 @@ impl<'h> Context<'h> { let (parameters, _) = BodyBuilder::build(self, &substitutions, |builder| { builder.add_parameters(&function.signature.parameters); }); - (parameters, mono::BodyOrBuiltin::Builtin(*builtin_function)) + ( + parameters, + mono::BodyOrBuiltin::Builtin { + builtin_function: *builtin_function, + substitutions: substitutions + .iter() + .map(|(parameter_type, type_)| { + (parameter_type.name.clone(), self.lower_type(type_)) + }) + .collect(), + }, + ) } }; let return_type = function.signature.return_type.substitute(&substitutions); @@ -504,12 +524,20 @@ impl<'c, 'h> BodyBuilder<'c, 'h> { let enum_ = self.lower_type(enum_); let cases = cases .iter() - .map(|case| mono::SwitchCase { - variant: case.variant.clone(), - value_id: case.value_id.map(|id| self.lower_id(id)), - body: BodyBuilder::build_inner(self, |builder| { - builder.lower_expressions(&case.body.expressions); - }), + .map(|case| { + let value_ids = case + .value_id + .map(|hir_id| (hir_id, self.id_generator.generate())); + mono::SwitchCase { + variant: case.variant.clone(), + value_id: value_ids.map(|(_, mir_id)| mir_id), + body: BodyBuilder::build_inner(self, |builder| { + if let Some((hir_id, mir_id)) = value_ids { + builder.id_mapping.force_insert(hir_id, mir_id); + } + builder.lower_expressions(&case.body.expressions); + }), + } }) .collect(); self.push( diff --git a/compiler_v4/src/mono.rs b/compiler_v4/src/mono.rs index bb7beb05f..7145faffb 100644 --- a/compiler_v4/src/mono.rs +++ b/compiler_v4/src/mono.rs @@ -46,7 +46,7 @@ pub struct Assignment { pub body: Body, } -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Function { pub parameters: Box<[Parameter]>, pub return_type: Box, @@ -59,10 +59,13 @@ pub struct Parameter { pub type_: Box, } -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum BodyOrBuiltin { Body(Body), - Builtin(BuiltinFunction), + Builtin { + builtin_function: BuiltinFunction, + substitutions: FxHashMap, Box>, + }, } #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct Body { diff --git a/compiler_v4/src/mono_to_c.rs b/compiler_v4/src/mono_to_c.rs index be1f9d3ff..17aa46a5f 100644 --- a/compiler_v4/src/mono_to_c.rs +++ b/compiler_v4/src/mono_to_c.rs @@ -77,15 +77,15 @@ impl<'h> Context<'h> { type_arguments, } => { match name.as_ref() { - "Array" => { - assert_eq!(type_arguments.len(), 1); - self.push("uint64_t length;\n"); - self.push(format!("{}** values;\n", type_arguments[0])); - } "Int" => { assert!(type_arguments.is_empty()); self.push("uint64_t value;\n"); } + "List" => { + assert_eq!(type_arguments.len(), 1); + self.push("uint64_t length;\n"); + self.push(format!("{}** values;\n", type_arguments[0])); + } "Text" => { assert!(type_arguments.is_empty()); self.push("char* value;\n"); @@ -176,29 +176,12 @@ impl<'h> Context<'h> { } fn lower_body_or_builtin(&mut self, function: &Function) { match &function.body { - BodyOrBuiltin::Builtin(builtin_function) => { + BodyOrBuiltin::Builtin { + builtin_function, + substitutions, + } => { self.push("// builtin function\n"); match builtin_function { - BuiltinFunction::ArrayFilled => self.push(format!( - "\ - {array_type}* result_pointer = malloc(sizeof({array_type})); - result_pointer->length = {length}->value; - result_pointer->values = malloc({length}->value * sizeof({array_type})); - for (uint64_t i = 0; i < {length}->value; i++) {{ - result_pointer->values[i] = {item}; - }} - return result_pointer;", - array_type = function.return_type, - length = function.parameters[0].id, - item = function.parameters[1].id, - )), - BuiltinFunction::ArrayLength => self.push(format!( - "\ - Int* result_pointer = malloc(sizeof(Int)); - result_pointer->value = {array}->length; - return result_pointer;", - array = function.parameters[0].id, - )), BuiltinFunction::IntAdd => self.push(format!( "\ Int* result_pointer = malloc(sizeof(Int)); @@ -236,6 +219,199 @@ impl<'h> Context<'h> { return result_pointer;", int = function.parameters[0].id, )), + BuiltinFunction::ListFilled => self.push(format!( + "\ + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = {length}->value; + result_pointer->values = malloc({length}->value * sizeof({item_type})); + for (uint64_t i = 0; i < {length}->value; i++) {{ + result_pointer->values[i] = {item}; + }} + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + length = function.parameters[0].id, + item = function.parameters[1].id, + )), + BuiltinFunction::ListGet => self.push(format!( + "\ + {return_type}* result_pointer = malloc(sizeof({return_type})); + if (0 <= {index}->value && {index}->value < {list}->length) {{ + result_pointer->variant = {return_type}_some; + result_pointer->value.some = {list}->values[{index}->value]; + }} else {{ + result_pointer->variant = {return_type}_none; + }} + return result_pointer;", + return_type = function.return_type, + list = function.parameters[0].id, + index = function.parameters[1].id, + )), + BuiltinFunction::ListInsert => self.push(format!( + "\ + if (0 > {index}->value || {index}->value > {list}->length) {{ + char* message_format = \"Index out of bounds: Tried inserting at index %ld in list of length %ld.\"; + int length = snprintf(NULL, 0, message_format, {index}->value, {list}->length); + char *message = malloc(length + 1); + snprintf(message, length + 1, message_format, {index}->value, {list}->length); + + Text *message_pointer = malloc(sizeof(Text)); + message_pointer->value = message; + builtinPanic$$Text(message_pointer); + }} + + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = {list}->length + 1; + result_pointer->values = malloc(result_pointer->length * sizeof({item_type})); + memcpy(result_pointer->values, {list}->values, {index}->value * sizeof({item_type})); + result_pointer->values[{index}->value] = {item}; + memcpy(result_pointer->values + {index}->value + 1, {list}->values + {index}->value, ({list}->length - {index}->value) * sizeof({item_type})); + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + list = function.parameters[0].id, + index = function.parameters[1].id, + item = function.parameters[2].id, + )), + BuiltinFunction::ListLength => self.push(format!( + "\ + Int* result_pointer = malloc(sizeof(Int)); + result_pointer->value = {list}->length; + return result_pointer;", + list = function.parameters[0].id, + )), + BuiltinFunction::ListOf0 => self.push(format!( + "\ + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = 0; + result_pointer->values = nullptr; + return result_pointer;", + list_type = function.return_type, + )), + BuiltinFunction::ListOf1 => self.push(format!( + "\ + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = 1; + result_pointer->values = malloc(sizeof({item_type})); + result_pointer->values[0] = {item0}; + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + item0 = function.parameters[0].id, + )), + BuiltinFunction::ListOf2 => self.push(format!( + "\ + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = 2; + result_pointer->values = malloc(2 * sizeof({item_type})); + result_pointer->values[0] = {item0}; + result_pointer->values[1] = {item1}; + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + item0 = function.parameters[0].id, + item1 = function.parameters[1].id, + )), + BuiltinFunction::ListOf3 => self.push(format!( + "\ + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = 3; + result_pointer->values = malloc(3 * sizeof({item_type})); + result_pointer->values[0] = {item0}; + result_pointer->values[1] = {item1}; + result_pointer->values[2] = {item2}; + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + item0 = function.parameters[0].id, + item1 = function.parameters[1].id, + item2 = function.parameters[2].id, + )), + BuiltinFunction::ListOf4 => self.push(format!( + "\ + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = 4; + result_pointer->values = malloc(4 * sizeof({item_type})); + result_pointer->values[0] = {item0}; + result_pointer->values[1] = {item1}; + result_pointer->values[2] = {item2}; + result_pointer->values[3] = {item3}; + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + item0 = function.parameters[0].id, + item1 = function.parameters[1].id, + item2 = function.parameters[2].id, + item3 = function.parameters[3].id, + )), + BuiltinFunction::ListOf5 => self.push(format!( + "\ + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = 5; + result_pointer->values = malloc(5 * sizeof({item_type})); + result_pointer->values[0] = {item0}; + result_pointer->values[1] = {item1}; + result_pointer->values[2] = {item2}; + result_pointer->values[3] = {item3}; + result_pointer->values[4] = {item4}; + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + item0 = function.parameters[0].id, + item1 = function.parameters[1].id, + item2 = function.parameters[2].id, + item3 = function.parameters[3].id, + item4 = function.parameters[4].id, + )), + BuiltinFunction::ListRemoveAt => self.push(format!( + "\ + if (0 > {index}->value || {index}->value >= {list}->length) {{ + char* message_format = \"Index out of bounds: Tried removing item at index %ld from list of length %ld.\"; + int length = snprintf(NULL, 0, message_format, {index}->value, {list}->length); + char *message = malloc(length + 1); + snprintf(message, length + 1, message_format, {index}->value, {list}->length); + + Text *message_pointer = malloc(sizeof(Text)); + message_pointer->value = message; + builtinPanic$$Text(message_pointer); + }} + + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = {list}->length - 1; + result_pointer->values = malloc(result_pointer->length * sizeof({item_type})); + memcpy(result_pointer->values, {list}->values, {index}->value * sizeof({item_type})); + memcpy(result_pointer->values + {index}->value, {list}->values + {index}->value + 1, ({list}->length - {index}->value - 1) * sizeof({item_type})); + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + list = function.parameters[0].id, + index = function.parameters[1].id, + )), + BuiltinFunction::ListReplace => self.push(format!( + "\ + if (0 > {index}->value || {index}->value >= {list}->length) {{ + char* message_format = \"Index out of bounds: Tried replacing index %ld in list of length %ld.\"; + int length = snprintf(NULL, 0, message_format, {index}->value, {list}->length); + char *message = malloc(length + 1); + snprintf(message, length + 1, message_format, {index}->value, {list}->length); + + Text *message_pointer = malloc(sizeof(Text)); + message_pointer->value = message; + builtinPanic$$Text(message_pointer); + }} + + {list_type}* result_pointer = malloc(sizeof({list_type})); + result_pointer->length = {list}->length; + result_pointer->values = malloc(result_pointer->length * sizeof({item_type})); + memcpy(result_pointer->values, {list}->values, {list}->length * sizeof({item_type})); + result_pointer->values[{index}->value] = {new_item}; + return result_pointer;", + item_type = substitutions["T"], + list_type = function.return_type, + list = function.parameters[0].id, + index = function.parameters[1].id, + new_item = function.parameters[2].id, + )), BuiltinFunction::Panic => { self.push(format!( "\ @@ -383,7 +559,10 @@ impl<'h> Context<'h> { .value_type .as_ref() .unwrap(); - self.push(format!("{variant_type}* {value_id} = {value}->value;\n")); + self.push(format!( + "{variant_type}* {value_id} = {value}->value.{};\n", + case.variant, + )); } self.lower_body_expressions(&case.body); diff --git a/compiler_v4/src/string_to_ast/declarations.rs b/compiler_v4/src/string_to_ast/declarations.rs index 5e0d0e1d7..654092ad3 100644 --- a/compiler_v4/src/string_to_ast/declarations.rs +++ b/compiler_v4/src/string_to_ast/declarations.rs @@ -139,8 +139,8 @@ fn enum_<'a>(parser: Parser) -> Option<(Parser, AstEnum)> { .unwrap_or_ast_error_result(parser, "This enum is missing a name."); let (parser, type_parameters) = type_parameters(parser) - .and_trailing_whitespace() - .optional(parser); + .optional(parser) + .and_trailing_whitespace(); let (mut parser, opening_curly_brace_error) = opening_curly_brace(parser) .and_trailing_whitespace() diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 0bbfad42d..6f8ae94e2 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -108,14 +108,77 @@ impl Text: ToText { } } -struct Array[T] = builtin -fun arrayFilled[T](length: Int, item: T) Array[T] { - builtinArrayFilled(length, item) -} -fun length[T](array: Array[T]) Int { - builtinArrayLength(array) +fun panic(message: Text) Never { + builtinPanic(message) } +struct List[T] = builtin +fun listFilled[T](length: Int, item: T) List[T] { + builtinListFilled(length, item) +} +# TODO: listGenerate(…) +fun listOf[T]() List[T] { + builtinListOf[T]() +} +fun listOf[T](item0: T) List[T] { + builtinListOf(item0) +} +fun listOf[T](item0: T, item1: T) List[T] { + builtinListOf(item0, item1) +} +fun listOf[T](item0: T, item1: T, item2) List[T] { + builtinListOf(item0, item1, item2) +} +fun listOf[T](item0: T, item1: T, item2: T, item3: T) List[T] { + builtinListOf(item0, item1, item2, item3) +} +fun listOf[T](item0: T, item1: T, item2: T, item3: T, item4: T) List[T] { + builtinListOf(item0, item1, item2, item3, item4) +} +fun length[T](list: List[T]) Int { + builtinListLength(list) +} +fun isEmpty[T](list: List[T]) Bool { + list.length().equals(0) +} +fun lastIndex[T](list: List[T]) Maybe[Int] { + switch list.isEmpty() { + true => none[Int](), + false => some(list.length().subtract(1)), + } +} +fun get[T](list: List[T], index: Int) Maybe[T] { + builtinListGet(list, index) +} +fun single[T](list: List[T]) Maybe[T] { + switch list.length().equals(1) { + true => list.get(0), + false => none[T](), + } +} +fun first[T](list: List[T]) Maybe[T] { + list.get(0) +} +fun last[T](list: List[T]) Maybe[T] { + list.get(list.length().subtract(1)) +} +fun insert[T](list: List[T], index: Int, item: T) List[T] { + builtinListInsert(list, index, item) +} +fun prepend[T](list: List[T], item: T) List[T] { + insert(list, 0, item) +} +fun append[T](list: List[T], item: T) List[T] { + insert(list, list.length(), item) +} +fun replace[T](list: List[T], index: Int, item: T) List[T] { + builtinListReplace(list, index, item) +} +# TODO: list.update(…) +fun removeAt[T](list: List[T], index: Int) List[T] { + builtinListRemoveAt(list, index) +} +# TODO: list.getRange(…), .concatenate(…), .firstIndexWhere(…), .firstWhere(…), .firstIndexOf(…), .lastIndexWhere(…), .lastWhere(…), .lastIndexOf(…) fun print[T: ToText](t: T) { builtinPrint(t.toText()) } @@ -147,14 +210,20 @@ fun some[T](value: T) Maybe[T] { fun none[T]() Maybe[T] { Maybe.none[T]() } -# impl[T] Maybe[T]: ToText { -# fun toText(self: Maybe[T]) Text { -# switch self { -# some(value) => "some({value.toText()})", -# none => "none()", -# } -# } -# } +fun unwrap[T](self: Maybe[T]) T { + switch self { + some(value) => value, + none => panic("`unwrap()` called on `none()`"), + } +} +impl[T: ToText] Maybe[T]: ToText { + fun toText(self: Maybe[T]) Text { + switch self { + some(value) => "some({value.toText()})", + none => "none()", + } + } +} enum MyEnum { foo: Int, @@ -227,6 +296,10 @@ fun main() Int { print(true) print(false) print("1.equals(2): {1.equals(2).toText()}") + + let list = listOf(0, 1).insert(1, 2).replace(0, 3).removeAt(2) + print("Length: {list.length().toText()}") + print("[{list.get(0).toText()}, {list.get(1).toText()}, {list.get(2).toText()}]") 0 }