Prefix dereferences as lvalues

This commit is contained in:
2023-08-13 19:29:50 -05:00
parent 190ceb5f13
commit 1de091f269
4 changed files with 87 additions and 54 deletions
+5 -11
View File
@@ -79,26 +79,20 @@ pub struct Assignment {
} }
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub struct Lvalue { pub enum Lvalue {
pub name: String, Expr(BoxExpr),
pub subscripts: Vec<Suffix>, Name(String, Vec<Suffix>)
} }
impl From<&str> for Lvalue { impl From<&str> for Lvalue {
fn from(name: &str) -> Self { fn from(name: &str) -> Self {
Self { Self::Name(String::from(name), vec![])
name: String::from(name),
subscripts: vec![],
}
} }
} }
impl From<String> for Lvalue { impl From<String> for Lvalue {
fn from(name: String) -> Self { fn from(name: String) -> Self {
Self { Self::Name(name, vec![])
name,
subscripts: vec![],
}
} }
} }
+57 -25
View File
@@ -317,35 +317,43 @@ impl Compilable for Lvalue {
let global_scope = &state.global_scope; let global_scope = &state.global_scope;
let mut sig = sig.expect("lvalue outside a function"); let mut sig = sig.expect("lvalue outside a function");
if !self.subscripts.is_empty() { match self {
todo!("Arrays and structs aren't implemented yet") Self::Name(name, subscripts) => {
} if !subscripts.is_empty() {
todo!("Arrays and structs aren't implemented yet")
}
if let Some(var) = lookup(&self.name, global_scope, &sig.local_scope) { if let Some(var) = lookup(&name, global_scope, &sig.local_scope) {
match var { match var {
Variable::Literal(_) | Variable::DirectLabel(_) => { Variable::Literal(_) | Variable::DirectLabel(_) => {
// Direct labels are (probably) functions, the important part is the // Direct labels are (probably) functions, the important part is the
// label itself, which we can't alter, so, error: // label itself, which we can't alter, so, error:
Err(CompileError(0, 0, format!("Invalid lvalue {}", self.name))) Err(CompileError(0, 0, format!("Invalid lvalue {}", name)))
} }
Variable::IndirectLabel(label) => { Variable::IndirectLabel(label) => {
// Indirect labels are variables, the label is where the data is stored, // Indirect labels are variables, the label is where the data is stored,
// so we push that label so we can store stuff there // so we push that label so we can store stuff there
let label = label.clone(); let label = label.clone();
sig.emit_arg("push", label); sig.emit_arg("push", label);
Ok(()) Ok(())
} }
Variable::Local(offset) => { Variable::Local(offset) => {
let offset = *offset; let offset = *offset;
sig.emit_arg("loadw", "frame"); sig.emit_arg("loadw", "frame");
if offset > 0 { if offset > 0 {
sig.emit_arg("add", offset); sig.emit_arg("add", offset);
}
Ok(())
}
} }
Ok(()) } else {
Err(CompileError(0, 0, format!("Unknown name {}", name)))
} }
} }
} else { Self::Expr(BoxExpr(expr)) => {
Err(CompileError(0, 0, format!("Unknown name {}", self.name))) // Just compile the expression and that's the address to write to
(*expr).process(state, Some(sig))
}
} }
} }
} }
@@ -748,4 +756,28 @@ mod test {
.join("\n") .join("\n")
) )
} }
#[test]
fn test_derefs() {
let mut state = State::default();
parse("fn blah() { var x = 3; *1000 = *x; }")
.unwrap()
.process(&mut state, None)
.expect("Failed to compile");
let body = body_as_string(state.functions.get("blah").unwrap());
assert_eq!(
body,
vec![
"push 3",
"loadw frame",
"storew", // the assignment for x
"loadw frame", // Now we're compiling *x, so load x's value, which is 3
"loadw", // Then load the value at 3
"push 1000", // Push the addr 1000, for the lvalue
"storew" // Store whatever's at 3 to 1000
]
.join("\n")
)
}
} }
+24 -17
View File
@@ -334,15 +334,20 @@ impl AstNode for Lvalue {
const RULE: Rule = Rule::lvalue; const RULE: Rule = Rule::lvalue;
fn from_pair(pair: Pair) -> Self { fn from_pair(pair: Pair) -> Self {
let mut pairs = pair.into_inner(); let mut pairs = pair.into_inner();
let name = String::from(pairs.next().unwrap().as_str()); let first = pairs.next().unwrap();
let subscripts: Vec<_> = pairs if first.as_rule() == Rule::expr {
.map(|p| match p.as_rule() { Self::Expr(Expr::from_pair(first).into())
Rule::subscript => Suffix::Subscript(Expr::from_pair(p.first()).into()), } else {
Rule::member => Suffix::Member(p.first_as_string()), let name = String::from(first.as_str());
_ => unreachable!(), let subscripts: Vec<_> = pairs
}) .map(|p| match p.as_rule() {
.collect(); Rule::subscript => Suffix::Subscript(Expr::from_pair(p.first()).into()),
Self { name, subscripts } Rule::member => Suffix::Member(p.first_as_string()),
_ => unreachable!(),
})
.collect();
Self::Name(name, subscripts)
}
} }
} }
@@ -846,10 +851,7 @@ mod test {
// Addresses // Addresses
assert_eq!( assert_eq!(
Expr::from_str("&foo.bar"), Expr::from_str("&foo.bar"),
Ok(Expr::Address(Lvalue { Ok(Expr::Address(Lvalue::Name("foo".into(), vec![Suffix::Member("bar".into())])))
name: "foo".into(),
subscripts: vec![Suffix::Member("bar".into())]
}))
); );
assert_eq!( assert_eq!(
@@ -930,13 +932,18 @@ mod test {
assert_eq!( assert_eq!(
Assignment::from_str("foo[45] = 7"), Assignment::from_str("foo[45] = 7"),
Ok(Assignment { Ok(Assignment {
lvalue: Lvalue { lvalue: Lvalue::Name("foo".into(), vec![Suffix::Subscript(45.into())]),
name: String::from("foo"),
subscripts: vec![Suffix::Subscript(45.into())]
},
rvalue: Rvalue::Expr(7.into()), rvalue: Rvalue::Expr(7.into()),
}) })
); );
assert_eq!(
Assignment::from_str("*foo = 12"),
Ok(Assignment {
lvalue: Lvalue::Expr("foo".into()),
rvalue: Rvalue::Expr(12.into())
})
);
} }
#[test] #[test]
+1 -1
View File
@@ -20,7 +20,7 @@ string_inner = ${ !("\"" | "\\") ~ ANY | escape }
string = ${ "\"" ~ string_inner* ~ "\"" } string = ${ "\"" ~ string_inner* ~ "\"" }
assignment = { lvalue ~ "=" ~ rvalue } assignment = { lvalue ~ "=" ~ rvalue }
lvalue = { name ~ (modifier)* } lvalue = { (name ~ (modifier)*) | ("*" ~ expr) }
rvalue = { expr | string } rvalue = { expr | string }
add = { "+" } add = { "+" }