mirror of
https://github.com/Dummi26/mers.git
synced 2025-12-14 19:26:17 +01:00
added match statements.
This commit is contained in:
@@ -256,7 +256,7 @@ fn parse_statement_adv(
|
||||
file.skip_whitespaces();
|
||||
if let Some('{') = file.next() {
|
||||
} else {
|
||||
eprintln!("switch statement should be followed by {{ (because they must be closed by }}). This might lead to errors when parsing, although it isn't fatal.");
|
||||
eprintln!("switch statements should be followed by {{ (because they must be closed by }}). This might lead to errors when parsing, although it isn't fatal.");
|
||||
}
|
||||
let mut cases = vec![];
|
||||
loop {
|
||||
@@ -269,6 +269,31 @@ fn parse_statement_adv(
|
||||
}
|
||||
break SStatementEnum::Switch(switch_on_what, cases, force).into();
|
||||
}
|
||||
"match" => {
|
||||
let mut match_what = String::new();
|
||||
loop {
|
||||
match file.next() {
|
||||
None => break,
|
||||
Some(ch) if ch.is_whitespace() => break,
|
||||
Some(ch) => match_what.push(ch),
|
||||
}
|
||||
}
|
||||
file.skip_whitespaces();
|
||||
if let Some('{') = file.next() {
|
||||
} else {
|
||||
eprintln!("match statements should be followed by {{ (because they must be closed by }}). This might lead to errors when parsing, although it isn't fatal.");
|
||||
}
|
||||
let mut cases = vec![];
|
||||
loop {
|
||||
file.skip_whitespaces();
|
||||
if let Some('}') = file.peek() {
|
||||
file.next();
|
||||
break;
|
||||
}
|
||||
cases.push((parse_statement(file)?, parse_statement(file)?));
|
||||
}
|
||||
break SStatementEnum::Match(match_what, cases).into();
|
||||
}
|
||||
"true" => {
|
||||
break SStatementEnum::Value(VDataEnum::Bool(true).to()).into()
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ pub enum SStatementEnum {
|
||||
While(SStatement),
|
||||
For(String, SStatement, SStatement),
|
||||
Switch(String, Vec<(VType, SStatement)>, bool),
|
||||
// Match(???),
|
||||
Match(String, Vec<(SStatement, SStatement)>),
|
||||
IndexFixed(SStatement, usize),
|
||||
}
|
||||
impl Into<SStatement> for SStatementEnum {
|
||||
@@ -117,6 +117,7 @@ pub mod to_runnable {
|
||||
},
|
||||
InvalidTypeForWhileLoop(VType),
|
||||
CaseForceButTypeNotCovered(VType),
|
||||
MatchConditionInvalidReturn(VType),
|
||||
NotIndexableFixed(VType, usize),
|
||||
}
|
||||
impl Debug for ToRunnableError {
|
||||
@@ -143,6 +144,7 @@ pub mod to_runnable {
|
||||
}
|
||||
Self::InvalidTypeForWhileLoop(v) => write!(f, "Invalid type: Expected bool or Tuples of length 0 or 1 as return types for the while loop, but found {v:?} instead."),
|
||||
Self::CaseForceButTypeNotCovered(v) => write!(f, "Switch! statement, but not all types covered. Types to cover: {v}"),
|
||||
Self::MatchConditionInvalidReturn(v) => write!(f, "match statement condition returned {v}, which is not necessarily a tuple of size 0 to 1."),
|
||||
Self::NotIndexableFixed(t, i) => write!(f, "Cannot use fixed-index {i} on type {t}."),
|
||||
}
|
||||
}
|
||||
@@ -386,10 +388,11 @@ pub mod to_runnable {
|
||||
let o = RStatementEnum::For(for_loop_var, container, block);
|
||||
o
|
||||
}
|
||||
|
||||
SStatementEnum::Switch(switch_on, cases, force) => {
|
||||
if let Some(switch_on_v) = linfo.vars.get(switch_on).cloned() {
|
||||
let mut ncases = Vec::with_capacity(cases.len());
|
||||
let og_type = linfo.vars.get(switch_on).unwrap().1.clone();
|
||||
let og_type = switch_on_v.1.clone(); // linfo.vars.get(switch_on).unwrap().1.clone();
|
||||
for case in cases {
|
||||
linfo.vars.get_mut(switch_on).unwrap().1 = case.0.clone();
|
||||
ncases.push((case.0.clone(), statement(&case.1, ginfo, linfo)?));
|
||||
@@ -426,6 +429,56 @@ pub mod to_runnable {
|
||||
return Err(ToRunnableError::UseOfUndefinedVariable(switch_on.clone()));
|
||||
}
|
||||
}
|
||||
SStatementEnum::Match(match_on, cases) => {
|
||||
if let Some(switch_on_v) = linfo.vars.get(match_on).cloned() {
|
||||
let mut ncases = Vec::with_capacity(cases.len());
|
||||
let og_type = switch_on_v.1.clone(); // linfo.vars.get(match_on).unwrap().1.clone();
|
||||
for case in cases {
|
||||
let case_condition = statement(&case.0, ginfo, linfo)?;
|
||||
let case_condition_out = case_condition.out();
|
||||
let mut refutable = false;
|
||||
let mut success_output = VType { types: vec![] };
|
||||
for case_type in case_condition_out.types.iter() {
|
||||
match case_type {
|
||||
VSingleType::Tuple(tuple) =>
|
||||
match tuple.len() {
|
||||
0 => refutable = true,
|
||||
1 => success_output = success_output | &tuple[0],
|
||||
_ => return Err(ToRunnableError::MatchConditionInvalidReturn(case_condition_out)),
|
||||
},
|
||||
VSingleType::Bool => {
|
||||
refutable = true;
|
||||
success_output = success_output | VSingleType::Bool.to()
|
||||
}
|
||||
_ => success_output = success_output | case_type.clone().to(),
|
||||
}
|
||||
}
|
||||
if refutable == false {
|
||||
eprintln!("WARN: Irrefutable match condition with return type {}", case_condition_out);
|
||||
}
|
||||
if !success_output.types.is_empty() {
|
||||
let var = linfo.vars.get_mut(match_on).unwrap();
|
||||
let og = var.1.clone();
|
||||
var.1 = success_output;
|
||||
let case_action = statement(&case.1, ginfo, linfo)?;
|
||||
linfo.vars.get_mut(match_on).unwrap().1 = og;
|
||||
ncases.push((case_condition, case_action));
|
||||
} else {
|
||||
eprintln!("WARN: Match condition with return type {} never returns a match and will be ignored entirely. Note: this also skips type-checking for the action part of this match arm because the success type is not known.", case_condition_out);
|
||||
}
|
||||
|
||||
}
|
||||
linfo.vars.get_mut(match_on).unwrap().1 = og_type;
|
||||
|
||||
RStatementEnum::Match(
|
||||
switch_on_v.0,
|
||||
ncases,
|
||||
)
|
||||
} else {
|
||||
return Err(ToRunnableError::UseOfUndefinedVariable(match_on.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
SStatementEnum::IndexFixed(st, i) => {
|
||||
let st = statement(st, ginfo, linfo)?;
|
||||
let ok = 'ok: {
|
||||
@@ -589,6 +642,7 @@ pub enum RStatementEnum {
|
||||
While(RStatement),
|
||||
For(usize, RStatement, RStatement),
|
||||
Switch(RStatement, Vec<(VType, RStatement)>),
|
||||
Match(usize, Vec<(RStatement, RStatement)>),
|
||||
IndexFixed(RStatement, usize),
|
||||
}
|
||||
impl RStatementEnum {
|
||||
@@ -694,6 +748,24 @@ impl RStatementEnum {
|
||||
}
|
||||
out
|
||||
}
|
||||
Self::Match(match_on, cases) => 'm: {
|
||||
for (case_condition, case_action) in cases {
|
||||
// [t] => Some(t), t => Some(t), [] => None
|
||||
if let Some(v) = match case_condition.run(vars).data {
|
||||
VDataEnum::Tuple(mut tuple) => tuple.pop(),
|
||||
VDataEnum::Bool(v) => if v { Some(VDataEnum::Bool(v).to()) } else { None },
|
||||
other => Some(other.to()),
|
||||
} {
|
||||
let og = {
|
||||
std::mem::replace(&mut *vars[*match_on].lock().unwrap(), v)
|
||||
};
|
||||
let res = case_action.run(vars);
|
||||
*vars[*match_on].lock().unwrap() = og;
|
||||
break 'm res;
|
||||
}
|
||||
}
|
||||
VDataEnum::Tuple(vec![]).to()
|
||||
}
|
||||
Self::IndexFixed(st, i) => st.run(vars).get(*i).unwrap(),
|
||||
}
|
||||
}
|
||||
@@ -782,6 +854,13 @@ impl RStatementEnum {
|
||||
}
|
||||
out
|
||||
}
|
||||
Self::Match(_, cases) => {
|
||||
let mut out = VSingleType::Tuple(vec![]).to();
|
||||
for case in cases {
|
||||
out = out | case.1.out();
|
||||
}
|
||||
out
|
||||
}
|
||||
Self::IndexFixed(st, i) => st.out().get(*i).unwrap(),
|
||||
}
|
||||
}
|
||||
@@ -949,6 +1028,13 @@ impl Display for SStatementEnum {
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
SStatementEnum::Match(match_on, cases) => {
|
||||
writeln!(f, "match {match_on} {{")?;
|
||||
for (case_cond, case_action) in cases.iter() {
|
||||
writeln!(f, "{} {}", case_cond, case_action)?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
SStatementEnum::IndexFixed(st, i) => write!(f, "{st}.{i}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,14 @@ pub enum BuiltinFunction {
|
||||
Print,
|
||||
Println,
|
||||
Debug,
|
||||
// stdin
|
||||
StdinReadLine,
|
||||
// format
|
||||
ToString,
|
||||
Format,
|
||||
// math and basic operators (not possible, need to be different for each type)
|
||||
// Add,
|
||||
// Sub,
|
||||
// Mul,
|
||||
// Div,
|
||||
// Mod,
|
||||
// match
|
||||
ParseInt,
|
||||
ParseFloat,
|
||||
// functions
|
||||
Run,
|
||||
Thread,
|
||||
@@ -47,6 +46,13 @@ pub enum BuiltinFunction {
|
||||
Div,
|
||||
Mod,
|
||||
Pow,
|
||||
Eq,
|
||||
Gt,
|
||||
Lt,
|
||||
Gtoe,
|
||||
Ltoe,
|
||||
Min,
|
||||
Max,
|
||||
// List
|
||||
Push,
|
||||
Insert,
|
||||
@@ -62,8 +68,11 @@ impl BuiltinFunction {
|
||||
"print" => Self::Print,
|
||||
"println" => Self::Println,
|
||||
"debug" => Self::Debug,
|
||||
"read_line" => Self::StdinReadLine,
|
||||
"to_string" => Self::ToString,
|
||||
"format" => Self::Format,
|
||||
"parse_int" => Self::ParseInt,
|
||||
"parse_float" => Self::ParseFloat,
|
||||
"run" => Self::Run,
|
||||
"thread" => Self::Thread,
|
||||
"await" => Self::Await,
|
||||
@@ -83,6 +92,13 @@ impl BuiltinFunction {
|
||||
"div" => Self::Div,
|
||||
"mod" => Self::Mod,
|
||||
"pow" => Self::Pow,
|
||||
"eq" => Self::Eq,
|
||||
"lt" => Self::Lt,
|
||||
"gt" => Self::Gt,
|
||||
"ltoe" => Self::Ltoe,
|
||||
"gtoe" => Self::Gtoe,
|
||||
"min" => Self::Min,
|
||||
"max" => Self::Max,
|
||||
"push" => Self::Push,
|
||||
"insert" => Self::Insert,
|
||||
"pop" => Self::Pop,
|
||||
@@ -110,6 +126,10 @@ impl BuiltinFunction {
|
||||
false
|
||||
}
|
||||
}
|
||||
Self::StdinReadLine => input.is_empty(),
|
||||
Self::ParseInt | Self::ParseFloat => {
|
||||
input.len() == 1 && input[0].fits_in(&VSingleType::String.to()).is_empty()
|
||||
}
|
||||
Self::Run | Self::Thread => {
|
||||
if input.len() >= 1 {
|
||||
input[0].types.iter().all(|v| {
|
||||
@@ -184,7 +204,31 @@ impl BuiltinFunction {
|
||||
false
|
||||
}
|
||||
}
|
||||
Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Mod | Self::Pow => {
|
||||
Self::Eq => {
|
||||
if input.len() == 2 {
|
||||
let num = &VType {
|
||||
types: vec![VSingleType::Int, VSingleType::Float],
|
||||
};
|
||||
let string = &VSingleType::String.to();
|
||||
(input[0].fits_in(num).is_empty() && input[1].fits_in(num).is_empty())
|
||||
|| (input[0].fits_in(string).is_empty()
|
||||
&& input[1].fits_in(string).is_empty())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Self::Add
|
||||
| Self::Sub
|
||||
| Self::Mul
|
||||
| Self::Div
|
||||
| Self::Mod
|
||||
| Self::Pow
|
||||
| Self::Gt
|
||||
| Self::Lt
|
||||
| Self::Gtoe
|
||||
| Self::Ltoe
|
||||
| Self::Min
|
||||
| Self::Max => {
|
||||
input.len() == 2 && {
|
||||
let num = VType {
|
||||
types: vec![VSingleType::Int, VSingleType::Float],
|
||||
@@ -229,8 +273,15 @@ impl BuiltinFunction {
|
||||
Self::Print | Self::Println | Self::Debug | Self::Sleep => VType {
|
||||
types: vec![VSingleType::Tuple(vec![])],
|
||||
},
|
||||
Self::StdinReadLine => VSingleType::String.to(),
|
||||
// String
|
||||
Self::ToString | Self::Format => VSingleType::String.into(),
|
||||
Self::ParseInt => VType {
|
||||
types: vec![VSingleType::Tuple(vec![]), VSingleType::Int],
|
||||
},
|
||||
Self::ParseFloat => VType {
|
||||
types: vec![VSingleType::Tuple(vec![]), VSingleType::Float],
|
||||
},
|
||||
// !
|
||||
Self::Run | Self::Thread => {
|
||||
if let Some(funcs) = input.first() {
|
||||
@@ -338,7 +389,14 @@ impl BuiltinFunction {
|
||||
]),
|
||||
],
|
||||
},
|
||||
Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Mod | Self::Pow => {
|
||||
Self::Add
|
||||
| Self::Sub
|
||||
| Self::Mul
|
||||
| Self::Div
|
||||
| Self::Mod
|
||||
| Self::Pow
|
||||
| Self::Min
|
||||
| Self::Max => {
|
||||
if input.len() == 2 {
|
||||
match (
|
||||
(
|
||||
@@ -360,6 +418,7 @@ impl BuiltinFunction {
|
||||
unreachable!("called add/sub/mul/div/mod/pow with args != 2")
|
||||
}
|
||||
}
|
||||
Self::Eq | Self::Lt | Self::Gt | Self::Ltoe | Self::Gtoe => VSingleType::Bool.to(),
|
||||
Self::Push | Self::Insert => VSingleType::Tuple(vec![]).into(),
|
||||
Self::Len => VSingleType::Int.into(),
|
||||
}
|
||||
@@ -386,6 +445,11 @@ impl BuiltinFunction {
|
||||
println!("{:#?}", args[0].run(vars).data);
|
||||
VDataEnum::Tuple(vec![]).to()
|
||||
}
|
||||
Self::StdinReadLine => {
|
||||
let mut line = String::new();
|
||||
_ = std::io::stdin().read_line(&mut line);
|
||||
VDataEnum::String(line.trim_end_matches(['\n', '\r']).to_string()).to()
|
||||
}
|
||||
BuiltinFunction::ToString => {
|
||||
VDataEnum::String(format!("{}", args[0].run(vars).data)).to()
|
||||
}
|
||||
@@ -400,6 +464,36 @@ impl BuiltinFunction {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
BuiltinFunction::ParseInt => {
|
||||
if args.len() == 1 {
|
||||
if let VDataEnum::String(s) = args[0].run(vars).data {
|
||||
if let Ok(s) = s.parse() {
|
||||
VDataEnum::Int(s).to()
|
||||
} else {
|
||||
VDataEnum::Tuple(vec![]).to()
|
||||
}
|
||||
} else {
|
||||
unreachable!("parse arg not string")
|
||||
}
|
||||
} else {
|
||||
unreachable!("parse args != 1")
|
||||
}
|
||||
}
|
||||
BuiltinFunction::ParseFloat => {
|
||||
if args.len() == 1 {
|
||||
if let VDataEnum::String(s) = args[0].run(vars).data {
|
||||
if let Ok(s) = s.parse() {
|
||||
VDataEnum::Float(s).to()
|
||||
} else {
|
||||
VDataEnum::Tuple(vec![]).to()
|
||||
}
|
||||
} else {
|
||||
unreachable!("parse arg not string")
|
||||
}
|
||||
} else {
|
||||
unreachable!("parse args != 1")
|
||||
}
|
||||
}
|
||||
BuiltinFunction::Run => {
|
||||
if args.len() >= 1 {
|
||||
if let VDataEnum::Function(f) = args[0].run(vars).data {
|
||||
@@ -780,6 +874,132 @@ impl BuiltinFunction {
|
||||
unreachable!("pow: not 2 args")
|
||||
}
|
||||
}
|
||||
Self::Eq => {
|
||||
if args.len() == 2 {
|
||||
match (args[0].run(vars).data, args[1].run(vars).data) {
|
||||
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a == b).to(),
|
||||
(VDataEnum::Int(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Bool(a as f64 == b).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Int(b)) => {
|
||||
VDataEnum::Bool(a == b as f64).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a == b).to(),
|
||||
(VDataEnum::String(a), VDataEnum::String(b)) => {
|
||||
VDataEnum::Bool(a == b).to()
|
||||
}
|
||||
_ => unreachable!("eq: not a number"),
|
||||
}
|
||||
} else {
|
||||
unreachable!("eq: not 2 args")
|
||||
}
|
||||
}
|
||||
Self::Gt => {
|
||||
if args.len() == 2 {
|
||||
match (args[0].run(vars).data, args[1].run(vars).data) {
|
||||
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a > b).to(),
|
||||
(VDataEnum::Int(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Bool(a as f64 > b).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Int(b)) => {
|
||||
VDataEnum::Bool(a > b as f64).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a > b).to(),
|
||||
_ => unreachable!("gt: not a number"),
|
||||
}
|
||||
} else {
|
||||
unreachable!("gt: not 2 args")
|
||||
}
|
||||
}
|
||||
Self::Lt => {
|
||||
if args.len() == 2 {
|
||||
match (args[0].run(vars).data, args[1].run(vars).data) {
|
||||
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a < b).to(),
|
||||
(VDataEnum::Int(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Bool((a as f64) < b).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Int(b)) => {
|
||||
VDataEnum::Bool(a < b as f64).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a < b).to(),
|
||||
_ => unreachable!("lt: not a number"),
|
||||
}
|
||||
} else {
|
||||
unreachable!("lt: not 2 args")
|
||||
}
|
||||
}
|
||||
Self::Gtoe => {
|
||||
if args.len() == 2 {
|
||||
match (args[0].run(vars).data, args[1].run(vars).data) {
|
||||
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a >= b).to(),
|
||||
(VDataEnum::Int(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Bool(a as f64 >= b).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Int(b)) => {
|
||||
VDataEnum::Bool(a >= b as f64).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a >= b).to(),
|
||||
_ => unreachable!("gtoe: not a number"),
|
||||
}
|
||||
} else {
|
||||
unreachable!("gtoe: not 2 args")
|
||||
}
|
||||
}
|
||||
Self::Ltoe => {
|
||||
if args.len() == 2 {
|
||||
match (args[0].run(vars).data, args[1].run(vars).data) {
|
||||
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Bool(a <= b).to(),
|
||||
(VDataEnum::Int(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Bool(a as f64 <= b).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Int(b)) => {
|
||||
VDataEnum::Bool(a <= b as f64).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Float(b)) => VDataEnum::Bool(a <= b).to(),
|
||||
_ => unreachable!("ltoe: not a number"),
|
||||
}
|
||||
} else {
|
||||
unreachable!("ltoe: not 2 args")
|
||||
}
|
||||
}
|
||||
Self::Min => {
|
||||
if args.len() == 2 {
|
||||
match (args[0].run(vars).data, args[1].run(vars).data) {
|
||||
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.min(b)).to(),
|
||||
(VDataEnum::Int(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Float((a as f64).min(b)).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Int(b)) => {
|
||||
VDataEnum::Float(a.min(b as f64)).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Float(a.min(b)).to()
|
||||
}
|
||||
_ => unreachable!("min: not a number"),
|
||||
}
|
||||
} else {
|
||||
unreachable!("min: not 2 args")
|
||||
}
|
||||
}
|
||||
Self::Max => {
|
||||
if args.len() == 2 {
|
||||
match (args[0].run(vars).data, args[1].run(vars).data) {
|
||||
(VDataEnum::Int(a), VDataEnum::Int(b)) => VDataEnum::Int(a.max(b)).to(),
|
||||
(VDataEnum::Int(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Float((a as f64).max(b)).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Int(b)) => {
|
||||
VDataEnum::Float(a.max(b as f64)).to()
|
||||
}
|
||||
(VDataEnum::Float(a), VDataEnum::Float(b)) => {
|
||||
VDataEnum::Float(a.max(b)).to()
|
||||
}
|
||||
_ => unreachable!("max: not a number"),
|
||||
}
|
||||
} else {
|
||||
unreachable!("max: not 2 args")
|
||||
}
|
||||
}
|
||||
Self::Push => {
|
||||
if args.len() == 2 {
|
||||
if let VDataEnum::Reference(v) = args[0].run(vars).data {
|
||||
|
||||
Reference in New Issue
Block a user