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

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() {
write!(f, "<unreachable>")
} 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(())
}
}

View File

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

View File

@ -3,26 +3,27 @@ use std::fmt::Debug;
#[derive(Clone, Debug)]
pub struct Info<L: Local> {
pub scopes: Vec<L>,
pub global: L::Global,
}
impl<L: Local> Info<L> {
/// 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<L: Local> Info<L> {
impl<L: Local> Local for Info<L> {
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<L: Local> Local for Info<L> {
fn duplicate(&self) -> Self {
Self {
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 {
Self {
scopes: vec![L::default()],
global: L::Global::default(),
}
}
}

View File

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

View File

@ -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<Option<Box<dyn program::parsed::MersStatement>>, 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<Option<Box<dyn program::parsed::MersStatement>>, 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<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();
loop {
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('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<String,
.msg(format!("EOF in backslash escape")));
}
});
} else if ch == '"' {
} else if ch == closing_char {
break;
} else {
s.push(ch);
@ -375,11 +478,27 @@ pub fn parse_string(src: &mut Source, double_quote: SourcePos) -> Result<String,
} else {
return Err(CheckError::new()
.src(vec![(
(double_quote, src.get_pos()).into(),
(opening, src.get_pos()).into(),
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)
}
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,21 +1,59 @@
use std::sync::Arc;
use crate::{
data::{self, Type},
errors::{error_colors, CheckError},
};
use super::Source;
/// multiple types are represented as a `Vec<ParsedType>`.
#[derive(Clone, Debug)]
pub enum ParsedType {
Reference(Vec<Self>),
Tuple(Vec<Vec<Self>>),
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.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![];
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();
@ -24,24 +62,52 @@ fn parse_single_type(src: &mut Source) -> Result<ParsedType, ()> {
Some(',') => {
src.next_char();
}
_ => todo!("err: bad char in tuple inner type"),
_ => {
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 ')'."
)));
}
}
}
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<Vec<ParsedType>, ()> {
pub fn parse_type(src: &mut Source) -> Result<Vec<ParsedType>, 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<Vec<ParsedType>, ()> {
}
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 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 {
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 {
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(),
)));
}
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 {
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(),
))));
}
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],
)))