├── examples ├── hello.duero ├── f2.duero ├── f4.duero ├── f1.duero ├── f3.duero ├── stream.duero └── fib.duero ├── Cargo.toml ├── .gitignore ├── src ├── main.rs ├── types.rs ├── parser │ └── mod.rs └── machine │ └── mod.rs ├── README.md └── LICENSE /examples/hello.duero: -------------------------------------------------------------------------------- 1 | main :- print hello, print world. 2 | -------------------------------------------------------------------------------- /examples/f2.duero: -------------------------------------------------------------------------------- 1 | main :- f(23), f(64). 2 | f(X) :- print(X). 3 | -------------------------------------------------------------------------------- /examples/f4.duero: -------------------------------------------------------------------------------- 1 | main :- X is 10 + N, N := 1, res(X), print(X). 2 | res(11) :- print(ok). 3 | res(X) :- print(notok). 4 | -------------------------------------------------------------------------------- /examples/f1.duero: -------------------------------------------------------------------------------- 1 | main :- one, two, three, four. 2 | one :- print(one). 3 | two :- print(two). 4 | three :- print(three). 5 | four :- print(four). 6 | -------------------------------------------------------------------------------- /examples/f3.duero: -------------------------------------------------------------------------------- 1 | main :- f(X), g(X), h(X). 2 | f(Y) :- prinka(Y). 3 | h(X) :- prinka(X). 4 | g(Z) :- Z := 42. 5 | 6 | prinka(42) :- print(prinka). 7 | -------------------------------------------------------------------------------- /examples/stream.duero: -------------------------------------------------------------------------------- 1 | producer(Stream) :- Stream := a(5, Stream1), producer(Stream1). 2 | consumer(a(N, Stream)) :- print(N), consumer(Stream). 3 | 4 | main :- producer(Stream), consumer(Stream). 5 | -------------------------------------------------------------------------------- /examples/fib.duero: -------------------------------------------------------------------------------- 1 | fib(N, X) :- N =:= 1 | X := 1. 2 | fib(N, X) :- N =:= 2 | X := 1. 3 | fib(N, X) :- X is X0 + X1, fib(N0, X0), fib(N1, X1), N0 is N - 2, N1 is N - 1. 4 | 5 | main :- fib(12, X), print(X). 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "duero" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | nom = "7.1.3" 10 | tokio = { version = "1.35.0", features = ["full"] } 11 | cranelift-jit = "0.102.1" 12 | clap = { version = "4.4.11", features = ["derive"] } 13 | async-recursion = "1.0.5" 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | 17 | # Added by cargo 18 | 19 | /target 20 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::Parser; 4 | use tokio::runtime::Builder; 5 | 6 | mod types; 7 | mod parser; 8 | mod machine; 9 | 10 | #[derive(Parser)] 11 | #[command(author, version, about, long_about = None)] 12 | struct Cli { 13 | #[arg(required = true)] 14 | file: PathBuf 15 | } 16 | 17 | fn main() { 18 | let args = Cli::parse(); 19 | 20 | let program = match parser::parse_file(&args.file) { 21 | Ok(program) => program, 22 | Err(parser::ParseError::ErrorReadingFile) => panic!("Error while reading file"), 23 | Err(parser::ParseError::ErrorParsingFile) => panic!("Error while parsing file"), 24 | }; 25 | 26 | let rt = Builder::new_multi_thread() 27 | .enable_all() 28 | .build() 29 | .expect("Tokio initialization error"); 30 | 31 | rt.block_on(async { 32 | let query = types::BasicType::Atom("main".into()); 33 | machine::run(query, &program).await; 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Duero - a toy Strand interpreter 2 | 3 | **Duero** is a _toy_ Strand interpreter. Strand is a programming language from the late 80's, part of the concurrent logic programming paradigm. Think of it as a Prolog cousin, in which everything executes in parallel. It's made in Rust and uses Tokio. 4 | 5 | This implementation does not try to be complete, it's just a toy to experiment. [FLENG](https://www.call-with-current-continuation.org/fleng/fleng.html) is a more mature and complete option. 6 | 7 | # Examples 8 | 9 | ## Fibonacci 10 | 11 | ``` 12 | fib(N, X) :- N =:= 1 | X := 1. 13 | fib(N, X) :- N =:= 2 | X := 1. 14 | fib(N, X) :- X is X0 + X1, fib(N0, X0), fib(N1, X1), N0 is N - 2, N1 is N - 1. 15 | 16 | main :- fib(12, X), print(X). 17 | ``` 18 | 19 | This program calculates the fibonacci number 12. It does it in parallel, as you can see that the order of each function call does not matter to compute the end result. 20 | 21 | ## Producer - Consumer 22 | 23 | ``` 24 | producer(Stream) :- Stream := a(5, Stream1), producer(Stream1). 25 | consumer(a(N, Stream)) :- print(N), consumer(Stream). 26 | 27 | main :- producer(Stream), consumer(Stream). 28 | ``` 29 | 30 | This program has a recursive producer (produces 5) and a recursive consumer (prints it). They work in parallel to create an endless loop. 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Adrián Arroyo Calle 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use tokio::sync::{Notify, RwLock}; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct Program { 6 | pub rules: Vec 7 | } 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct Rule { 11 | pub head: BasicType, 12 | pub guard: Vec, 13 | pub body: Vec, 14 | } 15 | 16 | #[derive(Debug, Clone)] 17 | pub enum GuardExpr { 18 | Equal(BasicType, BasicType), 19 | Data(BasicType), 20 | } 21 | 22 | #[derive(Debug, Clone)] 23 | pub enum BodyExpr { 24 | Is { 25 | left: BasicType, 26 | right: MathExpr, 27 | }, 28 | Print { 29 | msg: BasicType, 30 | }, 31 | Call(BasicType), 32 | Assign { 33 | var: BasicType, 34 | value: BasicType, 35 | } 36 | } 37 | 38 | #[derive(Debug, Clone)] 39 | pub enum MathExpr { 40 | Num(BasicType), 41 | Sum(Box, Box), 42 | Sub(Box, Box), 43 | } 44 | 45 | #[derive(Debug, Clone)] 46 | pub struct VarValue { 47 | pub channel: Arc 48 | } 49 | 50 | #[derive(Debug)] 51 | pub struct VarChannel { 52 | pub notify: Notify, 53 | pub lock: RwLock>, 54 | } 55 | impl VarValue { 56 | pub fn new() -> Self { 57 | VarValue{ channel: Arc::new(VarChannel { 58 | notify: Notify::new(), 59 | lock: RwLock::new(None)})} 60 | } 61 | 62 | pub async fn set(&self, data: BasicType) { 63 | let mut internal = self.channel.lock.write().await; 64 | match *internal { 65 | None => { 66 | *internal = Some(data); 67 | }, 68 | _ => { 69 | dbg!(internal); 70 | unreachable!(); 71 | } 72 | } 73 | self.channel.notify.notify_waiters(); 74 | self.channel.notify.notify_one(); 75 | } 76 | 77 | pub async fn get(&self) -> BasicType { 78 | let notification = self.channel.notify.notified(); 79 | { 80 | let data = self.channel.lock.read().await; 81 | if let Some(ref data) = *data { 82 | return data.clone(); 83 | } 84 | } 85 | notification.await; 86 | { 87 | let data = self.channel.lock.read().await; 88 | if let Some(ref data) = *data { 89 | return data.clone(); 90 | } 91 | } 92 | panic!("Can't read data"); 93 | } 94 | } 95 | 96 | #[derive(Debug, Clone)] 97 | pub enum BasicType { 98 | Number(i64), 99 | Var { 100 | name: String, 101 | value: VarValue, 102 | }, 103 | Atom(String), 104 | Str { 105 | name: String, 106 | args: Vec, 107 | } 108 | } 109 | 110 | impl std::fmt::Display for BasicType { 111 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 112 | match self { 113 | BasicType::Number(n) => write!(f, "{}", n), 114 | BasicType::Var { name, value } => write!(f, "{}", name), 115 | BasicType::Atom(atom) => write!(f, "{}", atom), 116 | BasicType::Str { name, args} => { 117 | let args_str: Vec = args.iter().map(|x| x.to_string()).collect(); 118 | let args = args_str.join(","); 119 | write!(f, "{}({})", name, args) 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | use nom::{ 3 | IResult, 4 | branch::alt, 5 | bytes::complete::tag, 6 | multi::{many0, many1, separated_list1}, 7 | combinator::{recognize, verify}, 8 | character::complete::{char, alpha1, alphanumeric1, one_of, space0, space1, multispace1, multispace0}, 9 | }; 10 | 11 | use crate::types::*; 12 | 13 | pub fn parse_file(path: &PathBuf) -> Result { 14 | match std::fs::read_to_string(path) { 15 | Ok(contents) => { 16 | match program(&contents) { 17 | Ok(("", program)) => Ok(program), 18 | _ => Err(ParseError::ErrorParsingFile), 19 | } 20 | }, 21 | Err(_) => Err(ParseError::ErrorReadingFile) 22 | } 23 | } 24 | 25 | pub enum ParseError { 26 | ErrorReadingFile, 27 | ErrorParsingFile, 28 | } 29 | 30 | fn spaced_comma(input: &str) -> IResult<&str, ()> { 31 | let (input, _) = many0(char(' '))(input)?; 32 | let (input, _) = char(',')(input)?; 33 | let (input, _) = many0(char(' '))(input)?; 34 | 35 | Ok((input, ())) 36 | } 37 | 38 | fn program(input: &str) -> IResult<&str, Program> { 39 | let (input, rules) = separated_list1(multispace1, rule)(input)?; 40 | let (input, _) = multispace0(input)?; 41 | 42 | Ok((input, Program { rules })) 43 | } 44 | 45 | fn rule(input: &str) -> IResult<&str, Rule> { 46 | alt((rule_empty_guards, rule_with_guards))(input) 47 | } 48 | 49 | fn rule_with_guards(input: &str) -> IResult<&str, Rule> { 50 | let (input, head) = basic_type(input)?; 51 | let (input, _) = space1(input)?; 52 | let (input, _) = tag(":-")(input)?; 53 | let (input, _) = space1(input)?; 54 | let (input, guard) = separated_list1(spaced_comma, guard)(input)?; 55 | let (input, _) = space1(input)?; 56 | let (input, _) = tag("|")(input)?; 57 | let (input, _) = space1(input)?; 58 | let (input, body) = separated_list1(spaced_comma, body)(input)?; 59 | let (input, _) = space0(input)?; 60 | let (input, _) = tag(".")(input)?; 61 | 62 | let rule = Rule { 63 | head, 64 | guard, 65 | body, 66 | }; 67 | 68 | Ok((input, rule)) 69 | } 70 | 71 | fn rule_empty_guards(input: &str) -> IResult<&str, Rule> { 72 | let (input, head) = basic_type(input)?; 73 | let (input, _) = space1(input)?; 74 | let (input, _) = tag(":-")(input)?; 75 | let (input, _) = space1(input)?; 76 | let (input, body) = separated_list1(spaced_comma, body)(input)?; 77 | let (input, _) = space0(input)?; 78 | let (input, _) = tag(".")(input)?; 79 | 80 | let rule = Rule { 81 | head, 82 | guard: Vec::new(), 83 | body 84 | }; 85 | 86 | Ok((input, rule)) 87 | } 88 | 89 | fn guard(input: &str) -> IResult<&str, GuardExpr> { 90 | alt((guard_data, guard_equal))(input) 91 | } 92 | 93 | fn guard_data(input: &str) -> IResult<&str, GuardExpr> { 94 | let (input, _) = tag("data(")(input)?; 95 | let (input, var) = basic_type(input)?; 96 | let (input, _) = tag(")")(input)?; 97 | 98 | Ok((input, GuardExpr::Data(var))) 99 | } 100 | 101 | fn guard_equal(input: &str) -> IResult<&str, GuardExpr> { 102 | let (input, left) = basic_type(input)?; 103 | let (input, _) = space1(input)?; 104 | let (input, _) = tag("=:=")(input)?; 105 | let (input, _) = space1(input)?; 106 | let (input, right) = basic_type(input)?; 107 | 108 | Ok((input, GuardExpr::Equal(left, right))) 109 | } 110 | 111 | fn body(input: &str) -> IResult<&str, BodyExpr> { 112 | alt((body_print, body_str_call, body_assign, body_is, body_atom_call))(input) 113 | } 114 | 115 | fn body_str_call(input: &str) -> IResult<&str, BodyExpr> { 116 | let (input, str_type) = str_type(input)?; 117 | 118 | Ok((input, BodyExpr::Call(str_type))) 119 | } 120 | 121 | fn body_atom_call(input: &str) -> IResult<&str, BodyExpr> { 122 | let (input, atom) = atom(input)?; 123 | 124 | Ok((input, BodyExpr::Call(atom))) 125 | } 126 | 127 | fn body_is(input: &str) -> IResult<&str, BodyExpr> { 128 | let (input, var) = var(input)?; 129 | let (input, _) = space1(input)?; 130 | let (input, tag) = tag("is")(input)?; 131 | let (input, _) = space1(input)?; 132 | let (input, math) = math(input)?; 133 | 134 | Ok((input, BodyExpr::Is { left: var, right: math })) 135 | } 136 | 137 | fn body_assign(input: &str) -> IResult<&str, BodyExpr> { 138 | let (input, var) = var(input)?; 139 | let (input, _) = space1(input)?; 140 | let (input, _) = tag(":=")(input)?; 141 | let (input, _) = space1(input)?; 142 | let (input, basic_type) = basic_type(input)?; 143 | 144 | Ok((input, BodyExpr::Assign { var, value: basic_type })) 145 | } 146 | 147 | fn body_print(input: &str) -> IResult<&str, BodyExpr> { 148 | let (input, _) = tag("print ")(input)?; 149 | 150 | let (input, data) = basic_type(input)?; 151 | 152 | Ok((input, BodyExpr::Print { 153 | msg: data 154 | })) 155 | } 156 | 157 | fn math(input: &str) -> IResult<&str, MathExpr> { 158 | alt((math_sum, math_sub, math_num))(input) 159 | } 160 | 161 | fn math_sum(input: &str) -> IResult<&str, MathExpr> { 162 | let (input, a) = math_num(input)?; 163 | let (input, _) = space1(input)?; 164 | let (input, _) = tag("+")(input)?; 165 | let (input, _) = space1(input)?; 166 | let (input, b) = math(input)?; 167 | 168 | Ok((input, MathExpr::Sum(Box::new(a), Box::new(b)))) 169 | } 170 | 171 | fn math_sub(input: &str) -> IResult<&str, MathExpr> { 172 | let (input, math_num) = math_num(input)?; 173 | let (input, _) = space1(input)?; 174 | let (input, _) = tag("-")(input)?; 175 | let (input, _) = space1(input)?; 176 | let (input, math) = math(input)?; 177 | 178 | Ok((input, MathExpr::Sub(Box::new(math_num), Box::new(math)))) 179 | } 180 | 181 | fn math_num(input: &str) -> IResult<&str, MathExpr> { 182 | let (input, math_num) = alt((number, var))(input)?; 183 | 184 | Ok((input, MathExpr::Num(math_num))) 185 | } 186 | 187 | fn basic_type(input: &str) -> IResult<&str, BasicType> { 188 | alt((str_type, number, atom, var))(input) 189 | } 190 | 191 | fn atom(input: &str) -> IResult<&str, BasicType> { 192 | let (input, atom) = verify(alphanumeric1, |s: &str| s.chars().next().unwrap().is_ascii_lowercase())(input)?; 193 | 194 | Ok((input, BasicType::Atom(atom.to_string()))) 195 | } 196 | 197 | fn var(input: &str) -> IResult<&str, BasicType> { 198 | let (input, var) = verify(alphanumeric1, |s: &str| s.chars().next().unwrap().is_ascii_uppercase())(input)?; 199 | 200 | Ok((input, BasicType::Var{ name: var.to_string(), value: VarValue::new()})) 201 | } 202 | 203 | fn number(input: &str) -> IResult<&str, BasicType> { 204 | let (input, num) = nom::character::complete::i64(input)?; 205 | 206 | Ok((input, BasicType::Number(num))) 207 | } 208 | 209 | fn str_type(input: &str) -> IResult<&str, BasicType> { 210 | let (input, name) = atom(input)?; 211 | 212 | let BasicType::Atom(name) = name else { 213 | panic!("Not an Atom"); 214 | }; 215 | 216 | let (input, _) = char('(')(input)?; 217 | 218 | let (input, args) = separated_list1(spaced_comma, basic_type)(input)?; 219 | let (input, _) = char(')')(input)?; 220 | 221 | let str_type = BasicType::Str { 222 | name, 223 | args 224 | }; 225 | 226 | Ok((input, str_type)) 227 | } 228 | 229 | 230 | #[test] 231 | fn parse_number() { 232 | let input = "42"; 233 | let result = basic_type(input); 234 | assert_eq!(result, Ok(("", BasicType::Number(42)))); 235 | } 236 | 237 | #[test] 238 | fn parse_atom() { 239 | let input = "stop".into(); 240 | let result = basic_type(input); 241 | assert_eq!(result, Ok(("", BasicType::Atom("stop".into())))); 242 | } 243 | 244 | #[test] 245 | fn parse_var() { 246 | let input = "Var".into(); 247 | let result = basic_type(input); 248 | assert_eq!(result, Ok(("", BasicType::Var("Var".into())))); 249 | } 250 | 251 | #[test] 252 | fn parse_str_type() { 253 | let input = "person(adrian, arroyo, calle)"; 254 | let result = basic_type(input); 255 | assert_eq!(result, Ok(("", BasicType::Str { 256 | name: "person".into(), 257 | args: vec![BasicType::Atom("adrian".into()), BasicType::Atom("arroyo".into()), BasicType::Atom("calle".into())] 258 | }))); 259 | } 260 | 261 | #[test] 262 | fn parse_rule() { 263 | let input = "print(X) :- print X."; 264 | let result = rule(input); 265 | let expected = Rule { 266 | head: BasicType::Str { 267 | name: "print".into(), 268 | args: vec![BasicType::Var("X".into())], 269 | }, 270 | guard: vec![], 271 | body: vec![BodyExpr::Print { 272 | msg: BasicType::Var("X".into()) 273 | }] 274 | }; 275 | assert_eq!(result, Ok(("", expected))); 276 | } 277 | 278 | 279 | #[test] 280 | fn parse_program() { 281 | let input = "print(X) :- print X.\nprint(X) :- print X."; 282 | let result = program(input); 283 | let expected = Program { 284 | rules: vec![ 285 | Rule { 286 | head: BasicType::Str { 287 | name: "print".into(), 288 | args: vec![BasicType::Var("X".into())] 289 | }, 290 | guard: vec![], 291 | body: vec![BodyExpr::Print { 292 | msg: BasicType::Var("X".into()) 293 | }] 294 | }, 295 | Rule { 296 | head: BasicType::Str { 297 | name: "print".into(), 298 | args: vec![BasicType::Var("X".into())] 299 | }, 300 | guard: vec![], 301 | body: vec![BodyExpr::Print { 302 | msg: BasicType::Var("X".into()) 303 | }] 304 | }], 305 | }; 306 | assert_eq!(result, Ok(("", expected))); 307 | } 308 | 309 | #[test] 310 | fn parse_program2() { 311 | let input = include_str!("../../examples/hello.duero"); 312 | let result = program(input); 313 | let expected = Program { 314 | rules: vec![Rule { 315 | head: BasicType::Atom("main".into()), 316 | guard: vec![], 317 | body: vec![ 318 | BodyExpr::Print { 319 | msg: BasicType::Atom("hello".into()) 320 | }, 321 | BodyExpr::Print { 322 | msg: BasicType::Atom("world".into()) 323 | }] 324 | }], 325 | }; 326 | assert_eq!(result, Ok(("", expected))); 327 | } 328 | 329 | #[test] 330 | fn parse_program3() { 331 | let input = include_str!("../../examples/f1.duero"); 332 | let result = program(input); 333 | let expected = Program { 334 | rules: vec![Rule { 335 | head: BasicType::Atom("main".into()), 336 | guard: vec![], 337 | body: vec![ 338 | BodyExpr::Print { 339 | msg: BasicType::Atom("hello".into()) 340 | }, 341 | BodyExpr::Print { 342 | msg: BasicType::Atom("world".into()) 343 | }] 344 | }], 345 | }; 346 | assert_eq!(result, Ok(("", expected))); 347 | } 348 | 349 | -------------------------------------------------------------------------------- /src/machine/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use tokio::task::JoinSet; 3 | use async_recursion::async_recursion; 4 | 5 | use crate::types::*; 6 | 7 | pub fn run_body_expr_spawn(join_set: &mut JoinSet<()>, body: &BodyExpr, matches: &VarSubstitution, program: &Program) { 8 | let body = body.clone(); 9 | let matches = matches.clone(); 10 | let program = program.clone(); 11 | join_set.spawn(async move { 12 | match run_body_expr(&body, &matches).await { 13 | State::Stop => (), 14 | State::Call(call) => run(call, &program).await, 15 | } 16 | }); 17 | } 18 | 19 | pub async fn run(query: BasicType, program: &Program) { 20 | let mut query = query; 21 | let mut join_set = tokio::task::JoinSet::new(); 22 | 'outer: loop { 23 | for rule in program.rules.iter() { 24 | if let Some(mut matches) = pattern_match(&query, &rule.head).await { 25 | if eval_guard(&rule.guard, &matches).await == true { 26 | add_vars_not_in_head(&mut matches, &rule.body); 27 | for body in &rule.body[1..] { 28 | run_body_expr_spawn(&mut join_set, body, &matches, program); 29 | } 30 | match run_body_expr(&rule.body[0], &matches).await { 31 | State::Stop => break 'outer, 32 | State::Call(call) => query = call, 33 | } 34 | break; 35 | } 36 | } 37 | } 38 | } 39 | 40 | while let Some(_) = join_set.join_next().await { } 41 | } 42 | 43 | fn add_vars_not_in_head(matches: &mut VarSubstitution, body_exprs: &Vec) { 44 | for body in body_exprs { 45 | match body { 46 | BodyExpr::Assign { var, value } => { 47 | add_vars_not_in_head_basic_type(matches, &var); 48 | add_vars_not_in_head_basic_type(matches, &value); 49 | }, 50 | BodyExpr::Is { left, right } => { 51 | if let BasicType::Var { name, value } = left { 52 | if let None = matches.get(name) { 53 | matches.insert(name.clone(), BasicType::Var { name: name.clone(), value: VarValue::new()}); 54 | } 55 | } 56 | add_vars_not_in_head_math_expr(matches, right); 57 | }, 58 | BodyExpr::Call(call) => { 59 | add_vars_not_in_head_basic_type(matches, &call); 60 | }, 61 | _ => unreachable!() 62 | } 63 | } 64 | } 65 | 66 | fn add_vars_not_in_head_math_expr(matches: &mut VarSubstitution, math_expr: &MathExpr) { 67 | match math_expr { 68 | MathExpr::Sum(a, b) => { 69 | add_vars_not_in_head_math_expr(matches, a); 70 | add_vars_not_in_head_math_expr(matches, b); 71 | }, 72 | MathExpr::Sub(a, b) => { 73 | add_vars_not_in_head_math_expr(matches, a); 74 | add_vars_not_in_head_math_expr(matches, b); 75 | }, 76 | MathExpr::Num(basic_type) => { 77 | add_vars_not_in_head_basic_type(matches, basic_type); 78 | } 79 | } 80 | } 81 | 82 | fn add_vars_not_in_head_basic_type(matches: &mut VarSubstitution, basic_type: &BasicType) { 83 | match basic_type { 84 | BasicType::Var { name, value } => { 85 | if let None = matches.get(name) { 86 | matches.insert(name.clone(), BasicType::Var { name: name.clone(), value: VarValue::new()}); 87 | } 88 | }, 89 | BasicType::Str { args, .. } => { 90 | for arg in args { 91 | add_vars_not_in_head_basic_type(matches, arg); 92 | } 93 | }, 94 | _ => {} 95 | } 96 | } 97 | 98 | type VarSubstitution = HashMap; 99 | 100 | enum State { 101 | Stop, 102 | Call(BasicType), 103 | } 104 | 105 | async fn run_body_expr(body: &BodyExpr, matches: &VarSubstitution) -> State { 106 | match body { 107 | BodyExpr::Call(call) => { 108 | match call { 109 | BasicType::Atom(..) => State::Call(call.clone()), 110 | BasicType::Str { name, args } => { 111 | let args = subst_matches(args, matches); 112 | if name == "print" { 113 | match &args[0] { 114 | BasicType::Var { value, .. } => { 115 | println!("{}", value.get().await); 116 | }, 117 | _ => { 118 | println!("{}", args[0]); 119 | } 120 | } 121 | return State::Stop; 122 | } 123 | State::Call(BasicType::Str { name: name.clone(), args }) 124 | } 125 | _ => unreachable!() 126 | } 127 | } 128 | BodyExpr::Print { msg } => { 129 | println!("{}", msg); 130 | State::Stop 131 | } 132 | BodyExpr::Assign { var, value } => { 133 | let args = subst_matches(&vec![var.clone(), value.clone()], matches); 134 | // tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; 135 | if let BasicType::Var { value: var_value, .. } = &args[0] { 136 | var_value.set(args[1].clone()).await; 137 | } 138 | State::Stop 139 | } 140 | BodyExpr::Is { left, right } => { 141 | let args = subst_matches(&vec![left.clone()], matches); 142 | let math_expr = subst_matches_math_expr(right, matches); 143 | 144 | if let BasicType::Var { value: var_value, name } = &args[0] { 145 | let result = eval_math_expr(&math_expr).await; 146 | var_value.set(BasicType::Number(result)).await; 147 | } 148 | State::Stop 149 | } 150 | _ => unreachable!() 151 | } 152 | } 153 | 154 | fn subst_matches(args: &Vec, matches: &VarSubstitution) -> Vec { 155 | args.iter().map(|x| { 156 | match x { 157 | BasicType::Var { name, .. } => { 158 | match matches.get(name) { 159 | Some(value) => value.clone(), 160 | None => x.clone(), 161 | } 162 | }, 163 | BasicType::Str { name, args } => { 164 | BasicType::Str { name: name.clone(), args: subst_matches(args, matches) } 165 | } 166 | _ => x.clone() 167 | } 168 | }).collect() 169 | } 170 | 171 | fn subst_matches_math_expr(math_expr: &MathExpr, matches: &VarSubstitution) -> MathExpr { 172 | match math_expr { 173 | MathExpr::Num(a) => { 174 | let args = subst_matches(&vec![a.clone()], matches); 175 | MathExpr::Num(args[0].clone()) 176 | } 177 | MathExpr::Sum(a, b) => { 178 | MathExpr::Sum(Box::new(subst_matches_math_expr(a, matches)), Box::new(subst_matches_math_expr(b, matches))) 179 | } 180 | MathExpr::Sub(a, b) => { 181 | MathExpr::Sub(Box::new(subst_matches_math_expr(a, matches)), Box::new(subst_matches_math_expr(b, matches))) 182 | } 183 | } 184 | } 185 | 186 | #[async_recursion] 187 | async fn pattern_match(data: &BasicType, pattern: &BasicType) -> Option { 188 | match (data, pattern) { 189 | (BasicType::Atom(x), BasicType::Atom(y)) => { 190 | if x == y { 191 | Some(HashMap::new()) 192 | } else { 193 | None 194 | } 195 | } 196 | (BasicType::Number(x), BasicType::Number(y)) => { 197 | if x == y { 198 | Some(HashMap::new()) 199 | } else { 200 | None 201 | } 202 | } 203 | (BasicType::Var { name: name1, value: value1}, BasicType::Var { name: name2, .. }) => { 204 | Some(HashMap::from([(name2.clone(), BasicType::Var { name: name1.clone(), value: value1.clone()})])) 205 | } 206 | (BasicType::Str { name: name1, args: args1 }, BasicType::Str { name: name2, args: args2 }) => { 207 | if name1 == name2 && args1.len() == args2.len() { 208 | let mut subst = HashMap::new(); 209 | for (arg1, arg2) in args1.iter().zip(args2.iter()) { 210 | match pattern_match(arg1, arg2).await { 211 | Some(s) => { 212 | for (key, val1) in s.iter() { 213 | match subst.get(key) { 214 | Some(val2) => { 215 | // TODO: Fix this 216 | //if val1 != val2 { 217 | // return None; 218 | //} 219 | }, 220 | None => { 221 | subst.insert(key.clone(), val1.clone()); 222 | } 223 | } 224 | } 225 | }, 226 | None => return None, 227 | } 228 | } 229 | Some(subst) 230 | } else { 231 | None 232 | } 233 | }, 234 | (BasicType::Atom(x), BasicType::Var { name, .. }) => { 235 | Some(HashMap::from([(name.clone(), BasicType::Atom(x.clone()))])) 236 | }, 237 | (BasicType::Number(x), BasicType::Var{ name, .. }) => { 238 | Some(HashMap::from([(name.clone(), BasicType::Number(*x))])) 239 | }, 240 | (BasicType::Str { name, args }, BasicType::Var { name: var_name, .. }) => { 241 | Some(HashMap::from([(var_name.clone(), BasicType::Str { name: name.clone(), args: args.clone() })])) 242 | }, 243 | (BasicType::Var { value, .. }, BasicType::Number(x)) => { 244 | let data = value.get().await; 245 | if let BasicType::Number(y) = data { 246 | if *x == y { 247 | Some(HashMap::new()) 248 | } else { 249 | None 250 | } 251 | } else { 252 | None 253 | } 254 | }, 255 | (BasicType::Var { value, .. }, BasicType::Atom(x)) => { 256 | let data = value.get().await; 257 | if let BasicType::Atom(y) = data { 258 | if x == &y { 259 | Some(HashMap::new()) 260 | } else { 261 | None 262 | } 263 | } else { 264 | None 265 | } 266 | }, 267 | (BasicType::Var { value, .. }, BasicType::Str { .. }) => { 268 | let data = value.get().await; 269 | pattern_match(&data, pattern).await 270 | }, 271 | (BasicType::Atom(_), BasicType::Number(_)) => None, 272 | (BasicType::Number(_), BasicType::Atom(_)) => None, 273 | (BasicType::Atom(_), BasicType::Str { .. }) => None, 274 | (BasicType::Str { .. }, BasicType::Atom(_)) => None, 275 | (BasicType::Number(_), BasicType::Str { .. }) => None, 276 | (BasicType::Str { .. }, BasicType::Number(_)) => None, 277 | } 278 | } 279 | 280 | #[async_recursion] 281 | async fn eval_math_expr(expr: &MathExpr) -> i64 { 282 | match expr { 283 | MathExpr::Num(basic_type) => { 284 | match basic_type { 285 | BasicType::Number(x) => *x, 286 | BasicType::Var { value, .. } => { 287 | let data = value.get().await; 288 | if let BasicType::Number(x) = data { 289 | return x; 290 | } else { 291 | panic!("Not a number"); 292 | } 293 | }, 294 | _ => unreachable!() 295 | } 296 | }, 297 | MathExpr::Sum(a, b) => { 298 | let (a, b) = tokio::join!(eval_math_expr(a), eval_math_expr(b)); 299 | a + b 300 | }, 301 | MathExpr::Sub(a, b) => { 302 | let (a, b) = tokio::join!(eval_math_expr(a), eval_math_expr(b)); 303 | a - b 304 | } 305 | } 306 | } 307 | 308 | async fn eval_guard(guards: &Vec, matches: &VarSubstitution) -> bool { 309 | for guard in guards { 310 | let guard = subst_matches_guard(&guard, matches); 311 | match guard { 312 | GuardExpr::Data(basic_type) => { 313 | match basic_type { 314 | BasicType::Var { value, .. } => { 315 | value.get().await; 316 | }, 317 | _ => unreachable!(), 318 | } 319 | }, 320 | GuardExpr::Equal(left, right) => { 321 | let mut left = left; 322 | let mut right = right; 323 | if let BasicType::Var { value, .. } = left { 324 | left = value.get().await; 325 | } 326 | if let BasicType::Var { value, .. } = right { 327 | right = value.get().await; 328 | } 329 | if pattern_match(&left, &right).await.is_none() { 330 | return false; 331 | } 332 | } 333 | } 334 | } 335 | true 336 | } 337 | 338 | fn subst_matches_guard(guard: &GuardExpr, matches: &VarSubstitution) -> GuardExpr { 339 | match guard { 340 | GuardExpr::Data(var) => { 341 | if let BasicType::Var { ref name, .. } = var { 342 | match matches.get(name) { 343 | None => GuardExpr::Data(var.clone()), 344 | Some(x) => GuardExpr::Data(x.clone()), 345 | } 346 | } else { 347 | guard.clone() 348 | } 349 | }, 350 | GuardExpr::Equal(left, right) => { 351 | let left = if let BasicType::Var { ref name, .. } = left { 352 | match matches.get(name) { 353 | None => left.clone(), 354 | Some(x) => x.clone(), 355 | } 356 | } else { 357 | left.clone() 358 | }; 359 | let right = if let BasicType::Var { ref name, .. } = right { 360 | match matches.get(name) { 361 | None => right.clone(), 362 | Some(x) => x.clone(), 363 | } 364 | } else { 365 | right.clone() 366 | }; 367 | GuardExpr::Equal(left, right) 368 | } 369 | } 370 | } 371 | --------------------------------------------------------------------------------