diff --git a/on-host/arpist-traject/src/main.rs b/on-host/arpist-traject/src/main.rs index 5f289aaf790bb3b888bf189af8ad16275a1f94da..0837d14e49c93fea67abafd2dadf79ed4c0bb787 100644 --- a/on-host/arpist-traject/src/main.rs +++ b/on-host/arpist-traject/src/main.rs @@ -1,8 +1,11 @@ -use syntax::CachedLexer; - +mod semantics; mod syntax; mod tokens; +use syntax::CachedLexer; + +use crate::semantics::Executor; + // use logos::Logos; // use self::tokens::Token; @@ -15,6 +18,8 @@ fn main() { // println!("{:?}", token.0.as_ref().unwrap()); // } - let p = CachedLexer::new(&input).parse_program(); - dbg!(p); + let p = CachedLexer::new(&input).parse_program().unwrap(); + dbg!(&p); + let traj = Executor::new(p).run().unwrap(); + dbg!(traj); } diff --git a/on-host/arpist-traject/src/semantics.rs b/on-host/arpist-traject/src/semantics.rs new file mode 100644 index 0000000000000000000000000000000000000000..045b5450da811106345c8d99dfe906f2605ff361 --- /dev/null +++ b/on-host/arpist-traject/src/semantics.rs @@ -0,0 +1,232 @@ +use crate::syntax::{ + Blocks, GeneratorKind, GroupExpr, Operation, Operator, Program, SettingKind, TimeAnchorBlock, + TimeExpr, +}; + +pub struct Executor { + initial_coordinates: (f64, f64, f64), + last_timeref: Option<f64>, + generator: Option<PositionParametricGenerator>, + trajectory: Trajectory, + execution: ProgramExecution, +} + +impl Executor { + pub fn new(program: Program) -> Self { + Self { + initial_coordinates: (0.0, 0.0, 0.0), + last_timeref: None, + generator: None, + trajectory: Trajectory { points: vec![] }, + execution: ProgramExecution::from(program), + } + } + + /// Validate the program execution order + fn validate(&self) -> bool { + let execution = self.execution.clone(); + let mut in_settings = true; + let mut started_first_time_ref = false; + let mut waiting_generator = false; + for block in execution { + match block { + Blocks::Setting(_) => { + // check that settings are at the beginning + if !in_settings { + return false; + } + } + Blocks::TimeDefinition(_) => { + // not allowed if waiting for generator + if waiting_generator { + return false; + } + // allow generator after the first time reference + if !started_first_time_ref { + started_first_time_ref = true; + } + // disable settings after the first time reference + in_settings = false; + // wait for generator + waiting_generator = true; + } + Blocks::Generator(_) => { + // check that generator is after the first time reference + if !waiting_generator { + return false; + } + // disable generator after it's been used + waiting_generator = false; + } + } + } + true + } + + pub fn run(mut self) -> Result<Trajectory, String> { + if !self.validate() { + return Err("Invalid program".to_string()); + } + for block in self.execution { + match block { + Blocks::Setting(setting) => match setting.kind { + SettingKind::GPS => { + self.initial_coordinates = ( + **setting.get("latitude").unwrap(), + **setting.get("longitude").unwrap(), + **setting.get("altitude").unwrap(), + ); + } + }, + Blocks::Generator(generator) => match generator.kind { + GeneratorKind::PositionParametric => { + let x = generator.get("x").unwrap(); + let y = generator.get("y").unwrap(); + let z = generator.get("z").unwrap(); + self.generator = Some(PositionParametricGenerator::new( + x.clone(), + y.clone(), + z.clone(), + )); + } + }, + Blocks::TimeDefinition(TimeAnchorBlock { timeref, freq, .. }) => { + match self.last_timeref { + Some(init_t) => { + let generator = self.generator.as_ref().unwrap(); + // get the frequency + let freq = freq.ok_or("Missing frequency.")?; + // set last position as the initial position + let starting_pos = if self.trajectory.points.is_empty() { + let (ix, iy, iz) = self.initial_coordinates; + Point::new(ix, iy, iz, init_t) + } else { + self.trajectory.points.last().unwrap().clone() + }; + let end_t = *timeref; + if end_t > init_t { + let mut prev_t = init_t; + let mut t = prev_t; + while t < end_t { + let (x, y, z) = generator.generate(t - init_t); + let mut p = Point::new(x, y, z, t); + p.absolute_from(&starting_pos); + self.trajectory.push(p); + + // update time reference + prev_t = t; + t = prev_t + freq + } + self.last_timeref = Some(t); + } else { + return Err("Invalid time reference".to_string()); + } + } + None => { + self.last_timeref = Some(*timeref); + } + } + } + } + } + Ok(self.trajectory) + } +} + +#[derive(Debug, Clone)] +pub struct Trajectory { + points: Vec<Point>, +} + +impl Trajectory { + fn push(&mut self, point: Point) { + self.points.push(point); + } +} + +#[derive(Debug, Clone)] +struct Point { + x: f64, + y: f64, + z: f64, + time: f64, +} + +impl Point { + fn new(x: f64, y: f64, z: f64, time: f64) -> Self { + Self { x, y, z, time } + } + + fn absolute_from(&mut self, other: &Point) { + self.x += other.x; + self.y += other.y; + self.z += other.z; + } +} + +#[derive(Debug, Clone)] +struct ProgramExecution { + blocks: Vec<Blocks>, +} + +impl From<Program> for ProgramExecution { + fn from(program: Program) -> Self { + let Program(mut blocks) = program; + blocks.reverse(); + Self { blocks } + } +} + +impl Iterator for ProgramExecution { + type Item = Blocks; + + fn next(&mut self) -> Option<Self::Item> { + self.blocks.pop() + } +} + +impl TimeExpr { + fn eval(&self, t: f64) -> f64 { + match self { + TimeExpr::Group(group) => group.eval(t), + TimeExpr::Num(num) => num.value, + TimeExpr::Op(op) => op.eval(t), + TimeExpr::TimeVar(_) => t, + } + } +} + +impl Operation { + fn eval(&self, t: f64) -> f64 { + let (l, r) = (self.lhs.eval(t), self.rhs.eval(t)); + match self.op { + Operator::Add => l + r, + Operator::Sub => l - r, + Operator::Mul => l * r, + Operator::Div => l / r, + Operator::Pow => l.powf(r), + } + } +} + +impl GroupExpr { + fn eval(&self, t: f64) -> f64 { + self.value.eval(t) + } +} + +struct PositionParametricGenerator { + x: TimeExpr, + y: TimeExpr, + z: TimeExpr, +} + +impl PositionParametricGenerator { + fn new(x: TimeExpr, y: TimeExpr, z: TimeExpr) -> Self { + Self { x, y, z } + } + + fn generate(&self, t: f64) -> (f64, f64, f64) { + (self.x.eval(t), self.y.eval(t), self.z.eval(t)) + } +} diff --git a/on-host/arpist-traject/src/syntax.rs b/on-host/arpist-traject/src/syntax.rs index a48bf781ef365841f67feb3735afb782d1e8b633..f6b27dee025a47c27541b25e43bca26e4ca90ff9 100644 --- a/on-host/arpist-traject/src/syntax.rs +++ b/on-host/arpist-traject/src/syntax.rs @@ -44,6 +44,15 @@ pub struct GeneratorBlock { pub parameters: Vec<ExprParameter>, } +impl GeneratorBlock { + pub fn get(&self, name: &str) -> Option<&TimeExpr> { + self.parameters + .iter() + .find(|p| p.name == name) + .map(|p| &p.value) + } +} + #[derive(Debug, Clone)] pub struct NumberParameter { span: Range<usize>, @@ -55,7 +64,7 @@ pub struct NumberParameter { pub struct ExprParameter { span: Range<usize>, name: String, - value: TimeExpr, + pub value: TimeExpr, } #[derive(Debug, Clone)] @@ -80,15 +89,15 @@ impl TimeExpr { #[derive(Debug, Clone)] pub struct GroupExpr { span: Range<usize>, - value: Box<TimeExpr>, + pub value: Box<TimeExpr>, } #[derive(Debug, Clone)] pub struct Operation { span: Range<usize>, - op: Operator, - lhs: Box<TimeExpr>, - rhs: Box<TimeExpr>, + pub op: Operator, + pub lhs: Box<TimeExpr>, + pub rhs: Box<TimeExpr>, } #[derive(Debug, Clone)] @@ -103,7 +112,7 @@ pub enum Operator { #[derive(Debug, Clone)] pub struct Number { span: Range<usize>, - value: f64, + pub value: f64, } impl Deref for Number { diff --git a/on-host/test.txt b/on-host/test.txt index d2e9fa60490bf2bf72a1975ad9ca7848a4a453a0..1115aea1b082ffe3e6ff12754613fd942c094e3a 100644 --- a/on-host/test.txt +++ b/on-host/test.txt @@ -1,7 +1,7 @@ = [GPS] -\ x: 0 -\ y: 0 -\ z: 0 +\ latitude: 0 +\ longitude: 0 +\ altitude: 0 | @ T+0 |