mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-09 21:13:54 +01:00
make :
syntax an associated function call
This commit is contained in:
parent
e07010dcfc
commit
ac6b405a3c
@ -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 }
|
||||
|
@ -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`
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
58
mers_lib/src/program/parsed/field_chain.rs
Normal file
58
mers_lib/src/program/parsed/field_chain.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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))])),
|
||||
}
|
||||
}
|
||||
|
159
mers_lib/src/program/run/field_chain.rs
Normal file
159
mers_lib/src/program/run/field_chain.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user