make : syntax an associated function call

This commit is contained in:
Mark 2025-02-07 21:04:17 +01:00
parent e07010dcfc
commit ac6b405a3c
9 changed files with 354 additions and 84 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "mers"
version = "0.9.21"
version = "0.9.22"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "dynamically typed but type-checked programming language"
@ -15,7 +15,7 @@ default = ["colored-output"]
colored-output = ["mers_lib/ecolor-term", "mers_lib/pretty-print", "dep:colored"]
[dependencies]
mers_lib = "0.9.21"
mers_lib = "0.9.22"
# mers_lib = { path = "../mers_lib" }
clap = { version = "4.3.19", features = ["derive"] }
colored = { version = "2.1.0", optional = true }

View File

@ -96,6 +96,7 @@ mers only has a few different expressions:
- blocks: `{ a, b, c }`
- functions: `arg -> expression`
- function calls: `arg.func` or `a.func(b, c)`, which becomes `(a, b, c).func`
- associated function calls on objects: `obj:func` or `obj:func(b, c)`, as above - `obj` must contain a function `func`
- `if condition expression` and `if condition expression_1 else expression_2`
- `loop expression`
- type hints `[Int] 5`

View File

@ -1,6 +1,6 @@
[package]
name = "mers_lib"
version = "0.9.21"
version = "0.9.22"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "library to use the mers language in other projects"

View File

