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
This commit is contained in:
Mark
2023-11-21 22:10:58 +01:00
parent b6d708db3d
commit 4144d6cf71
21 changed files with 756 additions and 63 deletions

View File

@@ -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<dyn MersType>,
Arc<dyn Fn(&str, &CheckInfo) -> Result<Arc<dyn MersType>, CheckError> + Send + Sync>,
>,
) -> Self {
self.info_check.scopes[0].types.insert(name, t);
self
}

View File

@@ -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::<Self>()
.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(())
}
}

View File

@@ -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, "<Thread>")
write!(f, "Thread<{}>", to_string_literal(&self.0.to_string(), '>'))
}
}

View File

@@ -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<dyn MersStatement>,
pub as_type: Vec<ParsedType>,
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<super::Local>,
comp: CompInfo,
) -> Result<Box<dyn program::run::MersStatement>, 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
}
}

View File

@@ -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<Vec<ParsedType>, Box<dyn MersStatement>>,
}
impl MersStatement for CustomType {
fn compile_custom(
&self,
info: &mut Info,
comp: CompInfo,
) -> Result<Box<dyn crate::program::run::MersStatement>, 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)
}
}

View File

@@ -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);

View File

@@ -22,20 +22,29 @@ impl MersStatement for Variable {
info: &mut crate::info::Info<super::Local>,
comp: CompInfo,
) -> Result<Box<dyn program::run::MersStatement>, 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()

View File

@@ -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<dyn MersStatement>,
pub as_type: Vec<ParsedType>,
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<Type, CheckError> {
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
}
}

View File

@@ -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<dyn MersType>,
Arc<
dyn Fn(&str, &CheckInfo) -> Result<Arc<dyn MersType>, CheckError>
+ Send
+ Sync,
>,
>,
CheckError,
> + Send
+ Sync,
>,
}
impl MersStatement for CustomType {
fn check_custom(
&self,
info: &mut CheckInfo,
init_to: Option<&Type>,
) -> Result<Type, CheckError> {
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, "<CustomType>")
}
}

View File

@@ -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<CheckLocal>;
pub struct Local {
vars: Vec<Arc<RwLock<Data>>>,
}
#[derive(Default, Clone, Debug)]
#[derive(Default, Clone)]
pub struct CheckLocal {
vars: Vec<Type>,
pub types: HashMap<
String,
Result<
Arc<dyn MersType>,
Arc<dyn Fn(&str, &CheckInfo) -> Result<Arc<dyn MersType>, 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<RwLock<Data>>;
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());

View File

@@ -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<data::Type, super::CheckError> {
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(&nothing));
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(&nothing));
}
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],
)))