mirror of
https://github.com/Dummi26/mers.git
synced 2025-12-14 03:16:15 +01:00
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:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(), '>'))
|
||||
}
|
||||
}
|
||||
|
||||
38
mers_lib/src/program/parsed/as_type.rs
Executable file
38
mers_lib/src/program/parsed/as_type.rs
Executable 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
|
||||
}
|
||||
}
|
||||
46
mers_lib/src/program/parsed/custom_type.rs
Normal file
46
mers_lib/src/program/parsed/custom_type.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
78
mers_lib/src/program/run/as_type.rs
Normal file
78
mers_lib/src/program/run/as_type.rs
Normal 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
|
||||
}
|
||||
}
|
||||
63
mers_lib/src/program/run/custom_type.rs
Normal file
63
mers_lib/src/program/run/custom_type.rs
Normal 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>")
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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(¬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],
|
||||
)))
|
||||
|
||||
Reference in New Issue
Block a user