added match statements.

This commit is contained in:
Dummi26
2023-03-17 16:29:42 +01:00
parent 664de5c347
commit 6712097829
4 changed files with 376 additions and 11 deletions

View File

@@ -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()
}

View File

@@ -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}"),
}
}

View File

@@ -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 {