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

47
Quickstart.md Normal file
View File

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

3
README.md Executable file → Normal file
View File

@ -3,6 +3,9 @@
Mers is a high-level programming language. Mers is a high-level programming language.
It is designed to be safe (it doesn't crash at runtime) and as simple as possible. 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 ## what makes it special
### Simplicity ### Simplicity

View File

@ -0,0 +1,76 @@
// Change a statement's output type:
// `[type] statement`
my_list := [List<Int/Float>] ().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<T> 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<String/Int/()>`

View File

@ -374,16 +374,16 @@ impl Display for Type {
if self.types.is_empty() { if self.types.is_empty() {
write!(f, "<unreachable>") write!(f, "<unreachable>")
} else { } else {
if self.types.len() > 1 { // if self.types.len() > 1 {
write!(f, "{{")?; // write!(f, "{{")?;
} // }
write!(f, "{}", self.types[0])?; write!(f, "{}", self.types[0])?;
for t in self.types.iter().skip(1) { for t in self.types.iter().skip(1) {
write!(f, "/{t}")?; write!(f, "/{t}")?;
} }
if self.types.len() > 1 { // if self.types.len() > 1 {
write!(f, "}}")?; // write!(f, "}}")?;
} // }
Ok(()) Ok(())
} }
} }

View File

@ -74,6 +74,10 @@ impl Display for Reference {
} }
impl Display for ReferenceT { impl Display for ReferenceT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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)
}
} }
} }

View File

@ -63,6 +63,13 @@ pub mod error_colors {
pub const AssignFrom: Color = InitFrom; pub const AssignFrom: Color = InitFrom;
pub const AssignTo: Color = InitTo; pub const AssignTo: Color = InitTo;
pub const AssignTargetNonReference: Color = Color::BrightYellow; 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 { enum CheckErrorComponent {
Message(String), Message(String),

View File

@ -3,26 +3,27 @@ use std::fmt::Debug;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Info<L: Local> { pub struct Info<L: Local> {
pub scopes: Vec<L>, pub scopes: Vec<L>,
pub global: L::Global,
} }
impl<L: Local> Info<L> { impl<L: Local> Info<L> {
/// Returns self, but completely empty (even without globals). /// Returns self, but completely empty (even without globals).
/// Only use this if you assume this Info will never be used. /// Only use this if you assume this Info will never be used.
pub fn neverused() -> Self { pub fn neverused() -> Self {
Self { scopes: vec![] } Self {
scopes: vec![],
global: L::Global::default(),
}
} }
} }
pub trait Local: Default + Debug { pub trait Local: Default + Debug {
type VariableIdentifier; type VariableIdentifier;
type VariableData; type VariableData;
// type TypesIdentifier; type Global: Default + Debug + Clone;
// type TypesType;
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData); fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData);
fn get_var(&self, id: &Self::VariableIdentifier) -> Option<&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 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; fn duplicate(&self) -> Self;
} }
@ -39,6 +40,7 @@ impl<L: Local> Info<L> {
impl<L: Local> Local for Info<L> { impl<L: Local> Local for Info<L> {
type VariableIdentifier = L::VariableIdentifier; type VariableIdentifier = L::VariableIdentifier;
type VariableData = L::VariableData; type VariableData = L::VariableData;
type Global = ();
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
self.scopes.last_mut().unwrap().init_var(id, value) self.scopes.last_mut().unwrap().init_var(id, value)
} }
@ -51,6 +53,7 @@ impl<L: Local> Local for Info<L> {
fn duplicate(&self) -> Self { fn duplicate(&self) -> Self {
Self { Self {
scopes: self.scopes.iter().map(|v| v.duplicate()).collect(), scopes: self.scopes.iter().map(|v| v.duplicate()).collect(),
global: self.global.clone(),
} }
} }
} }
@ -59,6 +62,7 @@ impl<L: Local> Default for Info<L> {
fn default() -> Self { fn default() -> Self {
Self { Self {
scopes: vec![L::default()], scopes: vec![L::default()],
global: L::Global::default(),
} }
} }
} }

View File

@ -56,6 +56,17 @@ impl Source {
let content = std::fs::read_to_string(&path)?; let content = std::fs::read_to_string(&path)?;
Ok(Self::new(SourceFrom::File(path), content)) 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 { pub fn new_from_string(source: String) -> Self {
Self::new(SourceFrom::Unspecified, source) Self::new(SourceFrom::Unspecified, source)
} }
@ -167,7 +178,7 @@ impl Source {
Some(ch) Some(ch)
} }
fn word_splitter(ch: char) -> bool { fn word_splitter(ch: char) -> bool {
ch.is_whitespace() || ".,;[](){}".contains(ch) ch.is_whitespace() || ".,;[](){}/<".contains(ch)
} }
pub fn peek_word(&self) -> &str { pub fn peek_word(&self) -> &str {
self.src[self.i..] self.src[self.i..]

View File

@ -4,13 +4,107 @@ use super::{Source, SourcePos};
use crate::{ use crate::{
data::Data, data::Data,
errors::{error_colors, CheckError}, errors::{error_colors, CheckError},
program::{self, parsed::MersStatement}, program::{
self,
parsed::{as_type::AsType, MersStatement},
},
}; };
pub fn parse( pub fn parse(
src: &mut Source, src: &mut Source,
) -> Result<Option<Box<dyn program::parsed::MersStatement>>, CheckError> { ) -> Result<Option<Box<dyn program::parsed::MersStatement>>, CheckError> {
src.section_begin("statement".to_string()); 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)? { let mut first = if let Some(s) = parse_no_chain(src)? {
s s
} else { } else {
@ -128,8 +222,8 @@ pub fn parse_multiple(
pub fn parse_no_chain( pub fn parse_no_chain(
src: &mut Source, src: &mut Source,
) -> Result<Option<Box<dyn program::parsed::MersStatement>>, CheckError> { ) -> Result<Option<Box<dyn program::parsed::MersStatement>>, CheckError> {
src.section_begin("statement no chain".to_string());
src.skip_whitespace(); src.skip_whitespace();
src.section_begin("statement no chain".to_string());
match src.peek_char() { match src.peek_char() {
Some('#') => { Some('#') => {
let pos_in_src = src.get_pos(); 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 /// expects to be called *after* a " character is consumed from src
pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String, CheckError> { pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String, CheckError> {
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<String, CheckError> {
let mut s = String::new(); let mut s = String::new();
loop { loop {
if let Some(ch) = src.next_char() { if let Some(ch) = src.next_char() {
@ -350,6 +452,7 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String,
Some('n') => '\n', Some('n') => '\n',
Some('t') => '\t', Some('t') => '\t',
Some('"') => '"', Some('"') => '"',
Some(c) if c == closing_char || c == opening_char => c,
Some(o) => { Some(o) => {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
@ -367,7 +470,7 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String,
.msg(format!("EOF in backslash escape"))); .msg(format!("EOF in backslash escape")));
} }
}); });
} else if ch == '"' { } else if ch == closing_char {
break; break;
} else { } else {
s.push(ch); s.push(ch);
@ -375,11 +478,27 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String,
} else { } else {
return Err(CheckError::new() return Err(CheckError::new()
.src(vec![( .src(vec![(
(double_quote, src.get_pos()).into(), (opening, src.get_pos()).into(),
Some(error_colors::StringEOF), Some(error_colors::StringEOF),
)]) )])
.msg(format!("EOF in string literal"))); .msg(format!(
"EOF in string literal{}",
if closing_char != '"' {
format!(
"{opening_char}...{closing_char} (end string with '{closing_char}')"
)
} else {
String::new()
}
)));
} }
} }
Ok(s) Ok(s)
} }
pub fn to_string_literal(val: &str, end: char) -> String {
val.replace("\\", "\\\\")
.replace("\r", "\\r")
.replace("\n", "\\n")
.replace("\"", "\\\"")
.replace(end, format!("\\{end}").as_str())
}

View File

@ -1,47 +1,113 @@
use std::sync::Arc;
use crate::{
data::{self, Type},
errors::{error_colors, CheckError},
};
use super::Source; use super::Source;
/// multiple types are represented as a `Vec<ParsedType>`. /// multiple types are represented as a `Vec<ParsedType>`.
#[derive(Clone, Debug)]
pub enum ParsedType { pub enum ParsedType {
Reference(Vec<Self>),
Tuple(Vec<Vec<Self>>), Tuple(Vec<Vec<Self>>),
Type(String), Type(String),
TypeWithInfo(String, String),
} }
fn parse_single_type(src: &mut Source) -> Result<ParsedType, ()> { pub fn parse_single_type(src: &mut Source) -> Result<ParsedType, CheckError> {
src.section_begin("parse single type".to_string()); src.section_begin("parse single type".to_string());
src.skip_whitespace(); src.skip_whitespace();
Ok(match src.peek_char() { 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 // Tuple
Some('(') => { Some('(') => {
let pos_in_src = src.get_pos();
src.next_char(); src.next_char();
src.section_begin("parse tuple's inner types".to_string()); src.section_begin("parse tuple's inner types".to_string());
let mut inner = vec![]; let mut inner = vec![];
loop { src.skip_whitespace();
match src.peek_char() { if let Some(')') = src.peek_char() {
Some(')') => { // empty tuple, don't even start the loop
src.next_char(); } else {
break; 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) 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!(), None => todo!(),
}) })
} }
fn parse_type(src: &mut Source) -> Result<Vec<ParsedType>, ()> { pub fn parse_type(src: &mut Source) -> Result<Vec<ParsedType>, CheckError> {
src.section_begin("parse single type".to_string()); src.section_begin("parse single type".to_string());
let mut types = vec![]; let mut types = vec![];
loop { loop {
types.push(parse_single_type(src)?); types.push(parse_single_type(src)?);
src.skip_whitespace(); src.skip_whitespace();
if let Some('/') = src.peek_char() { if let Some('/') = src.peek_char() {
src.next_char();
continue; continue;
} else { } else {
break; break;
@ -49,3 +115,49 @@ fn parse_type(src: &mut Source) -> Result<Vec<ParsedType>, ()> {
} }
Ok(types) Ok(types)
} }
pub fn type_from_parsed(
parsed: &Vec<ParsedType>,
info: &crate::program::run::CheckInfo,
) -> Result<Type, CheckError> {
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::<Result<_, _>>()?,
)),
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)
}

View File

@ -1,8 +1,10 @@
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use crate::{ use crate::{
data::{Data, Type}, data::{self, Data, MersType},
errors::CheckError,
info::Local, info::Local,
program::run::CheckInfo,
}; };
mod with_base; mod with_base;
@ -58,11 +60,27 @@ impl Config {
} }
pub fn new() -> Self { 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 { Self {
globals: 0, globals: 0,
info_parsed: Default::default(), info_parsed: Default::default(),
info_run: Default::default(), info_run: Default::default(),
info_check: Default::default(), info_check,
} }
} }
@ -74,8 +92,15 @@ impl Config {
self.globals += 1; self.globals += 1;
self self
} }
pub fn add_type(self, _name: String, _t: Type) -> Self { pub fn add_type(
// TODO! needed for type syntax in the parser, everything else probably(?) works already 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 self
} }

View File

@ -5,6 +5,7 @@ use std::{
use crate::{ use crate::{
data::{self, Data, MersData, MersType, Type}, data::{self, Data, MersData, MersType, Type},
parsing::{statements::to_string_literal, Source},
program::{self, run::CheckInfo}, program::{self, run::CheckInfo},
}; };
@ -20,7 +21,10 @@ impl Config {
/// `get_mut: fn` like get, but returns a reference to the object /// `get_mut: fn` like get, but returns a reference to the object
pub fn with_list(self) -> Self { pub fn with_list(self) -> Self {
// TODO: Type with generics // 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( .add_var(
"pop".to_string(), "pop".to_string(),
Data::new(data::function::Function { 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)) .is_some_and(|v| self.0.is_same_type_as(&v.0))
} }
fn is_included_in_single(&self, target: &dyn MersType) -> bool { 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) { fn subtypes(&self, acc: &mut Type) {
for t in self.0.subtypes_type().types { for t in self.0.subtypes_type().types {
@ -234,7 +241,7 @@ impl Display for List {
} }
impl Display for ListT { impl Display for ListT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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(()) Ok(())
} }
} }

View File

@ -7,6 +7,7 @@ use std::{
use crate::{ use crate::{
data::{self, Data, MersData, MersType, Type}, data::{self, Data, MersData, MersType, Type},
errors::CheckError, errors::CheckError,
parsing::{statements::to_string_literal, Source},
program::{self, run::CheckInfo}, program::{self, run::CheckInfo},
}; };
@ -19,7 +20,10 @@ impl Config {
pub fn with_multithreading(self) -> Self { pub fn with_multithreading(self) -> Self {
self.add_type( self.add_type(
"Thread".to_string(), "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( .add_var(
"thread".to_string(), "thread".to_string(),
@ -170,6 +174,6 @@ impl Display for Thread {
} }
impl Display for ThreadT { impl Display for ThreadT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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, info,
}; };
#[cfg(feature = "parse")]
pub mod as_type;
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
pub mod assign_to; pub mod assign_to;
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
@ -12,6 +14,8 @@ pub mod block;
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
pub mod chain; pub mod chain;
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
pub mod custom_type;
#[cfg(feature = "parse")]
pub mod function; pub mod function;
#[cfg(feature = "parse")] #[cfg(feature = "parse")]
pub mod r#if; pub mod r#if;
@ -70,6 +74,7 @@ pub struct Local {
impl info::Local for Local { impl info::Local for Local {
type VariableIdentifier = String; type VariableIdentifier = String;
type VariableData = (usize, usize); type VariableData = (usize, usize);
type Global = ();
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
self.vars_count += 1; self.vars_count += 1;
self.vars.insert(id, value); self.vars.insert(id, value);

View File

@ -22,20 +22,29 @@ impl MersStatement for Variable {
info: &mut crate::info::Info<super::Local>, info: &mut crate::info::Info<super::Local>,
comp: CompInfo, comp: CompInfo,
) -> Result<Box<dyn program::run::MersStatement>, CheckError> { ) -> Result<Box<dyn program::run::MersStatement>, CheckError> {
let init_and_ignore = comp.is_init && self.var == "_";
if comp.is_init { if comp.is_init {
info.init_var( if !init_and_ignore {
self.var.clone(), info.init_var(
( self.var.clone(),
info.scopes.len() - 1, (
info.scopes.last().unwrap().vars_count, info.scopes.len() - 1,
), info.scopes.last().unwrap().vars_count,
) ),
);
}
} }
Ok(Box::new(program::run::variable::Variable { Ok(Box::new(program::run::variable::Variable {
pos_in_src: self.pos_in_src, pos_in_src: self.pos_in_src,
is_init: comp.is_init, is_init: comp.is_init,
is_ref: comp.is_init || self.is_ref, is_ref_not_ignore: if comp.is_init {
var: if let Some(v) = info.get_var(&self.var) { !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 *v
} else { } else {
return Err(CheckError::new() 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::{ use std::{
collections::HashMap,
fmt::Debug, fmt::Debug,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use crate::{ use crate::{
data::{self, Data, Type}, data::{self, Data, MersType, Type},
errors::{CheckError, SourceRange}, errors::{CheckError, SourceRange},
info, info,
}; };
#[cfg(feature = "run")]
pub mod as_type;
#[cfg(feature = "run")] #[cfg(feature = "run")]
pub mod assign_to; pub mod assign_to;
#[cfg(feature = "run")] #[cfg(feature = "run")]
@ -16,6 +19,8 @@ pub mod block;
#[cfg(feature = "run")] #[cfg(feature = "run")]
pub mod chain; pub mod chain;
#[cfg(feature = "run")] #[cfg(feature = "run")]
pub mod custom_type;
#[cfg(feature = "run")]
pub mod function; pub mod function;
#[cfg(feature = "run")] #[cfg(feature = "run")]
pub mod r#if; pub mod r#if;
@ -65,13 +70,26 @@ pub type CheckInfo = info::Info<CheckLocal>;
pub struct Local { pub struct Local {
vars: Vec<Arc<RwLock<Data>>>, vars: Vec<Arc<RwLock<Data>>>,
} }
#[derive(Default, Clone, Debug)] #[derive(Default, Clone)]
pub struct CheckLocal { pub struct CheckLocal {
vars: Vec<Type>, 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 { impl info::Local for Local {
type VariableIdentifier = usize; type VariableIdentifier = usize;
type VariableData = Arc<RwLock<Data>>; type VariableData = Arc<RwLock<Data>>;
type Global = ();
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false)))); let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false))));
while self.vars.len() <= id { while self.vars.len() <= id {
@ -104,6 +122,7 @@ impl info::Local for Local {
impl info::Local for CheckLocal { impl info::Local for CheckLocal {
type VariableIdentifier = usize; type VariableIdentifier = usize;
type VariableData = Type; type VariableData = Type;
type Global = ();
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
while self.vars.len() <= id { while self.vars.len() <= id {
self.vars.push(Type::empty()); self.vars.push(Type::empty());

View File

@ -11,7 +11,8 @@ use super::MersStatement;
pub struct Variable { pub struct Variable {
pub pos_in_src: SourceRange, pub pos_in_src: SourceRange,
pub is_init: bool, 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), pub var: (usize, usize),
} }
@ -25,14 +26,22 @@ impl MersStatement for Variable {
init_to: Option<&Type>, init_to: Option<&Type>,
) -> Result<data::Type, super::CheckError> { ) -> Result<data::Type, super::CheckError> {
if self.is_init { if self.is_init {
while info.scopes[self.var.0].vars.len() <= self.var.1 { if self.is_ref_not_ignore {
info.scopes[self.var.0].vars.push(Type::empty()); 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( Type::new(data::reference::ReferenceT(
info.scopes[self.var.0].vars[self.var.1].clone(), 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 { fn run_custom(&self, info: &mut super::Info) -> Data {
if self.is_init { if self.is_init {
let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false)))); if self.is_ref_not_ignore {
while info.scopes[self.var.0].vars.len() <= self.var.1 { let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false))));
info.scopes[self.var.0].vars.push(Arc::clone(&nothing)); 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( Data::new(data::reference::Reference(Arc::clone(
&info.scopes[self.var.0].vars[self.var.1], &info.scopes[self.var.0].vars[self.var.1],
))) )))