|
@@ -1,29 +1,10 @@
|
|
|
-use std::collections::HashMap;
|
|
|
-
|
|
|
use super::{
|
|
|
expr::{Expr, ExprOp},
|
|
|
opcode::OpCode,
|
|
|
- parser::parse_query,
|
|
|
- runtime::execute,
|
|
|
value::Value,
|
|
|
Addr, Error, Register,
|
|
|
};
|
|
|
-use crate::Transaction;
|
|
|
-
|
|
|
-#[derive(Debug, Clone)]
|
|
|
-pub struct Program {
|
|
|
- /// Debug op-codes, with the unresolved labels.
|
|
|
- dbg_opcodes: Vec<OpCode>,
|
|
|
- /// The list of opcodes that make up the program
|
|
|
- opcodes: Vec<OpCode>,
|
|
|
- /// If the program has some boilerplate that can be skipped. This is non-zero when the initial
|
|
|
- /// program has been executed at compile time, and the register has been populated to
|
|
|
- /// `initial_register`. Everytime a new Runtime is created, instead of executing the program,
|
|
|
- /// it can safely start-off a different Address, and clone the initial_register.
|
|
|
- start_at: Addr,
|
|
|
- /// The state of the register
|
|
|
- initial_register: Vec<Value>,
|
|
|
-}
|
|
|
+use std::collections::HashMap;
|
|
|
|
|
|
pub struct Compiler<'a> {
|
|
|
expr: &'a Expr,
|
|
@@ -58,7 +39,7 @@ impl<'a> Compiler<'a> {
|
|
|
let mut return_value = self.next_register();
|
|
|
Ok(match expr {
|
|
|
Expr::Variable(name) => (
|
|
|
- vec![OpCode::LOAD_EXTERNAL(return_value, name.into())],
|
|
|
+ vec![OpCode::LOAD_EXTERNAL(return_value, name.clone())],
|
|
|
return_value,
|
|
|
),
|
|
|
Expr::String(string) => (
|
|
@@ -89,7 +70,6 @@ impl<'a> Compiler<'a> {
|
|
|
return_value = term_return_value;
|
|
|
}
|
|
|
ExprOp::Eq => {
|
|
|
- let cmp = self.next_register();
|
|
|
let last_value = self.next_register();
|
|
|
let mut iter = compiled_terms.into_iter();
|
|
|
let (mut term_opcodes, return_from_expr) =
|
|
@@ -100,8 +80,8 @@ impl<'a> Compiler<'a> {
|
|
|
|
|
|
for (mut term_opcodes, term_return) in iter {
|
|
|
opcodes.append(&mut term_opcodes);
|
|
|
- opcodes.push(OpCode::EQ(cmp, last_value, term_return));
|
|
|
- opcodes.push(OpCode::JNE(cmp, exit_label));
|
|
|
+ opcodes.push(OpCode::EQ(return_value, last_value, term_return));
|
|
|
+ opcodes.push(OpCode::JNE(return_value, exit_label));
|
|
|
opcodes.push(OpCode::MOV(last_value, term_return));
|
|
|
}
|
|
|
|
|
@@ -147,7 +127,7 @@ impl<'a> Compiler<'a> {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- fn resolve_label_to_addr(opcodes: Vec<OpCode>) -> Result<Vec<OpCode>, Error> {
|
|
|
+ pub fn resolve_label_to_addr(opcodes: Vec<OpCode>) -> Result<Vec<OpCode>, Error> {
|
|
|
let mut pos = 0;
|
|
|
let used_labels = opcodes
|
|
|
.iter()
|
|
@@ -168,6 +148,7 @@ impl<'a> Compiler<'a> {
|
|
|
.into_iter()
|
|
|
.filter(|opcode| !matches!(opcode, OpCode::LABEL(_)))
|
|
|
.map(|opcode| {
|
|
|
+ // Rewrite JMP to not use labels but instead addresses
|
|
|
Ok(match opcode {
|
|
|
OpCode::JMP(label) => {
|
|
|
OpCode::JMP(*used_labels.get(&label).ok_or(Error::UnknownLabel(*label))?)
|
|
@@ -180,7 +161,7 @@ impl<'a> Compiler<'a> {
|
|
|
register,
|
|
|
*used_labels.get(&label).ok_or(Error::UnknownLabel(*label))?,
|
|
|
),
|
|
|
- x => x,
|
|
|
+ opcode => opcode,
|
|
|
})
|
|
|
})
|
|
|
.collect()
|
|
@@ -194,98 +175,3 @@ impl<'a> Compiler<'a> {
|
|
|
Ok(opcodes)
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-impl Program {
|
|
|
- pub fn new(code: &str) -> Result<Self, Error> {
|
|
|
- let ast = parse_query(code)?;
|
|
|
-
|
|
|
- let opcodes = ast.where_clause.map_or_else(
|
|
|
- || {
|
|
|
- Ok(vec![
|
|
|
- OpCode::LOAD(0.into(), true.into()),
|
|
|
- OpCode::HLT(0.into()),
|
|
|
- ])
|
|
|
- },
|
|
|
- |expr| Compiler::new(&expr).compile(),
|
|
|
- )?;
|
|
|
-
|
|
|
- Ok(Self {
|
|
|
- dbg_opcodes: opcodes.clone(),
|
|
|
- opcodes: Compiler::resolve_label_to_addr(opcodes)?,
|
|
|
- start_at: 0.into(),
|
|
|
- initial_register: vec![],
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- /// Returns a human readable version of the compiled program (generated op-codes)
|
|
|
- pub fn debug(&self) -> String {
|
|
|
- self.dbg_opcodes
|
|
|
- .iter()
|
|
|
- .map(|x| match x {
|
|
|
- OpCode::HLT(_) | OpCode::LABEL(_) => x.to_string(),
|
|
|
- x => format!("\t{}", x.to_string()),
|
|
|
- })
|
|
|
- .collect::<Vec<_>>()
|
|
|
- .join("\n")
|
|
|
- }
|
|
|
-
|
|
|
- pub fn dump(&self) -> String {
|
|
|
- self.opcodes
|
|
|
- .iter()
|
|
|
- .enumerate()
|
|
|
- .map(|(pos, opcode)| format!("{}: {}", pos, opcode.to_string()))
|
|
|
- .collect::<Vec<_>>()
|
|
|
- .join("\n")
|
|
|
- }
|
|
|
-
|
|
|
- /// Returns the opcodes of the program expression and the usize of the register where the result
|
|
|
- /// Creates a program tailored for a transaction
|
|
|
- pub fn execute(&self, transaction: Option<&Transaction>) -> Result<Value, Error> {
|
|
|
- execute(
|
|
|
- transaction,
|
|
|
- &self.opcodes,
|
|
|
- self.initial_register.clone(),
|
|
|
- self.start_at,
|
|
|
- )
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(test)]
|
|
|
-mod test {
|
|
|
- use super::Program;
|
|
|
- use crate::filter_expr::{opcode::OpCode, value::Value};
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn parse() {
|
|
|
- let x = Program::new(
|
|
|
- r#"
|
|
|
- WHERE
|
|
|
- $foo = 3
|
|
|
- AND $bar = "bar"
|
|
|
- AND ($a = $b OR $b = $c)
|
|
|
- AND $foo = 1 + 2 + 3 + ((1+ 2-3*4-5) * $bar.tx.lol)
|
|
|
- ORDER BY $bar DESC
|
|
|
- "#,
|
|
|
- )
|
|
|
- .unwrap();
|
|
|
- panic!("{}\n\n{}", x.debug(), x.dump());
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn simple_program() {
|
|
|
- let program = Program {
|
|
|
- dbg_opcodes: vec![],
|
|
|
- opcodes: vec![
|
|
|
- OpCode::LOAD(1.into(), 12.into()),
|
|
|
- OpCode::LOAD(2.into(), 13.into()),
|
|
|
- OpCode::ADD(3.into(), 1.into(), 2.into()),
|
|
|
- OpCode::ADD(4.into(), 0.into(), 3.into()),
|
|
|
- OpCode::HLT(4.into()),
|
|
|
- ],
|
|
|
- start_at: 0.into(),
|
|
|
- initial_register: vec![15.into()],
|
|
|
- };
|
|
|
- let x = program.execute(None).expect("valid execution");
|
|
|
- assert_eq!(x, 40.into());
|
|
|
- }
|
|
|
-}
|