| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2026 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use crate::{
- error::{Error, Result},
- //prop::{Property, PropertySubType, PropertyType, PropertySExprValue},
- };
- use std::collections::HashMap;
- use super::{Op, SExprCode};
- #[derive(Debug, Clone)]
- enum Token {
- LoadVar(String),
- Add,
- Sub,
- Mul,
- Div,
- LeftParen,
- RightParen,
- ConstFloat32(f32),
- NestedExpr(Box<Vec<Token>>),
- SubExpr(Box<Vec<Token>>),
- If,
- Else,
- LeftBrace,
- RightBrace,
- LessThan,
- IfElse((Box<Vec<Token>>, Box<Vec<Token>>, Box<Vec<Token>>)),
- LessThanCompare((Box<Vec<Token>>, Box<Vec<Token>>)),
- Equals,
- SetValue((String, Box<Vec<Token>>)),
- }
- impl Token {
- fn flatten(self) -> Vec<Self> {
- match self {
- Self::NestedExpr(tokens) => {
- let tokens: Vec<_> = *tokens;
- tokens.into_iter().map(|t| t.flatten()).flatten().collect()
- }
- _ => vec![self],
- }
- }
- }
- #[derive(Clone)]
- pub struct Compiler {
- table: HashMap<String, Token>,
- }
- impl Compiler {
- pub fn new() -> Self {
- Self { table: HashMap::new() }
- }
- pub fn add_const_f32<S: Into<String>>(&mut self, name: S, val: f32) {
- self.table.insert(name.into(), Token::ConstFloat32(val));
- }
- /*
- pub fn get_const_f32<S: AsRef<str>>(&self, name: S) -> Option<f32> {
- let name = name.as_ref();
- let val = self.table.get(name)?;
- match val {
- Token::ConstFloat32(v) => Some(v)
- _ => None
- }
- }
- */
- pub fn compile<S: AsRef<str>>(&self, prestmts: S) -> Result<SExprCode> {
- let prestmts = prestmts.as_ref();
- // Strip all comments
- let mut stmts = String::new();
- for line in prestmts.lines() {
- if let Some(chr) = line.trim_start().chars().next() {
- if chr != '#' {
- stmts.push_str(line);
- }
- }
- }
- let mut code = vec![];
- for stmt in stmts.split(';') {
- code.push(self.compile_line(stmt)?);
- }
- Ok(code)
- }
- fn compile_line(&self, stmt: &str) -> Result<Op> {
- let tokens = self.tokenize(&stmt);
- //println!("{tokens:#?}");
- let tokens = to_rpn(tokens)?;
- //println!("{tokens:#?}");
- Ok(convert(&mut tokens.into_iter())?)
- }
- fn tokenize(&self, stmt: &str) -> Vec<Token> {
- let mut tokens = Vec::new();
- let mut current_token = String::new();
- for chr in stmt.chars() {
- match chr {
- ' ' | '\t' | '\n' => {
- self.clear_accum(&mut current_token, &mut tokens);
- }
- '+' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::Add);
- }
- '-' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::Sub);
- }
- '*' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::Mul);
- }
- '/' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::Div);
- }
- '(' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::LeftParen);
- }
- ')' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::RightParen);
- }
- '{' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::LeftBrace);
- }
- '}' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::RightBrace);
- }
- '<' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::LessThan);
- }
- '=' => {
- self.clear_accum(&mut current_token, &mut tokens);
- tokens.push(Token::Equals);
- }
- _ => current_token.push(chr),
- }
- }
- self.clear_accum(&mut current_token, &mut tokens);
- tokens
- }
- fn clear_accum(&self, current_token: &mut String, tokens: &mut Vec<Token>) {
- let prev_token = std::mem::replace(current_token, String::new());
- if prev_token.is_empty() {
- return
- }
- if let Some(token) = self.table.get(&prev_token) {
- tokens.push(token.clone());
- return
- }
- match prev_token.as_str() {
- "if" => {
- tokens.push(Token::If);
- return
- }
- "else" => {
- tokens.push(Token::Else);
- return
- }
- _ => {}
- }
- // Number or var?
- match prev_token.parse::<f32>() {
- Ok(v) => tokens.push(Token::ConstFloat32(v)),
- Err(_) => tokens.push(Token::LoadVar(prev_token)),
- }
- }
- }
- /// Convert from infix to reverse polish notation
- fn to_rpn(tokens: Vec<Token>) -> Result<Vec<Token>> {
- //println!("to_rpn = {tokens:#?}");
- let mut out;
- let mut stack = Vec::new();
- // equals
- let mut iter = tokens.into_iter();
- let mut var = String::new();
- let mut lhs = vec![];
- let mut rhs = vec![];
- let mut comparison = 0;
- // 0: none
- // 1: =
- while let Some(token) = iter.next() {
- match token {
- Token::Equals => {
- if comparison != 0 {
- return Err(Error::UnexpectedToken)
- }
- comparison = 1;
- if lhs.len() != 1 {
- return Err(Error::UnexpectedToken)
- }
- let lhs = std::mem::take(&mut lhs);
- match lhs.into_iter().next().unwrap() {
- Token::LoadVar(v) => var = v,
- _ => return Err(Error::UnexpectedToken),
- }
- }
- token => {
- if comparison == 0 {
- lhs.push(token);
- } else {
- rhs.push(token);
- }
- }
- }
- }
- if comparison == 1 {
- let stack = std::mem::take(&mut rhs);
- let rpn = to_rpn(stack)?;
- out = vec![Token::SetValue((var, Box::new(rpn)))];
- } else {
- assert!(rhs.is_empty());
- out = lhs;
- }
- // Parens
- let tokens = std::mem::take(&mut out);
- let mut paren = 0;
- for token in tokens {
- match token {
- Token::LeftParen => {
- // Is this the first opening paren for this subexpr?
- if paren > 0 {
- stack.push(token);
- }
- paren += 1;
- continue
- }
- Token::RightParen => {
- paren -= 1;
- // Whoops non-matching number of parens!
- if paren < 0 {
- return Err(Error::UnexpectedToken)
- }
- // Did we finally reach the closing paren for this subexpr?
- if paren == 0 {
- let stack = std::mem::take(&mut stack);
- let rpn = to_rpn(stack)?;
- out.push(Token::NestedExpr(Box::new(rpn)));
- } else {
- stack.push(token);
- }
- continue
- }
- _ => {}
- }
- if paren > 0 {
- stack.push(token);
- } else {
- out.push(token);
- }
- }
- out.append(&mut stack);
- // Braces
- let tokens = std::mem::take(&mut out);
- assert!(stack.is_empty());
- let mut paren = 0;
- for token in tokens {
- match token {
- Token::LeftBrace => {
- // Is this the first opening paren for this subexpr?
- if paren > 0 {
- stack.push(token);
- }
- paren += 1;
- continue
- }
- Token::RightBrace => {
- paren -= 1;
- // Whoops non-matching number of parens!
- if paren < 0 {
- return Err(Error::UnexpectedToken)
- }
- // Did we finally reach the closing paren for this subexpr?
- if paren == 0 {
- let stack = std::mem::take(&mut stack);
- let rpn = to_rpn(stack)?;
- out.push(Token::SubExpr(Box::new(rpn)));
- } else {
- stack.push(token);
- }
- continue
- }
- _ => {}
- }
- if paren > 0 {
- stack.push(token);
- } else {
- out.push(token);
- }
- }
- out.append(&mut stack);
- let tokens = std::mem::take(&mut out);
- assert!(stack.is_empty());
- let mut iter = tokens.into_iter();
- 'mainloop: while let Some(token) = iter.next() {
- match token {
- Token::If => {
- let mut section = 0;
- let mut cond_expr = vec![];
- let mut if_expr = vec![];
- let mut else_expr = vec![];
- while let Some(token) = iter.next() {
- match token {
- Token::SubExpr(tokens) => {
- if section == 0 {
- let cexpr = std::mem::take(&mut cond_expr);
- cond_expr = to_rpn(cexpr)?;
- if_expr = *tokens;
- } else if section == 1 {
- else_expr = *tokens;
- } else {
- return Err(Error::UnexpectedToken)
- }
- section += 1;
- }
- Token::Else => {
- if section != 1 {
- return Err(Error::UnexpectedToken)
- }
- }
- token => {
- if section != 0 {
- out.push(Token::IfElse((
- Box::new(cond_expr),
- Box::new(if_expr),
- Box::new(else_expr),
- )));
- out.push(token);
- continue 'mainloop;
- }
- cond_expr.push(token);
- }
- }
- }
- // We reached the end
- out.push(Token::IfElse((
- Box::new(cond_expr),
- Box::new(if_expr),
- Box::new(else_expr),
- )));
- }
- token => out.push(token),
- }
- }
- // comparisons <>=
- let tokens = std::mem::take(&mut out);
- let mut iter = tokens.into_iter();
- let mut lhs = vec![];
- let mut rhs = vec![];
- let mut comparison = 0;
- // 0: none
- // 1: <
- while let Some(token) = iter.next() {
- match token {
- Token::LessThan => {
- if comparison != 0 {
- return Err(Error::UnexpectedToken)
- }
- comparison = 1;
- }
- token => {
- if comparison == 0 {
- lhs.push(token);
- } else {
- rhs.push(token);
- }
- }
- }
- }
- if comparison == 1 {
- out = vec![Token::LessThanCompare((Box::new(lhs), Box::new(rhs)))];
- } else {
- assert!(rhs.is_empty());
- out = lhs;
- }
- // */
- let tokens = std::mem::take(&mut out);
- assert!(stack.is_empty());
- let mut is_op = false;
- for token in tokens {
- match token {
- Token::Mul | Token::Div => {
- if is_op {
- return Err(Error::UnexpectedToken)
- }
- is_op = true;
- let Some(prev_item) = out.pop() else { return Err(Error::UnexpectedToken) };
- stack.push(token);
- stack.push(prev_item);
- }
- _ => {
- if is_op {
- is_op = false;
- let mut expr = std::mem::take(&mut stack);
- expr.push(token);
- out.push(Token::NestedExpr(Box::new(expr)));
- continue
- }
- out.push(token)
- }
- }
- }
- //println!("out = {out:#?}");
- //println!("stack = {stack:#?}");
- //assert!(!is_op);
- //assert!(stack.is_empty());
- if is_op || !stack.is_empty() {
- return Err(Error::UnexpectedToken)
- }
- // +-
- let tokens = std::mem::take(&mut out);
- let mut is_op = false;
- for token in tokens {
- match token {
- Token::Add | Token::Sub => {
- if is_op {
- return Err(Error::UnexpectedToken)
- }
- is_op = true;
- let Some(prev_item) = out.pop() else { return Err(Error::UnexpectedToken) };
- stack.push(token);
- stack.push(prev_item);
- }
- _ => {
- if is_op {
- is_op = false;
- let mut expr = std::mem::take(&mut stack);
- expr.push(token);
- out.push(Token::NestedExpr(Box::new(expr)));
- continue
- }
- out.push(token)
- }
- }
- }
- //assert!(!is_op);
- //assert!(stack.is_empty());
- if is_op || !stack.is_empty() {
- return Err(Error::UnexpectedToken)
- }
- // Flatten everything
- let out = out.into_iter().map(|t| t.flatten()).flatten().collect();
- Ok(out)
- }
- fn convert<I: Iterator<Item = Token>>(iter: &mut I) -> Result<Op> {
- let Some(token) = iter.next() else { return Err(Error::UnexpectedToken) };
- let op = match token {
- Token::ConstFloat32(v) => Op::ConstFloat32(v),
- Token::LoadVar(v) => Op::LoadVar(v),
- Token::Add => {
- let lhs = convert(iter)?;
- let rhs = convert(iter)?;
- Op::Add((Box::new(lhs), Box::new(rhs)))
- }
- Token::Sub => {
- let lhs = convert(iter)?;
- let rhs = convert(iter)?;
- Op::Sub((Box::new(lhs), Box::new(rhs)))
- }
- Token::Mul => {
- let lhs = convert(iter)?;
- let rhs = convert(iter)?;
- Op::Mul((Box::new(lhs), Box::new(rhs)))
- }
- Token::Div => {
- let lhs = convert(iter)?;
- let rhs = convert(iter)?;
- Op::Div((Box::new(lhs), Box::new(rhs)))
- }
- Token::IfElse((cond, if_val, else_val)) => {
- let cond = convert(&mut cond.into_iter())?;
- let if_val = convert(&mut if_val.into_iter())?;
- let else_val = convert(&mut else_val.into_iter())?;
- Op::IfElse((Box::new(cond), vec![if_val], vec![else_val]))
- }
- Token::LessThanCompare((lhs, rhs)) => {
- let lhs = convert(&mut lhs.into_iter())?;
- let rhs = convert(&mut rhs.into_iter())?;
- Op::LessThan((Box::new(lhs), Box::new(rhs)))
- }
- Token::SetValue((var, expr)) => {
- let expr = convert(&mut expr.into_iter())?;
- Op::StoreVar((var, Box::new(expr)))
- }
- _ => return Err(Error::UnexpectedToken),
- };
- Ok(op)
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn single_line() {
- let compiler = Compiler::new();
- let code = compiler.compile("h/2 - 200").unwrap();
- #[rustfmt::skip]
- let code2 = vec![Op::Sub((
- Box::new(Op::Div((
- Box::new(Op::LoadVar("h".to_string())),
- Box::new(Op::ConstFloat32(2.)),
- ))),
- Box::new(Op::ConstFloat32(200.)),
- ))];
- assert_eq!(code, code2);
- let code = compiler.compile("(x + h/2 + (y + 7)/5) - 200").unwrap();
- #[rustfmt::skip]
- let code2 = vec![Op::Sub((
- Box::new(Op::Add((
- Box::new(Op::Add((
- Box::new(Op::LoadVar("x".to_string())),
- Box::new(Op::Div((
- Box::new(Op::LoadVar("h".to_string())),
- Box::new(Op::ConstFloat32(2.))
- ))),
- ))),
- Box::new(Op::Div((
- Box::new(Op::Add((
- Box::new(Op::LoadVar("y".to_string())),
- Box::new(Op::ConstFloat32(7.))
- ))),
- Box::new(Op::ConstFloat32(5.))
- ))),
- ))),
- Box::new(Op::ConstFloat32(200.))
- ))];
- assert_eq!(code, code2);
- }
- #[test]
- fn h_minus_1() {
- let mut compiler = Compiler::new();
- let code = compiler.compile("h - 1").unwrap();
- #[rustfmt::skip]
- let code2 = vec![Op::Sub((
- Box::new(Op::LoadVar("h".to_string())),
- Box::new(Op::ConstFloat32(1.))
- ))];
- assert_eq!(code, code2);
- }
- #[test]
- fn dosub() {
- let mut compiler = Compiler::new();
- compiler.add_const_f32("HELLO", 110.);
- let code = compiler.compile("HELLO").unwrap();
- let code2 = vec![Op::ConstFloat32(110.)];
- assert_eq!(code, code2);
- }
- #[test]
- fn if_else() {
- let mut compiler = Compiler::new();
- let code = compiler
- .compile(
- "
- if h < 4 {
- h - 1
- } else {
- 2 * h + 5
- }
- ",
- )
- .unwrap();
- let code2 = vec![Op::IfElse((
- Box::new(Op::LessThan((
- Box::new(Op::LoadVar("h".to_string())),
- Box::new(Op::ConstFloat32(4.)),
- ))),
- vec![Op::Sub((Box::new(Op::LoadVar("h".to_string())), Box::new(Op::ConstFloat32(1.))))],
- vec![Op::Add((
- Box::new(Op::Mul((
- Box::new(Op::ConstFloat32(2.)),
- Box::new(Op::LoadVar("h".to_string())),
- ))),
- Box::new(Op::ConstFloat32(5.)),
- ))],
- ))];
- assert_eq!(code, code2);
- }
- #[test]
- fn set_val() {
- let mut compiler = Compiler::new();
- let code = compiler
- .compile(
- "
- r = 10 / 4
- ",
- )
- .unwrap();
- let code2 = vec![Op::StoreVar((
- "r".to_string(),
- Box::new(Op::Div((Box::new(Op::ConstFloat32(10.)), Box::new(Op::ConstFloat32(4.))))),
- ))];
- assert_eq!(code, code2);
- }
- #[test]
- fn multiline() {
- let mut compiler = Compiler::new();
- let code = compiler
- .compile(
- "
- # This is a comment
- r = 10 / 4;
- s = if h < 4 {
- h - 1
- } else {
- 2 * h + 5
- };
- r + 1
- ",
- )
- .unwrap();
- let code2 = vec![
- Op::StoreVar((
- "r".to_string(),
- Box::new(Op::Div((
- Box::new(Op::ConstFloat32(10.)),
- Box::new(Op::ConstFloat32(4.)),
- ))),
- )),
- Op::StoreVar((
- "s".to_string(),
- Box::new(Op::IfElse((
- Box::new(Op::LessThan((
- Box::new(Op::LoadVar("h".to_string())),
- Box::new(Op::ConstFloat32(4.)),
- ))),
- vec![Op::Sub((
- Box::new(Op::LoadVar("h".to_string())),
- Box::new(Op::ConstFloat32(1.)),
- ))],
- vec![Op::Add((
- Box::new(Op::Mul((
- Box::new(Op::ConstFloat32(2.)),
- Box::new(Op::LoadVar("h".to_string())),
- ))),
- Box::new(Op::ConstFloat32(5.)),
- ))],
- ))),
- )),
- Op::Add((Box::new(Op::LoadVar("r".to_string())), Box::new(Op::ConstFloat32(1.)))),
- ];
- assert_eq!(code, code2);
- }
- }
|