mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +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:
parent
b6d708db3d
commit
4144d6cf71
47
Quickstart.md
Normal file
47
Quickstart.md
Normal 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
3
README.md
Executable file → Normal 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
|
||||||
|
76
examples/08_Type_Annotations.mers
Normal file
76
examples/08_Type_Annotations.mers
Normal 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/()>`
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
if self.0.types.len() > 1 {
|
||||||
|
write!(f, "&{{{}}}", self.0)
|
||||||
|
} else {
|
||||||
write!(f, "&{}", self.0)
|
write!(f, "&{}", self.0)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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..]
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
@ -1,21 +1,59 @@
|
|||||||
|
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![];
|
||||||
|
src.skip_whitespace();
|
||||||
|
if let Some(')') = src.peek_char() {
|
||||||
|
// empty tuple, don't even start the loop
|
||||||
|
} else {
|
||||||
loop {
|
loop {
|
||||||
|
inner.push(parse_type(src)?);
|
||||||
match src.peek_char() {
|
match src.peek_char() {
|
||||||
Some(')') => {
|
Some(')') => {
|
||||||
src.next_char();
|
src.next_char();
|
||||||
@ -24,24 +62,52 @@ fn parse_single_type(src: &mut Source) -> Result<ParsedType, ()> {
|
|||||||
Some(',') => {
|
Some(',') => {
|
||||||
src.next_char();
|
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)
|
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)
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(), '>'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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,
|
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);
|
||||||
|
@ -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 {
|
||||||
|
if !init_and_ignore {
|
||||||
info.init_var(
|
info.init_var(
|
||||||
self.var.clone(),
|
self.var.clone(),
|
||||||
(
|
(
|
||||||
info.scopes.len() - 1,
|
info.scopes.len() - 1,
|
||||||
info.scopes.last().unwrap().vars_count,
|
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()
|
||||||
|
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::{
|
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());
|
||||||
|
@ -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 {
|
||||||
|
if self.is_ref_not_ignore {
|
||||||
while info.scopes[self.var.0].vars.len() <= self.var.1 {
|
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.push(Type::empty());
|
||||||
}
|
}
|
||||||
info.scopes[self.var.0].vars[self.var.1] = init_to
|
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?")
|
.expect("variable's is_init was true, but check_custom's assign was None? How?")
|
||||||
.clone();
|
.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(
|
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 {
|
||||||
|
if self.is_ref_not_ignore {
|
||||||
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 info.scopes[self.var.0].vars.len() <= self.var.1 {
|
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.push(Arc::clone(¬hing));
|
||||||
}
|
}
|
||||||
info.scopes[self.var.0].vars[self.var.1] = 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(
|
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],
|
||||||
)))
|
)))
|
||||||
|
Loading…
Reference in New Issue
Block a user