From 4144d6cf71b1990507f2e0486e677502578107bb Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 21 Nov 2023 22:10:58 +0100 Subject: [PATCH] Type Annotations - Add type annotations: [type] statement - Add type definitions: [[name] type], [[name] := statement] - Add type annotations example (08) - add Quickstart.md, reference it from README --- Quickstart.md | 47 ++++++ README.md | 3 + examples/08_Type_Annotations.mers | 76 ++++++++++ mers_lib/src/data/mod.rs | 12 +- mers_lib/src/data/reference.rs | 6 +- mers_lib/src/errors/mod.rs | 7 + mers_lib/src/info/mod.rs | 14 +- mers_lib/src/parsing/mod.rs | 13 +- mers_lib/src/parsing/statements.rs | 129 +++++++++++++++- mers_lib/src/parsing/types.rs | 138 ++++++++++++++++-- mers_lib/src/program/configs/mod.rs | 33 ++++- mers_lib/src/program/configs/with_list.rs | 13 +- .../program/configs/with_multithreading.rs | 8 +- mers_lib/src/program/parsed/as_type.rs | 38 +++++ mers_lib/src/program/parsed/custom_type.rs | 46 ++++++ mers_lib/src/program/parsed/mod.rs | 5 + mers_lib/src/program/parsed/variable.rs | 27 ++-- mers_lib/src/program/run/as_type.rs | 78 ++++++++++ mers_lib/src/program/run/custom_type.rs | 63 ++++++++ mers_lib/src/program/run/mod.rs | 23 ++- mers_lib/src/program/run/variable.rs | 40 +++-- 21 files changed, 756 insertions(+), 63 deletions(-) create mode 100644 Quickstart.md mode change 100755 => 100644 README.md create mode 100644 examples/08_Type_Annotations.mers create mode 100755 mers_lib/src/program/parsed/as_type.rs create mode 100644 mers_lib/src/program/parsed/custom_type.rs create mode 100644 mers_lib/src/program/run/as_type.rs create mode 100644 mers_lib/src/program/run/custom_type.rs diff --git a/Quickstart.md b/Quickstart.md new file mode 100644 index 0000000..56ca0ad --- /dev/null +++ b/Quickstart.md @@ -0,0 +1,47 @@ +# Hello, World! + +``` +"Hello, World!".println +``` +This calls the `println` function with the argument +`"Hello, World!"` (a string). + +`println` then prints the argument (to stdout), +causing it to show up in your terminal. + +# Hello, Variable! + +To declare a variable in mers, use `:=`: + +``` +greeting := "Hello, World!" +``` + +To retrieve its value, simply write its name. + +``` +greeting.println +``` + +# Conditions + +(todo) + +# Functions + +Functions represent actions. Many represent transformations from some input to some output. +They are written as `argument -> action`: + +``` +a -> if a false else true +``` + +# Types + +(todo) + +## Type Annotations + +## Custom Types + +### Defining Custom Types diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 7025e8a..9f467cf --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ Mers is a high-level programming language. It is designed to be safe (it doesn't crash at runtime) and as simple as possible. +See also: +[Quickstart](Quickstart.md), + ## what makes it special ### Simplicity diff --git a/examples/08_Type_Annotations.mers b/examples/08_Type_Annotations.mers new file mode 100644 index 0000000..396ec25 --- /dev/null +++ b/examples/08_Type_Annotations.mers @@ -0,0 +1,76 @@ +// Change a statement's output type: +// `[type] statement` + +my_list := [List] ().as_list + +&my_list.push(12) +&my_list.push(0.5) + +my_list.println + + +// This can also be used to cause a compiler error on invalid types: +// `[type] statement` +// If `statement` isn't included in `type`, this will cause an error. + +my_list.for_each(v -> + v.try(( + v -> { + [Int] v // this won't compile if v isn't a float + ("Int: ", v).concat + } + v -> { + [Float] v + ("Float: ", v).concat + } + )).println +) + + +// These annotations work on all kinds of statements + +// Function that always returns Int/Float +double := x -> [Int/Float] { + (x, x).sum +} + +// Function that must take Int/Float, but, +// if given Int, returns Int, and if given Float, returns Float. +// Therefore usually nicer than the other `double` +double := x -> { + [Int/Float] x + (x, x).sum +} + + +// Define custom types: +// `[[MyType] TypeDefinition]` + +[[City] (Int, Int, Int)] + +get_coords := city -> [(Int, Int)] { + // only works with values of type City + (_, x, y) := [City] city + // return the coords + (x, y) +} + +test_city := (56000, 127, -12) + +("Coords: ", test_city.get_coords).concat.println + + +// Define custom types that depend on the environment: +// `[[MyType] := some_statement]` +// Note: `some_statement` will be compiled and checked, but never executed, since we only care about its type at compile-time, not its actual value at runtime. + +make_list_maybe_none := (val_1, val_2) -> { + // ListType is List where T is the type of val_1/val_2/() + [[ListType] := (val_1, val_2, ()).as_list] + // return a list with that type + [ListType] (val_1, val_2).as_list +} + +maybe_none_list := ("one", 2).make_list_maybe_none +maybe_none_list.println +maybe_none_list // use `--check only` to see the type: `List` diff --git a/mers_lib/src/data/mod.rs b/mers_lib/src/data/mod.rs index d4a46de..5ab6b1e 100755 --- a/mers_lib/src/data/mod.rs +++ b/mers_lib/src/data/mod.rs @@ -374,16 +374,16 @@ impl Display for Type { if self.types.is_empty() { write!(f, "") } else { - if self.types.len() > 1 { - write!(f, "{{")?; - } + // if self.types.len() > 1 { + // write!(f, "{{")?; + // } write!(f, "{}", self.types[0])?; for t in self.types.iter().skip(1) { write!(f, "/{t}")?; } - if self.types.len() > 1 { - write!(f, "}}")?; - } + // if self.types.len() > 1 { + // write!(f, "}}")?; + // } Ok(()) } } diff --git a/mers_lib/src/data/reference.rs b/mers_lib/src/data/reference.rs index e9dad41..26b7518 100755 --- a/mers_lib/src/data/reference.rs +++ b/mers_lib/src/data/reference.rs @@ -74,6 +74,10 @@ impl Display for Reference { } impl Display for ReferenceT { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "&{}", self.0) + if self.0.types.len() > 1 { + write!(f, "&{{{}}}", self.0) + } else { + write!(f, "&{}", self.0) + } } } diff --git a/mers_lib/src/errors/mod.rs b/mers_lib/src/errors/mod.rs index fe7516a..eb87b69 100644 --- a/mers_lib/src/errors/mod.rs +++ b/mers_lib/src/errors/mod.rs @@ -63,6 +63,13 @@ pub mod error_colors { pub const AssignFrom: Color = InitFrom; pub const AssignTo: Color = InitTo; pub const AssignTargetNonReference: Color = Color::BrightYellow; + + pub const AsTypeStatementWithTooBroadType: Color = InitFrom; + pub const AsTypeTypeAnnotation: Color = InitTo; + + pub const BadCharInTupleType: Color = Color::Red; + pub const BadTypeFromParsed: Color = Color::Blue; + pub const TypeAnnotationNoClosingBracket: Color = Color::Blue; } enum CheckErrorComponent { Message(String), diff --git a/mers_lib/src/info/mod.rs b/mers_lib/src/info/mod.rs index b7241a5..85b6573 100755 --- a/mers_lib/src/info/mod.rs +++ b/mers_lib/src/info/mod.rs @@ -3,26 +3,27 @@ use std::fmt::Debug; #[derive(Clone, Debug)] pub struct Info { pub scopes: Vec, + pub global: L::Global, } impl Info { /// Returns self, but completely empty (even without globals). /// Only use this if you assume this Info will never be used. pub fn neverused() -> Self { - Self { scopes: vec![] } + Self { + scopes: vec![], + global: L::Global::default(), + } } } pub trait Local: Default + Debug { type VariableIdentifier; type VariableData; - // type TypesIdentifier; - // type TypesType; + type Global: Default + Debug + Clone; fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData); fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&Self::VariableData>; fn get_var_mut(&mut self, id: &Self::VariableIdentifier) -> Option<&mut Self::VariableData>; - // fn add_type(&mut self, id: Self::TypesIdentifier, new_type: Self::TypesType); - // fn get_type(&self, id: Self::TypesIdentifier) -> Option<&Self::TypesType>; fn duplicate(&self) -> Self; } @@ -39,6 +40,7 @@ impl Info { impl Local for Info { type VariableIdentifier = L::VariableIdentifier; type VariableData = L::VariableData; + type Global = (); fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { self.scopes.last_mut().unwrap().init_var(id, value) } @@ -51,6 +53,7 @@ impl Local for Info { fn duplicate(&self) -> Self { Self { scopes: self.scopes.iter().map(|v| v.duplicate()).collect(), + global: self.global.clone(), } } } @@ -59,6 +62,7 @@ impl Default for Info { fn default() -> Self { Self { scopes: vec![L::default()], + global: L::Global::default(), } } } diff --git a/mers_lib/src/parsing/mod.rs b/mers_lib/src/parsing/mod.rs index b76995f..6f62403 100755 --- a/mers_lib/src/parsing/mod.rs +++ b/mers_lib/src/parsing/mod.rs @@ -56,6 +56,17 @@ impl Source { let content = std::fs::read_to_string(&path)?; Ok(Self::new(SourceFrom::File(path), content)) } + pub fn new_from_string_raw(source: String) -> Self { + Self { + src_from: SourceFrom::Unspecified, + src_raw_len: source.len(), + src_og: source.clone(), + src: source, + comments: vec![], + i: 0, + sections: vec![], + } + } pub fn new_from_string(source: String) -> Self { Self::new(SourceFrom::Unspecified, source) } @@ -167,7 +178,7 @@ impl Source { Some(ch) } fn word_splitter(ch: char) -> bool { - ch.is_whitespace() || ".,;[](){}".contains(ch) + ch.is_whitespace() || ".,;[](){}/<".contains(ch) } pub fn peek_word(&self) -> &str { self.src[self.i..] diff --git a/mers_lib/src/parsing/statements.rs b/mers_lib/src/parsing/statements.rs index 8ad4a71..9476246 100755 --- a/mers_lib/src/parsing/statements.rs +++ b/mers_lib/src/parsing/statements.rs @@ -4,13 +4,107 @@ use super::{Source, SourcePos}; use crate::{ data::Data, errors::{error_colors, CheckError}, - program::{self, parsed::MersStatement}, + program::{ + self, + parsed::{as_type::AsType, MersStatement}, + }, }; pub fn parse( src: &mut Source, ) -> Result>, CheckError> { src.section_begin("statement".to_string()); + src.skip_whitespace(); + // type annotation: + // [type] statement // force output type to be `type` + // [[name] type] // define `name` as `type` + // [[name] := statement] // define `name` as the type of `statement` (`statement` is never executed) + if matches!(src.peek_char(), Some('[')) { + let pos_in_src = src.get_pos(); + src.next_char(); + return Ok(Some(if matches!(src.peek_char(), Some('[')) { + src.next_char(); + // [[... + let name = src.next_word(); + let name = name.trim().to_owned(); + src.skip_whitespace(); + if !matches!(src.next_char(), Some(']')) { + return Err( + CheckError::new().msg(format!("Expected ']' after type name in [[type_name]]")) + ); + } + src.skip_whitespace(); + if src.peek_word() == ":=" { + src.next_word(); + // [[name] := statement] + let statement = match parse(src) { + Ok(Some(v)) => v, + Ok(None) => { + return Err(CheckError::new() + .src(vec![((pos_in_src, src.get_pos()).into(), None)]) + .msg(format!("EOF after `[...]` type annotation"))) + } + Err(e) => return Err(e), + }; + if !matches!(src.next_char(), Some(']')) { + return Err(CheckError::new().msg(format!( + "Expected ']' after statement in [[type_name] := statement]" + ))); + } + Box::new(program::parsed::custom_type::CustomType { + pos_in_src: (pos_in_src, src.get_pos()).into(), + name, + source: Err(statement), + }) + } else { + // [[name] type] + src.skip_whitespace(); + let as_type = super::types::parse_type(src)?; + src.skip_whitespace(); + if !matches!(src.next_char(), Some(']')) { + return Err(CheckError::new().msg(format!( + "Expected ']' after type definition in [[type_name] type_definition]" + ))); + } + Box::new(program::parsed::custom_type::CustomType { + pos_in_src: (pos_in_src, src.get_pos()).into(), + name, + source: Ok(as_type), + }) + } + } else { + // [type] statement + src.skip_whitespace(); + let type_pos_in_src = src.get_pos(); + let as_type = super::types::parse_type(src)?; + let type_pos_in_src = (type_pos_in_src, src.get_pos()).into(); + src.skip_whitespace(); + if !matches!(src.next_char(), Some(']')) { + return Err(CheckError::new() + .src(vec![( + (pos_in_src, src.get_pos()).into(), + Some(error_colors::TypeAnnotationNoClosingBracket), + )]) + .msg(format!("Missing closing bracket ']' after type annotation"))); + } + let statement = match parse(src) { + Ok(Some(v)) => v, + Ok(None) => { + return Err(CheckError::new() + .src(vec![((pos_in_src, src.get_pos()).into(), None)]) + .msg(format!("EOF after `[...]` type annotation"))) + } + Err(e) => return Err(e), + }; + Box::new(AsType { + pos_in_src: (pos_in_src, src.get_pos()).into(), + statement, + as_type, + type_pos_in_src, + expand_type: true, + }) + })); + } let mut first = if let Some(s) = parse_no_chain(src)? { s } else { @@ -128,8 +222,8 @@ pub fn parse_multiple( pub fn parse_no_chain( src: &mut Source, ) -> Result>, CheckError> { - src.section_begin("statement no chain".to_string()); src.skip_whitespace(); + src.section_begin("statement no chain".to_string()); match src.peek_char() { Some('#') => { let pos_in_src = src.get_pos(); @@ -339,6 +433,14 @@ pub fn parse_no_chain( /// expects to be called *after* a " character is consumed from src pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result { + parse_string_custom_end(src, double_quote, '"', '"') +} +pub fn parse_string_custom_end( + src: &mut Source, + opening: SourcePos, + opening_char: char, + closing_char: char, +) -> Result { let mut s = String::new(); loop { if let Some(ch) = src.next_char() { @@ -350,6 +452,7 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result '\n', Some('t') => '\t', Some('"') => '"', + Some(c) if c == closing_char || c == opening_char => c, Some(o) => { return Err(CheckError::new() .src(vec![( @@ -367,7 +470,7 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result Result String { + val.replace("\\", "\\\\") + .replace("\r", "\\r") + .replace("\n", "\\n") + .replace("\"", "\\\"") + .replace(end, format!("\\{end}").as_str()) +} diff --git a/mers_lib/src/parsing/types.rs b/mers_lib/src/parsing/types.rs index f548d02..1be85dc 100755 --- a/mers_lib/src/parsing/types.rs +++ b/mers_lib/src/parsing/types.rs @@ -1,47 +1,113 @@ +use std::sync::Arc; + +use crate::{ + data::{self, Type}, + errors::{error_colors, CheckError}, +}; + use super::Source; /// multiple types are represented as a `Vec`. +#[derive(Clone, Debug)] pub enum ParsedType { + Reference(Vec), Tuple(Vec>), Type(String), + TypeWithInfo(String, String), } -fn parse_single_type(src: &mut Source) -> Result { +pub fn parse_single_type(src: &mut Source) -> Result { src.section_begin("parse single type".to_string()); src.skip_whitespace(); Ok(match src.peek_char() { + // Reference + Some('&') => { + src.next_char(); + if let Some('{') = src.peek_char() { + src.next_char(); + let types = parse_type(src)?; + let nc = src.next_char(); + if !matches!(nc, Some('}')) { + let nc = if let Some(nc) = nc { + format!("'{nc}'") + } else { + format!("EOF") + }; + return Err(CheckError::new().msg(format!( + "No closing }} in reference type with opening {{! Found {nc} instead" + ))); + } + ParsedType::Reference(types) + } else { + ParsedType::Reference(vec![parse_single_type(src)?]) + } + } // Tuple Some('(') => { + let pos_in_src = src.get_pos(); src.next_char(); src.section_begin("parse tuple's inner types".to_string()); let mut inner = vec![]; - loop { - match src.peek_char() { - Some(')') => { - src.next_char(); - break; + src.skip_whitespace(); + if let Some(')') = src.peek_char() { + // empty tuple, don't even start the loop + } else { + loop { + inner.push(parse_type(src)?); + match src.peek_char() { + Some(')') => { + src.next_char(); + break; + } + Some(',') => { + src.next_char(); + } + _ => { + let ppos = src.get_pos(); + src.next_char(); + return Err(CheckError::new() + .src(vec![ + ((pos_in_src, src.get_pos()).into(), None), + ( + (ppos, src.get_pos()).into(), + Some(error_colors::BadCharInTupleType), + ), + ]) + .msg(format!( + "Unexpected character in tuple type, expected comma ',' or ')'." + ))); + } } - Some(',') => { - src.next_char(); - } - _ => todo!("err: bad char in tuple inner type"), } - inner.push(parse_type(src)?); } ParsedType::Tuple(inner) } - Some(_) => ParsedType::Type(src.next_word().to_lowercase()), + Some(_) => { + let t = src.next_word().to_owned(); + src.skip_whitespace(); + if let Some('<') = src.peek_char() { + let pos = src.get_pos(); + src.next_char(); + ParsedType::TypeWithInfo( + t, + super::statements::parse_string_custom_end(src, pos, '<', '>')?, + ) + } else { + ParsedType::Type(t) + } + } None => todo!(), }) } -fn parse_type(src: &mut Source) -> Result, ()> { +pub fn parse_type(src: &mut Source) -> Result, CheckError> { src.section_begin("parse single type".to_string()); let mut types = vec![]; loop { types.push(parse_single_type(src)?); src.skip_whitespace(); if let Some('/') = src.peek_char() { + src.next_char(); continue; } else { break; @@ -49,3 +115,49 @@ fn parse_type(src: &mut Source) -> Result, ()> { } Ok(types) } +pub fn type_from_parsed( + parsed: &Vec, + info: &crate::program::run::CheckInfo, +) -> Result { + let mut as_type = Type::empty(); + for t in parsed.iter() { + as_type.add(match t { + ParsedType::Reference(inner) => { + let inner = type_from_parsed(inner, info)?; + Arc::new(data::reference::ReferenceT(inner)) + } + ParsedType::Tuple(t) => Arc::new(data::tuple::TupleT( + t.iter() + .map(|v| type_from_parsed(v, info)) + .collect::>()?, + )), + ParsedType::Type(name) => match info + .scopes + .iter() + .find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v)) + { + Some(Ok(t)) => Arc::clone(t), + Some(Err(_)) => { + return Err(CheckError::new().msg(format!( + "Type: specified type without info, but type needs additional info" + ))) + } + None => return Err(CheckError::new().msg(format!("Unknown type '{name}'"))), + }, + ParsedType::TypeWithInfo(name, additional_info) => match info + .scopes + .iter() + .find_map(|scope| scope.types.iter().find(|v| v.0 == name).map(|(_, v)| v)) + { + Some(Ok(t)) => { + return Err(CheckError::new().msg(format!( + "Type: specified type with info, but type {t} doesn't need it" + ))) + } + Some(Err(f)) => f(&additional_info, info)?, + None => return Err(CheckError::new().msg(format!("Unknown type '{name}'"))), + }, + }); + } + Ok(as_type) +} diff --git a/mers_lib/src/program/configs/mod.rs b/mers_lib/src/program/configs/mod.rs index 1ebf805..2b3ddf4 100755 --- a/mers_lib/src/program/configs/mod.rs +++ b/mers_lib/src/program/configs/mod.rs @@ -1,8 +1,10 @@ use std::sync::{Arc, RwLock}; use crate::{ - data::{Data, Type}, + data::{self, Data, MersType}, + errors::CheckError, info::Local, + program::run::CheckInfo, }; mod with_base; @@ -58,11 +60,27 @@ impl Config { } pub fn new() -> Self { + let mut info_check: CheckInfo = Default::default(); + macro_rules! init_d { + ($e:expr) => { + let t = $e; + info_check + .scopes + .last_mut() + .unwrap() + .types + .insert(t.to_string(), Ok(Arc::new(t))); + }; + } + init_d!(data::bool::BoolT); + init_d!(data::int::IntT); + init_d!(data::float::FloatT); + init_d!(data::string::StringT); Self { globals: 0, info_parsed: Default::default(), info_run: Default::default(), - info_check: Default::default(), + info_check, } } @@ -74,8 +92,15 @@ impl Config { self.globals += 1; self } - pub fn add_type(self, _name: String, _t: Type) -> Self { - // TODO! needed for type syntax in the parser, everything else probably(?) works already + pub fn add_type( + mut self, + name: String, + t: Result< + Arc, + Arc Result, CheckError> + Send + Sync>, + >, + ) -> Self { + self.info_check.scopes[0].types.insert(name, t); self } diff --git a/mers_lib/src/program/configs/with_list.rs b/mers_lib/src/program/configs/with_list.rs index 017725e..75b1b16 100755 --- a/mers_lib/src/program/configs/with_list.rs +++ b/mers_lib/src/program/configs/with_list.rs @@ -5,6 +5,7 @@ use std::{ use crate::{ data::{self, Data, MersData, MersType, Type}, + parsing::{statements::to_string_literal, Source}, program::{self, run::CheckInfo}, }; @@ -20,7 +21,10 @@ impl Config { /// `get_mut: fn` like get, but returns a reference to the object pub fn with_list(self) -> Self { // TODO: Type with generics - self.add_type("List".to_string(), Type::new(ListT(Type::empty_tuple()))) + self.add_type("List".to_string(), + Err(Arc::new(|s, i| { + let t = crate::parsing::types::parse_type(&mut Source::new_from_string_raw(s.to_owned()))?; + Ok(Arc::new(ListT(crate::parsing::types::type_from_parsed(&t, i)?)))}))) .add_var( "pop".to_string(), Data::new(data::function::Function { @@ -202,7 +206,10 @@ impl MersType for ListT { .is_some_and(|v| self.0.is_same_type_as(&v.0)) } fn is_included_in_single(&self, target: &dyn MersType) -> bool { - self.is_same_type_as(target) + target + .as_any() + .downcast_ref::() + .is_some_and(|v| self.0.is_included_in(&v.0)) } fn subtypes(&self, acc: &mut Type) { for t in self.0.subtypes_type().types { @@ -234,7 +241,7 @@ impl Display for List { } impl Display for ListT { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[{}]", self.0)?; + write!(f, "List<{}>", to_string_literal(&self.0.to_string(), '>'))?; Ok(()) } } diff --git a/mers_lib/src/program/configs/with_multithreading.rs b/mers_lib/src/program/configs/with_multithreading.rs index f0b4711..c6d11c7 100755 --- a/mers_lib/src/program/configs/with_multithreading.rs +++ b/mers_lib/src/program/configs/with_multithreading.rs @@ -7,6 +7,7 @@ use std::{ use crate::{ data::{self, Data, MersData, MersType, Type}, errors::CheckError, + parsing::{statements::to_string_literal, Source}, program::{self, run::CheckInfo}, }; @@ -19,7 +20,10 @@ impl Config { pub fn with_multithreading(self) -> Self { self.add_type( "Thread".to_string(), - Type::new(ThreadT(Type::empty_tuple())), + Err(Arc::new(|s, i| { + let t = crate::parsing::types::parse_type(&mut Source::new_from_string_raw(s.to_owned()))?; + Ok(Arc::new(ThreadT(crate::parsing::types::type_from_parsed(&t, i)?))) + })), ) .add_var( "thread".to_string(), @@ -170,6 +174,6 @@ impl Display for Thread { } impl Display for ThreadT { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "") + write!(f, "Thread<{}>", to_string_literal(&self.0.to_string(), '>')) } } diff --git a/mers_lib/src/program/parsed/as_type.rs b/mers_lib/src/program/parsed/as_type.rs new file mode 100755 index 0000000..5cdbb3e --- /dev/null +++ b/mers_lib/src/program/parsed/as_type.rs @@ -0,0 +1,38 @@ +use crate::{ + errors::{CheckError, SourceRange}, + parsing::types::ParsedType, + program::{self}, +}; + +use super::{CompInfo, MersStatement}; + +#[derive(Debug)] +pub struct AsType { + pub pos_in_src: SourceRange, + pub statement: Box, + pub as_type: Vec, + pub type_pos_in_src: SourceRange, + pub expand_type: bool, +} + +impl MersStatement for AsType { + fn has_scope(&self) -> bool { + false + } + fn compile_custom( + &self, + info: &mut crate::info::Info, + comp: CompInfo, + ) -> Result, CheckError> { + Ok(Box::new(program::run::as_type::AsType { + pos_in_src: self.pos_in_src, + statement: self.statement.compile(info, comp)?, + as_type: self.as_type.clone(), + type_pos_in_src: self.type_pos_in_src, + expand_type: self.expand_type, + })) + } + fn source_range(&self) -> SourceRange { + self.pos_in_src + } +} diff --git a/mers_lib/src/program/parsed/custom_type.rs b/mers_lib/src/program/parsed/custom_type.rs new file mode 100644 index 0000000..690d958 --- /dev/null +++ b/mers_lib/src/program/parsed/custom_type.rs @@ -0,0 +1,46 @@ +use std::{fmt::Debug, sync::Arc}; + +use crate::{ + errors::{CheckError, SourceRange}, + parsing::types::{type_from_parsed, ParsedType}, +}; + +use super::{CompInfo, Info, MersStatement}; + +pub struct CustomType { + pub pos_in_src: SourceRange, + pub name: String, + pub source: Result, Box>, +} +impl MersStatement for CustomType { + fn compile_custom( + &self, + info: &mut Info, + comp: CompInfo, + ) -> Result, CheckError> { + let src = match &self.source { + Ok(p) => Ok(p.clone()), + Err(s) => Err(s.compile(info, comp)?), + }; + Ok(Box::new(crate::program::run::custom_type::CustomType { + pos_in_src: self.pos_in_src, + name: self.name.clone(), + source: Box::new(move |ci| match &src { + Ok(parsed) => Ok(Ok(Arc::new(type_from_parsed(parsed, ci)?))), + Err(statement) => Ok(Ok(Arc::new(statement.check(&mut ci.clone(), None)?))), + }), + })) + } + fn has_scope(&self) -> bool { + false + } + fn source_range(&self) -> SourceRange { + self.pos_in_src + } +} + +impl Debug for CustomType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "type {} <...>", self.name) + } +} diff --git a/mers_lib/src/program/parsed/mod.rs b/mers_lib/src/program/parsed/mod.rs index 22aafff..34da79a 100755 --- a/mers_lib/src/program/parsed/mod.rs +++ b/mers_lib/src/program/parsed/mod.rs @@ -5,6 +5,8 @@ use crate::{ info, }; +#[cfg(feature = "parse")] +pub mod as_type; #[cfg(feature = "parse")] pub mod assign_to; #[cfg(feature = "parse")] @@ -12,6 +14,8 @@ pub mod block; #[cfg(feature = "parse")] pub mod chain; #[cfg(feature = "parse")] +pub mod custom_type; +#[cfg(feature = "parse")] pub mod function; #[cfg(feature = "parse")] pub mod r#if; @@ -70,6 +74,7 @@ pub struct Local { impl info::Local for Local { type VariableIdentifier = String; type VariableData = (usize, usize); + type Global = (); fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { self.vars_count += 1; self.vars.insert(id, value); diff --git a/mers_lib/src/program/parsed/variable.rs b/mers_lib/src/program/parsed/variable.rs index 35fb165..342494b 100755 --- a/mers_lib/src/program/parsed/variable.rs +++ b/mers_lib/src/program/parsed/variable.rs @@ -22,20 +22,29 @@ impl MersStatement for Variable { info: &mut crate::info::Info, comp: CompInfo, ) -> Result, CheckError> { + let init_and_ignore = comp.is_init && self.var == "_"; if comp.is_init { - info.init_var( - self.var.clone(), - ( - info.scopes.len() - 1, - info.scopes.last().unwrap().vars_count, - ), - ) + if !init_and_ignore { + info.init_var( + self.var.clone(), + ( + info.scopes.len() - 1, + info.scopes.last().unwrap().vars_count, + ), + ); + } } Ok(Box::new(program::run::variable::Variable { pos_in_src: self.pos_in_src, is_init: comp.is_init, - is_ref: comp.is_init || self.is_ref, - var: if let Some(v) = info.get_var(&self.var) { + is_ref_not_ignore: if comp.is_init { + !init_and_ignore + } else { + self.is_ref + }, + var: if init_and_ignore { + (usize::MAX, usize::MAX) + } else if let Some(v) = info.get_var(&self.var) { *v } else { return Err(CheckError::new() diff --git a/mers_lib/src/program/run/as_type.rs b/mers_lib/src/program/run/as_type.rs new file mode 100644 index 0000000..3d02e30 --- /dev/null +++ b/mers_lib/src/program/run/as_type.rs @@ -0,0 +1,78 @@ +use colored::Colorize; + +use crate::{ + data::{Data, MersType, Type}, + errors::{error_colors, CheckError, SourceRange}, + parsing::types::ParsedType, +}; + +use super::MersStatement; + +#[derive(Debug)] +pub struct AsType { + pub pos_in_src: SourceRange, + pub statement: Box, + pub as_type: Vec, + pub type_pos_in_src: SourceRange, + /// if false, only return an error if type doesn't fit, but don't expand type if it fits + pub expand_type: bool, +} + +impl MersStatement for AsType { + fn check_custom( + &self, + info: &mut super::CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if init_to.is_some() { + return Err("can't init to statement type AsType (move type annotations from initialization to statement?)".to_string().into()); + } + let return_type = self.statement.check(info, None)?; + let as_type = + crate::parsing::types::type_from_parsed(&self.as_type, info).map_err(|e| { + CheckError::new() + .src(vec![( + self.type_pos_in_src, + Some(error_colors::BadTypeFromParsed), + )]) + .err(e) + })?; + if !return_type.is_included_in(&as_type) { + return Err(CheckError::new() + .src(vec![ + (self.pos_in_src, None), + ( + self.type_pos_in_src, + Some(error_colors::AsTypeTypeAnnotation), + ), + ( + self.statement.source_range(), + Some(error_colors::AsTypeStatementWithTooBroadType), + ), + ]) + .msg(format!( + "Type must be included in {}, but the actual type {} isn't.", + as_type + .to_string() + .color(error_colors::AsTypeTypeAnnotation), + return_type + .to_string() + .color(error_colors::AsTypeStatementWithTooBroadType) + ))); + } + Ok(if self.expand_type { + as_type.clone() + } else { + return_type + }) + } + fn run_custom(&self, info: &mut super::Info) -> Data { + self.statement.run(info) + } + fn has_scope(&self) -> bool { + false + } + fn source_range(&self) -> SourceRange { + self.pos_in_src + } +} diff --git a/mers_lib/src/program/run/custom_type.rs b/mers_lib/src/program/run/custom_type.rs new file mode 100644 index 0000000..b462c83 --- /dev/null +++ b/mers_lib/src/program/run/custom_type.rs @@ -0,0 +1,63 @@ +use std::{fmt::Debug, sync::Arc}; + +use crate::{ + data::{Data, MersType, Type}, + errors::{CheckError, SourceRange}, +}; + +use super::{CheckInfo, Info, MersStatement}; + +pub struct CustomType { + pub pos_in_src: SourceRange, + pub name: String, + pub source: Box< + dyn Fn( + &CheckInfo, + ) -> Result< + Result< + Arc, + Arc< + dyn Fn(&str, &CheckInfo) -> Result, CheckError> + + Send + + Sync, + >, + >, + CheckError, + > + Send + + Sync, + >, +} + +impl MersStatement for CustomType { + fn check_custom( + &self, + info: &mut CheckInfo, + init_to: Option<&Type>, + ) -> Result { + if init_to.is_some() { + return Err("can't init to `type` statement".to_string().into()); + } + let t = (self.source)(info)?; + info.scopes + .last_mut() + .unwrap() + .types + .insert(self.name.clone(), t); + Ok(Type::empty_tuple()) + } + fn run_custom(&self, _info: &mut Info) -> Data { + Data::empty_tuple() + } + fn has_scope(&self) -> bool { + false + } + fn source_range(&self) -> SourceRange { + self.pos_in_src + } +} + +impl Debug for CustomType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} diff --git a/mers_lib/src/program/run/mod.rs b/mers_lib/src/program/run/mod.rs index 71de367..fa0405f 100755 --- a/mers_lib/src/program/run/mod.rs +++ b/mers_lib/src/program/run/mod.rs @@ -1,14 +1,17 @@ use std::{ + collections::HashMap, fmt::Debug, sync::{Arc, RwLock}, }; use crate::{ - data::{self, Data, Type}, + data::{self, Data, MersType, Type}, errors::{CheckError, SourceRange}, info, }; +#[cfg(feature = "run")] +pub mod as_type; #[cfg(feature = "run")] pub mod assign_to; #[cfg(feature = "run")] @@ -16,6 +19,8 @@ pub mod block; #[cfg(feature = "run")] pub mod chain; #[cfg(feature = "run")] +pub mod custom_type; +#[cfg(feature = "run")] pub mod function; #[cfg(feature = "run")] pub mod r#if; @@ -65,13 +70,26 @@ pub type CheckInfo = info::Info; pub struct Local { vars: Vec>>, } -#[derive(Default, Clone, Debug)] +#[derive(Default, Clone)] pub struct CheckLocal { vars: Vec, + pub types: HashMap< + String, + Result< + Arc, + Arc Result, CheckError> + Send + Sync>, + >, + >, +} +impl Debug for CheckLocal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "CheckLocal {:?}, {:?}", self.vars, self.types.keys()) + } } impl info::Local for Local { type VariableIdentifier = usize; type VariableData = Arc>; + type Global = (); fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false)))); while self.vars.len() <= id { @@ -104,6 +122,7 @@ impl info::Local for Local { impl info::Local for CheckLocal { type VariableIdentifier = usize; type VariableData = Type; + type Global = (); fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { while self.vars.len() <= id { self.vars.push(Type::empty()); diff --git a/mers_lib/src/program/run/variable.rs b/mers_lib/src/program/run/variable.rs index dc0bcea..c3198c8 100755 --- a/mers_lib/src/program/run/variable.rs +++ b/mers_lib/src/program/run/variable.rs @@ -11,7 +11,8 @@ use super::MersStatement; pub struct Variable { pub pos_in_src: SourceRange, pub is_init: bool, - pub is_ref: bool, + // if `is_init` is true, this must also be true unless using the "ignore" `_` pattern + pub is_ref_not_ignore: bool, pub var: (usize, usize), } @@ -25,14 +26,22 @@ impl MersStatement for Variable { init_to: Option<&Type>, ) -> Result { if self.is_init { - while info.scopes[self.var.0].vars.len() <= self.var.1 { - info.scopes[self.var.0].vars.push(Type::empty()); + if self.is_ref_not_ignore { + while info.scopes[self.var.0].vars.len() <= self.var.1 { + info.scopes[self.var.0].vars.push(Type::empty()); + } + info.scopes[self.var.0].vars[self.var.1] = init_to + .expect("variable's is_init was true, but check_custom's assign was None? How?") + .clone(); + } else { + return Ok(Type::new(data::reference::ReferenceT( + init_to + .expect("var's is_init was true, but init_to was None???") + .clone(), + ))); } - info.scopes[self.var.0].vars[self.var.1] = init_to - .expect("variable's is_init was true, but check_custom's assign was None? How?") - .clone(); } - let val = if self.is_ref { + let val = if self.is_ref_not_ignore { Type::new(data::reference::ReferenceT( info.scopes[self.var.0].vars[self.var.1].clone(), )) @@ -43,13 +52,20 @@ impl MersStatement for Variable { } fn run_custom(&self, info: &mut super::Info) -> Data { if self.is_init { - let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false)))); - while info.scopes[self.var.0].vars.len() <= self.var.1 { - info.scopes[self.var.0].vars.push(Arc::clone(¬hing)); + if self.is_ref_not_ignore { + let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false)))); + while info.scopes[self.var.0].vars.len() <= self.var.1 { + info.scopes[self.var.0].vars.push(Arc::clone(¬hing)); + } + info.scopes[self.var.0].vars[self.var.1] = nothing; + } else { + // (reference to) data which will never be referenced again + return Data::new(data::reference::Reference(Arc::new(RwLock::new( + Data::empty_tuple(), + )))); } - info.scopes[self.var.0].vars[self.var.1] = nothing; } - if self.is_ref { + if self.is_ref_not_ignore { Data::new(data::reference::Reference(Arc::clone( &info.scopes[self.var.0].vars[self.var.1], )))