@ -220,11 +220,26 @@ pub fn parse(
}
} else if let Some(':') = src.peek_char() {
src.next_char();
let first_start = first.source_range().start();
let field_start = src.get_pos();
let field = src.next_word().to_owned();
first = Box::new(program::parsed::field::Field {
pos_in_src: (first.source_range().start(), src.get_pos(), srca).into(),
let field_end = src.get_pos();
// allow a.f(b, c) syntax (but not f(a, b, c))
let args = if let Some('(') = src.peek_char() {
src.next_char();
Some((
parse_multiple(src, srca, ")")?,
(first_start, src.get_pos(), srca).into(),
))
} else {
None
};
first = Box::new(program::parsed::field_chain::FieldChain {
pos_in_src: (first_start, src.get_pos(), srca).into(),
object: first,
args,
field,
field_pos: (field_start, field_end, srca).into(),
});
pos_after_first = src.get_pos();
} else {

View File

@ -0,0 +1,58 @@
use crate::{
data::object::ObjectFieldsMap,
errors::{CheckError, SourceRange},
info, program,
};
use super::{CompInfo, MersStatement};
/// Extracts a property with the given name from the object
#[derive(Debug)]
pub struct FieldChain {
pub pos_in_src: SourceRange,
pub object: Box<dyn MersStatement>,
pub args: Option<(Vec<Box<dyn MersStatement>>, SourceRange)>,
pub field: String,
pub field_pos: SourceRange,
}
impl MersStatement for FieldChain {
fn has_scope(&self) -> bool {
false
}
fn compile_custom(
&self,
info: &mut info::Info<super::Local>,
comp: CompInfo,
) -> Result<Box<dyn program::run::MersStatement>, CheckError> {
Ok(Box::new(program::run::field_chain::FieldChain {
pos_in_src: self.pos_in_src.clone(),
object: self.object.compile(info, comp)?,
args: if let Some((args, pos)) = &self.args {
Some((
args.iter()
.map(|arg| arg.compile(info, comp))
.collect::<Result<Vec<_>, _>>()?,
pos.clone(),
))
} else {
None
},
field_str: self.field.clone(),
field_pos: self.field_pos.clone(),
field: info.global.object_fields.get_or_add_field(&self.field),
}))
}
fn source_range(&self) -> SourceRange {
self.pos_in_src.clone()
}
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
let mut o = vec![&*self.object];
if let Some((args, _)) = &self.args {
o.extend(args.iter().map(|v| &**v));
}
o
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}

View File

@ -22,6 +22,8 @@ pub mod custom_type;
#[cfg(feature = "parse")]
pub mod field;
#[cfg(feature = "parse")]
pub mod field_chain;
#[cfg(feature = "parse")]
pub mod function;
#[cfg(feature = "parse")]
pub mod r#if;

View File

@ -29,89 +29,28 @@ impl MersStatement for Chain {
let arg = self.first.check(info, None)?;
let func = self.chained.check(info, None)?;
info.global.enable_hooks = prev_enable_hooks;
let mut o = Type::empty();
for func in &func.types {
if let Some(func) = func.executable() {
match func.o(&arg) {
Ok(t) => o.add_all(&t),
Err(e) => {
return Err(if let Some(_) = &self.as_part_of_include {
CheckError::new()
.src(vec![(
self.pos_in_src.clone(),
Some(EColor::HashIncludeErrorInIncludedFile),
)])
.msg(vec![(
"Error in #include:".to_owned(),
Some(EColor::HashIncludeErrorInIncludedFile),
)])
.err_with_diff_src(e)
} else {
CheckError::new()
.src(vec![
(self.pos_in_src.clone(), None),
(self.first.source_range(), Some(EColor::FunctionArgument)),
(self.chained.source_range(), Some(EColor::Function)),
])
.msg(vec![
("Can't call ".to_owned(), None),
("this function".to_owned(), Some(EColor::Function)),
(" with an argument of type ".to_owned(), None),
(
arg.simplified_as_string(info),
Some(EColor::FunctionArgument),
),
(":".to_owned(), None),
])
.err(e)
})
}
}
} else {
return Err(CheckError::new()
.src(vec![
(self.pos_in_src.clone(), None),
(
self.chained.source_range(),
Some(EColor::ChainWithNonFunction),
),
])
.msg(vec![
("cannot chain with a non-function (".to_owned(), None),
(
func.simplified_as_string(info),
Some(EColor::ChainWithNonFunction),
),
(")".to_owned(), None),
]));
}
}
Ok(o)
check(
&arg,
&func,
info,
self.pos_in_src.clone(),
self.first.source_range(),
self.chained.source_range(),
self.as_part_of_include.as_ref(),
)
}
fn run_custom(&self, info: &mut super::Info) -> Result<Data, CheckError> {
let f = self.first.run(info)?;
let c = self.chained.run(info)?;
let c = c.get();
match c.execute(f, &info.global) {
Some(Ok(v)) => Ok(v),
Some(Err(e)) => Err(if let Some(_) = &self.as_part_of_include {
CheckError::new().err_with_diff_src(e).src(vec![(
self.pos_in_src.clone(),
Some(EColor::StacktraceDescendHashInclude),
)])
} else {
CheckError::new().err(e).src(vec![
(self.pos_in_src.clone(), None),
(self.source_range(), Some(EColor::StacktraceDescend)),
])
}),
None => Err(CheckError::new()
.msg_str("tried to chain with non-function".to_owned())
.src(vec![(
self.chained.source_range(),
Some(EColor::ChainWithNonFunction),
)])),
}
run(
f,
c,
info,
self.pos_in_src.clone(),
self.first.source_range(),
self.chained.source_range(),
self.as_part_of_include.as_ref(),
)
}
fn has_scope(&self) -> bool {
false
@ -126,3 +65,97 @@ impl MersStatement for Chain {
self
}
}
pub fn check(
arg: &Type,
func: &Type,
info: &mut super::CheckInfo,
pos_in_src: SourceRange,
arg_pos: SourceRange,
func_pos: SourceRange,
as_part_of_include: Option<&Source>,
) -> Result<Type, CheckError> {
let mut o = Type::empty();
for func in &func.types {
if let Some(func) = func.executable() {
match func.o(&arg) {
Ok(t) => o.add_all(&t),
Err(e) => {
return Err(if let Some(_) = as_part_of_include {
CheckError::new()
.src(vec![(
pos_in_src.clone(),
Some(EColor::HashIncludeErrorInIncludedFile),
)])
.msg(vec![(
"Error in #include:".to_owned(),
Some(EColor::HashIncludeErrorInIncludedFile),
)])
.err_with_diff_src(e)
} else {
CheckError::new()
.src(vec![
(pos_in_src.clone(), None),
(arg_pos, Some(EColor::FunctionArgument)),
(func_pos, Some(EColor::Function)),
])
.msg(vec![
("Can't call ".to_owned(), None),
("this function".to_owned(), Some(EColor::Function)),
(" with an argument of type ".to_owned(), None),
(
arg.simplified_as_string(info),
Some(EColor::FunctionArgument),
),
(":".to_owned(), None),
])
.err(e)
})
}
}
} else {
return Err(CheckError::new()
.src(vec![
(pos_in_src, None),
(func_pos, Some(EColor::ChainWithNonFunction)),
])
.msg(vec![
("cannot chain with a non-function (".to_owned(), None),
(
func.simplified_as_string(info),
Some(EColor::ChainWithNonFunction),
),
(")".to_owned(), None),
]));
}
}
Ok(o)
}
pub fn run(
arg: Data,
func: Data,
info: &mut super::Info,
pos_in_src: SourceRange,
_arg_pos: SourceRange,
func_pos: SourceRange,
as_part_of_include: Option<&Source>,
) -> Result<Data, CheckError> {
let func = func.get();
match func.execute(arg, &info.global) {
Some(Ok(v)) => Ok(v),
Some(Err(e)) => Err(if let Some(_) = &as_part_of_include {
CheckError::new().err_with_diff_src(e).src(vec![(
pos_in_src.clone(),
Some(EColor::StacktraceDescendHashInclude),
)])
} else {
CheckError::new()
.err(e)
.src(vec![(pos_in_src.clone(), Some(EColor::StacktraceDescend))])
}),
None => Err(CheckError::new()
.msg_str("tried to chain with non-function".to_owned())
.src(vec![(func_pos, Some(EColor::ChainWithNonFunction))])),
}
}

View File

@ -0,0 +1,159 @@
use std::sync::Arc;
use crate::{
data::{
self,
object::ObjectT,
tuple::{Tuple, TupleT},
Data, MersDataWInfo, MersTypeWInfo, Type,
},
errors::{CheckError, EColor, SourceRange},
};
use super::MersStatement;
#[derive(Debug)]
pub struct FieldChain {
pub pos_in_src: SourceRange,
pub object: Box<dyn MersStatement>,
pub args: Option<(Vec<Box<dyn MersStatement>>, SourceRange)>,
pub field_str: String,
pub field_pos: SourceRange,
pub field: usize,
}
impl MersStatement for FieldChain {
fn check_custom(
&self,
info: &mut super::CheckInfo,
init_to: Option<&Type>,
) -> Result<data::Type, super::CheckError> {
if init_to.is_some() {
return Err("can't init to statement type Field".to_string().into());
}
let object = self.object.check(info, init_to)?;
let mut o = Type::empty();
for arg in object.types.iter() {
let iter;
let iter = if let Some(t) = arg.is_reference_to() {
t.types.iter()
} else {
iter = vec![Arc::clone(arg)];
iter.iter()
};
let arg = Type::newm(vec![Arc::clone(arg)]);
let arg = if let Some((more_args, _)) = &self.args {
let mut args = vec![arg];
for res in more_args.iter().map(|arg| arg.check(info, None)) {
args.push(res?);
}
Type::new(TupleT(args))
} else {
arg
};
for t in iter {
if let Some(t) = t.as_any().downcast_ref::<ObjectT>() {
if let Some(func) = t.get(self.field) {
o.add_all(&super::chain::check(
&arg,
func,
info,
self.pos_in_src.clone(),
self.object.source_range(),
self.field_pos.clone(),
None,
)?);
} else {
return Err(CheckError::new().msg(vec![
("can't get field ".to_owned(), None),
(self.field_str.clone(), Some(EColor::ObjectField)),
(" of object ".to_owned(), None),
(t.with_info(info).to_string(), Some(EColor::InitFrom)),
]));
}
} else {
return Err(CheckError::new().msg(vec![
("can't get field ".to_owned(), None),
(self.field_str.clone(), Some(EColor::ObjectField)),
(" of non-object type ".to_owned(), None),
(arg.with_info(info).to_string(), Some(EColor::InitFrom)),
]));
}
}
}
Ok(o)
}
fn run_custom(&self, info: &mut super::Info) -> Result<Data, CheckError> {
let object = self.object.run(info)?;
let func = {
let object_lock = object.get();
let obj_ref;
let obj_in_ref;
let object = if let Some(o) =
if let Some(o) = object_lock.as_any().downcast_ref::<data::object::Object>() {
Some(o)
} else if let Some(r) = object_lock
.as_any()
.downcast_ref::<data::reference::Reference>()
{
obj_ref = r.0.read().unwrap();
obj_in_ref = obj_ref.get();
obj_in_ref.as_any().downcast_ref::<data::object::Object>()
} else {
None
} {
o
} else {
Err(format!(
"couldn't extract field {} from non-object and non-&object value {}",
self.field_str,
object_lock.with_info(info)
))?
};
let func = object
.get(self.field)
.ok_or_else(|| {
format!(
"couldn't extract field {} from object {}",
self.field_str,
object.with_info(info)
)
})?
.clone();
func
};
let arg = if let Some((more_args, _)) = &self.args {
let mut args = vec![object];
for res in more_args.iter().map(|arg| arg.run(info)) {
args.push(res?);
}
Data::new(Tuple(args))
} else {
object
};
super::chain::run(
arg,
func,
info,
self.pos_in_src.clone(),
self.object.source_range(),
self.field_pos.clone(),
None,
)
}
fn has_scope(&self) -> bool {
false
}
fn source_range(&self) -> SourceRange {
self.pos_in_src.clone()
}
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
let mut o = vec![&*self.object];
if let Some((args, _)) = &self.args {
o.extend(args.iter().map(|v| &**v));
}
o
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}

View File

@ -25,6 +25,8 @@ pub mod custom_type;
#[cfg(feature = "run")]
pub mod field;
#[cfg(feature = "run")]
pub mod field_chain;
#[cfg(feature = "run")]
pub mod function;
#[cfg(feature = "run")]
pub mod r#if;