diff --git a/forge_core/src/ast.rs b/forge_core/src/ast.rs index 27617d4..f278f6a 100644 --- a/forge_core/src/ast.rs +++ b/forge_core/src/ast.rs @@ -79,26 +79,20 @@ pub struct Assignment { } #[derive(PartialEq, Clone, Debug)] -pub struct Lvalue { - pub name: String, - pub subscripts: Vec, +pub enum Lvalue { + Expr(BoxExpr), + Name(String, Vec) } impl From<&str> for Lvalue { fn from(name: &str) -> Self { - Self { - name: String::from(name), - subscripts: vec![], - } + Self::Name(String::from(name), vec![]) } } impl From for Lvalue { fn from(name: String) -> Self { - Self { - name, - subscripts: vec![], - } + Self::Name(name, vec![]) } } diff --git a/forge_core/src/compiler.rs b/forge_core/src/compiler.rs index 4362174..0c8fbcb 100644 --- a/forge_core/src/compiler.rs +++ b/forge_core/src/compiler.rs @@ -317,35 +317,43 @@ impl Compilable for Lvalue { let global_scope = &state.global_scope; let mut sig = sig.expect("lvalue outside a function"); - if !self.subscripts.is_empty() { - todo!("Arrays and structs aren't implemented yet") - } + match self { + 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) { - match var { - Variable::Literal(_) | Variable::DirectLabel(_) => { - // Direct labels are (probably) functions, the important part is the - // label itself, which we can't alter, so, error: - Err(CompileError(0, 0, format!("Invalid lvalue {}", self.name))) - } - Variable::IndirectLabel(label) => { - // Indirect labels are variables, the label is where the data is stored, - // so we push that label so we can store stuff there - let label = label.clone(); - sig.emit_arg("push", label); - Ok(()) - } - Variable::Local(offset) => { - let offset = *offset; - sig.emit_arg("loadw", "frame"); - if offset > 0 { - sig.emit_arg("add", offset); + if let Some(var) = lookup(&name, global_scope, &sig.local_scope) { + match var { + Variable::Literal(_) | Variable::DirectLabel(_) => { + // Direct labels are (probably) functions, the important part is the + // label itself, which we can't alter, so, error: + Err(CompileError(0, 0, format!("Invalid lvalue {}", name))) + } + Variable::IndirectLabel(label) => { + // Indirect labels are variables, the label is where the data is stored, + // so we push that label so we can store stuff there + let label = label.clone(); + sig.emit_arg("push", label); + Ok(()) + } + Variable::Local(offset) => { + let offset = *offset; + sig.emit_arg("loadw", "frame"); + if offset > 0 { + sig.emit_arg("add", offset); + } + Ok(()) + } } - Ok(()) + } else { + Err(CompileError(0, 0, format!("Unknown name {}", name))) } } - } else { - Err(CompileError(0, 0, format!("Unknown name {}", self.name))) + Self::Expr(BoxExpr(expr)) => { + // 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") ) } + + #[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") + ) + + } } diff --git a/forge_core/src/forge_parser.rs b/forge_core/src/forge_parser.rs index f17a867..410208b 100644 --- a/forge_core/src/forge_parser.rs +++ b/forge_core/src/forge_parser.rs @@ -334,15 +334,20 @@ impl AstNode for Lvalue { const RULE: Rule = Rule::lvalue; fn from_pair(pair: Pair) -> Self { let mut pairs = pair.into_inner(); - let name = String::from(pairs.next().unwrap().as_str()); - let subscripts: Vec<_> = pairs - .map(|p| match p.as_rule() { - Rule::subscript => Suffix::Subscript(Expr::from_pair(p.first()).into()), - Rule::member => Suffix::Member(p.first_as_string()), - _ => unreachable!(), - }) - .collect(); - Self { name, subscripts } + let first = pairs.next().unwrap(); + if first.as_rule() == Rule::expr { + Self::Expr(Expr::from_pair(first).into()) + } else { + let name = String::from(first.as_str()); + let subscripts: Vec<_> = pairs + .map(|p| match p.as_rule() { + Rule::subscript => Suffix::Subscript(Expr::from_pair(p.first()).into()), + Rule::member => Suffix::Member(p.first_as_string()), + _ => unreachable!(), + }) + .collect(); + Self::Name(name, subscripts) + } } } @@ -846,10 +851,7 @@ mod test { // Addresses assert_eq!( Expr::from_str("&foo.bar"), - Ok(Expr::Address(Lvalue { - name: "foo".into(), - subscripts: vec![Suffix::Member("bar".into())] - })) + Ok(Expr::Address(Lvalue::Name("foo".into(), vec![Suffix::Member("bar".into())]))) ); assert_eq!( @@ -930,13 +932,18 @@ mod test { assert_eq!( Assignment::from_str("foo[45] = 7"), Ok(Assignment { - lvalue: Lvalue { - name: String::from("foo"), - subscripts: vec![Suffix::Subscript(45.into())] - }, + lvalue: Lvalue::Name("foo".into(), vec![Suffix::Subscript(45.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] diff --git a/forge_core/src/new.pest b/forge_core/src/new.pest index d07d237..d06ed32 100644 --- a/forge_core/src/new.pest +++ b/forge_core/src/new.pest @@ -20,7 +20,7 @@ string_inner = ${ !("\"" | "\\") ~ ANY | escape } string = ${ "\"" ~ string_inner* ~ "\"" } assignment = { lvalue ~ "=" ~ rvalue } -lvalue = { name ~ (modifier)* } +lvalue = { (name ~ (modifier)*) | ("*" ~ expr) } rvalue = { expr | string } add = { "+" }