mirror of
https://github.com/Dummi26/mers.git
synced 2025-03-10 05:43:53 +01:00
make :
syntax an associated function call
This commit is contained in:
parent
e07010dcfc
commit
ac6b405a3c
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mers"
|
name = "mers"
|
||||||
version = "0.9.21"
|
version = "0.9.22"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "dynamically typed but type-checked programming language"
|
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"]
|
colored-output = ["mers_lib/ecolor-term", "mers_lib/pretty-print", "dep:colored"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mers_lib = "0.9.21"
|
mers_lib = "0.9.22"
|
||||||
# mers_lib = { path = "../mers_lib" }
|
# mers_lib = { path = "../mers_lib" }
|
||||||
clap = { version = "4.3.19", features = ["derive"] }
|
clap = { version = "4.3.19", features = ["derive"] }
|
||||||
colored = { version = "2.1.0", optional = true }
|
colored = { version = "2.1.0", optional = true }
|
||||||
|
@ -96,6 +96,7 @@ mers only has a few different expressions:
|
|||||||
- blocks: `{ a, b, c }`
|
- blocks: `{ a, b, c }`
|
||||||
- functions: `arg -> expression`
|
- functions: `arg -> expression`
|
||||||
- function calls: `arg.func` or `a.func(b, c)`, which becomes `(a, b, c).func`
|
- 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`
|
- `if condition expression` and `if condition expression_1 else expression_2`
|
||||||
- `loop expression`
|
- `loop expression`
|
||||||
- type hints `[Int] 5`
|
- type hints `[Int] 5`
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mers_lib"
|
name = "mers_lib"
|
||||||
version = "0.9.21"
|
version = "0.9.22"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "library to use the mers language in other projects"
|
description = "library to use the mers language in other projects"
|
||||||
|
@ -220,11 +220,26 @@ pub fn parse(
|
|||||||
}
|
}
|
||||||
} else if let Some(':') = src.peek_char() {
|
} else if let Some(':') = src.peek_char() {
|
||||||
src.next_char();
|
src.next_char();
|
||||||
|
let first_start = first.source_range().start();
|
||||||
|
let field_start = src.get_pos();
|
||||||
let field = src.next_word().to_owned();
|
let field = src.next_word().to_owned();
|
||||||
first = Box::new(program::parsed::field::Field {
|
let field_end = src.get_pos();
|
||||||
pos_in_src: (first.source_range().start(), src.get_pos(), srca).into(),
|
// 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,
|
object: first,
|
||||||
|
args,
|
||||||
field,
|
field,
|
||||||
|
field_pos: (field_start, field_end, srca).into(),
|
||||||
});
|
});
|
||||||
pos_after_first = src.get_pos();
|
pos_after_first = src.get_pos();
|
||||||
} else {
|
} 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")]
|
#[cfg(feature = "parse")]
|
||||||
pub mod field;
|
pub mod field;
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
|
pub mod field_chain;
|
||||||
|
#[cfg(feature = "parse")]
|
||||||
pub mod function;
|
pub mod function;
|
||||||
#[cfg(feature = "parse")]
|
#[cfg(feature = "parse")]
|
||||||
pub mod r#if;
|
pub mod r#if;
|
||||||
|
@ -29,16 +29,62 @@ impl MersStatement for Chain {
|
|||||||
let arg = self.first.check(info, None)?;
|
let arg = self.first.check(info, None)?;
|
||||||
let func = self.chained.check(info, None)?;
|
let func = self.chained.check(info, None)?;
|
||||||
info.global.enable_hooks = prev_enable_hooks;
|
info.global.enable_hooks = prev_enable_hooks;
|
||||||
|
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)?;
|
||||||
|
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
|
||||||
|
}
|
||||||
|
fn source_range(&self) -> SourceRange {
|
||||||
|
self.pos_in_src.clone()
|
||||||
|
}
|
||||||
|
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
|
||||||
|
vec![self.first.as_ref(), self.chained.as_ref()]
|
||||||
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
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();
|
let mut o = Type::empty();
|
||||||
for func in &func.types {
|
for func in &func.types {
|
||||||
if let Some(func) = func.executable() {
|
if let Some(func) = func.executable() {
|
||||||
match func.o(&arg) {
|
match func.o(&arg) {
|
||||||
Ok(t) => o.add_all(&t),
|
Ok(t) => o.add_all(&t),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(if let Some(_) = &self.as_part_of_include {
|
return Err(if let Some(_) = as_part_of_include {
|
||||||
CheckError::new()
|
CheckError::new()
|
||||||
.src(vec![(
|
.src(vec![(
|
||||||
self.pos_in_src.clone(),
|
pos_in_src.clone(),
|
||||||
Some(EColor::HashIncludeErrorInIncludedFile),
|
Some(EColor::HashIncludeErrorInIncludedFile),
|
||||||
)])
|
)])
|
||||||
.msg(vec![(
|
.msg(vec![(
|
||||||
@ -49,9 +95,9 @@ impl MersStatement for Chain {
|
|||||||
} else {
|
} else {
|
||||||
CheckError::new()
|
CheckError::new()
|
||||||
.src(vec![
|
.src(vec![
|
||||||
(self.pos_in_src.clone(), None),
|
(pos_in_src.clone(), None),
|
||||||
(self.first.source_range(), Some(EColor::FunctionArgument)),
|
(arg_pos, Some(EColor::FunctionArgument)),
|
||||||
(self.chained.source_range(), Some(EColor::Function)),
|
(func_pos, Some(EColor::Function)),
|
||||||
])
|
])
|
||||||
.msg(vec![
|
.msg(vec![
|
||||||
("Can't call ".to_owned(), None),
|
("Can't call ".to_owned(), None),
|
||||||
@ -70,11 +116,8 @@ impl MersStatement for Chain {
|
|||||||
} else {
|
} else {
|
||||||
return Err(CheckError::new()
|
return Err(CheckError::new()
|
||||||
.src(vec![
|
.src(vec![
|
||||||
(self.pos_in_src.clone(), None),
|
(pos_in_src, None),
|
||||||
(
|
(func_pos, Some(EColor::ChainWithNonFunction)),
|
||||||
self.chained.source_range(),
|
|
||||||
Some(EColor::ChainWithNonFunction),
|
|
||||||
),
|
|
||||||
])
|
])
|
||||||
.msg(vec![
|
.msg(vec![
|
||||||
("cannot chain with a non-function (".to_owned(), None),
|
("cannot chain with a non-function (".to_owned(), None),
|
||||||
@ -88,41 +131,31 @@ impl MersStatement for Chain {
|
|||||||
}
|
}
|
||||||
Ok(o)
|
Ok(o)
|
||||||
}
|
}
|
||||||
fn run_custom(&self, info: &mut super::Info) -> Result<Data, CheckError> {
|
|
||||||
let f = self.first.run(info)?;
|
pub fn run(
|
||||||
let c = self.chained.run(info)?;
|
arg: Data,
|
||||||
let c = c.get();
|
func: Data,
|
||||||
match c.execute(f, &info.global) {
|
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(Ok(v)) => Ok(v),
|
||||||
Some(Err(e)) => Err(if let Some(_) = &self.as_part_of_include {
|
Some(Err(e)) => Err(if let Some(_) = &as_part_of_include {
|
||||||
CheckError::new().err_with_diff_src(e).src(vec![(
|
CheckError::new().err_with_diff_src(e).src(vec![(
|
||||||
self.pos_in_src.clone(),
|
pos_in_src.clone(),
|
||||||
Some(EColor::StacktraceDescendHashInclude),
|
Some(EColor::StacktraceDescendHashInclude),
|
||||||
)])
|
)])
|
||||||
} else {
|
} else {
|
||||||
CheckError::new().err(e).src(vec![
|
CheckError::new()
|
||||||
(self.pos_in_src.clone(), None),
|
.err(e)
|
||||||
(self.source_range(), Some(EColor::StacktraceDescend)),
|
.src(vec![(pos_in_src.clone(), Some(EColor::StacktraceDescend))])
|
||||||
])
|
|
||||||
}),
|
}),
|
||||||
None => Err(CheckError::new()
|
None => Err(CheckError::new()
|
||||||
.msg_str("tried to chain with non-function".to_owned())
|
.msg_str("tried to chain with non-function".to_owned())
|
||||||
.src(vec![(
|
.src(vec![(func_pos, Some(EColor::ChainWithNonFunction))])),
|
||||||
self.chained.source_range(),
|
|
||||||
Some(EColor::ChainWithNonFunction),
|
|
||||||
)])),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn has_scope(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
fn source_range(&self) -> SourceRange {
|
|
||||||
self.pos_in_src.clone()
|
|
||||||
}
|
|
||||||
fn inner_statements(&self) -> Vec<&dyn MersStatement> {
|
|
||||||
vec![self.first.as_ref(), self.chained.as_ref()]
|
|
||||||
}
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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")]
|
#[cfg(feature = "run")]
|
||||||
pub mod field;
|
pub mod field;
|
||||||
#[cfg(feature = "run")]
|
#[cfg(feature = "run")]
|
||||||
|
pub mod field_chain;
|
||||||
|
#[cfg(feature = "run")]
|
||||||
pub mod function;
|
pub mod function;
|
||||||
#[cfg(feature = "run")]
|
#[cfg(feature = "run")]
|
||||||
pub mod r#if;
|
pub mod r#if;
|
||||||
|
Loading…
Reference in New Issue
Block a user