├── Makefile ├── .gitignore ├── optpy-runtime ├── Cargo.toml ├── README.md └── src │ ├── stdlib │ ├── mod.rs │ ├── sys.rs │ ├── math.rs │ ├── collections.rs │ └── heapq.rs │ ├── typed_value │ ├── mod.rs │ ├── traits.rs │ ├── boolean.rs │ ├── string.rs │ ├── number.rs │ └── list.rs │ ├── value │ ├── mod.rs │ ├── deque.rs │ ├── string.rs │ ├── iter.rs │ ├── dict.rs │ ├── list.rs │ └── value.rs │ ├── lib.rs │ ├── cell.rs │ ├── typed_builtin.rs │ ├── number.rs │ └── builtin.rs ├── optpy-test-macro ├── README.md ├── Cargo.toml └── src │ └── lib.rs ├── optpy-resolver ├── README.md ├── Cargo.toml └── src │ ├── util.rs │ ├── lib.rs │ ├── module │ ├── module_map.rs │ └── mod.rs │ ├── call │ ├── referencestore.rs │ └── mod.rs │ ├── builtin │ └── mod.rs │ └── name │ └── mod.rs ├── optpy-dump ├── README.md ├── Cargo.toml └── src │ └── lib.rs ├── .github ├── codecov.yml └── workflows │ ├── ci.yml │ └── release.yml ├── optpy-parser ├── Cargo.toml └── src │ ├── simplify │ ├── mod.rs │ ├── tuple_assign.rs │ ├── for_loop.rs │ └── list_comprehension.rs │ ├── lib.rs │ ├── expression │ ├── types.rs │ └── mod.rs │ └── statement.rs ├── optpy-generator ├── Cargo.toml └── src │ ├── lib.rs │ └── typed.rs ├── tests ├── test-index.rs ├── test-stdlib.rs ├── test-list-comprehension.rs ├── test-number.rs ├── test-heapq.rs ├── test-deque.rs ├── test-resolve.rs └── test-syntax.rs ├── tools └── source-code-downloader │ ├── Cargo.toml │ └── src │ └── main.rs ├── Cargo.toml ├── src ├── lib.rs └── main.rs ├── README.md └── LICENSE /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | cargo test --workspace 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | generated/*.rs 3 | 4 | a.rs 5 | /submissions 6 | run.sh 7 | -------------------------------------------------------------------------------- /optpy-runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optpy-runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | -------------------------------------------------------------------------------- /optpy-test-macro/README.md: -------------------------------------------------------------------------------- 1 | # optpy-test-macro 2 | 3 | This library provides macros to expand Python code on Rust tests as Rust code. 4 | -------------------------------------------------------------------------------- /optpy-resolver/README.md: -------------------------------------------------------------------------------- 1 | # optpy-resolver 2 | 3 | This library provides a set of functions to modify Python code to make it easier to convert to Rust. -------------------------------------------------------------------------------- /optpy-dump/README.md: -------------------------------------------------------------------------------- 1 | # optpy-dump 2 | 3 | This library provides a function to dump AST as Python code. 4 | It's useful to compare the input and the resolved code. -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | informational: true 6 | patch: 7 | default: 8 | informational: true 9 | -------------------------------------------------------------------------------- /optpy-dump/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optpy-dump" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | optpy-parser = { path = "../optpy-parser" } 9 | -------------------------------------------------------------------------------- /optpy-runtime/README.md: -------------------------------------------------------------------------------- 1 | # optpy-runtime 2 | 3 | This library provides a set of functions equivalent to the Python's built-in functions. 4 | The compiler includes this library in the output file. -------------------------------------------------------------------------------- /optpy-resolver/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optpy-resolver" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | optpy-parser = { path = "../optpy-parser" } 9 | -------------------------------------------------------------------------------- /optpy-runtime/src/stdlib/mod.rs: -------------------------------------------------------------------------------- 1 | mod collections; 2 | mod heapq; 3 | mod math; 4 | mod sys; 5 | 6 | pub use collections::*; 7 | pub use heapq::*; 8 | pub use math::*; 9 | pub use sys::*; 10 | -------------------------------------------------------------------------------- /optpy-runtime/src/stdlib/sys.rs: -------------------------------------------------------------------------------- 1 | use crate::Value; 2 | 3 | /// It does nothing, just for pass the compile. 4 | #[allow(non_snake_case)] 5 | pub fn __sys__setrecursionlimit(_: &Value) -> Value { 6 | Value::None 7 | } 8 | -------------------------------------------------------------------------------- /optpy-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optpy-parser" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | rustpython-parser = { git = "https://github.com/RustPython/RustPython.git", rev = "5b5abe2" } 9 | -------------------------------------------------------------------------------- /optpy-runtime/src/typed_value/mod.rs: -------------------------------------------------------------------------------- 1 | mod boolean; 2 | mod list; 3 | mod number; 4 | mod string; 5 | mod traits; 6 | 7 | pub use boolean::*; 8 | pub use list::*; 9 | pub use number::*; 10 | pub use string::*; 11 | pub use traits::*; 12 | -------------------------------------------------------------------------------- /optpy-generator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optpy-generator" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | quote = "1.0" 9 | proc-macro2 = "1.0" 10 | 11 | optpy-parser = { path = "../optpy-parser" } 12 | -------------------------------------------------------------------------------- /optpy-runtime/src/value/mod.rs: -------------------------------------------------------------------------------- 1 | mod deque; 2 | mod dict; 3 | mod iter; 4 | mod list; 5 | mod string; 6 | mod value; 7 | 8 | pub use deque::*; 9 | pub use dict::*; 10 | pub use iter::*; 11 | pub use list::*; 12 | pub use string::*; 13 | pub use value::*; 14 | -------------------------------------------------------------------------------- /optpy-parser/src/simplify/mod.rs: -------------------------------------------------------------------------------- 1 | mod for_loop; 2 | mod list_comprehension; 3 | mod tuple_assign; 4 | 5 | pub(super) use for_loop::simplify_for_loops; 6 | pub(super) use list_comprehension::simplify_list_comprehensions; 7 | pub(super) use tuple_assign::simplify_tuple_assignments; 8 | -------------------------------------------------------------------------------- /optpy-runtime/src/typed_value/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::number::Number; 2 | 3 | pub trait TypedValue: Sized { 4 | fn __shallow_copy(&self) -> Self; 5 | fn assign(&mut self, value: Self) { 6 | *self = value; 7 | } 8 | } 9 | 10 | pub trait IndexValue { 11 | fn __as_number(&self) -> Number; 12 | } 13 | -------------------------------------------------------------------------------- /tests/test-index.rs: -------------------------------------------------------------------------------- 1 | use optpy_runtime::Value; 2 | use optpy_test_macro::python_function; 3 | 4 | #[test] 5 | fn test_list_index() { 6 | python_function! {r" 7 | def test(x): 8 | a = [1, 2, 3] 9 | return a.index(x)"} 10 | 11 | assert_eq!(test(&Value::from(1)), Value::from(0)); 12 | assert_eq!(test(&Value::from(3)), Value::from(2)); 13 | } 14 | -------------------------------------------------------------------------------- /optpy-test-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optpy-test-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | quote = "1.0" 12 | syn = "1.0" 13 | 14 | optpy-parser = { path = "../optpy-parser" } 15 | optpy-resolver = { path = "../optpy-resolver" } 16 | optpy-generator = { path = "../optpy-generator" } 17 | -------------------------------------------------------------------------------- /tools/source-code-downloader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "source-code-downloader" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.66" 8 | clap = { version = "4.0.26", features = ["derive"] } 9 | env_logger = "0.9.3" 10 | log = "0.4.17" 11 | reqwest = { version = "0.11.13", features = ["gzip"] } 12 | scraper = "0.13.0" 13 | tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] } 14 | -------------------------------------------------------------------------------- /optpy-runtime/src/stdlib/math.rs: -------------------------------------------------------------------------------- 1 | use crate::{number::Number, Value}; 2 | 3 | #[allow(non_snake_case)] 4 | pub fn __math__gcd(a: &Value, b: &Value) -> Value { 5 | let a = a.__number(); 6 | let b = b.__number(); 7 | 8 | fn gcd(a: Number, b: Number) -> Number { 9 | if b == Number::Int64(0) { 10 | a 11 | } else { 12 | gcd(b, a % b) 13 | } 14 | } 15 | Value::Number(gcd(a, b)) 16 | } 17 | -------------------------------------------------------------------------------- /optpy-runtime/src/typed_value/boolean.rs: -------------------------------------------------------------------------------- 1 | pub struct Bool(pub bool); 2 | impl Bool { 3 | pub fn test(&self) -> bool { 4 | self.0 5 | } 6 | 7 | pub fn __unary_not(&self) -> Self { 8 | Self(!self.0) 9 | } 10 | } 11 | 12 | impl Default for Bool { 13 | fn default() -> Self { 14 | Self(Default::default()) 15 | } 16 | } 17 | 18 | impl From for Bool { 19 | fn from(v: bool) -> Self { 20 | Self(v) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/test-stdlib.rs: -------------------------------------------------------------------------------- 1 | use optpy_runtime::Value; 2 | use optpy_test_macro::python_function; 3 | 4 | #[test] 5 | fn test_gcd() { 6 | python_function! {r#" 7 | def test(a, b): 8 | import math 9 | return math.gcd(a, b)"#} 10 | 11 | assert_eq!(test(&Value::from(10), &Value::from(15),), Value::from(5)); 12 | } 13 | 14 | #[test] 15 | fn test_sys_setrecursionlimit() { 16 | python_function! {r" 17 | def test(): 18 | import sys 19 | sys.setrecursionlimit(1) 20 | return"} 21 | test(); 22 | } 23 | -------------------------------------------------------------------------------- /tests/test-list-comprehension.rs: -------------------------------------------------------------------------------- 1 | use optpy_runtime::Value; 2 | use optpy_test_macro::python_function; 3 | 4 | #[test] 5 | fn test_same_list_comprehension() { 6 | python_function! {r" 7 | def test(a): 8 | x = [[] for _ in range(a)] 9 | y = [[] for _ in range(a)] 10 | return x, y"} 11 | 12 | assert_eq!( 13 | test(&Value::from(2)), 14 | Value::from(vec![ 15 | Value::from(vec![Value::from(vec![]), Value::from(vec![])]), 16 | Value::from(vec![Value::from(vec![]), Value::from(vec![])]) 17 | ]) 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /optpy-runtime/src/stdlib/collections.rs: -------------------------------------------------------------------------------- 1 | use crate::{Deque, Value}; 2 | 3 | #[allow(non_snake_case)] 4 | pub fn __collections__deque0() -> Value { 5 | Value::Deque(Default::default()) 6 | } 7 | 8 | #[allow(non_snake_case)] 9 | pub fn __collections__deque1(value: &Value) -> Value { 10 | match value { 11 | Value::List(list) => Value::Deque(Deque::from(list)), 12 | _ => todo!(), 13 | } 14 | } 15 | 16 | #[macro_export] 17 | macro_rules! __collections__deque { 18 | () => { 19 | __collections__deque0() 20 | }; 21 | ($value:expr) => { 22 | __collections__deque1($value) 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /optpy-runtime/src/typed_value/string.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use super::{TypedList, TypedValue}; 4 | 5 | pub struct TypedString(pub Rc); 6 | 7 | impl TypedString { 8 | pub fn split(&self) -> TypedList { 9 | let list = self 10 | .0 11 | .split_ascii_whitespace() 12 | .map(|s| TypedString::from(s)) 13 | .collect::>(); 14 | TypedList::from(list) 15 | } 16 | } 17 | 18 | impl TypedValue for TypedString { 19 | fn __shallow_copy(&self) -> Self { 20 | todo!() 21 | } 22 | } 23 | 24 | impl Default for TypedString { 25 | fn default() -> Self { 26 | Self(Rc::new(String::new())) 27 | } 28 | } 29 | impl From<&str> for TypedString { 30 | fn from(v: &str) -> Self { 31 | Self(Rc::new(v.to_string())) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "optpy" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | clap = { version = "4.0", features = ["derive"] } 9 | anyhow = { version = "1.0", features = ["backtrace"] } 10 | env_logger = "0.9" 11 | log = "0.4" 12 | 13 | optpy-parser = { path = "./optpy-parser" } 14 | optpy-resolver = { path = "./optpy-resolver" } 15 | optpy-generator = { path = "./optpy-generator" } 16 | optpy-runtime = { path = "./optpy-runtime" } 17 | optpy-dump = { path = "./optpy-dump" } 18 | 19 | [dev-dependencies] 20 | optpy-test-macro = { path = "./optpy-test-macro" } 21 | 22 | [workspace] 23 | members = [ 24 | "optpy-parser", 25 | "optpy-resolver", 26 | "optpy-runtime", 27 | "optpy-generator", 28 | "optpy-test-macro", 29 | "optpy-dump", 30 | "tools/source-code-downloader", 31 | ] 32 | -------------------------------------------------------------------------------- /optpy-resolver/src/util.rs: -------------------------------------------------------------------------------- 1 | pub trait StripMargin { 2 | fn strip_margin(&self) -> String; 3 | } 4 | 5 | impl StripMargin for S 6 | where 7 | S: AsRef, 8 | { 9 | fn strip_margin(&self) -> String { 10 | let mut lines = vec![]; 11 | for line in self.as_ref().split('\n') { 12 | lines.push( 13 | line.chars() 14 | .skip_while(|&c| c != '|') 15 | .skip(1) 16 | .collect::(), 17 | ) 18 | } 19 | lines.join("\n").trim().to_string() 20 | } 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | #[test] 27 | fn test_strip_margin() { 28 | let x = r" 29 | |abcd 30 | |efgh 31 | " 32 | .strip_margin(); 33 | assert_eq!(x, "abcd\nefgh"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/test-number.rs: -------------------------------------------------------------------------------- 1 | use optpy_runtime::Value; 2 | use optpy_test_macro::python_function; 3 | 4 | #[test] 5 | fn test_bool() { 6 | python_function! {r#" 7 | def test(x): 8 | if x: 9 | return "OK" 10 | else: 11 | return "NO" 12 | "#} 13 | 14 | assert_eq!(test(&Value::from(1)), Value::from("OK")); 15 | assert_eq!(test(&Value::from(0)), Value::from("NO")); 16 | assert_eq!(test(&Value::from(-1)), Value::from("OK")); 17 | assert_eq!(test(&Value::from(0.1)), Value::from("OK")); 18 | assert_eq!(test(&Value::from(0.0)), Value::from("NO")); 19 | assert_eq!(test(&Value::from(-0.1)), Value::from("OK")); 20 | } 21 | 22 | #[test] 23 | fn test_shift() { 24 | python_function! {r" 25 | def test(): 26 | return [1 << 2, 4 >> 1]" 27 | } 28 | 29 | assert_eq!(test(), Value::from(vec![Value::from(4), Value::from(2)])) 30 | } 31 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use optpy_generator::{generate_code, generate_typed_code}; 3 | use optpy_parser::parse; 4 | use optpy_resolver::resolve; 5 | use optpy_runtime::{OPTPY_RUNTIME, OPTPY_TYPED_RUNTIME}; 6 | 7 | pub fn compile>(code: S) -> Result { 8 | let ast = parse(code)?; 9 | let (ast, definitions) = resolve(&ast); 10 | let code = generate_code(&ast, &definitions); 11 | 12 | let mut result = OPTPY_RUNTIME.to_string(); 13 | result += &code.to_string(); 14 | Ok(result) 15 | } 16 | 17 | pub fn typed_compile>(code: S) -> Result { 18 | let ast = parse(code)?; 19 | let (ast, definitions) = resolve(&ast); 20 | let code = generate_typed_code(&ast, &definitions); 21 | 22 | let mut result = OPTPY_TYPED_RUNTIME.to_string(); 23 | result += &code.to_string(); 24 | Ok(result) 25 | } 26 | -------------------------------------------------------------------------------- /optpy-resolver/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, BTreeSet}; 2 | 3 | use module::ModuleMap; 4 | use optpy_parser::Statement; 5 | 6 | mod builtin; 7 | mod call; 8 | mod module; 9 | mod name; 10 | 11 | pub mod util; 12 | 13 | pub fn resolve(statements: &[Statement]) -> (Vec, BTreeMap>) { 14 | let mut statements = statements.to_vec(); 15 | loop { 16 | let new_statements = name::resolve_names(&statements); 17 | let new_statements = module::resolve_modules(new_statements, &mut ModuleMap::default()); 18 | let new_statements = builtin::resolve_builtin_functions(&new_statements); 19 | let (new_statements, definitions) = call::resolve_function_calls(&new_statements); 20 | if new_statements == statements { 21 | return (new_statements, definitions); 22 | } 23 | statements = new_statements; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # optpy 2 | 3 | [![CI](https://github.com/kenkoooo/optpy/actions/workflows/ci.yml/badge.svg)](https://github.com/kenkoooo/optpy/actions/workflows/ci.yml) 4 | [![codecov](https://codecov.io/gh/kenkoooo/optpy/branch/main/graph/badge.svg?token=HIXDTWK17I)](https://codecov.io/gh/kenkoooo/optpy) 5 | 6 | `optpy` is a transpiler to generate a Rust file from a Python file. 7 | 8 | 9 | https://user-images.githubusercontent.com/9150073/200675788-267792f1-a14c-4746-a51d-1e0ecb0c3bac.mp4 10 | 11 | ## Installation 12 | 13 | [Archives of precompiled binaries are available for Linux and macOS.](https://github.com/kenkoooo/optpy/releases) 14 | 15 | ## Usage 16 | 17 | ```sh 18 | ./optpy compile [Output Rust file] 19 | ``` 20 | 21 | # TODO 22 | - [x] Functions 23 | - [x] `for` & `while` loops 24 | - [x] List comprehension 25 | - [ ] Bigint 26 | - [ ] Classes 27 | - [ ] Modules 28 | - [ ] and so many things ... 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 kenkoooo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /optpy-runtime/src/value/deque.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::VecDeque, iter::FromIterator, rc::Rc}; 2 | 3 | use crate::{cell::UnsafeRefCell, List, Value}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Deque(Rc>>); 7 | 8 | impl Default for Deque { 9 | fn default() -> Self { 10 | Self(UnsafeRefCell::rc(Default::default())) 11 | } 12 | } 13 | 14 | impl Deque { 15 | pub fn popleft(&self) -> Value { 16 | self.0 17 | .borrow_mut() 18 | .pop_front() 19 | .expect("pop from an empty deque") 20 | } 21 | pub fn append(&self, value: &Value) { 22 | self.0.borrow_mut().push_back(value.clone()); 23 | } 24 | pub fn appendleft(&self, value: &Value) { 25 | self.0.borrow_mut().push_front(value.clone()); 26 | } 27 | pub fn test(&self) -> bool { 28 | !self.0.borrow().is_empty() 29 | } 30 | } 31 | 32 | impl From<&List> for Deque { 33 | fn from(list: &List) -> Self { 34 | Deque(UnsafeRefCell::rc(VecDeque::from_iter( 35 | list.0.borrow().iter().map(|v| v.borrow().clone()), 36 | ))) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/test-heapq.rs: -------------------------------------------------------------------------------- 1 | use optpy_runtime::Value; 2 | use optpy_test_macro::python_function; 3 | 4 | #[test] 5 | fn test_heapify() { 6 | python_function! {r" 7 | def test(): 8 | import heapq 9 | a = [1, 6, 8, 0, -1] 10 | heapq.heapify(a) 11 | return a"} 12 | 13 | assert_eq!( 14 | test(), 15 | Value::from(vec![ 16 | Value::from(-1), 17 | Value::from(0), 18 | Value::from(8), 19 | Value::from(1), 20 | Value::from(6) 21 | ]) 22 | ); 23 | } 24 | 25 | #[test] 26 | fn test_heap_push_pop() { 27 | python_function! {r" 28 | def test(): 29 | from heapq import heapify, heappop, heappush 30 | a = [1, 4, 7] 31 | heapify(a) 32 | heappush(a, 2) 33 | heappush(a, 6) 34 | heappush(a, 5) 35 | heappush(a, 3) 36 | x = [] 37 | for _ in range(7): 38 | x.append(heappop(a)) 39 | return x"} 40 | 41 | assert_eq!( 42 | test(), 43 | Value::from(vec![ 44 | Value::from(1), 45 | Value::from(2), 46 | Value::from(3), 47 | Value::from(4), 48 | Value::from(5), 49 | Value::from(6), 50 | Value::from(7) 51 | ]), 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | tests: 11 | name: Tests 12 | runs-on: ubuntu-latest 13 | env: 14 | RUSTFLAGS: -Cinstrument-coverage 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Cache dependencies 20 | uses: Swatinem/rust-cache@v2 21 | 22 | - name: Install llvm-tools-preview 23 | run: rustup component add llvm-tools-preview 24 | 25 | - name: Build 26 | run: cargo build --verbose 27 | 28 | - name: Test 29 | run: cargo test --workspace 30 | env: 31 | LLVM_PROFILE_FILE: optpy-%p-%m.profraw 32 | 33 | - name: Install grcov 34 | run: curl -L https://github.com/mozilla/grcov/releases/latest/download/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf - 35 | 36 | - name: Generate lcov 37 | run: ./grcov . --binary-path ./target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore "/*" -o lcov.info 38 | 39 | - name: Upload coverage to Codecov 40 | uses: codecov/codecov-action@v3 41 | with: 42 | token: ${{ secrets.CODECOV_TOKEN }} 43 | files: ./lcov.info 44 | fail_ci_if_error: true 45 | -------------------------------------------------------------------------------- /tests/test-deque.rs: -------------------------------------------------------------------------------- 1 | use optpy_runtime::Value; 2 | use optpy_test_macro::python_function; 3 | 4 | #[test] 5 | fn test_deque() { 6 | { 7 | python_function! {r" 8 | def test(): 9 | from collections import deque 10 | x = deque() 11 | x.append(1) 12 | x.append(2) 13 | return [x.popleft(), x.popleft()] 14 | "} 15 | assert_eq!(test(), Value::from(vec![Value::from(1), Value::from(2)])) 16 | } 17 | { 18 | python_function! {r" 19 | def test(): 20 | import collections 21 | x = collections.deque([1, 2]) 22 | return [x.popleft(), x.popleft()] 23 | "} 24 | assert_eq!(test(), Value::from(vec![Value::from(1), Value::from(2)])) 25 | } 26 | } 27 | #[test] 28 | fn test_appendleft() { 29 | python_function! {r" 30 | def test(): 31 | import collections 32 | d = collections.deque() 33 | d.append(1) 34 | d.appendleft(2) 35 | return d.popleft()"} 36 | assert_eq!(test(), Value::from(2)); 37 | } 38 | 39 | #[test] 40 | fn test_bool() { 41 | python_function! {r" 42 | def test(a): 43 | from collections import deque 44 | d = deque(a) 45 | if d: 46 | return 1 47 | else: 48 | return 2"} 49 | assert_eq!(test(&Value::from(vec![Value::from(0)])), Value::from(1)); 50 | assert_eq!(test(&Value::from(vec![])), Value::from(2)); 51 | } 52 | -------------------------------------------------------------------------------- /optpy-runtime/src/typed_value/number.rs: -------------------------------------------------------------------------------- 1 | use crate::number::Number; 2 | 3 | use super::{Bool, IndexValue, TypedValue}; 4 | 5 | impl Number { 6 | pub fn __min(&self, rhs: Self) -> Self { 7 | if self < &rhs { 8 | *self 9 | } else { 10 | rhs 11 | } 12 | } 13 | 14 | pub fn __sub(&self, rhs: Self) -> Self { 15 | *self - rhs 16 | } 17 | 18 | pub fn __add(&self, rhs: Self) -> Self { 19 | *self + rhs 20 | } 21 | pub fn __mul(&self, rhs: Self) -> Self { 22 | *self * rhs 23 | } 24 | 25 | pub fn __gt(&self, rhs: Self) -> Bool { 26 | Bool::from(*self > rhs) 27 | } 28 | pub fn __eq(&self, rhs: Self) -> Bool { 29 | Bool::from(*self == rhs) 30 | } 31 | pub fn __ne(&self, rhs: Self) -> Bool { 32 | Bool::from(*self != rhs) 33 | } 34 | pub fn __unary_sub(&self) -> Self { 35 | match self { 36 | Number::Int64(i) => Number::Int64(-i), 37 | Number::Float(f) => Number::Float(-f), 38 | } 39 | } 40 | } 41 | 42 | impl TypedValue for Number { 43 | fn __shallow_copy(&self) -> Self { 44 | *self 45 | } 46 | } 47 | 48 | impl Default for Number { 49 | fn default() -> Self { 50 | Number::Int64(0) 51 | } 52 | } 53 | impl IndexValue for Number { 54 | fn __as_number(&self) -> Number { 55 | *self 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/test-resolve.rs: -------------------------------------------------------------------------------- 1 | use optpy_dump::DumpPython; 2 | use optpy_parser::parse; 3 | use optpy_resolver::resolve; 4 | 5 | fn dump(code: &str) -> String { 6 | let ast = parse(code).unwrap(); 7 | let (ast, _) = resolve(&ast); 8 | ast.to_python_code() 9 | } 10 | 11 | #[test] 12 | fn test_sibling_function_resolve() { 13 | let code = r" 14 | N = 2 15 | def f1(): 16 | return N + 1 17 | def f2(): 18 | return f1() 19 | return f2() 20 | "; 21 | 22 | let expected = r" 23 | v1 = 2 24 | def f1(v1): 25 | return v1 + 1 26 | def f2(v1): 27 | return f1(v1) 28 | return f2(v1)"; 29 | 30 | assert_eq!(dump(code), dump(expected)); 31 | } 32 | 33 | #[test] 34 | fn test_import() { 35 | let code = r" 36 | import math 37 | x = math.gcd() 38 | "; 39 | let expected = r" 40 | x = __math__gcd() 41 | "; 42 | assert_eq!(dump(code), dump(expected)); 43 | let code = r" 44 | import math as m 45 | x = m.gcd() 46 | "; 47 | let expected = r" 48 | x = __math__gcd() 49 | "; 50 | assert_eq!(dump(code), dump(expected)); 51 | 52 | let code = r" 53 | from math import gcd 54 | x = gcd() 55 | "; 56 | let expected = r" 57 | x = __math__gcd() 58 | "; 59 | assert_eq!(dump(code), dump(expected)); 60 | 61 | let code = r" 62 | from math import * 63 | x = gcd() 64 | "; 65 | let expected = r" 66 | x = __math__gcd() 67 | "; 68 | assert_eq!(dump(code), dump(expected)); 69 | 70 | let code = r" 71 | from math import gcd as g 72 | x = g() 73 | "; 74 | let expected = r" 75 | x = __math__gcd() 76 | "; 77 | assert_eq!(dump(code), dump(expected)); 78 | } 79 | -------------------------------------------------------------------------------- /optpy-resolver/src/module/module_map.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | pub(crate) struct ModuleMap { 4 | modules: BTreeMap, 5 | } 6 | 7 | impl Default for ModuleMap { 8 | fn default() -> Self { 9 | Self { 10 | modules: BTreeMap::from([ 11 | ("math.gcd".into(), "__math__gcd".into()), 12 | ("math.log".into(), "__math__log".into()), 13 | ("math.exp".into(), "__math__exp".into()), 14 | ("heapq.heapify".into(), "__heapq__heapify".into()), 15 | ("heapq.heappush".into(), "__heapq__heappush".into()), 16 | ("heapq.heappop".into(), "__heapq__heappop".into()), 17 | ( 18 | "sys.setrecursionlimit".into(), 19 | "__sys__setrecursionlimit".into(), 20 | ), 21 | ( 22 | "collections.deque".into(), 23 | "__collections__deque__macro__".into(), 24 | ), 25 | ]), 26 | } 27 | } 28 | } 29 | 30 | impl ModuleMap { 31 | pub(crate) fn find_children(&self, module: &str) -> Vec<(&str, &str)> { 32 | let mut result = vec![]; 33 | for (key, value) in self.modules.iter() { 34 | if let Some(child) = key.strip_prefix(&format!("{module}.")) { 35 | if !child.contains(".") { 36 | result.push((child, value.as_str())); 37 | } 38 | } 39 | } 40 | result 41 | } 42 | 43 | pub(crate) fn find_match(&self, module_function: &str) -> Option<&str> { 44 | self.modules.get(module_function).map(|s| s.as_str()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - v* 9 | pull_request: 10 | 11 | jobs: 12 | build_executable_binary: 13 | strategy: 14 | matrix: 15 | include: 16 | - target: x86_64-unknown-linux-musl 17 | os: ubuntu-latest 18 | - target: x86_64-apple-darwin 19 | os: macos-latest 20 | - target: aarch64-apple-darwin 21 | os: macos-latest 22 | runs-on: ${{ matrix.os }} 23 | steps: 24 | - uses: actions/checkout@v3 25 | 26 | - name: Set up rust toolchain 27 | run: rustup target add ${{ matrix.target }} 28 | 29 | - name: Cache dependencies 30 | uses: Swatinem/rust-cache@v2 31 | with: 32 | prefix-key: release-${{ matrix.target }} 33 | 34 | - name: Build executables 35 | run: cargo build --release --target ${{ matrix.target }} 36 | 37 | - name: Set version 38 | id: version 39 | run: | 40 | VERSION=$(echo ${{ github.ref }} | sed -e "s#refs/tags/##g") 41 | echo "version=$VERSION" >> $GITHUB_OUTPUT 42 | 43 | - name: Compress the executable 44 | if: startsWith(github.ref, 'refs/tags/') 45 | run: | 46 | gzip -f target/${{ matrix.target }}/release/optpy 47 | mv target/${{ matrix.target }}/release/optpy.gz optpy-${{ steps.version.outputs.version }}-${{ matrix.target }}.gz 48 | 49 | - name: Release 50 | uses: softprops/action-gh-release@v1 51 | if: startsWith(github.ref, 'refs/tags/') 52 | with: 53 | files: optpy-${{ steps.version.outputs.version }}-${{ matrix.target }}.gz 54 | -------------------------------------------------------------------------------- /optpy-resolver/src/call/referencestore.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, BTreeSet}; 2 | 3 | #[derive(Default, Debug)] 4 | pub(super) struct ReferenceStore { 5 | variable_functions: BTreeMap>, 6 | function_variables: BTreeMap>, 7 | } 8 | 9 | impl ReferenceStore { 10 | pub(super) fn record(&mut self, variable_name: &str, function_name: &str) { 11 | self.variable_functions 12 | .entry(variable_name.to_string()) 13 | .or_default() 14 | .insert(function_name.to_string()); 15 | self.function_variables 16 | .entry(function_name.to_string()) 17 | .or_default() 18 | .insert(variable_name.to_string()); 19 | } 20 | 21 | pub(super) fn list_by_function(&self, function_name: &str) -> BTreeSet { 22 | self.function_variables 23 | .get(function_name) 24 | .cloned() 25 | .unwrap_or_default() 26 | } 27 | 28 | pub(super) fn list_by_variable(&self, variable_name: &str) -> BTreeSet { 29 | self.variable_functions 30 | .get(variable_name) 31 | .cloned() 32 | .unwrap_or_default() 33 | } 34 | 35 | pub(super) fn remove_function(&mut self, function_name: &str) { 36 | let variables = self 37 | .function_variables 38 | .remove(function_name) 39 | .unwrap_or_default(); 40 | for variable in variables { 41 | assert!(self 42 | .variable_functions 43 | .get_mut(&variable) 44 | .expect("invalid") 45 | .remove(function_name)); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /optpy-runtime/src/value/string.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::{number::Number, Value}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct ImmutableString(pub Rc); 7 | 8 | impl PartialEq for ImmutableString { 9 | fn eq(&self, other: &Self) -> bool { 10 | self.0 == other.0 11 | } 12 | } 13 | 14 | impl PartialOrd for ImmutableString { 15 | fn partial_cmp(&self, other: &Self) -> Option { 16 | self.0.partial_cmp(&other.0) 17 | } 18 | } 19 | 20 | impl ImmutableString { 21 | pub fn split(&self) -> Value { 22 | let list = self 23 | .0 24 | .split_ascii_whitespace() 25 | .map(|s| Self(Rc::new(s.to_string()))) 26 | .map(|s| Value::String(s)) 27 | .collect::>(); 28 | Value::from(list) 29 | } 30 | pub fn strip(&self) -> Value { 31 | Value::String(Self(Rc::new(self.0.trim().to_string()))) 32 | } 33 | pub fn __len(&self) -> Value { 34 | Value::Number(Number::Int64(self.0.chars().count() as i64)) 35 | } 36 | pub fn count(&self, value: &Value) -> Value { 37 | match value { 38 | Value::String(value) => { 39 | let lhs = self.0.as_str(); 40 | let rhs = value.0.as_str(); 41 | Value::Number(Number::Int64(lhs.split(rhs).count() as i64 - 1)) 42 | } 43 | _ => todo!(), 44 | } 45 | } 46 | pub fn test(&self) -> bool { 47 | !self.0.is_empty() 48 | } 49 | } 50 | 51 | impl From<&str> for ImmutableString { 52 | fn from(s: &str) -> Self { 53 | Self(Rc::new(s.to_string())) 54 | } 55 | } 56 | 57 | impl ToString for ImmutableString { 58 | fn to_string(&self) -> String { 59 | self.0.to_string() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /optpy-test-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use optpy_generator::generate_function_body; 2 | use optpy_parser::{parse, Func}; 3 | use optpy_resolver::resolve; 4 | use proc_macro::TokenStream; 5 | use quote::{format_ident, quote}; 6 | use syn::{ 7 | parse::{Parse, ParseStream}, 8 | LitStr, Result, 9 | }; 10 | 11 | #[proc_macro] 12 | pub fn python_function(tokens: TokenStream) -> TokenStream { 13 | let input: PythonTestInput = syn::parse(tokens).unwrap(); 14 | let code = input.python_code.value(); 15 | 16 | let ast = parse(code).unwrap(); 17 | assert_eq!(ast.len(), 1); 18 | let function_name = match &ast[0] { 19 | optpy_parser::Statement::Func(Func { name, .. }) => name.clone(), 20 | _ => panic!(), 21 | }; 22 | let (ast, definitions) = resolve(&ast); 23 | let (name, args) = match &ast[0] { 24 | optpy_parser::Statement::Func(Func { name, args, .. }) => (name.clone(), args.clone()), 25 | _ => panic!(), 26 | }; 27 | 28 | let code = generate_function_body(&ast, "", &definitions); 29 | let function_name = format_ident!("{}", function_name); 30 | let args = args 31 | .into_iter() 32 | .map(|arg| format_ident!("{}", arg)) 33 | .collect::>(); 34 | let resolved_name = format_ident!("{}", name); 35 | 36 | let result = quote! { 37 | #[allow(unreachable_code)] 38 | fn #function_name(#(#args: &optpy_runtime::Value),*) -> optpy_runtime::Value { 39 | use optpy_runtime::*; 40 | #code 41 | 42 | #resolved_name( #(#args),* ) 43 | } 44 | }; 45 | 46 | result.into() 47 | } 48 | 49 | struct PythonTestInput { 50 | python_code: LitStr, 51 | } 52 | 53 | impl Parse for PythonTestInput { 54 | fn parse(input: ParseStream) -> Result { 55 | let python_code = input.parse()?; 56 | Ok(Self { python_code }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /optpy-parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod expression; 2 | use std::time::{SystemTime, UNIX_EPOCH}; 3 | 4 | pub use expression::{ 5 | BinaryOperation, BinaryOperator, BoolOperation, BoolOperator, CallFunction, CallMethod, 6 | Compare, CompareOperator, Dict, Expr, Index, Number, UnaryOperation, UnaryOperator, 7 | }; 8 | 9 | mod statement; 10 | pub(crate) use statement::For; 11 | use statement::RawStmt; 12 | pub use statement::{Assign, FromImport, Func, If, Import, Statement, While}; 13 | 14 | use rustpython_parser::error::ParseError; 15 | 16 | mod simplify; 17 | 18 | pub fn parse>(code: S) -> Result, ParseError> { 19 | let ast = rustpython_parser::parser::parse_program(code.as_ref(), "")?; 20 | let statements = ast 21 | .iter() 22 | .flat_map(|s| RawStmt::parse(&s.node)) 23 | .collect::>(); 24 | let statements = simplify::simplify_list_comprehensions(statements); 25 | let statements = simplify::simplify_for_loops(statements); 26 | let statements = simplify::simplify_tuple_assignments(statements); 27 | Ok(statements) 28 | } 29 | 30 | pub(crate) fn unixtime_nano() -> u128 { 31 | SystemTime::now() 32 | .duration_since(UNIX_EPOCH) 33 | .unwrap() 34 | .as_nanos() 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | 40 | use super::*; 41 | 42 | #[test] 43 | fn basic() { 44 | let code = r" 45 | a, b, c = input().split() 46 | print(a) 47 | "; 48 | 49 | let expected = r" 50 | __tmp_for_tuple = iter(input().split()) 51 | a = next(__tmp_for_tuple) 52 | b = next(__tmp_for_tuple) 53 | c = next(__tmp_for_tuple) 54 | print(a)"; 55 | assert_eq!(parse(code).unwrap(), parse(expected).unwrap()); 56 | } 57 | 58 | #[test] 59 | fn test_if_statement() { 60 | let code = r" 61 | if a <= c < b: 62 | result = 1 63 | else: 64 | result = 2 65 | print(result) 66 | "; 67 | let expected = r" 68 | if a<=c and c { 3 | concat!("mod ", stringify!($name), "{", include_str!($file), "}") 4 | }; 5 | } 6 | 7 | macro_rules! include_nested_modules { 8 | ($parent:ident, $($name:ident),+) => { 9 | concat!("mod ", stringify!($parent), "{",$(concat!( 10 | "mod ", 11 | stringify!($name), 12 | "{", 13 | include_str!(concat!("./", stringify!($parent), "/", stringify!($name), ".rs")), 14 | "}", 15 | "pub use self::", 16 | stringify!($name), 17 | "::*;" 18 | )),+, "}") 19 | }; 20 | } 21 | 22 | /// The compiler will bundle the following string to the generated code. 23 | /// Please add your module into not only this lib.rs file but also the following string when you add a new module. 24 | pub const OPTPY_RUNTIME: &str = concat!( 25 | include_module!("./builtin.rs", builtin), 26 | include_module!("./cell.rs", cell), 27 | include_module!("./number.rs", number), 28 | include_nested_modules!(stdlib, collections, math, sys, heapq), 29 | include_nested_modules!(value, value, list, dict, deque, string, iter), 30 | "pub use builtin::*;", 31 | "pub use stdlib::*;", 32 | "pub use value::*;" 33 | ); 34 | 35 | mod builtin; 36 | mod cell; 37 | mod number; 38 | mod stdlib; 39 | mod value; 40 | 41 | pub use builtin::*; 42 | pub use stdlib::*; 43 | pub use value::*; 44 | 45 | pub mod typed_builtin; 46 | pub mod typed_value; 47 | 48 | /// The compiler will bundle the following string to the generated code. 49 | /// Please add your module into not only this lib.rs file but also the following string when you add a new module. 50 | pub const OPTPY_TYPED_RUNTIME: &str = concat!( 51 | include_module!("./typed_builtin.rs", typed_builtin), 52 | include_module!("./cell.rs", cell), 53 | include_module!("./number.rs", number), 54 | include_nested_modules!(typed_value, boolean, list, number, string, traits), 55 | "pub use typed_builtin::*;", 56 | "pub use typed_value::*;", 57 | "pub use number::Number;" 58 | ); 59 | -------------------------------------------------------------------------------- /optpy-runtime/src/stdlib/heapq.rs: -------------------------------------------------------------------------------- 1 | use crate::{cell::UnsafeRefCell, Value}; 2 | 3 | #[allow(non_snake_case)] 4 | pub fn __heapq__heapify(x: &Value) { 5 | match x { 6 | Value::List(list) => heapify(&mut *list.0.borrow_mut()), 7 | _ => todo!(), 8 | } 9 | } 10 | #[allow(non_snake_case)] 11 | pub fn __heapq__heappush(heap: &Value, item: &Value) { 12 | match heap { 13 | Value::List(list) => heap_push(&mut *list.0.borrow_mut(), UnsafeRefCell::rc(item.clone())), 14 | _ => todo!(), 15 | } 16 | } 17 | #[allow(non_snake_case)] 18 | pub fn __heapq__heappop(heap: &Value) -> Value { 19 | match heap { 20 | Value::List(list) => heap_pop(&mut *list.0.borrow_mut()).borrow().clone(), 21 | _ => todo!(), 22 | } 23 | } 24 | 25 | fn shift_down(heap: &mut [T], start: usize, mut pos: usize) { 26 | while pos > start { 27 | let parent_pos = (pos - 1) >> 1; 28 | if heap[pos] < heap[parent_pos] { 29 | heap.swap(pos, parent_pos); 30 | pos = parent_pos; 31 | } else { 32 | break; 33 | } 34 | } 35 | } 36 | 37 | fn shift_up(heap: &mut [T], mut pos: usize) { 38 | let end = heap.len(); 39 | let start = pos; 40 | 41 | let mut child = 2 * pos + 1; 42 | while child < end { 43 | let right = child + 1; 44 | if right < end && heap[child] >= heap[right] { 45 | child = right; 46 | } 47 | 48 | heap.swap(pos, child); 49 | pos = child; 50 | child = 2 * pos + 1; 51 | } 52 | shift_down(heap, start, pos); 53 | } 54 | 55 | fn heapify(x: &mut [T]) { 56 | let n = x.len(); 57 | for i in (0..(n / 2)).rev() { 58 | shift_up(x, i); 59 | } 60 | } 61 | 62 | fn heap_push(heap: &mut Vec, item: T) { 63 | heap.push(item); 64 | let n = heap.len(); 65 | shift_down(heap, 0, n - 1); 66 | } 67 | 68 | fn heap_pop(heap: &mut Vec) -> T { 69 | if heap.len() >= 2 { 70 | let n = heap.len(); 71 | heap.swap(n - 1, 0); 72 | let response = heap.pop().expect("empty heap"); 73 | shift_up(heap, 0); 74 | response 75 | } else { 76 | heap.pop().expect("empty heap") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /optpy-runtime/src/cell.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::UnsafeCell, 3 | fmt::Debug, 4 | ops::{Deref, DerefMut}, 5 | ptr::NonNull, 6 | rc::Rc, 7 | }; 8 | 9 | pub struct UnsafeRef { 10 | value: NonNull, 11 | } 12 | impl Deref for UnsafeRef { 13 | type Target = T; 14 | 15 | #[inline] 16 | fn deref(&self) -> &T { 17 | unsafe { self.value.as_ref() } 18 | } 19 | } 20 | pub struct UnsafeRefMut { 21 | value: NonNull, 22 | } 23 | 24 | impl Deref for UnsafeRefMut { 25 | type Target = T; 26 | 27 | #[inline] 28 | fn deref(&self) -> &T { 29 | unsafe { self.value.as_ref() } 30 | } 31 | } 32 | 33 | impl DerefMut for UnsafeRefMut { 34 | #[inline] 35 | fn deref_mut(&mut self) -> &mut T { 36 | unsafe { self.value.as_mut() } 37 | } 38 | } 39 | 40 | impl PartialEq for UnsafeRef { 41 | fn eq(&self, other: &T) -> bool { 42 | self.deref() == other 43 | } 44 | } 45 | 46 | pub struct UnsafeRefCell { 47 | cell: UnsafeCell, 48 | } 49 | impl Debug for UnsafeRefCell { 50 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 51 | self.borrow().fmt(f) 52 | } 53 | } 54 | 55 | impl PartialOrd for UnsafeRefCell { 56 | fn partial_cmp(&self, other: &Self) -> Option { 57 | self.borrow().partial_cmp(&other.borrow()) 58 | } 59 | } 60 | 61 | impl PartialEq for UnsafeRefCell { 62 | fn eq(&self, other: &Self) -> bool { 63 | self.borrow().eq(&other.borrow()) 64 | } 65 | } 66 | 67 | impl UnsafeRefCell { 68 | pub fn new(value: T) -> UnsafeRefCell { 69 | Self { 70 | cell: UnsafeCell::new(value), 71 | } 72 | } 73 | pub fn rc(value: T) -> Rc> { 74 | Rc::new(Self::new(value)) 75 | } 76 | pub fn borrow(&self) -> UnsafeRef { 77 | let value = unsafe { NonNull::new_unchecked(self.cell.get()) }; 78 | UnsafeRef { value } 79 | } 80 | pub fn borrow_mut(&self) -> UnsafeRefMut { 81 | let value = unsafe { NonNull::new_unchecked(self.cell.get()) }; 82 | UnsafeRefMut { value } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /optpy-runtime/src/value/iter.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Debug, rc::Rc}; 2 | 3 | use crate::{cell::UnsafeRefCell, List, Value}; 4 | 5 | #[derive(Clone)] 6 | pub struct Iter { 7 | iter: Rc>>>, 8 | peeked: Rc>>, 9 | } 10 | 11 | impl Debug for Iter { 12 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 13 | f.debug_tuple("Iter").finish() 14 | } 15 | } 16 | 17 | impl Iter { 18 | pub fn new(iter: Box>) -> Self { 19 | Self { 20 | iter: UnsafeRefCell::rc(iter), 21 | peeked: UnsafeRefCell::rc(None), 22 | } 23 | } 24 | pub fn test(&self) -> bool { 25 | true 26 | } 27 | pub fn __next(&self) -> Option { 28 | if let Some(peeked) = self.peeked.borrow_mut().take() { 29 | return Some(peeked); 30 | } 31 | self.iter.borrow_mut().next() 32 | } 33 | pub fn __has_next(&self) -> bool { 34 | if self.peeked.borrow().is_some() { 35 | return true; 36 | } 37 | match self.iter.borrow_mut().next() { 38 | Some(peeked) => { 39 | self.peeked.borrow_mut().replace(peeked); 40 | true 41 | } 42 | None => false, 43 | } 44 | } 45 | 46 | pub fn __list(&self) -> Value { 47 | let mut list = vec![]; 48 | if let Some(peeked) = self.peeked.borrow_mut().take() { 49 | list.push(UnsafeRefCell::rc(peeked)); 50 | } 51 | while let Some(v) = self.iter.borrow_mut().next() { 52 | list.push(UnsafeRefCell::rc(v)); 53 | } 54 | Value::List(List(UnsafeRefCell::rc(list))) 55 | } 56 | 57 | pub fn any(&self) -> bool { 58 | if let Some(peeked) = self.peeked.borrow_mut().take() { 59 | if peeked.test() { 60 | return true; 61 | } 62 | } 63 | self.iter.borrow_mut().any(|v| v.test()) 64 | } 65 | pub fn all(&self) -> bool { 66 | if let Some(peeked) = self.peeked.borrow_mut().take() { 67 | if !peeked.test() { 68 | return false; 69 | } 70 | } 71 | self.iter.borrow_mut().all(|v| v.test()) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /optpy-parser/src/simplify/tuple_assign.rs: -------------------------------------------------------------------------------- 1 | use crate::{statement::Assign, CallFunction, Expr, Func, If, Statement, While}; 2 | 3 | pub(crate) fn simplify_tuple_assignments(stmts: Vec) -> Vec { 4 | stmts.into_iter().flat_map(simplify_stmt).collect() 5 | } 6 | 7 | fn simplify_stmt(stmt: Statement) -> Vec { 8 | match stmt { 9 | Statement::Assign(Assign { target, value }) => { 10 | if let Expr::Tuple(targets) = target { 11 | let tmp_target = Expr::VariableName("__tmp_for_tuple".into()); 12 | let mut result = vec![Statement::Assign(Assign { 13 | target: tmp_target.clone(), 14 | value: Expr::CallFunction(CallFunction { 15 | name: "iter".into(), 16 | args: vec![value], 17 | }), 18 | })]; 19 | for target in targets.into_iter() { 20 | result.push(Statement::Assign(Assign { 21 | target, 22 | value: Expr::CallFunction(CallFunction { 23 | name: "next".into(), 24 | args: vec![tmp_target.clone()], 25 | }), 26 | })) 27 | } 28 | result 29 | } else { 30 | vec![Statement::Assign(Assign { target, value })] 31 | } 32 | } 33 | Statement::If(If { test, body, orelse }) => { 34 | let body = simplify_tuple_assignments(body); 35 | let orelse = simplify_tuple_assignments(orelse); 36 | vec![Statement::If(If { test, body, orelse })] 37 | } 38 | Statement::Func(Func { name, args, body }) => { 39 | let body = simplify_tuple_assignments(body); 40 | vec![Statement::Func(Func { name, args, body })] 41 | } 42 | Statement::While(While { test, body }) => { 43 | let body = simplify_tuple_assignments(body); 44 | vec![Statement::While(While { test, body })] 45 | } 46 | Statement::Import(_) 47 | | Statement::FromImport(_) 48 | | Statement::Return(_) 49 | | Statement::Expression(_) 50 | | Statement::Break 51 | | Statement::Continue => vec![stmt], 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::{read_to_string, write}, 3 | path::PathBuf, 4 | }; 5 | 6 | use anyhow::Result; 7 | use clap::{Parser, Subcommand}; 8 | use optpy::{compile, typed_compile}; 9 | use optpy_dump::DumpPython; 10 | use optpy_parser::parse; 11 | use optpy_resolver::resolve; 12 | 13 | #[derive(Parser, Debug)] 14 | struct Args { 15 | #[command(subcommand)] 16 | command: Command, 17 | } 18 | 19 | #[derive(Subcommand, Debug)] 20 | enum Command { 21 | /// Generate a Rust file from a Python file 22 | Compile { 23 | /// Input Python file 24 | input: PathBuf, 25 | 26 | /// Path to output Rust file 27 | output: Option, 28 | }, 29 | /// Dump internal Python statements 30 | Dump { 31 | /// Input Python file 32 | input: PathBuf, 33 | }, 34 | /// Compile with type inference (experimental) 35 | Typed { 36 | /// Input Python file 37 | input: PathBuf, 38 | 39 | /// Path to output Rust file 40 | output: Option, 41 | }, 42 | } 43 | 44 | fn main() -> Result<()> { 45 | env_logger::init(); 46 | let args = Args::parse(); 47 | 48 | match args.command { 49 | Command::Compile { input, output } => { 50 | let code = read_to_string(&input)?; 51 | let result = compile(code)?; 52 | 53 | let output = match output { 54 | Some(output) => output, 55 | None => input.with_extension("rs"), 56 | }; 57 | write(&output, result)?; 58 | log::info!("Generated {:?}", output); 59 | } 60 | Command::Dump { input } => { 61 | let code = read_to_string(&input)?; 62 | let ast = parse(code)?; 63 | let (ast, _) = resolve(&ast); 64 | let python_code = ast.to_python_code(); 65 | println!("{}", python_code); 66 | } 67 | Command::Typed { input, output } => { 68 | let code = read_to_string(&input)?; 69 | let result = typed_compile(code)?; 70 | 71 | let output = match output { 72 | Some(output) => output, 73 | None => input.with_extension("rs"), 74 | }; 75 | write(&output, result)?; 76 | log::info!("Generated {:?}", output); 77 | } 78 | } 79 | 80 | Ok(()) 81 | } 82 | -------------------------------------------------------------------------------- /optpy-runtime/src/typed_value/list.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::{ 4 | cell::{UnsafeRefCell, UnsafeRefMut}, 5 | number::Number, 6 | }; 7 | 8 | use super::TypedValue; 9 | 10 | pub struct TypedList(pub Rc>>>>); 11 | impl From> for TypedList { 12 | fn from(v: Vec) -> Self { 13 | let list = v.into_iter().map(|v| UnsafeRefCell::rc(v)).collect(); 14 | Self(UnsafeRefCell::rc(list)) 15 | } 16 | } 17 | 18 | impl Default for TypedList { 19 | fn default() -> Self { 20 | Self(UnsafeRefCell::rc(vec![])) 21 | } 22 | } 23 | 24 | impl TypedList { 25 | pub fn __index_value(&self, index: Number) -> T { 26 | match index { 27 | Number::Int64(i) => { 28 | if i < 0 { 29 | let i = self.0.borrow().len() as i64 + i; 30 | self.0.borrow()[i as usize].borrow().__shallow_copy() 31 | } else { 32 | self.0.borrow()[i as usize].borrow().__shallow_copy() 33 | } 34 | } 35 | _ => todo!(), 36 | } 37 | } 38 | pub fn pop(&self) -> T { 39 | self.0.borrow_mut().pop().unwrap().borrow().__shallow_copy() 40 | } 41 | pub fn __mul(&self, x: Number) -> Self { 42 | let mut list = vec![]; 43 | let x = match x { 44 | Number::Int64(x) => x, 45 | Number::Float(_) => unreachable!(), 46 | }; 47 | for _ in 0..x { 48 | for element in self.0.borrow().iter() { 49 | list.push(element.borrow().__shallow_copy()); 50 | } 51 | } 52 | Self::from(list) 53 | } 54 | } 55 | 56 | impl TypedList { 57 | pub fn __len(&self) -> Number { 58 | Number::Int64(self.0.borrow().len() as i64) 59 | } 60 | pub fn reverse(&self) { 61 | self.0.borrow_mut().reverse(); 62 | } 63 | pub fn append(&self, x: T) { 64 | self.0.borrow_mut().push(UnsafeRefCell::rc(x)) 65 | } 66 | pub fn __index_ref(&self, index: Number) -> UnsafeRefMut { 67 | match index { 68 | Number::Int64(i) => { 69 | if i < 0 { 70 | let i = self.0.borrow().len() as i64 + i; 71 | self.0.borrow_mut()[i as usize].borrow_mut() 72 | } else { 73 | self.0.borrow_mut()[i as usize].borrow_mut() 74 | } 75 | } 76 | _ => todo!(), 77 | } 78 | } 79 | } 80 | 81 | impl TypedList { 82 | pub fn __list(&self) -> TypedList { 83 | let list = self 84 | .0 85 | .borrow() 86 | .iter() 87 | .map(|v| UnsafeRefCell::rc(v.borrow().__shallow_copy())) 88 | .collect::>(); 89 | Self(UnsafeRefCell::rc(list)) 90 | } 91 | } 92 | impl TypedValue for TypedList { 93 | fn __shallow_copy(&self) -> Self { 94 | Self(Rc::clone(&self.0)) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /optpy-parser/src/simplify/for_loop.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | statement::{Assign, FromImport, RawStmt}, 3 | unixtime_nano, CallFunction, Expr, For, Func, If, Import, Statement, While, 4 | }; 5 | 6 | pub(crate) fn simplify_for_loops(stmts: Vec>) -> Vec { 7 | stmts.into_iter().flat_map(simplify_statement).collect() 8 | } 9 | 10 | fn simplify_statement(stmt: RawStmt) -> Vec { 11 | match stmt { 12 | RawStmt::Assign(Assign { target, value }) => { 13 | vec![Statement::Assign(Assign { target, value })] 14 | } 15 | RawStmt::Expression(e) => vec![Statement::Expression(e)], 16 | RawStmt::If(If { test, body, orelse }) => { 17 | let body = simplify_for_loops(body); 18 | let orelse = simplify_for_loops(orelse); 19 | vec![Statement::If(If { test, body, orelse })] 20 | } 21 | RawStmt::Func(Func { name, args, body }) => { 22 | let body = simplify_for_loops(body); 23 | vec![Statement::Func(Func { name, args, body })] 24 | } 25 | RawStmt::Return(e) => vec![Statement::Return(e)], 26 | RawStmt::While(While { test, body }) => { 27 | let body = simplify_for_loops(body); 28 | vec![Statement::While(While { test, body })] 29 | } 30 | RawStmt::Break => vec![Statement::Break], 31 | RawStmt::Continue => vec![Statement::Continue], 32 | RawStmt::For(For { target, iter, body }) => { 33 | let tmp_iter = Expr::VariableName(format!("__tmp_for_loop_iter_{}", unixtime_nano())); 34 | 35 | let mut while_body = vec![Statement::Assign(Assign { 36 | target, 37 | value: Expr::CallFunction(CallFunction { 38 | name: "next".into(), 39 | args: vec![tmp_iter.clone()], 40 | }), 41 | })]; 42 | while_body.extend(simplify_for_loops(body)); 43 | 44 | vec![ 45 | Statement::Assign(Assign { 46 | target: tmp_iter.clone(), 47 | value: Expr::CallFunction(CallFunction { 48 | name: "iter".into(), 49 | args: vec![iter], 50 | }), 51 | }), 52 | Statement::While(While { 53 | test: Expr::CallFunction(CallFunction { 54 | name: "__has_next".into(), 55 | args: vec![tmp_iter], 56 | }), 57 | body: while_body, 58 | }), 59 | ] 60 | } 61 | RawStmt::Import(Import { import, alias }) => { 62 | vec![Statement::Import(Import { import, alias })] 63 | } 64 | RawStmt::FromImport(FromImport { 65 | import, 66 | alias, 67 | from, 68 | }) => { 69 | vec![Statement::FromImport(FromImport { 70 | import, 71 | alias, 72 | from, 73 | })] 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /optpy-runtime/src/typed_builtin.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | number::Number, 3 | typed_value::{TypedList, TypedString, TypedValue}, 4 | }; 5 | 6 | pub fn len(x: TypedList) -> Number { 7 | x.__len() 8 | } 9 | 10 | pub fn list(x: TypedList) -> TypedList { 11 | x.__list() 12 | } 13 | 14 | pub fn abs(x: Number) -> Number { 15 | x.abs() 16 | } 17 | pub fn __range1(x: Number) -> TypedList { 18 | match x { 19 | Number::Int64(i) => { 20 | let list = (0..i).map(|i| Number::from(i)).collect::>(); 21 | TypedList::from(list) 22 | } 23 | _ => unreachable!(), 24 | } 25 | } 26 | 27 | pub fn __range2(from: Number, to: Number) -> TypedList { 28 | match (from, to) { 29 | (Number::Int64(from), Number::Int64(to)) => { 30 | let list = (from..to).map(|i| Number::from(i)).collect::>(); 31 | TypedList::from(list) 32 | } 33 | _ => unreachable!(), 34 | } 35 | } 36 | 37 | pub fn __min2(a: Number, b: Number) -> Number { 38 | a.__min(b) 39 | } 40 | 41 | pub fn map_int(v: TypedList) -> TypedList { 42 | let list = 43 | v.0.borrow() 44 | .iter() 45 | .map(|v| Number::from(v.borrow().0.parse::().unwrap())) 46 | .collect::>(); 47 | TypedList::from(list) 48 | } 49 | 50 | pub fn input() -> TypedString { 51 | let mut buf = String::new(); 52 | std::io::stdin().read_line(&mut buf).unwrap(); 53 | TypedString::from(buf.as_str()) 54 | } 55 | 56 | #[macro_export] 57 | macro_rules! typed_range { 58 | ($stop:expr) => { 59 | __range1($stop) 60 | }; 61 | ($start:expr, $stop:expr) => { 62 | __range2($start, $stop) 63 | }; 64 | } 65 | 66 | #[macro_export] 67 | macro_rules! typed_print_values { 68 | ($($arg:expr),+) => { 69 | let s = [$($arg),+].iter().map(|v| v.to_string()).collect::>(); 70 | println!("{}", s.join(" ")); 71 | }; 72 | } 73 | 74 | #[macro_export] 75 | macro_rules! typed_pow { 76 | ($number:expr, $power:expr, $modulus:expr) => { 77 | __pow3($number, $power, $modulus) 78 | }; 79 | } 80 | #[macro_export] 81 | macro_rules! typed_set { 82 | () => { 83 | __set0() 84 | }; 85 | ($iter:expr) => { 86 | __set1($iter) 87 | }; 88 | } 89 | 90 | #[macro_export] 91 | macro_rules! typed_exit { 92 | () => { 93 | __exit0() 94 | }; 95 | ($code:expr) => { 96 | __exit1($code) 97 | }; 98 | } 99 | 100 | #[macro_export] 101 | macro_rules! typed_max { 102 | ($e:expr) => { 103 | __max1($e) 104 | }; 105 | ($a:expr, $b:expr) => { 106 | __max2($a, $b) 107 | }; 108 | ($a:expr, $($arg:expr),+) => { 109 | __max2($a, &max!($($arg),+)) 110 | }; 111 | } 112 | 113 | #[macro_export] 114 | macro_rules! typed_min { 115 | ($e:expr) => { 116 | __min1($e) 117 | }; 118 | ($a:expr, $b:expr) => { 119 | __min2($a, $b) 120 | }; 121 | ($a:expr, $($arg:expr),+) => { 122 | __min2($a, &min!($($arg),+)) 123 | }; 124 | } 125 | 126 | #[macro_export] 127 | macro_rules! typed_sum { 128 | ($e:expr) => { 129 | __sum1($e) 130 | }; 131 | ($a:expr, $b:expr) => { 132 | __sum2($a, $b) 133 | }; 134 | ($a:expr, $($arg:expr),+) => { 135 | __sum2($a, &sum!($($arg),+)) 136 | }; 137 | } 138 | -------------------------------------------------------------------------------- /optpy-runtime/src/value/dict.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, rc::Rc}; 2 | 3 | use crate::{ 4 | cell::{UnsafeRefCell, UnsafeRefMut}, 5 | number::Number, 6 | ImmutableString, Value, 7 | }; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct Dict(pub Rc>>>>); 11 | 12 | impl Default for Dict { 13 | fn default() -> Self { 14 | Self(UnsafeRefCell::rc(Default::default())) 15 | } 16 | } 17 | impl PartialEq for Dict { 18 | fn eq(&self, other: &Self) -> bool { 19 | self.0.borrow().eq(&other.0.borrow()) 20 | } 21 | } 22 | 23 | impl Dict { 24 | pub fn includes(&self, value: &Value) -> bool { 25 | self.0.borrow().contains_key(&DictKey::from(value)) 26 | } 27 | pub fn __delete(&self, index: &Value) { 28 | self.0.borrow_mut().remove(&DictKey::from(index)); 29 | } 30 | 31 | pub fn __index_ref(&self, index: &Value) -> UnsafeRefMut { 32 | let key = DictKey::from(index); 33 | self.0 34 | .borrow_mut() 35 | .entry(key) 36 | .or_insert_with(|| UnsafeRefCell::rc(Default::default())) 37 | .borrow_mut() 38 | } 39 | pub fn __index_value(&self, index: &Value) -> Value { 40 | let key = DictKey::from(index); 41 | self.0 42 | .borrow_mut() 43 | .entry(key) 44 | .or_insert_with(|| UnsafeRefCell::rc(Default::default())) 45 | .borrow() 46 | .clone() 47 | } 48 | 49 | pub fn keys(&self) -> Value { 50 | let list = self 51 | .0 52 | .borrow() 53 | .keys() 54 | .map(|s| s.clone().into()) 55 | .collect::>(); 56 | Value::from(list) 57 | } 58 | pub fn setdefault(&self, key: &Value, value: &Value) { 59 | let key = DictKey::from(key); 60 | self.0 61 | .borrow_mut() 62 | .entry(key) 63 | .or_insert_with(|| UnsafeRefCell::rc(value.clone())); 64 | } 65 | pub fn add(&self, value: &Value) { 66 | let key = DictKey::from(value); 67 | self.0 68 | .borrow_mut() 69 | .insert(key, UnsafeRefCell::rc(Value::None)); 70 | } 71 | pub fn __len(&self) -> Value { 72 | Value::Number(Number::Int64(self.0.borrow().len() as i64)) 73 | } 74 | pub fn test(&self) -> bool { 75 | !self.0.borrow().is_empty() 76 | } 77 | } 78 | 79 | impl From> for Dict { 80 | fn from(pairs: Vec<(Value, Value)>) -> Self { 81 | let map = pairs 82 | .into_iter() 83 | .map(|(key, value)| { 84 | let key = DictKey::from(&key); 85 | let value = UnsafeRefCell::rc(value); 86 | (key, value) 87 | }) 88 | .collect::>(); 89 | Self(UnsafeRefCell::rc(map)) 90 | } 91 | } 92 | 93 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 94 | pub enum DictKey { 95 | Number(Number), 96 | String(String), 97 | } 98 | 99 | impl Into for DictKey { 100 | fn into(self) -> Value { 101 | match self { 102 | DictKey::Number(n) => Value::Number(n), 103 | DictKey::String(s) => Value::String(ImmutableString(Rc::new(s))), 104 | } 105 | } 106 | } 107 | 108 | impl From<&Value> for DictKey { 109 | fn from(value: &Value) -> Self { 110 | match value { 111 | Value::String(s) => Self::String(s.to_string()), 112 | Value::Number(n) => Self::Number(*n), 113 | _ => unreachable!(), 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /optpy-parser/src/expression/types.rs: -------------------------------------------------------------------------------- 1 | use rustpython_parser::ast::{Boolop, Cmpop, Unaryop}; 2 | 3 | #[derive(Debug, PartialEq, Eq, Clone)] 4 | pub struct CallFunction { 5 | pub name: String, 6 | pub args: Vec, 7 | } 8 | #[derive(Debug, PartialEq, Eq, Clone)] 9 | pub struct CallMethod { 10 | pub value: Box, 11 | pub name: String, 12 | pub args: Vec, 13 | } 14 | #[derive(Debug, PartialEq, Eq, Clone)] 15 | pub struct BoolOperation { 16 | pub op: BoolOperator, 17 | pub conditions: Vec, 18 | } 19 | #[derive(Debug, PartialEq, Eq, Clone)] 20 | pub struct Compare { 21 | pub left: Box, 22 | pub right: Box, 23 | pub op: CompareOperator, 24 | } 25 | #[derive(Debug, PartialEq, Eq, Clone)] 26 | pub struct UnaryOperation { 27 | pub value: Box, 28 | pub op: UnaryOperator, 29 | } 30 | #[derive(Debug, PartialEq, Eq, Clone)] 31 | pub struct BinaryOperation { 32 | pub left: Box, 33 | pub right: Box, 34 | pub op: BinaryOperator, 35 | } 36 | #[derive(Debug, PartialEq, Eq, Clone)] 37 | pub struct Index { 38 | pub value: Box, 39 | pub index: Box, 40 | } 41 | 42 | #[derive(Debug, PartialEq, Eq, Clone)] 43 | pub struct Dict { 44 | pub pairs: Vec<(E, E)>, 45 | } 46 | 47 | #[derive(Debug, PartialEq, Eq, Clone)] 48 | pub(crate) struct ListComprehension { 49 | pub(crate) value: Box, 50 | pub(crate) generators: Vec>, 51 | } 52 | 53 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 54 | pub enum BoolOperator { 55 | And, 56 | Or, 57 | } 58 | impl BoolOperator { 59 | pub fn parse(op: &Boolop) -> Self { 60 | match op { 61 | Boolop::And => Self::And, 62 | Boolop::Or => Self::Or, 63 | } 64 | } 65 | } 66 | 67 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 68 | pub enum CompareOperator { 69 | Less, 70 | LessOrEqual, 71 | Greater, 72 | GreaterOrEqual, 73 | Equal, 74 | NotEqual, 75 | NotIn, 76 | In, 77 | } 78 | 79 | impl CompareOperator { 80 | pub fn parse(op: &Cmpop) -> Self { 81 | match op { 82 | Cmpop::Lt => Self::Less, 83 | Cmpop::LtE => Self::LessOrEqual, 84 | Cmpop::Gt => Self::Greater, 85 | Cmpop::GtE => Self::GreaterOrEqual, 86 | Cmpop::Eq => Self::Equal, 87 | Cmpop::NotEq => Self::NotEqual, 88 | Cmpop::NotIn => Self::NotIn, 89 | Cmpop::In => Self::In, 90 | op => todo!("{:?}", op), 91 | } 92 | } 93 | } 94 | 95 | #[derive(Debug, PartialEq, Eq, Clone)] 96 | pub enum Number { 97 | Int(String), 98 | Float(String), 99 | } 100 | 101 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 102 | pub enum BinaryOperator { 103 | Add, 104 | Sub, 105 | Mul, 106 | Div, 107 | Mod, 108 | FloorDiv, 109 | Pow, 110 | BitAnd, 111 | LeftShift, 112 | RightShift, 113 | } 114 | 115 | impl BinaryOperator { 116 | pub fn parse(op: &rustpython_parser::ast::Operator) -> Self { 117 | match op { 118 | rustpython_parser::ast::Operator::Add => Self::Add, 119 | rustpython_parser::ast::Operator::Sub => Self::Sub, 120 | rustpython_parser::ast::Operator::Mult => Self::Mul, 121 | rustpython_parser::ast::Operator::Div => Self::Div, 122 | rustpython_parser::ast::Operator::Mod => Self::Mod, 123 | rustpython_parser::ast::Operator::FloorDiv => Self::FloorDiv, 124 | rustpython_parser::ast::Operator::Pow => Self::Pow, 125 | rustpython_parser::ast::Operator::BitAnd => Self::BitAnd, 126 | rustpython_parser::ast::Operator::LShift => Self::LeftShift, 127 | rustpython_parser::ast::Operator::RShift => Self::RightShift, 128 | op => todo!("{:?}", op), 129 | } 130 | } 131 | } 132 | 133 | #[derive(Debug, PartialEq, Eq, Clone)] 134 | pub struct Comprehension { 135 | pub target: Box, 136 | pub iter: Box, 137 | pub ifs: Vec, 138 | } 139 | 140 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 141 | pub enum UnaryOperator { 142 | Add, 143 | Sub, 144 | Not, 145 | } 146 | 147 | impl UnaryOperator { 148 | pub fn parse(op: &Unaryop) -> Self { 149 | match op { 150 | Unaryop::UAdd => Self::Add, 151 | Unaryop::USub => Self::Sub, 152 | Unaryop::Not => Self::Not, 153 | op => todo!("{:?}", op), 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /optpy-runtime/src/number.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | hash::Hash, 3 | ops::{Add, Div, Mul, Rem, Sub}, 4 | }; 5 | 6 | #[derive(Debug, Clone, Copy)] 7 | pub enum Number { 8 | Int64(i64), 9 | Float(f64), 10 | } 11 | impl Hash for Number { 12 | fn hash(&self, state: &mut H) { 13 | match self { 14 | Number::Int64(i) => i.hash(state), 15 | Number::Float(_) => todo!(), 16 | } 17 | } 18 | } 19 | impl Eq for Number {} 20 | 21 | impl PartialOrd for Number { 22 | fn partial_cmp(&self, other: &Self) -> Option { 23 | match (self, other) { 24 | (Number::Int64(l0), Number::Int64(r0)) => l0.partial_cmp(r0), 25 | (Number::Float(l0), Number::Float(r0)) => l0.partial_cmp(r0), 26 | (Number::Int64(l0), Number::Float(r0)) => (*l0 as f64).partial_cmp(r0), 27 | (Number::Float(l0), Number::Int64(r0)) => l0.partial_cmp(&(*r0 as f64)), 28 | } 29 | } 30 | } 31 | impl PartialEq for Number { 32 | fn eq(&self, other: &Self) -> bool { 33 | match (self, other) { 34 | (Number::Int64(l0), Number::Int64(r0)) => l0.eq(r0), 35 | (Number::Float(l0), Number::Float(r0)) => l0.eq(r0), 36 | (Number::Int64(l0), Number::Float(r0)) => *l0 as f64 == *r0, 37 | (Number::Float(l0), Number::Int64(r0)) => *l0 == *r0 as f64, 38 | } 39 | } 40 | } 41 | 42 | impl Number { 43 | pub fn floor_div(&self, rhs: &Number) -> Number { 44 | match (self, rhs) { 45 | (Number::Int64(l0), Number::Int64(r0)) => Number::Int64(l0 / r0), 46 | _ => todo!(), 47 | } 48 | } 49 | pub fn pow(&self, rhs: Number) -> Number { 50 | match (self, rhs) { 51 | (Number::Int64(l0), Number::Int64(r0)) => Number::Int64(l0.pow(r0 as u32)), 52 | _ => todo!(), 53 | } 54 | } 55 | pub fn abs(&self) -> Number { 56 | match self { 57 | Number::Int64(i) => Number::Int64(i.abs()), 58 | Number::Float(f) => Number::Float(f.abs()), 59 | } 60 | } 61 | pub fn test(&self) -> bool { 62 | match self { 63 | Number::Int64(i) => *i != 0, 64 | Number::Float(f) => *f != 0.0, 65 | } 66 | } 67 | pub fn __left_shift(&self, value: &Number) -> Number { 68 | match (self, value) { 69 | (Number::Int64(i), Number::Int64(x)) => Number::Int64(i << x), 70 | _ => unreachable!(), 71 | } 72 | } 73 | pub fn __right_shift(&self, value: &Number) -> Number { 74 | match (self, value) { 75 | (Number::Int64(i), Number::Int64(x)) => Number::Int64(i >> x), 76 | _ => unreachable!(), 77 | } 78 | } 79 | } 80 | impl ToString for Number { 81 | fn to_string(&self) -> String { 82 | match self { 83 | Number::Int64(i) => i.to_string(), 84 | Number::Float(f) => f.to_string(), 85 | } 86 | } 87 | } 88 | 89 | macro_rules! impl_binop { 90 | ($t:tt, $name:ident) => { 91 | impl $t for Number { 92 | type Output = Number; 93 | 94 | fn $name(self, rhs: Self) -> Self::Output { 95 | match (self, rhs) { 96 | (Number::Int64(lhs), Number::Int64(rhs)) => Number::Int64(lhs.$name(rhs)), 97 | (Number::Int64(lhs), Number::Float(rhs)) => { 98 | Number::Float((lhs as f64).$name(rhs)) 99 | } 100 | (Number::Float(lhs), Number::Int64(rhs)) => { 101 | Number::Float(lhs.$name(rhs as f64)) 102 | } 103 | (Number::Float(lhs), Number::Float(rhs)) => Number::Float(lhs.$name(rhs)), 104 | } 105 | } 106 | } 107 | }; 108 | } 109 | impl_binop!(Add, add); 110 | impl_binop!(Mul, mul); 111 | impl_binop!(Sub, sub); 112 | impl_binop!(Rem, rem); 113 | impl Div for Number { 114 | type Output = Number; 115 | 116 | fn div(self, rhs: Self) -> Self::Output { 117 | match (self, rhs) { 118 | (Number::Int64(lhs), Number::Int64(rhs)) => Number::Float(lhs as f64 / rhs as f64), 119 | (Number::Int64(lhs), Number::Float(rhs)) => Number::Float(lhs as f64 / rhs), 120 | (Number::Float(lhs), Number::Int64(rhs)) => Number::Float(lhs / rhs as f64), 121 | (Number::Float(lhs), Number::Float(rhs)) => Number::Float(lhs / rhs), 122 | } 123 | } 124 | } 125 | 126 | impl From for Number { 127 | fn from(v: i64) -> Self { 128 | Number::Int64(v) 129 | } 130 | } 131 | 132 | impl From for Number { 133 | fn from(v: f64) -> Self { 134 | Number::Float(v) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tools/source-code-downloader/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::BTreeSet, 3 | fs::{create_dir_all, write}, 4 | path::PathBuf, 5 | }; 6 | 7 | use anyhow::{Context, Result}; 8 | use clap::Parser; 9 | use reqwest::Client; 10 | use scraper::{Html, Selector}; 11 | 12 | #[derive(Parser)] 13 | struct Args { 14 | /// AtCoder contest id 15 | #[clap(short, long)] 16 | contest_id: String, 17 | 18 | /// directory to save the code 19 | #[clap(short, long)] 20 | out: PathBuf, 21 | 22 | /// number of source code to download for each problem 23 | #[clap(short, long, default_value = "50")] 24 | limit: usize, 25 | } 26 | 27 | #[tokio::main] 28 | async fn main() -> Result<()> { 29 | env_logger::init(); 30 | let args = Args::parse(); 31 | let client = Client::builder().gzip(true).build()?; 32 | 33 | let problem_ids = list_problems(&args.contest_id, &client).await?; 34 | 35 | for problem_id in problem_ids { 36 | let mut downloaded = 0; 37 | for page in 1.. { 38 | log::info!("scraping page={} for {}", page, problem_id); 39 | let ids = list_submissions(&args.contest_id, &problem_id, page, &client).await?; 40 | if ids.is_empty() { 41 | break; 42 | } 43 | 44 | for submission_id in ids { 45 | let (code, problem_id) = 46 | download_submission(&args.contest_id, submission_id, &client).await?; 47 | let path = args.out.join(&args.contest_id).join(&problem_id); 48 | create_dir_all(&path)?; 49 | let path = path.join(format!("{}_{}.py", problem_id, submission_id)); 50 | 51 | write(path, code)?; 52 | downloaded += 1; 53 | if downloaded >= args.limit { 54 | break; 55 | } 56 | } 57 | 58 | if downloaded >= args.limit { 59 | break; 60 | } 61 | } 62 | } 63 | 64 | Ok(()) 65 | } 66 | 67 | async fn download_submission( 68 | contest_id: &str, 69 | submission_id: i64, 70 | client: &Client, 71 | ) -> Result<(String, String)> { 72 | let url = format!( 73 | "https://atcoder.jp/contests/{}/submissions/{}", 74 | contest_id, submission_id 75 | ); 76 | let body = client.get(url).send().await?.text().await?; 77 | let document = Html::parse_document(&body); 78 | let selector = Selector::parse("pre").expect("invalid selector"); 79 | 80 | let code = document 81 | .select(&selector) 82 | .flat_map(|e| e.text()) 83 | .collect::(); 84 | 85 | let selector = Selector::parse("a").expect("invalid selector"); 86 | let problem_id = document 87 | .select(&selector) 88 | .filter_map(|e| e.value().attr("href")) 89 | .filter_map(|h| h.strip_prefix(&format!("/contests/{}/tasks/", contest_id))) 90 | .next() 91 | .context("no problem id")? 92 | .to_string(); 93 | Ok((code, problem_id)) 94 | } 95 | 96 | async fn list_submissions( 97 | contest_id: &str, 98 | problem_id: &str, 99 | page: usize, 100 | client: &Client, 101 | ) -> Result> { 102 | let url = format!( 103 | r"https://atcoder.jp/contests/{}/submissions?f.LanguageName=Python3&f.Status=AC&orderBy=created&page={}&f.Task={}", 104 | contest_id, page, problem_id 105 | ); 106 | let mut submission_ids = vec![]; 107 | let body = client.get(url).send().await?.text().await?; 108 | let document = Html::parse_document(&body); 109 | let selector = Selector::parse("a").expect("invalid selector"); 110 | for element in document.select(&selector) { 111 | if let Some(href) = element.value().attr("href") { 112 | if let Some(a) = href.strip_prefix(&format!("/contests/{}/submissions/", contest_id)) { 113 | let id = a.parse::()?; 114 | submission_ids.push(id); 115 | } 116 | } 117 | } 118 | Ok(submission_ids) 119 | } 120 | 121 | async fn list_problems(contest_id: &str, client: &Client) -> Result> { 122 | let url = format!(r"https://atcoder.jp/contests/{}/tasks", contest_id); 123 | let body = client.get(url).send().await?.text().await?; 124 | let document = Html::parse_document(&body); 125 | let selector = Selector::parse("a").expect("invalid selector"); 126 | let mut problem_ids = BTreeSet::new(); 127 | for element in document.select(&selector) { 128 | if let Some(href) = element.value().attr("href") { 129 | if let Some(problem_id) = href.strip_prefix(&format!("/contests/{}/tasks/", contest_id)) 130 | { 131 | problem_ids.insert(problem_id.to_string()); 132 | } 133 | } 134 | } 135 | Ok(problem_ids) 136 | } 137 | -------------------------------------------------------------------------------- /optpy-runtime/src/value/list.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::{ 4 | cell::{UnsafeRefCell, UnsafeRefMut}, 5 | number::Number, 6 | Iter, Value, 7 | }; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct List(pub Rc>>>>); 11 | 12 | impl PartialEq for List { 13 | fn eq(&self, other: &Self) -> bool { 14 | self.0.borrow().eq(&other.0.borrow()) 15 | } 16 | } 17 | impl PartialOrd for List { 18 | fn partial_cmp(&self, other: &Self) -> Option { 19 | self.0.borrow().partial_cmp(&other.0.borrow()) 20 | } 21 | } 22 | 23 | impl List { 24 | pub fn __mul(&self, rhs: &Value) -> Value { 25 | match rhs { 26 | Value::Number(Number::Int64(n)) => { 27 | let mut result = vec![]; 28 | for _ in 0..(*n) { 29 | for element in self.0.borrow().iter() { 30 | result.push(UnsafeRefCell::rc(element.borrow().clone())); 31 | } 32 | } 33 | Value::List(List(UnsafeRefCell::rc(result))) 34 | } 35 | _ => todo!(), 36 | } 37 | } 38 | pub fn includes(&self, value: &Value) -> bool { 39 | self.0.borrow().iter().any(|e| e.borrow().eq(value)) 40 | } 41 | pub fn __delete(&self, index: &Value) { 42 | match index { 43 | Value::Number(Number::Int64(i)) => { 44 | if *i < 0 { 45 | let i = self.0.borrow().len() as i64 + *i; 46 | self.0.borrow_mut().remove(i as usize); 47 | } else { 48 | self.0.borrow_mut().remove(*i as usize); 49 | } 50 | } 51 | _ => todo!(), 52 | } 53 | } 54 | pub fn __index_ref(&self, index: &Value) -> UnsafeRefMut { 55 | match index { 56 | Value::Number(Number::Int64(i)) => { 57 | if *i < 0 { 58 | let i = self.0.borrow().len() as i64 + *i; 59 | self.0.borrow_mut()[i as usize].borrow_mut() 60 | } else { 61 | self.0.borrow_mut()[*i as usize].borrow_mut() 62 | } 63 | } 64 | _ => todo!(), 65 | } 66 | } 67 | pub fn __index_value(&self, index: &Value) -> Value { 68 | match index { 69 | Value::Number(Number::Int64(i)) => { 70 | if *i < 0 { 71 | let i = self.0.borrow().len() as i64 + *i; 72 | self.0.borrow()[i as usize].borrow().clone() 73 | } else { 74 | self.0.borrow()[*i as usize].borrow().clone() 75 | } 76 | } 77 | _ => todo!(), 78 | } 79 | } 80 | pub fn reverse(&self) { 81 | self.0.borrow_mut().reverse(); 82 | } 83 | pub fn pop(&self) -> Value { 84 | let last = self.0.borrow_mut().pop().expect("empty list"); 85 | last.borrow().clone() 86 | } 87 | pub fn append(&self, value: &Value) { 88 | self.0.borrow_mut().push(UnsafeRefCell::rc(value.clone())); 89 | } 90 | pub fn __len(&self) -> Value { 91 | Value::Number(Number::Int64(self.0.borrow().len() as i64)) 92 | } 93 | pub fn sort(&self) { 94 | self.0.borrow_mut().sort_by(|a, b| { 95 | let a = a.borrow(); 96 | let b = b.borrow(); 97 | a.partial_cmp(&b).unwrap() 98 | }) 99 | } 100 | pub fn index(&self, value: &Value) -> Value { 101 | let index = self 102 | .0 103 | .borrow() 104 | .iter() 105 | .enumerate() 106 | .find(|(_, e)| e.borrow().eq(value)) 107 | .expect("not found") 108 | .0; 109 | Value::from(index as i64) 110 | } 111 | pub fn count(&self, value: &Value) -> Value { 112 | let count = self 113 | .0 114 | .borrow() 115 | .iter() 116 | .filter(|v| v.borrow().eq(value)) 117 | .count(); 118 | Value::Number(Number::Int64(count as i64)) 119 | } 120 | pub fn test(&self) -> bool { 121 | self.0.borrow().len() > 0 122 | } 123 | pub fn __iter(&self) -> Value { 124 | let v = self.0.borrow().clone(); 125 | let iter = v.into_iter().map(|v| v.borrow().clone()); 126 | Value::Iter(Iter::new(Box::new(iter))) 127 | } 128 | } 129 | 130 | impl ToString for List { 131 | fn to_string(&self) -> String { 132 | let mut result = String::from("["); 133 | for (i, v) in self.0.borrow().iter().enumerate() { 134 | if i > 0 { 135 | result.push_str(", "); 136 | } 137 | result.push_str(&v.borrow().to_string()); 138 | } 139 | result.push_str("]"); 140 | result 141 | } 142 | } 143 | 144 | impl From> for List { 145 | fn from(list: Vec) -> Self { 146 | let list = list.into_iter().map(|v| UnsafeRefCell::rc(v)).collect(); 147 | Self(UnsafeRefCell::rc(list)) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /optpy-resolver/src/builtin/mod.rs: -------------------------------------------------------------------------------- 1 | use optpy_parser::{ 2 | Assign, BinaryOperation, BoolOperation, CallFunction, CallMethod, Compare, Dict, Expr, Func, 3 | If, Index, Statement, UnaryOperation, While, 4 | }; 5 | 6 | pub fn resolve_builtin_functions(statements: &[Statement]) -> Vec { 7 | statements.resolve() 8 | } 9 | 10 | trait StatementResolve { 11 | fn resolve(&self) -> Statement; 12 | } 13 | 14 | impl StatementResolve for Statement { 15 | fn resolve(&self) -> Statement { 16 | match self { 17 | Statement::Assign(Assign { target, value }) => Statement::Assign(Assign { 18 | target: target.resolve(), 19 | value: value.resolve(), 20 | }), 21 | Statement::Expression(e) => Statement::Expression(e.resolve()), 22 | Statement::If(If { test, body, orelse }) => Statement::If(If { 23 | test: test.resolve(), 24 | body: body.resolve(), 25 | orelse: orelse.resolve(), 26 | }), 27 | Statement::Func(Func { name, args, body }) => Statement::Func(Func { 28 | name: name.clone(), 29 | args: args.clone(), 30 | body: body.resolve(), 31 | }), 32 | Statement::Return(v) => Statement::Return(v.as_ref().map(|e| e.resolve())), 33 | Statement::While(While { test, body }) => Statement::While(While { 34 | test: test.resolve(), 35 | body: body.resolve(), 36 | }), 37 | Statement::Import(_) 38 | | Statement::FromImport(_) 39 | | Statement::Break 40 | | Statement::Continue => self.clone(), 41 | } 42 | } 43 | } 44 | 45 | trait StatementResolves { 46 | fn resolve(&self) -> Vec; 47 | } 48 | impl StatementResolves for [Statement] { 49 | fn resolve(&self) -> Vec { 50 | self.iter().map(|s| s.resolve()).collect() 51 | } 52 | } 53 | 54 | trait ExprResolve { 55 | fn resolve(&self) -> Expr; 56 | } 57 | 58 | impl ExprResolve for Expr { 59 | fn resolve(&self) -> Self { 60 | match self { 61 | Expr::CallFunction(CallFunction { name, args }) => { 62 | if name == "map" && args[0] == Expr::VariableName("int".into()) { 63 | let args = args[1..].resolve(); 64 | Expr::CallFunction(CallFunction { 65 | name: "map_int".into(), 66 | args, 67 | }) 68 | } else { 69 | match name.as_str() { 70 | "range" | "pow" | "set" | "exit" | "max" | "min" | "sum" | "next" => { 71 | Expr::CallFunction(CallFunction { 72 | name: format!("{name}__macro__"), 73 | args: args.resolve(), 74 | }) 75 | } 76 | "print" => Expr::CallFunction(CallFunction { 77 | name: "print_values__macro__".into(), 78 | args: args.resolve(), 79 | }), 80 | _ => Expr::CallFunction(CallFunction { 81 | name: name.to_string(), 82 | args: args.resolve(), 83 | }), 84 | } 85 | } 86 | } 87 | Expr::CallMethod(CallMethod { value, name, args }) => Expr::CallMethod(CallMethod { 88 | value: Box::new(value.resolve()), 89 | name: name.to_string(), 90 | args: args.resolve(), 91 | }), 92 | Expr::Tuple(tuple) => Expr::Tuple(tuple.resolve()), 93 | Expr::BoolOperation(BoolOperation { op, conditions }) => { 94 | Expr::BoolOperation(BoolOperation { 95 | op: *op, 96 | conditions: conditions.resolve(), 97 | }) 98 | } 99 | Expr::Compare(Compare { left, right, op }) => Expr::Compare(Compare { 100 | left: Box::new(left.resolve()), 101 | right: Box::new(right.resolve()), 102 | op: *op, 103 | }), 104 | Expr::BinaryOperation(BinaryOperation { left, right, op }) => { 105 | Expr::BinaryOperation(BinaryOperation { 106 | left: Box::new(left.resolve()), 107 | right: Box::new(right.resolve()), 108 | op: *op, 109 | }) 110 | } 111 | Expr::Index(Index { value, index }) => Expr::Index(Index { 112 | value: Box::new(value.resolve()), 113 | index: Box::new(index.resolve()), 114 | }), 115 | Expr::List(list) => Expr::List(list.resolve()), 116 | Expr::Dict(Dict { pairs }) => { 117 | let pairs = pairs 118 | .iter() 119 | .map(|(key, value)| (key.resolve(), value.resolve())) 120 | .collect(); 121 | Expr::Dict(Dict { pairs }) 122 | } 123 | Expr::ConstantNumber(_) 124 | | Expr::ConstantString(_) 125 | | Expr::VariableName(_) 126 | | Expr::ConstantBoolean(_) 127 | | Expr::None => self.clone(), 128 | Expr::UnaryOperation(UnaryOperation { value, op }) => { 129 | Expr::UnaryOperation(UnaryOperation { 130 | value: Box::new(value.resolve()), 131 | op: *op, 132 | }) 133 | } 134 | } 135 | } 136 | } 137 | 138 | trait ExprResolves { 139 | fn resolve(&self) -> Vec; 140 | } 141 | impl ExprResolves for [Expr] { 142 | fn resolve(&self) -> Vec { 143 | self.iter().map(|e| e.resolve()).collect() 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /optpy-dump/src/lib.rs: -------------------------------------------------------------------------------- 1 | use optpy_parser::{ 2 | Assign, BinaryOperation, BoolOperation, CallFunction, CallMethod, Compare, Dict, Expr, 3 | FromImport, Func, If, Import, Index, Number, Statement, UnaryOperation, While, 4 | }; 5 | 6 | pub trait DumpPython { 7 | fn to_python_code(&self) -> String; 8 | } 9 | 10 | impl DumpPython for Statement { 11 | fn to_python_code(&self) -> String { 12 | match self { 13 | Statement::Assign(Assign { target, value }) => { 14 | format!("{} = {}", target.to_python_code(), value.to_python_code()) 15 | } 16 | Statement::Expression(expr) => expr.to_python_code(), 17 | Statement::If(If { test, body, orelse }) => { 18 | let test = test.to_python_code(); 19 | let body = body.to_python_code(); 20 | let orelse = orelse.to_python_code(); 21 | format!( 22 | "if {test}:\n{}\nelse:\n{}", 23 | indent_code(&body), 24 | indent_code(&orelse) 25 | ) 26 | } 27 | Statement::Func(Func { name, args, body }) => { 28 | let args = args.join(", "); 29 | let body = body.to_python_code(); 30 | format!("def {name}({args}):\n{}", indent_code(&body)) 31 | } 32 | Statement::Return(r) => match r { 33 | Some(r) => format!("return {}", r.to_python_code()), 34 | None => "return".into(), 35 | }, 36 | Statement::While(While { test, body }) => { 37 | let test = test.to_python_code(); 38 | let body = body.to_python_code(); 39 | format!("while {test}:\n{}", indent_code(&body)) 40 | } 41 | Statement::Break => "break".into(), 42 | Statement::Continue => "continue".into(), 43 | Statement::Import(Import { import, alias }) => { 44 | format!("import {import} as {alias}") 45 | } 46 | Statement::FromImport(FromImport { 47 | from, 48 | import, 49 | alias, 50 | }) => { 51 | format!("from {from} import {import} as {alias}") 52 | } 53 | } 54 | } 55 | } 56 | 57 | impl DumpPython for Expr { 58 | fn to_python_code(&self) -> String { 59 | match self { 60 | Expr::CallFunction(CallFunction { name, args }) => { 61 | let args = args 62 | .iter() 63 | .map(|arg| arg.to_python_code()) 64 | .collect::>() 65 | .join(", "); 66 | format!("{name}({args})") 67 | } 68 | Expr::CallMethod(CallMethod { value, name, args }) => { 69 | let value = value.to_python_code(); 70 | let args = args 71 | .iter() 72 | .map(|arg| arg.to_python_code()) 73 | .collect::>() 74 | .join(", "); 75 | format!("{value}.{name}({args})") 76 | } 77 | Expr::Tuple(tuple) => { 78 | let tuple = tuple 79 | .iter() 80 | .map(|arg| arg.to_python_code()) 81 | .collect::>() 82 | .join(", "); 83 | format!("({tuple})") 84 | } 85 | Expr::VariableName(name) => name.to_string(), 86 | Expr::BoolOperation(BoolOperation { op, conditions }) => { 87 | if conditions.len() == 1 { 88 | conditions[0].to_python_code() 89 | } else { 90 | conditions 91 | .iter() 92 | .map(|c| format!("({})", c.to_python_code())) 93 | .collect::>() 94 | .join(match op { 95 | optpy_parser::BoolOperator::And => " and ", 96 | optpy_parser::BoolOperator::Or => " or ", 97 | }) 98 | } 99 | } 100 | Expr::Compare(Compare { left, right, op }) => { 101 | let left = left.to_python_code(); 102 | let right = right.to_python_code(); 103 | format!( 104 | "({left} {} {right})", 105 | match op { 106 | optpy_parser::CompareOperator::Less => "<", 107 | optpy_parser::CompareOperator::LessOrEqual => "<=", 108 | optpy_parser::CompareOperator::Greater => ">", 109 | optpy_parser::CompareOperator::GreaterOrEqual => ">=", 110 | optpy_parser::CompareOperator::Equal => "==", 111 | optpy_parser::CompareOperator::NotEqual => "!=", 112 | optpy_parser::CompareOperator::NotIn => "not in", 113 | optpy_parser::CompareOperator::In => "in", 114 | } 115 | ) 116 | } 117 | Expr::UnaryOperation(UnaryOperation { value, op }) => { 118 | format!( 119 | "({}{})", 120 | match op { 121 | optpy_parser::UnaryOperator::Add => "+", 122 | optpy_parser::UnaryOperator::Sub => "-", 123 | optpy_parser::UnaryOperator::Not => "not ", 124 | }, 125 | value.to_python_code() 126 | ) 127 | } 128 | Expr::BinaryOperation(BinaryOperation { left, right, op }) => { 129 | let left = left.to_python_code(); 130 | let right = right.to_python_code(); 131 | format!( 132 | "({left} {} {right})", 133 | match op { 134 | optpy_parser::BinaryOperator::Add => "+", 135 | optpy_parser::BinaryOperator::Sub => "-", 136 | optpy_parser::BinaryOperator::Mul => "*", 137 | optpy_parser::BinaryOperator::Div => "/", 138 | optpy_parser::BinaryOperator::Mod => "%", 139 | optpy_parser::BinaryOperator::FloorDiv => "//", 140 | optpy_parser::BinaryOperator::Pow => "**", 141 | optpy_parser::BinaryOperator::BitAnd => "&", 142 | optpy_parser::BinaryOperator::LeftShift => "<<", 143 | optpy_parser::BinaryOperator::RightShift => ">>", 144 | } 145 | ) 146 | } 147 | Expr::Index(Index { value, index }) => { 148 | format!("{}[{}]", value.to_python_code(), index.to_python_code()) 149 | } 150 | Expr::ConstantNumber(n) => match n { 151 | Number::Int(n) => n.to_string(), 152 | Number::Float(n) => n.to_string(), 153 | }, 154 | Expr::ConstantString(s) => format!(r#""{}""#, s), 155 | Expr::ConstantBoolean(b) => { 156 | if *b { 157 | "True".into() 158 | } else { 159 | "False".into() 160 | } 161 | } 162 | Expr::None => "None".into(), 163 | Expr::List(list) => { 164 | let list = list.iter().map(|e| e.to_python_code()).collect::>(); 165 | format!("[{}]", list.join(", ")) 166 | } 167 | Expr::Dict(Dict { pairs }) => { 168 | let pairs = pairs 169 | .iter() 170 | .map(|(k, v)| format!("{}:{}", k.to_python_code(), v.to_python_code())) 171 | .collect::>(); 172 | format!("{{{}}}", pairs.join(", ")) 173 | } 174 | } 175 | } 176 | } 177 | 178 | fn indent_code(code: &str) -> String { 179 | let mut result = String::new(); 180 | for line in code.split('\n') { 181 | result += " "; 182 | result += line; 183 | result += "\n"; 184 | } 185 | result 186 | } 187 | 188 | impl DumpPython for Vec { 189 | fn to_python_code(&self) -> String { 190 | if self.is_empty() { 191 | "pass".into() 192 | } else { 193 | self.iter() 194 | .map(|s| s.to_python_code()) 195 | .collect::>() 196 | .join("\n") 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /optpy-resolver/src/module/mod.rs: -------------------------------------------------------------------------------- 1 | mod module_map; 2 | pub(crate) use module_map::ModuleMap; 3 | 4 | use std::collections::BTreeMap; 5 | 6 | use optpy_parser::{ 7 | Assign, BinaryOperation, BoolOperation, CallFunction, CallMethod, Compare, Dict, Expr, 8 | FromImport, Func, If, Import, Index, Statement, UnaryOperation, While, 9 | }; 10 | 11 | pub(super) fn resolve_modules( 12 | statements: Vec, 13 | module_map: &ModuleMap, 14 | ) -> Vec { 15 | let mut modules = EnabledModules { 16 | layers: vec![], 17 | module_map, 18 | }; 19 | 20 | modules.push_layer(); 21 | let result = resolve_statements(statements, &mut modules); 22 | modules.pop_layer(); 23 | result 24 | } 25 | 26 | fn resolve_statements<'a>( 27 | statements: Vec, 28 | modules: &mut EnabledModules<'a>, 29 | ) -> Vec { 30 | let mut result = vec![]; 31 | for statement in statements { 32 | match statement { 33 | Statement::Assign(Assign { target, value }) => result.push(Statement::Assign(Assign { 34 | target: resolve_expr(target, modules), 35 | value: resolve_expr(value, modules), 36 | })), 37 | Statement::Expression(expr) => { 38 | result.push(Statement::Expression(resolve_expr(expr, modules))) 39 | } 40 | Statement::If(If { test, body, orelse }) => result.push(Statement::If(If { 41 | test: resolve_expr(test, modules), 42 | body: resolve_statements(body, modules), 43 | orelse: resolve_statements(orelse, modules), 44 | })), 45 | Statement::Func(Func { name, args, body }) => { 46 | modules.push_layer(); 47 | let body = resolve_statements(body, modules); 48 | modules.pop_layer(); 49 | result.push(Statement::Func(Func { name, args, body })); 50 | } 51 | Statement::Return(ret) => { 52 | result.push(Statement::Return(ret.map(|r| resolve_expr(r, modules)))) 53 | } 54 | Statement::While(While { test, body }) => result.push(Statement::While(While { 55 | test: resolve_expr(test, modules), 56 | body: resolve_statements(body, modules), 57 | })), 58 | Statement::Break => result.push(Statement::Break), 59 | Statement::Continue => result.push(Statement::Continue), 60 | Statement::Import(import) => { 61 | modules.declare_import(&import); 62 | } 63 | Statement::FromImport(import) => { 64 | modules.declare_from_import(&import); 65 | } 66 | } 67 | } 68 | result 69 | } 70 | 71 | fn resolve_expr<'a>(expr: Expr, modules: &mut EnabledModules<'a>) -> Expr { 72 | match expr { 73 | Expr::CallFunction(CallFunction { name, args }) => { 74 | let expr = Expr::CallFunction(CallFunction { 75 | name, 76 | args: exprs(args, modules), 77 | }); 78 | modules.query(&expr).unwrap_or(expr) 79 | } 80 | Expr::CallMethod(CallMethod { value, name, args }) => { 81 | let expr = Expr::CallMethod(CallMethod { 82 | value: Box::new(resolve_expr(*value, modules)), 83 | name, 84 | args: exprs(args, modules), 85 | }); 86 | modules.query(&expr).unwrap_or(expr) 87 | } 88 | Expr::Tuple(tuple) => Expr::Tuple(exprs(tuple, modules)), 89 | Expr::BoolOperation(BoolOperation { op, conditions }) => { 90 | Expr::BoolOperation(BoolOperation { 91 | op, 92 | conditions: exprs(conditions, modules), 93 | }) 94 | } 95 | Expr::Compare(Compare { left, right, op }) => Expr::Compare(Compare { 96 | left: Box::new(resolve_expr(*left, modules)), 97 | right: Box::new(resolve_expr(*right, modules)), 98 | op, 99 | }), 100 | Expr::UnaryOperation(UnaryOperation { value, op }) => { 101 | Expr::UnaryOperation(UnaryOperation { 102 | value: Box::new(resolve_expr(*value, modules)), 103 | op, 104 | }) 105 | } 106 | Expr::BinaryOperation(BinaryOperation { left, right, op }) => { 107 | Expr::BinaryOperation(BinaryOperation { 108 | left: Box::new(resolve_expr(*left, modules)), 109 | right: Box::new(resolve_expr(*right, modules)), 110 | op, 111 | }) 112 | } 113 | Expr::Index(Index { value, index }) => Expr::Index(Index { 114 | value: Box::new(resolve_expr(*value, modules)), 115 | index: Box::new(resolve_expr(*index, modules)), 116 | }), 117 | Expr::ConstantNumber(_) 118 | | Expr::ConstantString(_) 119 | | Expr::ConstantBoolean(_) 120 | | Expr::None 121 | | Expr::VariableName(_) => expr, 122 | Expr::List(list) => Expr::List(exprs(list, modules)), 123 | Expr::Dict(Dict { pairs }) => { 124 | let (keys, values) = pairs.into_iter().unzip(); 125 | let keys = exprs(keys, modules); 126 | let values = exprs(values, modules); 127 | let pairs = keys.into_iter().zip(values).collect(); 128 | Expr::Dict(Dict { pairs }) 129 | } 130 | } 131 | } 132 | fn exprs<'a>(exprs: Vec, modules: &mut EnabledModules<'a>) -> Vec { 133 | exprs 134 | .into_iter() 135 | .map(|expr| resolve_expr(expr, modules)) 136 | .collect() 137 | } 138 | 139 | struct EnabledModules<'a> { 140 | layers: Vec>, 141 | module_map: &'a ModuleMap, 142 | } 143 | 144 | impl<'a> EnabledModules<'a> { 145 | fn push_layer(&mut self) { 146 | self.layers.push(BTreeMap::new()); 147 | } 148 | fn pop_layer(&mut self) { 149 | self.layers.pop().expect("there's no layer"); 150 | } 151 | fn declare_import(&mut self, import: &Import) { 152 | let layer = self.layers.last_mut().expect("layers should not be empty."); 153 | let imported = self.module_map.find_children(&import.import); 154 | for (python_function, actual_function) in imported { 155 | layer.insert( 156 | format!("{}.{python_function}", import.alias), 157 | actual_function.to_string(), 158 | ); 159 | } 160 | } 161 | fn declare_from_import(&mut self, import: &FromImport) { 162 | let layer = self.layers.last_mut().expect("layers should not be empty."); 163 | if import.import == "*" { 164 | let imported = self.module_map.find_children(&import.from); 165 | for (python_function, actual_function) in imported { 166 | layer.insert(python_function.to_string(), actual_function.to_string()); 167 | } 168 | } else { 169 | let ident = format!("{}.{}", import.from, import.import); 170 | if let Some(actual_function) = self.module_map.find_match(&ident) { 171 | layer.insert(import.alias.to_string(), actual_function.to_string()); 172 | } 173 | } 174 | } 175 | 176 | fn query(&self, expr: &Expr) -> Option { 177 | match expr { 178 | Expr::CallFunction(CallFunction { name, args }) => { 179 | let replaced = self.find(name)?; 180 | Some(Expr::CallFunction(CallFunction { 181 | name: replaced.to_string(), 182 | args: args.clone(), 183 | })) 184 | } 185 | Expr::CallMethod(CallMethod { value, name, args }) => { 186 | let ident = format_value_chain(value)?; 187 | let ident = format!("{}.{}", ident, name); 188 | let replaced = self.find(&ident)?; 189 | Some(Expr::CallFunction(CallFunction { 190 | name: replaced.to_string(), 191 | args: args.clone(), 192 | })) 193 | } 194 | _ => unreachable!(), 195 | } 196 | } 197 | 198 | fn find(&self, ident: &str) -> Option<&String> { 199 | for layer in self.layers.iter().rev() { 200 | if let Some(ident) = layer.get(ident) { 201 | return Some(ident); 202 | } 203 | } 204 | None 205 | } 206 | } 207 | 208 | // TODO attribute 209 | fn format_value_chain(value: &Expr) -> Option { 210 | match value { 211 | Expr::VariableName(name) => Some(name.to_string()), 212 | _ => None, 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /optpy-parser/src/statement.rs: -------------------------------------------------------------------------------- 1 | use rustpython_parser::ast::{Stmt, StmtKind}; 2 | 3 | use crate::{ 4 | expression::{Expr, RawExpr}, 5 | unixtime_nano, BinaryOperation, BinaryOperator, CallMethod, Index, 6 | }; 7 | 8 | #[derive(Debug, PartialEq, Eq, Clone)] 9 | pub enum Statement { 10 | Assign(Assign), 11 | Expression(Expr), 12 | If(If), 13 | Func(Func), 14 | Return(Option), 15 | While(While), 16 | Break, 17 | Continue, 18 | Import(Import), 19 | FromImport(FromImport), 20 | } 21 | #[derive(Debug, PartialEq, Eq, Clone)] 22 | 23 | pub struct Assign { 24 | pub target: E, 25 | pub value: E, 26 | } 27 | #[derive(Debug, PartialEq, Eq, Clone)] 28 | 29 | pub struct If { 30 | pub test: E, 31 | pub body: Vec, 32 | pub orelse: Vec, 33 | } 34 | 35 | #[derive(Debug, PartialEq, Eq, Clone)] 36 | pub struct Func { 37 | pub name: String, 38 | pub args: Vec, 39 | pub body: Vec, 40 | } 41 | 42 | #[derive(Debug, PartialEq, Eq, Clone)] 43 | pub struct While { 44 | pub test: E, 45 | pub body: Vec, 46 | } 47 | #[derive(Debug, PartialEq, Eq, Clone)] 48 | 49 | pub struct For { 50 | pub(crate) target: E, 51 | pub(crate) iter: E, 52 | pub(crate) body: Vec, 53 | } 54 | #[derive(Debug, PartialEq, Eq, Clone)] 55 | 56 | pub struct Import { 57 | pub import: String, 58 | pub alias: String, 59 | } 60 | 61 | #[derive(Debug, PartialEq, Eq, Clone)] 62 | 63 | pub struct FromImport { 64 | pub from: String, 65 | pub import: String, 66 | pub alias: String, 67 | } 68 | 69 | impl RawStmt { 70 | pub fn parse(statement: &StmtKind) -> Vec { 71 | match statement { 72 | StmtKind::Assign { 73 | targets, 74 | value, 75 | type_comment: _, 76 | } => { 77 | let value = RawExpr::parse(&value.node); 78 | if targets.len() == 1 { 79 | let target = RawExpr::parse(&targets[0].node); 80 | vec![Self::Assign(Assign { target, value })] 81 | } else { 82 | let first_target = 83 | RawExpr::VariableName(format!("__assign_tmp_{}", unixtime_nano())); 84 | let mut result = vec![Self::Assign(Assign { 85 | target: first_target.clone(), 86 | value, 87 | })]; 88 | for target in targets { 89 | let target = RawExpr::parse(&target.node); 90 | result.push(Self::Assign(Assign { 91 | target, 92 | value: first_target.clone(), 93 | })); 94 | } 95 | result 96 | } 97 | } 98 | StmtKind::Expr { value } => vec![Self::Expression(RawExpr::parse(&value.node))], 99 | StmtKind::If { test, body, orelse } => { 100 | let test = RawExpr::parse(&test.node); 101 | let body = parse_statements(body); 102 | let orelse = parse_statements(orelse); 103 | vec![Self::If(If { test, body, orelse })] 104 | } 105 | StmtKind::FunctionDef { 106 | decorator_list: _, 107 | returns: _, 108 | name, 109 | args, 110 | body, 111 | type_comment: _, 112 | } => { 113 | let name = name.to_string(); 114 | let args = args.args.iter().map(|arg| arg.node.arg.clone()).collect(); 115 | let body = parse_statements(body); 116 | vec![Self::Func(Func { name, args, body })] 117 | } 118 | StmtKind::Return { value } => { 119 | let value = value.as_ref().map(|value| RawExpr::parse(&value.node)); 120 | vec![Self::Return(value)] 121 | } 122 | StmtKind::While { 123 | test, 124 | body, 125 | orelse: _, 126 | } => { 127 | let test = RawExpr::parse(&test.node); 128 | let body = parse_statements(body); 129 | vec![Self::While(While { test, body })] 130 | } 131 | StmtKind::For { 132 | target, 133 | iter, 134 | body, 135 | orelse: _, 136 | type_comment: _, 137 | } => { 138 | let target = RawExpr::parse(&target.node); 139 | let iter = RawExpr::parse(&iter.node); 140 | let body = parse_statements(body); 141 | vec![Self::For(For { target, iter, body })] 142 | } 143 | StmtKind::Break => vec![Self::Break], 144 | StmtKind::Continue => vec![Self::Continue], 145 | StmtKind::AugAssign { target, op, value } => { 146 | let target = RawExpr::parse(&target.node); 147 | let value = RawExpr::parse(&value.node); 148 | vec![Self::Assign(Assign { 149 | target: target.clone(), 150 | value: RawExpr::BinaryOperation(BinaryOperation { 151 | left: Box::new(target), 152 | right: Box::new(value), 153 | op: BinaryOperator::parse(op), 154 | }), 155 | })] 156 | } 157 | StmtKind::Pass => vec![], 158 | StmtKind::Delete { targets } => targets 159 | .iter() 160 | .map(|target| { 161 | let target = RawExpr::parse(&target.node); 162 | match target { 163 | RawExpr::Index(Index { value, index }) => { 164 | Self::Expression(RawExpr::CallMethod(CallMethod { 165 | value, 166 | name: "__delete".into(), 167 | args: vec![*index], 168 | })) 169 | } 170 | target => Self::Assign(Assign { 171 | target, 172 | value: RawExpr::None, 173 | }), 174 | } 175 | }) 176 | .collect(), 177 | StmtKind::ImportFrom { 178 | module, 179 | names, 180 | level: _, 181 | } => { 182 | let mut statements = vec![]; 183 | let module = module.as_ref().cloned().expect("unknown module syntax"); 184 | for name in names { 185 | let import = name.node.name.clone(); 186 | let alias = name 187 | .node 188 | .asname 189 | .as_ref() 190 | .map(|alias| alias.clone()) 191 | .unwrap_or_else(|| import.clone()); 192 | statements.push(RawStmt::FromImport(FromImport { 193 | from: module.clone(), 194 | import, 195 | alias, 196 | })); 197 | } 198 | statements 199 | } 200 | StmtKind::Import { names } => { 201 | let mut statements = vec![]; 202 | for name in names { 203 | let import = name.node.name.clone(); 204 | let alias = name 205 | .node 206 | .asname 207 | .as_ref() 208 | .map(|alias| alias.clone()) 209 | .unwrap_or_else(|| import.clone()); 210 | statements.push(RawStmt::Import(Import { import, alias })); 211 | } 212 | statements 213 | } 214 | statement => todo!("{:?}", statement), 215 | } 216 | } 217 | } 218 | 219 | fn parse_statements(statements: &[Stmt]) -> Vec> { 220 | statements 221 | .iter() 222 | .flat_map(|s| RawStmt::parse(&s.node)) 223 | .collect() 224 | } 225 | 226 | pub(crate) enum RawStmt { 227 | Assign(Assign), 228 | Expression(E), 229 | If(If, E>), 230 | Func(Func>), 231 | Return(Option), 232 | While(While, E>), 233 | Break, 234 | Continue, 235 | For(For, E>), 236 | Import(Import), 237 | FromImport(FromImport), 238 | } 239 | -------------------------------------------------------------------------------- /optpy-parser/src/expression/mod.rs: -------------------------------------------------------------------------------- 1 | mod types; 2 | pub(crate) use types::ListComprehension; 3 | pub use types::{ 4 | BinaryOperation, BinaryOperator, BoolOperation, BoolOperator, CallFunction, CallMethod, 5 | Compare, CompareOperator, Comprehension, Dict, Index, Number, UnaryOperation, UnaryOperator, 6 | }; 7 | 8 | use rustpython_parser::ast::ExprKind; 9 | 10 | #[derive(Debug, PartialEq, Eq, Clone)] 11 | pub enum Expr { 12 | CallFunction(CallFunction), 13 | CallMethod(CallMethod), 14 | Tuple(Vec), 15 | VariableName(String), 16 | BoolOperation(BoolOperation), 17 | Compare(Compare), 18 | UnaryOperation(UnaryOperation), 19 | BinaryOperation(BinaryOperation), 20 | Index(Index), 21 | ConstantNumber(Number), 22 | ConstantString(String), 23 | ConstantBoolean(bool), 24 | None, 25 | List(Vec), 26 | Dict(Dict), 27 | } 28 | 29 | #[derive(Clone)] 30 | pub(crate) enum RawExpr { 31 | CallFunction(CallFunction), 32 | CallMethod(CallMethod), 33 | Tuple(Vec), 34 | VariableName(String), 35 | BoolOperation(BoolOperation), 36 | Compare(Compare), 37 | UnaryOperation(UnaryOperation), 38 | BinaryOperation(BinaryOperation), 39 | Index(Index), 40 | ConstantNumber(Number), 41 | ConstantString(String), 42 | ConstantBoolean(bool), 43 | None, 44 | List(Vec), 45 | ListComprehension(ListComprehension), 46 | Dict(Dict), 47 | } 48 | 49 | impl RawExpr { 50 | pub fn parse(expr: &ExprKind) -> Self { 51 | match expr { 52 | ExprKind::Tuple { elts, ctx: _ } => { 53 | let elements = parse_expressions(elts); 54 | RawExpr::Tuple(elements) 55 | } 56 | ExprKind::Name { id, ctx: _ } => RawExpr::VariableName(id.into()), 57 | ExprKind::Call { 58 | args, 59 | keywords, 60 | func, 61 | } => { 62 | assert!(keywords.is_empty()); 63 | let args = parse_expressions(args); 64 | match &func.node { 65 | ExprKind::Attribute { 66 | value, 67 | attr, 68 | ctx: _, 69 | } => { 70 | let value = RawExpr::parse(&value.node); 71 | RawExpr::CallMethod(CallMethod { 72 | value: Box::new(value), 73 | name: attr.into(), 74 | args, 75 | }) 76 | } 77 | ExprKind::Name { id, ctx: _ } => RawExpr::CallFunction(CallFunction { 78 | name: id.into(), 79 | args, 80 | }), 81 | function => todo!("{:#?}", function), 82 | } 83 | } 84 | ExprKind::BoolOp { op, values } => { 85 | let conditions = parse_expressions(values); 86 | let op = BoolOperator::parse(op); 87 | RawExpr::BoolOperation(BoolOperation { op, conditions }) 88 | } 89 | ExprKind::Compare { 90 | left, 91 | ops, 92 | comparators, 93 | } => { 94 | let mut conditions = vec![]; 95 | let mut left = RawExpr::parse(&left.node); 96 | for (op, right) in ops.iter().zip(comparators) { 97 | let op = CompareOperator::parse(op); 98 | let right = RawExpr::parse(&right.node); 99 | conditions.push(RawExpr::Compare(Compare { 100 | left: Box::new(left), 101 | right: Box::new(right.clone()), 102 | op, 103 | })); 104 | left = right; 105 | } 106 | assert!(conditions.len() > 0); 107 | if conditions.len() == 1 { 108 | let condition = conditions.pop().expect("no condition"); 109 | condition 110 | } else { 111 | RawExpr::BoolOperation(BoolOperation { 112 | op: BoolOperator::And, 113 | conditions, 114 | }) 115 | } 116 | } 117 | ExprKind::BinOp { op, left, right } => { 118 | let left = RawExpr::parse(&left.node); 119 | let right = RawExpr::parse(&right.node); 120 | Self::BinaryOperation(BinaryOperation { 121 | left: Box::new(left), 122 | right: Box::new(right), 123 | op: BinaryOperator::parse(op), 124 | }) 125 | } 126 | ExprKind::Subscript { 127 | value, 128 | slice, 129 | ctx: _, 130 | } => { 131 | let value = RawExpr::parse(&value.node); 132 | let index = RawExpr::parse(&slice.node); 133 | Self::Index(Index { 134 | value: Box::new(value), 135 | index: Box::new(index), 136 | }) 137 | } 138 | ExprKind::Constant { value, kind: _ } => match value { 139 | rustpython_parser::ast::Constant::Bool(b) => RawExpr::ConstantBoolean(*b), 140 | rustpython_parser::ast::Constant::Str(s) => RawExpr::ConstantString(s.clone()), 141 | rustpython_parser::ast::Constant::Int(i) => { 142 | RawExpr::ConstantNumber(Number::Int(i.to_string())) 143 | } 144 | rustpython_parser::ast::Constant::Float(f) => { 145 | RawExpr::ConstantNumber(Number::Float(f.to_string())) 146 | } 147 | rustpython_parser::ast::Constant::None => RawExpr::None, 148 | value => todo!("{:?}", value), 149 | }, 150 | ExprKind::List { elts, ctx: _ } => { 151 | let list = parse_expressions(elts); 152 | Self::List(list) 153 | } 154 | ExprKind::Dict { keys, values } => { 155 | let keys = parse_expressions(keys); 156 | let values = parse_expressions(values); 157 | let pairs = keys.into_iter().zip(values).collect::>(); 158 | Self::Dict(Dict { pairs }) 159 | } 160 | ExprKind::Set { elts } => { 161 | let elts = parse_expressions(elts); 162 | let pairs = elts.into_iter().map(|elt| (elt, RawExpr::None)).collect(); 163 | Self::Dict(Dict { pairs }) 164 | } 165 | ExprKind::ListComp { elt, generators } => list_comprehension(elt, generators), 166 | ExprKind::UnaryOp { op, operand } => { 167 | let value = RawExpr::parse(&operand.node); 168 | let op = UnaryOperator::parse(op); 169 | Self::UnaryOperation(UnaryOperation { 170 | value: Box::new(value), 171 | op, 172 | }) 173 | } 174 | ExprKind::GeneratorExp { elt, generators } => Self::CallFunction(CallFunction { 175 | name: "iter".into(), 176 | args: vec![list_comprehension(elt, generators)], 177 | }), 178 | expr => todo!("unsupported expression: {:?}", expr), 179 | } 180 | } 181 | } 182 | 183 | fn parse_expressions(expressions: &[rustpython_parser::ast::Expr]) -> Vec { 184 | expressions 185 | .iter() 186 | .map(|e| RawExpr::parse(&e.node)) 187 | .collect() 188 | } 189 | 190 | fn list_comprehension( 191 | elt: &rustpython_parser::ast::Expr, 192 | generators: &[rustpython_parser::ast::Comprehension], 193 | ) -> RawExpr { 194 | let value = RawExpr::parse(&elt.node); 195 | let generators = generators 196 | .iter() 197 | .map( 198 | |rustpython_parser::ast::Comprehension { 199 | target, iter, ifs, .. 200 | }| { 201 | let target = RawExpr::parse(&target.node); 202 | let iter = RawExpr::parse(&iter.node); 203 | let ifs = parse_expressions(ifs); 204 | Comprehension { 205 | target: Box::new(target), 206 | iter: Box::new(iter), 207 | ifs, 208 | } 209 | }, 210 | ) 211 | .collect(); 212 | RawExpr::ListComprehension(ListComprehension { 213 | value: Box::new(value), 214 | generators, 215 | }) 216 | } 217 | -------------------------------------------------------------------------------- /tests/test-syntax.rs: -------------------------------------------------------------------------------- 1 | use optpy_runtime::Value; 2 | use optpy_test_macro::python_function; 3 | 4 | #[test] 5 | fn test_if_statement() { 6 | python_function! {r#" 7 | def test(a, b): 8 | ans = a * b 9 | if ans % 2 == 0: 10 | return "Even" 11 | else: 12 | return "Odd" 13 | "#} 14 | 15 | let result = test(&Value::from(3), &Value::from(4)); 16 | assert_eq!(result, Value::from("Even")); 17 | 18 | let result = test(&Value::from(3), &Value::from(5)); 19 | assert_eq!(result, Value::from("Odd")); 20 | } 21 | 22 | #[test] 23 | fn test_multiple_if_conditions() { 24 | python_function! {r#" 25 | def test(a, b, c): 26 | ans = a * b 27 | if a <= b < c: 28 | return "IN" 29 | else: 30 | return "OUT" 31 | "#} 32 | 33 | let result = test(&Value::from(3), &Value::from(4), &Value::from(5)); 34 | assert_eq!(result, Value::from("IN")); 35 | 36 | let result = test(&Value::from(3), &Value::from(5), &Value::from(4)); 37 | assert_eq!(result, Value::from("OUT")); 38 | } 39 | 40 | #[test] 41 | fn test_list_add_assign() { 42 | python_function! {r" 43 | def test(A): 44 | A[0] += 1 45 | return A[0] 46 | "} 47 | assert_eq!( 48 | test(&Value::from(vec![ 49 | Value::from(1), 50 | Value::from(2), 51 | Value::from(3) 52 | ])), 53 | Value::from(2) 54 | ); 55 | } 56 | 57 | #[test] 58 | fn test_solve_abc081_b() { 59 | python_function! {r" 60 | def solve(N, A): 61 | flag = 0 62 | count = 0 63 | 64 | while True: 65 | for i in range(N): 66 | if A[i] % 2 != 0: 67 | flag = 1 68 | if flag == 1: 69 | break 70 | for i in range(N): 71 | A[i] = A[i]//2 72 | count += 1 73 | return count 74 | "} 75 | 76 | let result = solve( 77 | &Value::from(3), 78 | &Value::from(vec![Value::from(8), Value::from(12), Value::from(40)]), 79 | ); 80 | assert_eq!(result, Value::from(2)); 81 | 82 | let result = solve( 83 | &Value::from(4), 84 | &Value::from(vec![ 85 | Value::from(5), 86 | Value::from(6), 87 | Value::from(8), 88 | Value::from(10), 89 | ]), 90 | ); 91 | assert_eq!(result, Value::from(0)); 92 | } 93 | 94 | #[test] 95 | fn test_for_loop() { 96 | python_function! {r#" 97 | def test(N): 98 | ans = 0 99 | for i in range(N): 100 | ans += i 101 | return ans 102 | "#} 103 | 104 | let result = test(&Value::from(5)); 105 | assert_eq!(result, Value::from(10)); 106 | 107 | let result = test(&Value::from(10)); 108 | assert_eq!(result, Value::from(45)); 109 | } 110 | 111 | #[test] 112 | fn test_recursive_fibonacci() { 113 | python_function! {r#" 114 | def test(n): 115 | def fib(n): 116 | if n == 1 or n == 0: 117 | return 1 118 | return fib(n - 1) + fib(n - 2) 119 | n = fib(n) 120 | return n 121 | "#} 122 | 123 | assert_eq!(test(&Value::from(0)), Value::from(1)); 124 | assert_eq!(test(&Value::from(1)), Value::from(1)); 125 | assert_eq!(test(&Value::from(2)), Value::from(2)); 126 | assert_eq!(test(&Value::from(3)), Value::from(3)); 127 | assert_eq!(test(&Value::from(4)), Value::from(5)); 128 | } 129 | 130 | #[test] 131 | fn test_list_initialization() { 132 | python_function! {r#" 133 | def test(): 134 | A = [] 135 | A.append("A") 136 | A.append("B") 137 | return [A[0], A[1]] 138 | "#} 139 | assert_eq!( 140 | test(), 141 | Value::from(vec![Value::from("A"), Value::from("B")]) 142 | ); 143 | } 144 | 145 | #[test] 146 | fn test_tuple_for_loop_target() { 147 | python_function! {r#" 148 | def test(): 149 | A = [["A", "B"] , ["C", "D"]] 150 | result = [] 151 | for a, b in A: 152 | result.append([b, a]) 153 | return result 154 | "#} 155 | 156 | assert_eq!( 157 | test(), 158 | Value::from(vec![ 159 | Value::from(vec![Value::from("B"), Value::from("A")]), 160 | Value::from(vec![Value::from("D"), Value::from("C")]) 161 | ]) 162 | ); 163 | } 164 | 165 | #[test] 166 | fn test_assign_self() { 167 | python_function! {r#" 168 | def test(): 169 | x = [0] 170 | x[0] = x[0] 171 | return x[0] 172 | "#} 173 | assert_eq!(test(), Value::from(0)); 174 | } 175 | 176 | #[test] 177 | fn test_mut_mutated_self() { 178 | python_function! {r#" 179 | def test(): 180 | x = [0] 181 | x.append(x.pop()+200) 182 | return x[0] 183 | "#} 184 | assert_eq!(test(), Value::from(200)); 185 | } 186 | 187 | #[test] 188 | fn test_assign_in_loop() { 189 | python_function! {r#" 190 | def test(): 191 | for i in [0, 1, 2]: 192 | x = i 193 | return x 194 | "#} 195 | assert_eq!(test(), Value::from(2)); 196 | } 197 | 198 | #[test] 199 | fn test_mutate_argument() { 200 | python_function! {r" 201 | def test_mutate_argument(): 202 | def f(arr): 203 | arr[0] = 200 204 | arr = [0] 205 | f(arr) 206 | return arr[0] 207 | "}; 208 | let result = test_mutate_argument(); 209 | assert_eq!(result, Value::from(200)); 210 | } 211 | 212 | #[test] 213 | fn test_short_circuit_evaluation() { 214 | python_function! {r#" 215 | def test_short_circuit_evaluation(N): 216 | eval = [] 217 | def a(): 218 | eval.append(1) 219 | return True 220 | if N == 1 and a(): 221 | return eval 222 | else: 223 | return eval 224 | "#}; 225 | let result = test_short_circuit_evaluation(&Value::from(0)); 226 | assert_eq!(result, Value::from(vec![])); 227 | 228 | let result = test_short_circuit_evaluation(&Value::from(1)); 229 | assert_eq!(result, Value::from(vec![Value::from(1)])); 230 | } 231 | 232 | #[test] 233 | fn test_array_assignment() { 234 | python_function! { 235 | r" 236 | def test_array_assignment(): 237 | a = [0, 1, 2] 238 | a[0] = a[1] 239 | a[1] = a[2] 240 | return [a[0], a[1], a[2]] 241 | " 242 | }; 243 | let result = test_array_assignment(); 244 | assert_eq!( 245 | result, 246 | Value::from(vec![Value::from(1), Value::from(2), Value::from(2)]) 247 | ); 248 | } 249 | #[test] 250 | fn test_return_list_ref() { 251 | python_function! { 252 | r" 253 | def test_return_list_ref(): 254 | def f(): 255 | a = [0, 1, 2] 256 | return a[1] 257 | return f() + 1 258 | " 259 | }; 260 | let result = test_return_list_ref(); 261 | assert_eq!(result, Value::from(2)); 262 | } 263 | 264 | #[test] 265 | fn test_continue() { 266 | python_function! {r#" 267 | def test(): 268 | res = [] 269 | for i in range(5): 270 | if i%2==1: 271 | continue 272 | res.append(i) 273 | return res 274 | "#} 275 | assert_eq!( 276 | test(), 277 | Value::from(vec![Value::from(0), Value::from(2), Value::from(4)]) 278 | ); 279 | } 280 | 281 | #[test] 282 | fn test_list_comprehension() { 283 | python_function! {r#" 284 | def test(N, M): 285 | a = [[i*j for j in range(M)] for i in range(N)] 286 | return a 287 | "#} 288 | 289 | assert_eq!( 290 | test(&Value::from(3), &Value::from(2)), 291 | Value::from(vec![ 292 | Value::from(vec![Value::from(0), Value::from(0)]), 293 | Value::from(vec![Value::from(0), Value::from(1)]), 294 | Value::from(vec![Value::from(0), Value::from(2)]) 295 | ]) 296 | ); 297 | } 298 | 299 | #[test] 300 | fn test_multiple_target_assign() { 301 | python_function! {r" 302 | def test(): 303 | x = [1, 2, 3] 304 | def f(): 305 | return x.pop() 306 | a = b = c = f() 307 | return [a, b, c] 308 | "} 309 | assert_eq!( 310 | test(), 311 | Value::from(vec![Value::from(3), Value::from(3), Value::from(3)]) 312 | ); 313 | } 314 | 315 | #[test] 316 | fn test_list_move() { 317 | { 318 | python_function! {r" 319 | def test(): 320 | a = 1 321 | b = 2 322 | x = [] 323 | x.append((a, b)) 324 | return [x, a, b]"} 325 | } 326 | { 327 | python_function! {r" 328 | def test(): 329 | a = 1 330 | b = 2 331 | x = [] 332 | x.append([a, b]) 333 | return [x, a, b]"} 334 | } 335 | { 336 | python_function! {r" 337 | def test(): 338 | a = 1 339 | b = 2 340 | x = {a: b} 341 | return [x, a, b]"} 342 | } 343 | } 344 | 345 | #[test] 346 | fn test_none() { 347 | python_function! { 348 | r" 349 | def test(): 350 | a = None 351 | return a" 352 | } 353 | assert_eq!(test(), Value::None); 354 | } 355 | 356 | #[test] 357 | fn test_del() { 358 | python_function! { 359 | r" 360 | def test(): 361 | a = 1 362 | del a 363 | return a" 364 | } 365 | assert_eq!(test(), Value::None); 366 | } 367 | 368 | #[test] 369 | fn test_set() { 370 | python_function! {r" 371 | def test(b): 372 | a = {b} 373 | return 1 in a"} 374 | assert_eq!(test(&Value::from("a")), Value::from(false)); 375 | assert_eq!(test(&Value::from(1)), Value::from(true)); 376 | } 377 | 378 | #[test] 379 | fn test_index_eval_edge_case() { 380 | python_function! {r" 381 | def test(): 382 | def f(x): 383 | x[0] = 200 384 | return 0 385 | 386 | x = [0] 387 | return x[0]+f(x) 388 | "} 389 | 390 | assert_eq!(test(), Value::from(0)); 391 | } 392 | 393 | #[test] 394 | fn test_sibling_function_resolve() { 395 | python_function! {r" 396 | def test(): 397 | N = 2 398 | def f1(): 399 | return N + 1 400 | def f2(): 401 | return f1() 402 | return f2() 403 | "} 404 | 405 | assert_eq!(test(), Value::from(3)); 406 | } 407 | -------------------------------------------------------------------------------- /optpy-parser/src/simplify/list_comprehension.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | expression::{Dict, ListComprehension, RawExpr}, 3 | statement::{Assign, FromImport, RawStmt}, 4 | unixtime_nano, BinaryOperation, BoolOperation, BoolOperator, CallFunction, CallMethod, Compare, 5 | Expr, For, Func, If, Import, Index, UnaryOperation, While, 6 | }; 7 | 8 | pub(crate) fn simplify_list_comprehensions(stmts: Vec>) -> Vec> { 9 | stmts.into_iter().flat_map(stmt).collect() 10 | } 11 | 12 | fn stmt(stmt: RawStmt) -> Vec> { 13 | match stmt { 14 | RawStmt::Assign(Assign { target, value }) => { 15 | let (target, mut s1) = eval_expr(target); 16 | let (value, s2) = eval_expr(value); 17 | s1.extend(s2); 18 | s1.push(RawStmt::Assign(Assign { target, value })); 19 | s1 20 | } 21 | RawStmt::Expression(e) => { 22 | let (e, mut s) = eval_expr(e); 23 | s.push(RawStmt::Expression(e)); 24 | s 25 | } 26 | RawStmt::If(If { test, body, orelse }) => { 27 | let (test, mut s) = eval_expr(test); 28 | let body = simplify_list_comprehensions(body); 29 | let orelse = simplify_list_comprehensions(orelse); 30 | s.push(RawStmt::If(If { test, body, orelse })); 31 | s 32 | } 33 | RawStmt::Func(Func { name, args, body }) => { 34 | let body = simplify_list_comprehensions(body); 35 | vec![RawStmt::Func(Func { name, args, body })] 36 | } 37 | RawStmt::Return(None) => { 38 | vec![RawStmt::Return(None)] 39 | } 40 | RawStmt::Return(Some(r)) => { 41 | let (r, mut s) = eval_expr(r); 42 | s.push(RawStmt::Return(Some(r))); 43 | s 44 | } 45 | RawStmt::While(While { test, body }) => { 46 | let (test, mut s) = eval_expr(test); 47 | let body = simplify_list_comprehensions(body); 48 | s.push(RawStmt::While(While { test, body })); 49 | s 50 | } 51 | RawStmt::Break => vec![RawStmt::Break], 52 | RawStmt::Continue => vec![RawStmt::Continue], 53 | RawStmt::For(For { target, iter, body }) => { 54 | let (target, s) = eval_expr(target); 55 | assert!(s.is_empty(), "target contains list comprehension"); 56 | let (iter, mut s) = eval_expr(iter); 57 | let body = simplify_list_comprehensions(body); 58 | s.push(RawStmt::For(For { target, iter, body })); 59 | s 60 | } 61 | RawStmt::Import(Import { import, alias }) => { 62 | vec![RawStmt::Import(Import { import, alias })] 63 | } 64 | RawStmt::FromImport(FromImport { 65 | from, 66 | import, 67 | alias, 68 | }) => { 69 | vec![RawStmt::FromImport(FromImport { 70 | from, 71 | import, 72 | alias, 73 | })] 74 | } 75 | } 76 | } 77 | 78 | fn exprs(exprs: Vec) -> (Vec, Vec>) { 79 | let (exprs, stmts): (Vec<_>, Vec<_>) = exprs.into_iter().map(eval_expr).unzip(); 80 | (exprs, stmts.into_iter().flatten().collect()) 81 | } 82 | 83 | fn eval_expr(expr: RawExpr) -> (Expr, Vec>) { 84 | match expr { 85 | RawExpr::CallFunction(CallFunction { name, args }) => { 86 | let (args, s) = exprs(args); 87 | (Expr::CallFunction(CallFunction { name, args }), s) 88 | } 89 | RawExpr::CallMethod(CallMethod { value, name, args }) => { 90 | let (value, mut s1) = eval_expr(*value); 91 | let (args, s2) = exprs(args); 92 | s1.extend(s2); 93 | ( 94 | Expr::CallMethod(CallMethod { 95 | value: Box::new(value), 96 | name, 97 | args, 98 | }), 99 | s1, 100 | ) 101 | } 102 | RawExpr::Tuple(tuple) => { 103 | let (tuple, s) = exprs(tuple); 104 | (Expr::Tuple(tuple), s) 105 | } 106 | RawExpr::VariableName(v) => (Expr::VariableName(v), vec![]), 107 | RawExpr::ConstantNumber(v) => (Expr::ConstantNumber(v), vec![]), 108 | RawExpr::ConstantString(v) => (Expr::ConstantString(v), vec![]), 109 | RawExpr::ConstantBoolean(v) => (Expr::ConstantBoolean(v), vec![]), 110 | RawExpr::None => (Expr::None, vec![]), 111 | RawExpr::BoolOperation(BoolOperation { op, conditions }) => { 112 | let (conditions, s) = exprs(conditions); 113 | (Expr::BoolOperation(BoolOperation { op, conditions }), s) 114 | } 115 | RawExpr::Compare(Compare { left, right, op }) => { 116 | let (left, mut s1) = eval_expr(*left); 117 | let (right, s2) = eval_expr(*right); 118 | s1.extend(s2); 119 | ( 120 | Expr::Compare(Compare { 121 | left: Box::new(left), 122 | right: Box::new(right), 123 | op, 124 | }), 125 | s1, 126 | ) 127 | } 128 | RawExpr::BinaryOperation(BinaryOperation { left, right, op }) => { 129 | let (left, mut s1) = eval_expr(*left); 130 | let (right, s2) = eval_expr(*right); 131 | s1.extend(s2); 132 | ( 133 | Expr::BinaryOperation(BinaryOperation { 134 | left: Box::new(left), 135 | right: Box::new(right), 136 | op, 137 | }), 138 | s1, 139 | ) 140 | } 141 | RawExpr::Index(Index { value, index }) => { 142 | let (value, mut s1) = eval_expr(*value); 143 | let (index, s2) = eval_expr(*index); 144 | s1.extend(s2); 145 | ( 146 | Expr::Index(Index { 147 | value: Box::new(value), 148 | index: Box::new(index), 149 | }), 150 | s1, 151 | ) 152 | } 153 | RawExpr::List(list) => { 154 | let (list, s) = exprs(list); 155 | (Expr::List(list), s) 156 | } 157 | RawExpr::Dict(Dict { pairs }) => { 158 | let mut statements = vec![]; 159 | let mut p = vec![]; 160 | for (key, value) in pairs { 161 | let (key, s) = eval_expr(key); 162 | statements.extend(s); 163 | let (value, s) = eval_expr(value); 164 | statements.extend(s); 165 | p.push((key, value)); 166 | } 167 | (Expr::Dict(Dict { pairs: p }), statements) 168 | } 169 | RawExpr::ListComprehension(ListComprehension { value, generators }) => { 170 | let tmp_list = Expr::VariableName("__result".into()); 171 | let (value, mut generation_body) = eval_expr(*value); 172 | generation_body.push(RawStmt::Expression(Expr::CallMethod(CallMethod { 173 | value: Box::new(tmp_list.clone()), 174 | name: "append".into(), 175 | args: vec![value], 176 | }))); 177 | 178 | for generator in generators { 179 | let (ifs, s) = exprs(generator.ifs); 180 | assert!( 181 | s.is_empty(), 182 | "filter statement in a list comprehension also contains list comprehension" 183 | ); 184 | if !ifs.is_empty() { 185 | generation_body = vec![RawStmt::If(If { 186 | test: Expr::BoolOperation(BoolOperation { 187 | op: BoolOperator::And, 188 | conditions: ifs, 189 | }), 190 | body: generation_body, 191 | orelse: vec![], 192 | })]; 193 | } 194 | let (iter, mut new_generation_body) = eval_expr(*generator.iter); 195 | let (target, s) = eval_expr(*generator.target); 196 | assert!( 197 | s.is_empty(), 198 | "list generator target contains list comprehension" 199 | ); 200 | new_generation_body.push(RawStmt::For(For { 201 | target, 202 | iter, 203 | body: generation_body, 204 | })); 205 | generation_body = new_generation_body; 206 | } 207 | 208 | let mut function_body = vec![RawStmt::Assign(Assign { 209 | target: tmp_list.clone(), 210 | value: Expr::List(vec![]), 211 | })]; 212 | function_body.extend(generation_body); 213 | function_body.push(RawStmt::Return(Some(tmp_list))); 214 | 215 | let function_name = format!("__f{}", unixtime_nano()); 216 | 217 | ( 218 | Expr::CallFunction(CallFunction { 219 | name: function_name.clone(), 220 | args: vec![], 221 | }), 222 | vec![RawStmt::Func(Func { 223 | name: function_name, 224 | args: vec![], 225 | body: function_body, 226 | })], 227 | ) 228 | } 229 | RawExpr::UnaryOperation(UnaryOperation { value, op }) => { 230 | let (value, s) = eval_expr(*value); 231 | ( 232 | Expr::UnaryOperation(UnaryOperation { 233 | value: Box::new(value), 234 | op, 235 | }), 236 | s, 237 | ) 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /optpy-runtime/src/builtin.rs: -------------------------------------------------------------------------------- 1 | use std::{io::stdin, rc::Rc}; 2 | 3 | use crate::{number::Number, value::Value, ImmutableString, Iter}; 4 | 5 | pub fn input() -> Value { 6 | let mut buf = String::new(); 7 | stdin().read_line(&mut buf).unwrap(); 8 | Value::from(buf.trim()) 9 | } 10 | 11 | pub fn __has_next(iter: &Value) -> Value { 12 | match iter { 13 | Value::Iter(iter) => Value::Boolean(iter.__has_next()), 14 | _ => todo!(), 15 | } 16 | } 17 | 18 | pub fn map_int(value: &Value) -> Value { 19 | match value { 20 | Value::List(list) => { 21 | let list = list.0.borrow().clone(); 22 | let iter = list.into_iter().map(|v| int(&v.borrow())); 23 | Value::Iter(Iter::new(Box::new(iter))) 24 | } 25 | _ => unreachable!(), 26 | } 27 | } 28 | pub fn int(value: &Value) -> Value { 29 | match value { 30 | Value::String(s) => Value::Number(Number::Int64(s.0.parse::().expect("non-integer"))), 31 | Value::Number(Number::Int64(i)) => Value::Number(Number::Int64(*i)), 32 | _ => unreachable!(), 33 | } 34 | } 35 | pub fn float(value: &Value) -> Value { 36 | match value { 37 | Value::String(s) => Value::Number(Number::Float(s.0.parse::().expect("non-float"))), 38 | Value::Number(Number::Int64(i)) => Value::Number(Number::Float(*i as f64)), 39 | _ => unreachable!(), 40 | } 41 | } 42 | pub fn str(value: &Value) -> Value { 43 | match value { 44 | Value::String(_) => value.clone(), 45 | Value::Number(n) => Value::String(ImmutableString(Rc::new(n.to_string()))), 46 | _ => todo!(), 47 | } 48 | } 49 | 50 | pub fn list(value: &Value) -> Value { 51 | match value { 52 | Value::List(list) => { 53 | let vec = list 54 | .0 55 | .borrow() 56 | .iter() 57 | .map(|v| v.borrow().clone()) 58 | .collect::>(); 59 | Value::from(vec) 60 | } 61 | Value::Iter(iter) => iter.__list(), 62 | _ => todo!(), 63 | } 64 | } 65 | pub fn tuple(value: &Value) -> Value { 66 | list(value) 67 | } 68 | 69 | pub fn __range1(value: &Value) -> Value { 70 | __range2(&Value::Number(Number::Int64(0)), value) 71 | } 72 | 73 | pub fn __range2(start: &Value, stop: &Value) -> Value { 74 | match (start, stop) { 75 | (Value::Number(Number::Int64(start)), Value::Number(Number::Int64(stop))) => { 76 | let iter = (*start..*stop).map(|i| Value::Number(Number::Int64(i))); 77 | Value::Iter(Iter::new(Box::new(iter))) 78 | } 79 | _ => unreachable!(), 80 | } 81 | } 82 | pub fn __min1(list: &Value) -> Value { 83 | match list { 84 | Value::List(list) => list 85 | .0 86 | .borrow() 87 | .iter() 88 | .min_by(|a, b| a.borrow().partial_cmp(&b.borrow()).unwrap()) 89 | .unwrap() 90 | .borrow() 91 | .clone(), 92 | _ => todo!(), 93 | } 94 | } 95 | pub fn __min2(a: &Value, b: &Value) -> Value { 96 | if a > b { 97 | b.clone() 98 | } else { 99 | a.clone() 100 | } 101 | } 102 | pub fn __max1(a: &Value) -> Value { 103 | match a { 104 | Value::List(list) => list 105 | .0 106 | .borrow() 107 | .iter() 108 | .max_by(|a, b| a.borrow().partial_cmp(&b.borrow()).unwrap()) 109 | .unwrap() 110 | .borrow() 111 | .clone(), 112 | _ => todo!(), 113 | } 114 | } 115 | pub fn __max2(a: &Value, b: &Value) -> Value { 116 | if a > b { 117 | a.clone() 118 | } else { 119 | b.clone() 120 | } 121 | } 122 | pub fn __sum1(a: &Value) -> Value { 123 | match a { 124 | Value::List(list) => list 125 | .0 126 | .borrow() 127 | .iter() 128 | .fold(Value::from(0), |a, b| a.__add(&b.borrow())), 129 | _ => todo!(), 130 | } 131 | } 132 | pub fn __sum2(a: &Value, b: &Value) -> Value { 133 | a.__add(b) 134 | } 135 | 136 | pub fn sorted(value: &Value) -> Value { 137 | let cloned_value = list(value); 138 | cloned_value.sort(); 139 | cloned_value 140 | } 141 | 142 | pub fn len(value: &Value) -> Value { 143 | value.__len() 144 | } 145 | pub fn any(value: &Value) -> Value { 146 | match value { 147 | Value::List(list) => Value::Boolean(list.0.borrow().iter().any(|v| v.borrow().test())), 148 | Value::Iter(iter) => Value::Boolean(iter.any()), 149 | _ => todo!(), 150 | } 151 | } 152 | pub fn all(value: &Value) -> Value { 153 | match value { 154 | Value::List(list) => Value::Boolean(list.0.borrow().iter().all(|v| v.borrow().test())), 155 | Value::Iter(iter) => Value::Boolean(iter.all()), 156 | _ => todo!(), 157 | } 158 | } 159 | pub fn __set1(iter: &Value) -> Value { 160 | match iter { 161 | Value::List(list) => { 162 | let pairs = list 163 | .0 164 | .borrow() 165 | .iter() 166 | .map(|v| (v.borrow().clone(), Value::None)) 167 | .collect::>(); 168 | Value::dict(pairs) 169 | } 170 | Value::Dict(dict) => __set1(&dict.keys()), 171 | Value::String(_) => todo!(), 172 | _ => unreachable!(), 173 | } 174 | } 175 | 176 | pub fn enumerate(iter: &Value) -> Value { 177 | match iter { 178 | Value::List(list) => { 179 | let list = list 180 | .0 181 | .borrow() 182 | .iter() 183 | .enumerate() 184 | .map(|(i, v)| Value::from(vec![Value::from(i as i64), v.borrow().clone()])) 185 | .collect::>(); 186 | Value::from(list) 187 | } 188 | iter => enumerate(&list(iter)), 189 | } 190 | } 191 | pub fn __next1(iter: &Value) -> Value { 192 | match iter { 193 | Value::Iter(iter) => iter.__next().expect("stop iteration"), 194 | _ => todo!("{:?}", iter), 195 | } 196 | } 197 | pub fn __next2(iter: &Value, default: &Value) -> Value { 198 | match iter { 199 | Value::Iter(iter) => match iter.__next() { 200 | Some(value) => value, 201 | None => default.clone(), 202 | }, 203 | _ => todo!(), 204 | } 205 | } 206 | pub fn iter(iter: &Value) -> Value { 207 | match iter { 208 | Value::List(list) => list.__iter(), 209 | Value::Iter(_) => iter.clone(), 210 | _ => todo!(), 211 | } 212 | } 213 | pub fn __pow3(number: &Value, power: &Value, modulus: &Value) -> Value { 214 | let int = |n: &Value| match n.__number() { 215 | Number::Int64(i) => i, 216 | Number::Float(_) => unreachable!(), 217 | }; 218 | 219 | let number = int(number); 220 | let power = int(power); 221 | let modulus = int(modulus); 222 | 223 | let mut result = 1; 224 | let mut cur = number; 225 | let mut e = power; 226 | while e > 0 { 227 | if e & 1 == 1 { 228 | result = (result * cur) % modulus; 229 | } 230 | cur = (cur * cur) % modulus; 231 | e >>= 1; 232 | } 233 | Value::Number(Number::Int64(result)) 234 | } 235 | 236 | pub fn __exit1(code: &Value) -> ! { 237 | match code.__number() { 238 | Number::Int64(code) => std::process::exit(code as i32), 239 | _ => unreachable!(), 240 | } 241 | } 242 | pub fn __exit0() -> ! { 243 | std::process::exit(0) 244 | } 245 | 246 | pub fn __set0() -> Value { 247 | Value::Dict(Default::default()) 248 | } 249 | 250 | pub fn dict() -> Value { 251 | Value::Dict(Default::default()) 252 | } 253 | 254 | pub fn abs(v: &Value) -> Value { 255 | match v { 256 | Value::Number(n) => Value::Number(n.abs()), 257 | _ => unreachable!(), 258 | } 259 | } 260 | #[macro_export] 261 | macro_rules! range { 262 | ($stop:expr) => { 263 | __range1($stop) 264 | }; 265 | ($start:expr, $stop:expr) => { 266 | __range2($start, $stop) 267 | }; 268 | } 269 | 270 | #[macro_export] 271 | macro_rules! print_values { 272 | ($($arg:expr),+) => { 273 | let s = [$($arg),+].iter().map(|v| v.to_string()).collect::>(); 274 | println!("{}", s.join(" ")); 275 | }; 276 | } 277 | 278 | #[macro_export] 279 | macro_rules! pow { 280 | ($number:expr, $power:expr, $modulus:expr) => { 281 | __pow3($number, $power, $modulus) 282 | }; 283 | } 284 | #[macro_export] 285 | macro_rules! set { 286 | () => { 287 | __set0() 288 | }; 289 | ($iter:expr) => { 290 | __set1($iter) 291 | }; 292 | } 293 | 294 | #[macro_export] 295 | macro_rules! exit { 296 | () => { 297 | __exit0() 298 | }; 299 | ($code:expr) => { 300 | __exit1($code) 301 | }; 302 | } 303 | 304 | #[macro_export] 305 | macro_rules! max { 306 | ($e:expr) => { 307 | __max1($e) 308 | }; 309 | ($a:expr, $b:expr) => { 310 | __max2($a, $b) 311 | }; 312 | ($a:expr, $($arg:expr),+) => { 313 | __max2($a, &max!($($arg),+)) 314 | }; 315 | } 316 | 317 | #[macro_export] 318 | macro_rules! min { 319 | ($e:expr) => { 320 | __min1($e) 321 | }; 322 | ($a:expr, $b:expr) => { 323 | __min2($a, $b) 324 | }; 325 | ($a:expr, $($arg:expr),+) => { 326 | __min2($a, &min!($($arg),+)) 327 | }; 328 | } 329 | 330 | #[macro_export] 331 | macro_rules! sum { 332 | ($e:expr) => { 333 | __sum1($e) 334 | }; 335 | ($a:expr, $b:expr) => { 336 | __sum2($a, $b) 337 | }; 338 | ($a:expr, $($arg:expr),+) => { 339 | __sum2($a, &sum!($($arg),+)) 340 | }; 341 | } 342 | 343 | #[macro_export] 344 | macro_rules! next { 345 | ($e:expr) => { 346 | __next1($e) 347 | }; 348 | ($a:expr, $b:expr) => { 349 | __next2($a, $b) 350 | }; 351 | } 352 | -------------------------------------------------------------------------------- /optpy-runtime/src/value/value.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Mul; 2 | 3 | use crate::{cell::UnsafeRefMut, number::Number, Deque, Dict, ImmutableString, Iter, List}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub enum Value { 7 | List(List), 8 | String(ImmutableString), 9 | Number(Number), 10 | Boolean(bool), 11 | Dict(Dict), 12 | Deque(Deque), 13 | Iter(Iter), 14 | None, 15 | } 16 | 17 | impl PartialOrd for Value { 18 | fn partial_cmp(&self, other: &Self) -> Option { 19 | match (self, other) { 20 | (Value::Number(lhs), Value::Number(rhs)) => lhs.partial_cmp(rhs), 21 | (Value::String(lhs), Value::String(rhs)) => lhs.partial_cmp(rhs), 22 | (Value::List(lhs), Value::List(rhs)) => lhs.partial_cmp(rhs), 23 | _ => todo!(), 24 | } 25 | } 26 | } 27 | impl PartialEq for Value { 28 | fn eq(&self, other: &Self) -> bool { 29 | match (self, other) { 30 | (Self::String(l0), Self::String(r0)) => l0 == r0, 31 | (Self::Number(l0), Self::Number(r0)) => l0 == r0, 32 | (Self::Boolean(l0), Self::Boolean(r0)) => l0 == r0, 33 | (Self::List(l0), Self::List(r0)) => l0 == r0, 34 | (Self::Dict(l0), Self::Dict(r0)) => l0 == r0, 35 | (Self::None, Self::None) => true, 36 | _ => false, 37 | } 38 | } 39 | } 40 | 41 | impl Default for Value { 42 | fn default() -> Self { 43 | Value::None 44 | } 45 | } 46 | 47 | macro_rules! impl_binop { 48 | ($name:ident, $op:ident) => { 49 | impl Value { 50 | pub fn $name(&self, rhs: &Value) -> Value { 51 | #[allow(unused_imports)] 52 | use std::ops::*; 53 | match (self, rhs) { 54 | (Value::Number(lhs), Value::Number(rhs)) => Value::Number(lhs.$op(*rhs)), 55 | _ => unreachable!(), 56 | } 57 | } 58 | } 59 | }; 60 | } 61 | impl_binop!(__add, add); 62 | impl_binop!(__sub, sub); 63 | impl_binop!(__rem, rem); 64 | impl_binop!(__div, div); 65 | impl_binop!(__pow, pow); 66 | 67 | impl Value { 68 | pub fn __mul(&self, rhs: &Value) -> Value { 69 | match (self, rhs) { 70 | (Value::List(list), rhs) => list.__mul(rhs), 71 | (Value::Number(a), Value::Number(b)) => Value::Number(*a * *b), 72 | _ => todo!(), 73 | } 74 | } 75 | } 76 | 77 | macro_rules! impl_compare { 78 | ($name:ident, $op:ident) => { 79 | impl Value { 80 | pub fn $name(&self, rhs: &Value) -> Value { 81 | Value::Boolean(self.$op(rhs)) 82 | } 83 | } 84 | }; 85 | } 86 | impl_compare!(__gt, gt); 87 | impl_compare!(__ge, ge); 88 | impl_compare!(__lt, lt); 89 | impl_compare!(__le, le); 90 | impl_compare!(__eq, eq); 91 | impl_compare!(__ne, ne); 92 | impl Value { 93 | fn includes(&self, value: &Value) -> bool { 94 | match self { 95 | Value::List(list) => list.includes(value), 96 | Value::Dict(map) => map.includes(value), 97 | _ => todo!(), 98 | } 99 | } 100 | pub fn __in(&self, rhs: &Value) -> Value { 101 | Value::Boolean(rhs.includes(self)) 102 | } 103 | pub fn __not_in(&self, rhs: &Value) -> Value { 104 | Value::Boolean(!rhs.includes(self)) 105 | } 106 | 107 | pub fn __bit_and(&self, rhs: &Value) -> Value { 108 | match (self, rhs) { 109 | (Value::Boolean(a), Value::Boolean(b)) => Value::Boolean(*a && *b), 110 | _ => todo!(), 111 | } 112 | } 113 | 114 | pub fn __delete(&self, index: &Value) { 115 | match self { 116 | Value::List(list) => list.__delete(index), 117 | Value::Dict(dict) => dict.__delete(index), 118 | _ => todo!(), 119 | } 120 | } 121 | 122 | pub fn dict(pairs: Vec<(Value, Value)>) -> Value { 123 | Value::Dict(Dict::from(pairs)) 124 | } 125 | 126 | pub fn __shallow_copy(&self) -> Value { 127 | self.clone() 128 | } 129 | 130 | pub fn split(&self) -> Self { 131 | match self { 132 | Value::String(s) => s.split(), 133 | _ => unreachable!(), 134 | } 135 | } 136 | 137 | pub fn __index_ref(&self, index: &Value) -> UnsafeRefMut { 138 | match self { 139 | Value::List(list) => list.__index_ref(index), 140 | Value::Dict(dict) => dict.__index_ref(index), 141 | _ => todo!(), 142 | } 143 | } 144 | pub fn __index_value(&self, index: &Value) -> Value { 145 | match self { 146 | Value::List(list) => list.__index_value(index), 147 | Value::Dict(dict) => dict.__index_value(index), 148 | _ => todo!(), 149 | } 150 | } 151 | 152 | pub fn keys(&self) -> Value { 153 | match self { 154 | Value::Dict(dict) => dict.keys(), 155 | _ => todo!(), 156 | } 157 | } 158 | 159 | pub fn assign(&mut self, value: &Value) { 160 | *self = value.clone(); 161 | } 162 | 163 | pub fn reverse(&self) { 164 | match self { 165 | Value::List(list) => list.reverse(), 166 | _ => unreachable!(), 167 | } 168 | } 169 | 170 | pub fn pop(&self) -> Value { 171 | match self { 172 | Value::List(list) => list.pop(), 173 | _ => unreachable!(), 174 | } 175 | } 176 | pub fn popleft(&self) -> Value { 177 | match self { 178 | Value::Deque(deque) => deque.popleft(), 179 | _ => todo!(), 180 | } 181 | } 182 | pub fn strip(&self) -> Value { 183 | match self { 184 | Value::String(s) => s.strip(), 185 | _ => unreachable!(), 186 | } 187 | } 188 | pub fn append(&self, value: &Value) { 189 | match self { 190 | Value::List(list) => list.append(value), 191 | Value::Deque(deque) => deque.append(value), 192 | _ => unreachable!(), 193 | } 194 | } 195 | pub fn appendleft(&self, value: &Value) { 196 | match self { 197 | Value::Deque(deque) => deque.appendleft(value), 198 | _ => unreachable!(), 199 | } 200 | } 201 | 202 | pub fn setdefault(&self, key: &Value, value: &Value) { 203 | match self { 204 | Value::Dict(dict) => dict.setdefault(key, value), 205 | _ => todo!(), 206 | } 207 | } 208 | pub fn add(&self, value: &Value) { 209 | match self { 210 | Value::Dict(dict) => dict.add(value), 211 | _ => unreachable!(), 212 | } 213 | } 214 | 215 | pub fn __floor_div(&self, rhs: &Value) -> Value { 216 | match (self, rhs) { 217 | (Value::Number(lhs), Value::Number(rhs)) => Value::Number(lhs.floor_div(rhs)), 218 | _ => unreachable!(), 219 | } 220 | } 221 | 222 | pub fn __unary_add(&self) -> Value { 223 | self.clone() 224 | } 225 | pub fn __unary_sub(&self) -> Value { 226 | match self { 227 | Value::Number(i) => Value::Number(i.mul(Number::Int64(-1))), 228 | _ => unreachable!(), 229 | } 230 | } 231 | pub fn __unary_not(&self) -> Value { 232 | match self { 233 | Value::Boolean(b) => Value::Boolean(!*b), 234 | _ => unreachable!(), 235 | } 236 | } 237 | pub fn __left_shift(&self, value: &Value) -> Value { 238 | match (self, value) { 239 | (Value::Number(i), Value::Number(x)) => Value::Number(i.__left_shift(x)), 240 | _ => todo!(), 241 | } 242 | } 243 | pub fn __right_shift(&self, value: &Value) -> Value { 244 | match (self, value) { 245 | (Value::Number(i), Value::Number(x)) => Value::Number(i.__right_shift(x)), 246 | _ => todo!(), 247 | } 248 | } 249 | pub fn __len(&self) -> Value { 250 | match self { 251 | Value::List(list) => list.__len(), 252 | Value::Dict(dict) => dict.__len(), 253 | Value::String(s) => s.__len(), 254 | _ => unreachable!("{:?}", self), 255 | } 256 | } 257 | pub fn sort(&self) { 258 | match self { 259 | Value::List(list) => list.sort(), 260 | _ => unreachable!(), 261 | } 262 | } 263 | 264 | pub fn test(&self) -> bool { 265 | match self { 266 | Value::Boolean(b) => *b, 267 | Value::List(list) => list.test(), 268 | Value::String(s) => s.test(), 269 | Value::Number(n) => n.test(), 270 | Value::Dict(dict) => dict.test(), 271 | Value::Deque(deque) => deque.test(), 272 | Value::Iter(iter) => iter.test(), 273 | Value::None => false, 274 | } 275 | } 276 | 277 | pub fn __number(&self) -> Number { 278 | match self { 279 | Value::Number(n) => *n, 280 | _ => unreachable!(), 281 | } 282 | } 283 | 284 | pub fn count(&self, value: &Value) -> Value { 285 | match self { 286 | Value::List(list) => list.count(value), 287 | Value::String(s) => s.count(value), 288 | _ => todo!(), 289 | } 290 | } 291 | pub fn index(&self, value: &Value) -> Value { 292 | match self { 293 | Value::List(list) => list.index(value), 294 | _ => todo!(), 295 | } 296 | } 297 | } 298 | 299 | impl From<&str> for Value { 300 | fn from(s: &str) -> Self { 301 | Value::String(ImmutableString::from(s)) 302 | } 303 | } 304 | impl From for Value { 305 | fn from(v: i64) -> Self { 306 | Value::Number(Number::Int64(v)) 307 | } 308 | } 309 | impl From for Value { 310 | fn from(v: f64) -> Self { 311 | Value::Number(Number::Float(v)) 312 | } 313 | } 314 | impl From> for Value { 315 | fn from(list: Vec) -> Self { 316 | Value::List(List::from(list)) 317 | } 318 | } 319 | impl From for Value { 320 | fn from(b: bool) -> Self { 321 | Value::Boolean(b) 322 | } 323 | } 324 | impl From<&Value> for Value { 325 | fn from(v: &Value) -> Self { 326 | v.__shallow_copy() 327 | } 328 | } 329 | impl ToString for Value { 330 | fn to_string(&self) -> String { 331 | match self { 332 | Value::String(s) => s.to_string(), 333 | Value::Number(n) => n.to_string(), 334 | Value::List(list) => list.to_string(), 335 | _ => todo!(), 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /optpy-generator/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod typed; 2 | pub use typed::generate_typed_code; 3 | 4 | use std::collections::{BTreeMap, BTreeSet}; 5 | 6 | use optpy_parser::{ 7 | Assign, BinaryOperation, BinaryOperator, BoolOperation, BoolOperator, CallFunction, CallMethod, 8 | Compare, CompareOperator, Dict, Expr, Func, If, Index, Number, Statement, UnaryOperation, 9 | UnaryOperator, While, 10 | }; 11 | use proc_macro2::{Ident, TokenStream}; 12 | use quote::{format_ident, quote, TokenStreamExt}; 13 | 14 | pub fn generate_code( 15 | statements: &[Statement], 16 | definitions: &BTreeMap>, 17 | ) -> TokenStream { 18 | let body = generate_function_body(statements, "", definitions); 19 | quote! { 20 | fn main() { 21 | #body 22 | } 23 | } 24 | } 25 | 26 | pub fn generate_function_body( 27 | body: &[Statement], 28 | function_name: &str, 29 | definitions: &BTreeMap>, 30 | ) -> TokenStream { 31 | let mut result = TokenStream::new(); 32 | if let Some(definitions) = definitions.get(function_name) { 33 | for variable in definitions { 34 | let variable = format_ident!("{}", variable); 35 | result.append_all(quote! { 36 | let mut #variable = Value::None; 37 | }); 38 | } 39 | } 40 | for statement in body { 41 | let statement = format_statement(statement, definitions); 42 | result.append_all(statement); 43 | } 44 | result 45 | } 46 | 47 | fn format_statement( 48 | statement: &Statement, 49 | definitions: &BTreeMap>, 50 | ) -> TokenStream { 51 | match statement { 52 | Statement::Assign(Assign { target, value }) => { 53 | let target = format_expr(target, true); 54 | let value = format_expr(value, false); 55 | quote! { 56 | #target.assign(& #value); 57 | } 58 | } 59 | Statement::Expression(expr) => { 60 | let value = format_expr(expr, false); 61 | quote! { 62 | #value; 63 | } 64 | } 65 | Statement::If(If { test, body, orelse }) => { 66 | let test = format_expr(test, false); 67 | let body = body 68 | .iter() 69 | .map(|s| format_statement(s, definitions)) 70 | .collect::>(); 71 | let orelse = orelse 72 | .iter() 73 | .map(|s| format_statement(s, definitions)) 74 | .collect::>(); 75 | quote! { 76 | if (#test).test() { 77 | #(#body);* 78 | } else { 79 | #(#orelse);* 80 | } 81 | } 82 | } 83 | Statement::Func(Func { name, args, body }) => { 84 | let args = args 85 | .iter() 86 | .map(|arg| format_ident!("{}", arg)) 87 | .collect::>(); 88 | let body = generate_function_body(body, name, definitions); 89 | let name = format_ident!("{}", name); 90 | quote! { 91 | fn #name( #(#args: &Value),* ) -> Value { 92 | #(let mut #args = #args.__shallow_copy();)* 93 | #body 94 | return Value::None; 95 | } 96 | } 97 | } 98 | Statement::Return(value) => match value { 99 | Some(value) => { 100 | let value = format_expr(value, false); 101 | quote! { 102 | return Value::from(#value); 103 | } 104 | } 105 | None => { 106 | quote! { 107 | return Value::None; 108 | } 109 | } 110 | }, 111 | Statement::While(While { test, body }) => { 112 | let test = format_expr(test, false); 113 | let body = body 114 | .iter() 115 | .map(|s| format_statement(s, definitions)) 116 | .collect::>(); 117 | quote! { 118 | while (#test).test() { 119 | #(#body);* 120 | } 121 | } 122 | } 123 | Statement::Break => quote! { break; }, 124 | Statement::Continue => quote! { continue; }, 125 | Statement::Import(_) | Statement::FromImport(_) => unreachable!(), 126 | } 127 | } 128 | 129 | fn format_expr(expr: &Expr, assign_lhs: bool) -> TokenStream { 130 | match expr { 131 | Expr::CallFunction(CallFunction { name, args }) => { 132 | let args = format_exprs(args); 133 | if let Some(macro_name) = name.strip_suffix("__macro__") { 134 | let name = format_ident!("{}", macro_name); 135 | quote! { 136 | #name !( #(&#args),* ) 137 | } 138 | } else { 139 | let name = format_ident!("{}", name); 140 | quote! { 141 | #name ( #(&#args),* ) 142 | } 143 | } 144 | } 145 | Expr::CallMethod(CallMethod { value, name, args }) => { 146 | let value = format_expr(value, false); 147 | let name = format_ident!("{}", name); 148 | let args = format_exprs(args); 149 | quote! { 150 | #value . #name ( #(&#args),* ) 151 | } 152 | } 153 | Expr::Tuple(values) => { 154 | let list = format_exprs(values); 155 | quote! { 156 | Value::from(vec![ #(Value::from(&#list)),* ]) 157 | } 158 | } 159 | Expr::VariableName(name) => { 160 | let name = format_ident!("{}", name); 161 | quote! { 162 | #name 163 | } 164 | } 165 | Expr::BoolOperation(BoolOperation { op, conditions }) => { 166 | let op = format_boolean_operation(op); 167 | let conditions = format_exprs(conditions); 168 | 169 | let mut result = TokenStream::new(); 170 | for (i, condition) in conditions.iter().enumerate() { 171 | if i > 0 { 172 | result.append_all(quote! { # op }); 173 | } 174 | result.append_all(quote! { #condition .test() }); 175 | } 176 | quote! { Value::from(#result) } 177 | } 178 | Expr::Compare(Compare { left, right, op }) => { 179 | let left = format_expr(left, false); 180 | let right = format_expr(right, false); 181 | let op = format_compare_ident(op); 182 | quote! { #left . #op (&#right) } 183 | } 184 | Expr::BinaryOperation(BinaryOperation { left, right, op }) => { 185 | let left = format_expr(left, false); 186 | let right = format_expr(right, false); 187 | let op = format_binary_ident(op); 188 | quote! { #left . #op (&#right) } 189 | } 190 | Expr::ConstantNumber(number) => format_number(number), 191 | Expr::None => { 192 | quote! { 193 | Value::None 194 | } 195 | } 196 | Expr::Index(Index { value, index }) => { 197 | let value = format_expr(value, assign_lhs); 198 | let index = format_expr(index, false); 199 | if assign_lhs { 200 | quote! { 201 | #value .__index_ref(& #index ) 202 | } 203 | } else { 204 | quote! { 205 | #value .__index_value(& #index ) 206 | } 207 | } 208 | } 209 | Expr::List(list) => { 210 | let list = format_exprs(list); 211 | quote! { 212 | Value::from(vec![#(Value::from(&#list)),*]) 213 | } 214 | } 215 | Expr::Dict(Dict { pairs }) => { 216 | let pairs = pairs 217 | .iter() 218 | .map(|(key, value)| { 219 | let key = format_expr(key, false); 220 | let value = format_expr(value, false); 221 | quote! { 222 | (Value::from(&#key), Value::from(&#value)) 223 | } 224 | }) 225 | .collect::>(); 226 | quote! { 227 | Value::dict(vec![ #(#pairs),* ]) 228 | } 229 | } 230 | Expr::ConstantString(value) => { 231 | quote! { 232 | Value::from(#value) 233 | } 234 | } 235 | Expr::ConstantBoolean(b) => { 236 | if *b { 237 | quote! { 238 | Value::from(true) 239 | } 240 | } else { 241 | quote! { 242 | Value::from(false) 243 | } 244 | } 245 | } 246 | Expr::UnaryOperation(UnaryOperation { value, op }) => { 247 | let value = format_expr(value, false); 248 | let op = format_unary_ident(op); 249 | quote! { 250 | #value . #op () 251 | } 252 | } 253 | } 254 | } 255 | 256 | fn format_exprs(exprs: &[Expr]) -> Vec { 257 | exprs.iter().map(|e| format_expr(e, false)).collect() 258 | } 259 | 260 | fn format_boolean_operation(op: &BoolOperator) -> TokenStream { 261 | match op { 262 | BoolOperator::And => quote! { && }, 263 | BoolOperator::Or => quote! { || }, 264 | } 265 | } 266 | fn format_compare_ident(op: &CompareOperator) -> Ident { 267 | match op { 268 | CompareOperator::Less => format_ident!("__lt"), 269 | CompareOperator::LessOrEqual => format_ident!("__le"), 270 | CompareOperator::Greater => format_ident!("__gt"), 271 | CompareOperator::GreaterOrEqual => format_ident!("__ge"), 272 | CompareOperator::Equal => format_ident!("__eq"), 273 | CompareOperator::NotEqual => format_ident!("__ne"), 274 | CompareOperator::In => format_ident!("__in"), 275 | CompareOperator::NotIn => format_ident!("__not_in"), 276 | } 277 | } 278 | fn format_binary_ident(op: &BinaryOperator) -> Ident { 279 | match op { 280 | BinaryOperator::Add => format_ident!("__add"), 281 | BinaryOperator::Sub => format_ident!("__sub"), 282 | BinaryOperator::Mul => format_ident!("__mul"), 283 | BinaryOperator::Div => format_ident!("__div"), 284 | BinaryOperator::Mod => format_ident!("__rem"), 285 | BinaryOperator::FloorDiv => format_ident!("__floor_div"), 286 | BinaryOperator::Pow => format_ident!("__pow"), 287 | BinaryOperator::BitAnd => format_ident!("__bit_and"), 288 | BinaryOperator::LeftShift => format_ident!("__left_shift"), 289 | BinaryOperator::RightShift => format_ident!("__right_shift"), 290 | } 291 | } 292 | fn format_unary_ident(op: &UnaryOperator) -> Ident { 293 | match op { 294 | UnaryOperator::Add => format_ident!("__unary_add"), 295 | UnaryOperator::Sub => format_ident!("__unary_sub"), 296 | UnaryOperator::Not => format_ident!("__unary_not"), 297 | } 298 | } 299 | 300 | fn format_number(number: &Number) -> TokenStream { 301 | match number { 302 | Number::Int(int) => match int.parse::() { 303 | Ok(int) => { 304 | quote! { 305 | Value::from(#int) 306 | } 307 | } 308 | Err(_) => { 309 | todo!("bigint is not supported"); 310 | } 311 | }, 312 | Number::Float(float) => match float.parse::() { 313 | Ok(float) => { 314 | quote! { 315 | Value::from(#float) 316 | } 317 | } 318 | Err(e) => { 319 | panic!("unsupported float value: {} {:?}", float, e); 320 | } 321 | }, 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /optpy-generator/src/typed.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, BTreeSet}; 2 | 3 | use optpy_parser::{ 4 | Assign, BinaryOperation, BinaryOperator, BoolOperation, BoolOperator, CallFunction, CallMethod, 5 | Compare, CompareOperator, Dict, Expr, Func, If, Index, Number, Statement, UnaryOperation, 6 | UnaryOperator, While, 7 | }; 8 | use proc_macro2::{Ident, TokenStream}; 9 | use quote::{format_ident, quote, TokenStreamExt}; 10 | 11 | pub fn generate_typed_code( 12 | statements: &[Statement], 13 | definitions: &BTreeMap>, 14 | ) -> TokenStream { 15 | let body = generate_function_body(statements, "", definitions); 16 | quote! { 17 | fn main() { 18 | #body 19 | } 20 | } 21 | } 22 | 23 | pub fn generate_function_body( 24 | body: &[Statement], 25 | function_name: &str, 26 | definitions: &BTreeMap>, 27 | ) -> TokenStream { 28 | let mut result = TokenStream::new(); 29 | if let Some(definitions) = definitions.get(function_name) { 30 | for variable in definitions { 31 | let variable = format_ident!("{}", variable); 32 | result.append_all(quote! { 33 | let mut #variable = Default::default(); 34 | }); 35 | } 36 | } 37 | for statement in body { 38 | let statement = format_statement(statement, definitions); 39 | result.append_all(statement); 40 | } 41 | result 42 | } 43 | 44 | fn format_statement( 45 | statement: &Statement, 46 | definitions: &BTreeMap>, 47 | ) -> TokenStream { 48 | match statement { 49 | Statement::Assign(Assign { target, value }) => match target { 50 | Expr::VariableName(name) => { 51 | let target = format_ident!("{}", name); 52 | let value = format_expr(value, false); 53 | quote! { 54 | #target = #value; 55 | } 56 | } 57 | target => { 58 | let target = format_expr(target, true); 59 | let value = format_expr(value, false); 60 | quote! { 61 | #target.assign(#value . __shallow_copy()); 62 | } 63 | } 64 | }, 65 | Statement::Expression(expr) => { 66 | let value = format_expr(expr, false); 67 | quote! { 68 | #value; 69 | } 70 | } 71 | Statement::If(If { test, body, orelse }) => { 72 | let test = format_expr(test, false); 73 | let body = body 74 | .iter() 75 | .map(|s| format_statement(s, definitions)) 76 | .collect::>(); 77 | let orelse = orelse 78 | .iter() 79 | .map(|s| format_statement(s, definitions)) 80 | .collect::>(); 81 | quote! { 82 | if (#test).test() { 83 | #(#body);* 84 | } else { 85 | #(#orelse);* 86 | } 87 | } 88 | } 89 | Statement::Func(Func { name, args, body }) => { 90 | let args = args 91 | .iter() 92 | .map(|arg| format_ident!("{}", arg)) 93 | .collect::>(); 94 | let body = generate_function_body(body, name, definitions); 95 | let name = format_ident!("{}", name); 96 | quote! { 97 | let #name = |#(mut #args),*| { 98 | #body 99 | return Default::default(); 100 | }; 101 | } 102 | } 103 | Statement::Return(value) => match value { 104 | Some(value) => { 105 | let value = format_expr(value, false); 106 | quote! { 107 | return #value . __shallow_copy(); 108 | } 109 | } 110 | None => { 111 | quote! { 112 | return Default::default(); 113 | } 114 | } 115 | }, 116 | Statement::While(While { test, body }) => { 117 | let test = format_expr(test, false); 118 | let body = body 119 | .iter() 120 | .map(|s| format_statement(s, definitions)) 121 | .collect::>(); 122 | quote! { 123 | while (#test).test() { 124 | #(#body);* 125 | } 126 | } 127 | } 128 | Statement::Break => quote! { break; }, 129 | Statement::Continue => quote! { continue; }, 130 | Statement::Import(_) | Statement::FromImport(_) => unreachable!(), 131 | } 132 | } 133 | 134 | fn format_expr(expr: &Expr, assign_lhs: bool) -> TokenStream { 135 | match expr { 136 | Expr::CallFunction(CallFunction { name, args }) => { 137 | let args = format_exprs(args); 138 | if let Some(macro_name) = name.strip_suffix("__macro__") { 139 | let name = format_ident!("typed_{}", macro_name); 140 | quote! { 141 | #name !( #(#args . __shallow_copy()),* ) 142 | } 143 | } else { 144 | let name = format_ident!("{}", name); 145 | quote! { 146 | #name ( #(#args . __shallow_copy()),* ) 147 | } 148 | } 149 | } 150 | Expr::CallMethod(CallMethod { value, name, args }) => { 151 | let value = format_expr(value, false); 152 | let name = format_ident!("{}", name); 153 | let args = format_exprs(args); 154 | quote! { 155 | #value . #name ( #(#args . __shallow_copy()),* ) 156 | } 157 | } 158 | Expr::Tuple(values) => { 159 | let list = format_exprs(values); 160 | quote! { 161 | TypedList::from(vec![ #(#list . __shallow_copy()),* ]) 162 | } 163 | } 164 | Expr::VariableName(name) => { 165 | let name = format_ident!("{}", name); 166 | quote! { 167 | #name 168 | } 169 | } 170 | Expr::BoolOperation(BoolOperation { op, conditions }) => { 171 | let op = format_boolean_operation(op); 172 | let conditions = format_exprs(conditions); 173 | 174 | let mut result = TokenStream::new(); 175 | for (i, condition) in conditions.iter().enumerate() { 176 | if i > 0 { 177 | result.append_all(quote! { # op }); 178 | } 179 | result.append_all(quote! { #condition .test() }); 180 | } 181 | quote! { Bool::from(#result) } 182 | } 183 | Expr::Compare(Compare { left, right, op }) => { 184 | let left = format_expr(left, false); 185 | let right = format_expr(right, false); 186 | let op = format_compare_ident(op); 187 | quote! { #left . #op (#right . __shallow_copy()) } 188 | } 189 | Expr::BinaryOperation(BinaryOperation { left, right, op }) => { 190 | let left = format_expr(left, false); 191 | let right = format_expr(right, false); 192 | let op = format_binary_ident(op); 193 | quote! { #left . #op (#right . __shallow_copy()) } 194 | } 195 | Expr::ConstantNumber(number) => format_number(number), 196 | Expr::None => { 197 | quote! { 198 | Value::none() 199 | } 200 | } 201 | Expr::Index(Index { value, index }) => { 202 | let value = format_expr(value, assign_lhs); 203 | let index = format_expr(index, false); 204 | if assign_lhs { 205 | quote! { 206 | #value .__index_ref( #index . __shallow_copy()) 207 | } 208 | } else { 209 | quote! { 210 | #value .__index_value( #index . __shallow_copy()) 211 | } 212 | } 213 | } 214 | Expr::List(list) => { 215 | let list = format_exprs(list); 216 | quote! { 217 | TypedList::from(vec![#(#list . __shallow_copy()),*]) 218 | } 219 | } 220 | Expr::Dict(Dict { pairs }) => { 221 | let pairs = pairs 222 | .iter() 223 | .map(|(key, value)| { 224 | let key = format_expr(key, false); 225 | let value = format_expr(value, false); 226 | quote! { 227 | (Value::from(&#key), Value::from(&#value)) 228 | } 229 | }) 230 | .collect::>(); 231 | quote! { 232 | Value::dict(vec![ #(#pairs),* ]) 233 | } 234 | } 235 | Expr::ConstantString(value) => { 236 | quote! { 237 | Value::from(#value) 238 | } 239 | } 240 | Expr::ConstantBoolean(b) => { 241 | if *b { 242 | quote! { 243 | Bool::from(true) 244 | } 245 | } else { 246 | quote! { 247 | Bool::from(false) 248 | } 249 | } 250 | } 251 | Expr::UnaryOperation(UnaryOperation { value, op }) => { 252 | let value = format_expr(value, false); 253 | let op = format_unary_ident(op); 254 | quote! { 255 | #value . #op () 256 | } 257 | } 258 | } 259 | } 260 | 261 | fn format_exprs(exprs: &[Expr]) -> Vec { 262 | exprs.iter().map(|e| format_expr(e, false)).collect() 263 | } 264 | 265 | fn format_boolean_operation(op: &BoolOperator) -> TokenStream { 266 | match op { 267 | BoolOperator::And => quote! { && }, 268 | BoolOperator::Or => quote! { || }, 269 | } 270 | } 271 | fn format_compare_ident(op: &CompareOperator) -> Ident { 272 | match op { 273 | CompareOperator::Less => format_ident!("__lt"), 274 | CompareOperator::LessOrEqual => format_ident!("__le"), 275 | CompareOperator::Greater => format_ident!("__gt"), 276 | CompareOperator::GreaterOrEqual => format_ident!("__ge"), 277 | CompareOperator::Equal => format_ident!("__eq"), 278 | CompareOperator::NotEqual => format_ident!("__ne"), 279 | CompareOperator::In => format_ident!("__in"), 280 | CompareOperator::NotIn => format_ident!("__not_in"), 281 | } 282 | } 283 | fn format_binary_ident(op: &BinaryOperator) -> Ident { 284 | match op { 285 | BinaryOperator::Add => format_ident!("__add"), 286 | BinaryOperator::Sub => format_ident!("__sub"), 287 | BinaryOperator::Mul => format_ident!("__mul"), 288 | BinaryOperator::Div => format_ident!("__div"), 289 | BinaryOperator::Mod => format_ident!("__rem"), 290 | BinaryOperator::FloorDiv => format_ident!("__floor_div"), 291 | BinaryOperator::Pow => format_ident!("__pow"), 292 | BinaryOperator::BitAnd => format_ident!("__bit_and"), 293 | BinaryOperator::LeftShift => format_ident!("__left_shift"), 294 | BinaryOperator::RightShift => format_ident!("__right_shift"), 295 | } 296 | } 297 | fn format_unary_ident(op: &UnaryOperator) -> Ident { 298 | match op { 299 | UnaryOperator::Add => format_ident!("__unary_add"), 300 | UnaryOperator::Sub => format_ident!("__unary_sub"), 301 | UnaryOperator::Not => format_ident!("__unary_not"), 302 | } 303 | } 304 | 305 | fn format_number(number: &Number) -> TokenStream { 306 | match number { 307 | Number::Int(int) => match int.parse::() { 308 | Ok(int) => { 309 | quote! { 310 | Number::from(#int) 311 | } 312 | } 313 | Err(_) => { 314 | todo!("bigint is not supported"); 315 | } 316 | }, 317 | Number::Float(float) => match float.parse::() { 318 | Ok(float) => { 319 | quote! { 320 | Number::from(#float) 321 | } 322 | } 323 | Err(e) => { 324 | panic!("unsupported float value: {} {:?}", float, e); 325 | } 326 | }, 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /optpy-resolver/src/name/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use optpy_parser::{ 4 | Assign, BinaryOperation, BoolOperation, CallFunction, CallMethod, Compare, Dict, Expr, Func, 5 | If, Index, Statement, UnaryOperation, While, 6 | }; 7 | 8 | pub(super) fn resolve_names(statements: &[Statement]) -> Vec { 9 | let mut variables = NameStore::new("__v"); 10 | let mut functions = NameStore::new("__f"); 11 | let ctx = ContextPath::default(); 12 | collect_declarations(statements, &mut variables, &mut functions, &ctx); 13 | let statements = resolve_statements(statements, &variables, &functions, &ctx); 14 | statements 15 | } 16 | 17 | fn collect_declarations( 18 | statements: &[Statement], 19 | variables: &mut NameStore, 20 | functions: &mut NameStore, 21 | ctx: &ContextPath, 22 | ) { 23 | for statement in statements { 24 | match statement { 25 | Statement::Assign(Assign { target, .. }) => { 26 | collect_variable_names(target, variables, ctx) 27 | } 28 | Statement::If(If { body, orelse, .. }) => { 29 | collect_declarations(body, variables, functions, ctx); 30 | collect_declarations(orelse, variables, functions, ctx); 31 | } 32 | Statement::Func(Func { name, args, body }) => { 33 | functions.declare(name, ctx); 34 | let ctx = ctx.join(name); 35 | for arg in args { 36 | variables.declare(arg, &ctx); 37 | } 38 | collect_declarations(body, variables, functions, &ctx); 39 | } 40 | Statement::While(While { body, .. }) => { 41 | collect_declarations(body, variables, functions, ctx); 42 | } 43 | Statement::Return(_) 44 | | Statement::Expression(_) 45 | | Statement::Break 46 | | Statement::Continue 47 | | Statement::Import(_) 48 | | Statement::FromImport(_) => continue, 49 | } 50 | } 51 | } 52 | 53 | fn collect_variable_names(expr: &Expr, variables: &mut NameStore, ctx: &ContextPath) { 54 | match expr { 55 | Expr::VariableName(name) => { 56 | variables.declare(name, ctx); 57 | } 58 | Expr::Index(Index { .. }) => {} 59 | expr => unreachable!("{:?}", expr), 60 | } 61 | } 62 | 63 | fn resolve_statements( 64 | statements: &[Statement], 65 | variables: &NameStore, 66 | functions: &NameStore, 67 | ctx: &ContextPath, 68 | ) -> Vec { 69 | statements 70 | .iter() 71 | .map(|s| match s { 72 | Statement::Assign(Assign { target, value }) => { 73 | let target = resolve_expr(target, variables, functions, ctx); 74 | let value = resolve_expr(value, variables, functions, ctx); 75 | Statement::Assign(Assign { target, value }) 76 | } 77 | Statement::Expression(expr) => { 78 | Statement::Expression(resolve_expr(expr, variables, functions, ctx)) 79 | } 80 | Statement::If(If { test, body, orelse }) => { 81 | let test = resolve_expr(test, variables, functions, ctx); 82 | let body = resolve_statements(body, variables, functions, ctx); 83 | let orelse = resolve_statements(orelse, variables, functions, ctx); 84 | Statement::If(If { test, body, orelse }) 85 | } 86 | Statement::Func(Func { name, args, body }) => { 87 | let resolved_name = functions.resolve(name, ctx).expect("invalid"); 88 | let ctx = ctx.join(name); 89 | let args = args 90 | .iter() 91 | .map(|arg| variables.resolve(arg, &ctx).expect("invalid")) 92 | .collect::>(); 93 | let body = resolve_statements(body, variables, functions, &ctx); 94 | Statement::Func(Func { 95 | name: resolved_name, 96 | args, 97 | body, 98 | }) 99 | } 100 | Statement::Return(expr) => match expr { 101 | Some(expr) => { 102 | Statement::Return(Some(resolve_expr(expr, variables, functions, ctx))) 103 | } 104 | None => Statement::Return(None), 105 | }, 106 | Statement::While(While { test, body }) => { 107 | let test = resolve_expr(test, variables, functions, ctx); 108 | let body = resolve_statements(body, variables, functions, ctx); 109 | Statement::While(While { test, body }) 110 | } 111 | Statement::Break 112 | | Statement::Continue 113 | | Statement::Import(_) 114 | | Statement::FromImport(_) => s.clone(), 115 | }) 116 | .collect() 117 | } 118 | 119 | fn resolve_expr( 120 | expr: &Expr, 121 | variables: &NameStore, 122 | functions: &NameStore, 123 | ctx: &ContextPath, 124 | ) -> Expr { 125 | match expr { 126 | Expr::CallFunction(CallFunction { name, args }) => { 127 | let name = match functions.resolve(name, ctx) { 128 | Some(name) => name, 129 | None => { 130 | // built-in function 131 | name.to_string() 132 | } 133 | }; 134 | let args = resolve_exprs(args, variables, functions, ctx); 135 | Expr::CallFunction(CallFunction { name, args }) 136 | } 137 | Expr::CallMethod(CallMethod { value, name, args }) => { 138 | let value = resolve_expr(value, variables, functions, ctx); 139 | let args = resolve_exprs(args, variables, functions, ctx); 140 | Expr::CallMethod(CallMethod { 141 | value: Box::new(value), 142 | name: name.clone(), 143 | args, 144 | }) 145 | } 146 | Expr::Tuple(exprs) => { 147 | let exprs = resolve_exprs(exprs, variables, functions, ctx); 148 | Expr::Tuple(exprs) 149 | } 150 | Expr::VariableName(name) => { 151 | let name = match variables.resolve(name, ctx) { 152 | Some(name) => name, 153 | None => { 154 | // built-in variable 155 | name.to_string() 156 | } 157 | }; 158 | Expr::VariableName(name) 159 | } 160 | Expr::BoolOperation(BoolOperation { op, conditions }) => { 161 | let conditions = resolve_exprs(conditions, variables, functions, ctx); 162 | Expr::BoolOperation(BoolOperation { 163 | op: *op, 164 | conditions, 165 | }) 166 | } 167 | Expr::Compare(Compare { left, right, op }) => { 168 | let left = resolve_expr(left, variables, functions, ctx); 169 | let right = resolve_expr(right, variables, functions, ctx); 170 | Expr::Compare(Compare { 171 | left: Box::new(left), 172 | right: Box::new(right), 173 | op: *op, 174 | }) 175 | } 176 | Expr::BinaryOperation(BinaryOperation { left, right, op }) => { 177 | let left = resolve_expr(left, variables, functions, ctx); 178 | let right = resolve_expr(right, variables, functions, ctx); 179 | Expr::BinaryOperation(BinaryOperation { 180 | left: Box::new(left), 181 | right: Box::new(right), 182 | op: *op, 183 | }) 184 | } 185 | Expr::Index(Index { value, index }) => { 186 | let value = resolve_expr(value, variables, functions, ctx); 187 | let index = resolve_expr(index, variables, functions, ctx); 188 | Expr::Index(Index { 189 | value: Box::new(value), 190 | index: Box::new(index), 191 | }) 192 | } 193 | Expr::List(list) => { 194 | let list = resolve_exprs(list, variables, functions, ctx); 195 | Expr::List(list) 196 | } 197 | Expr::ConstantString(_) 198 | | Expr::ConstantNumber(_) 199 | | Expr::ConstantBoolean(_) 200 | | Expr::None => expr.clone(), 201 | Expr::UnaryOperation(UnaryOperation { value, op }) => { 202 | let value = resolve_expr(value, variables, functions, ctx); 203 | Expr::UnaryOperation(UnaryOperation { 204 | value: Box::new(value), 205 | op: *op, 206 | }) 207 | } 208 | Expr::Dict(Dict { pairs }) => { 209 | let pairs = pairs 210 | .iter() 211 | .map(|(key, value)| { 212 | let key = resolve_expr(key, variables, functions, ctx); 213 | let value = resolve_expr(value, variables, functions, ctx); 214 | (key, value) 215 | }) 216 | .collect(); 217 | Expr::Dict(Dict { pairs }) 218 | } 219 | } 220 | } 221 | 222 | fn resolve_exprs( 223 | exprs: &[Expr], 224 | variables: &NameStore, 225 | functions: &NameStore, 226 | ctx: &ContextPath, 227 | ) -> Vec { 228 | exprs 229 | .iter() 230 | .map(|expr| resolve_expr(expr, variables, functions, ctx)) 231 | .collect::>() 232 | } 233 | 234 | #[derive(Clone, Eq, Hash, PartialEq, Debug)] 235 | struct ContextPath(Vec); 236 | 237 | impl ContextPath { 238 | fn join(&self, name: &str) -> Self { 239 | let mut path = self.0.clone(); 240 | path.push(name.to_string()); 241 | Self(path) 242 | } 243 | fn pop(&self) -> Option { 244 | let mut next = self.0.clone(); 245 | match next.pop() { 246 | Some(_) => Some(Self(next)), 247 | None => None, 248 | } 249 | } 250 | } 251 | 252 | impl Default for ContextPath { 253 | fn default() -> Self { 254 | Self(Default::default()) 255 | } 256 | } 257 | 258 | struct NameStore { 259 | prefix: String, 260 | map: HashMap>, 261 | global_counter: usize, 262 | } 263 | 264 | impl NameStore { 265 | fn new>(prefix: S) -> Self { 266 | Self { 267 | prefix: prefix.as_ref().into(), 268 | map: Default::default(), 269 | global_counter: 0, 270 | } 271 | } 272 | fn declare(&mut self, name: &str, ctx: &ContextPath) { 273 | let map = self.map.entry(ctx.clone()).or_default(); 274 | map.entry(name.to_string()).or_insert_with(|| { 275 | let result = format!("{}{}", self.prefix, self.global_counter); 276 | self.global_counter += 1; 277 | result 278 | }); 279 | } 280 | 281 | fn resolve(&self, name: &str, ctx: &ContextPath) -> Option { 282 | let mut ctx = ctx.clone(); 283 | loop { 284 | if let Some(name) = self.map.get(&ctx).and_then(|m| m.get(name)) { 285 | return Some(name.clone()); 286 | } 287 | 288 | match ctx.pop() { 289 | Some(next) => { 290 | ctx = next; 291 | } 292 | None => return None, 293 | } 294 | } 295 | } 296 | } 297 | 298 | #[cfg(test)] 299 | mod tests { 300 | use optpy_parser::parse; 301 | 302 | use crate::util::StripMargin; 303 | 304 | use super::*; 305 | 306 | #[test] 307 | fn test_basic_resolver() { 308 | let code = r" 309 | |a, b = map(int, input().split()) 310 | |print(a) 311 | |" 312 | .strip_margin(); 313 | let ast = parse(code).unwrap(); 314 | let resolved = resolve_names(&ast); 315 | 316 | let expected = r" 317 | |__v0 = iter(map(int, input().split())) 318 | |__v1 = next(__v0) 319 | |__v2 = next(__v0) 320 | |print(__v1)" 321 | .strip_margin(); 322 | assert_eq!(resolved, parse(expected).unwrap()); 323 | } 324 | 325 | #[test] 326 | fn test_function_resolver() { 327 | let code = r" 328 | |a, b = map(int, input().split()) 329 | |def func(a): 330 | | return a + b 331 | |c = func(a) 332 | |print(c) 333 | |" 334 | .strip_margin(); 335 | let ast = parse(code).unwrap(); 336 | let resolved = resolve_names(&ast); 337 | 338 | let expected = r" 339 | |__v0 = iter(map(int, input().split())) 340 | |__v1 = next(__v0) 341 | |__v2 = next(__v0) 342 | |def __f0(__v3): 343 | | return __v3 + __v2 344 | |__v4 = __f0(__v1) 345 | |print(__v4)" 346 | .strip_margin(); 347 | assert_eq!(resolved, parse(expected).unwrap()); 348 | 349 | let code = r" 350 | |a, b = map(int, input().split()) 351 | |c = a + b 352 | |def f(a): 353 | | def g(a): 354 | | return a + b + c 355 | | return g(b) + a 356 | |d = f(a + b + c) 357 | |print(d) 358 | " 359 | .strip_margin(); 360 | 361 | let expected = r" 362 | |__v0 = iter(map(int, input().split())) 363 | |__v1 = next(__v0) 364 | |__v2 = next(__v0) 365 | |__v3 = __v1 + __v2 366 | |def __f0(__v4): 367 | | def __f1(__v5): 368 | | return __v5 + __v2 + __v3 369 | | return __f1(__v2) + __v4 370 | |__v6 = __f0(__v1 + __v2 + __v3) 371 | |print(__v6)" 372 | .strip_margin(); 373 | 374 | assert_eq!( 375 | resolve_names(&parse(code).unwrap()), 376 | parse(expected).unwrap() 377 | ); 378 | 379 | let code = r" 380 | |a, b = map(int, input().split()) 381 | |c = a + b 382 | |def f(a): 383 | | def g(a): 384 | | d = b + c 385 | | return a + d 386 | | return g(b) + a 387 | |d = f(a + b + c) 388 | |print(d) 389 | " 390 | .strip_margin(); 391 | 392 | let expected = r" 393 | |__v0 = iter(map(int, input().split())) 394 | |__v1 = next(__v0) 395 | |__v2 = next(__v0) 396 | |__v3 = __v1 + __v2 397 | |def __f0(__v4): 398 | | def __f1(__v5): 399 | | __v6 = __v2 + __v3 400 | | return __v5 + __v6 401 | | return __f1(__v2) + __v4 402 | |__v7 = __f0(__v1 + __v2 + __v3) 403 | |print(__v7)" 404 | .strip_margin(); 405 | 406 | assert_eq!( 407 | resolve_names(&parse(code).unwrap()), 408 | parse(expected).unwrap() 409 | ); 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /optpy-resolver/src/call/mod.rs: -------------------------------------------------------------------------------- 1 | mod referencestore; 2 | 3 | use std::collections::{BTreeMap, BTreeSet}; 4 | 5 | use optpy_parser::{ 6 | Assign, BinaryOperation, BoolOperation, CallFunction, CallMethod, Compare, Dict, Expr, Func, 7 | If, Index, Statement, UnaryOperation, While, 8 | }; 9 | 10 | use self::referencestore::ReferenceStore; 11 | 12 | pub(super) fn resolve_function_calls( 13 | statements: &[Statement], 14 | ) -> (Vec, BTreeMap>) { 15 | let mut store = ReferenceStore::default(); 16 | list_variable_contexts(statements, "$", &mut store); 17 | 18 | let mut definitions = BTreeMap::new(); 19 | let mut extensions = BTreeMap::new(); 20 | collect_extension(statements, &mut store, &mut definitions, &mut extensions); 21 | 22 | let root_variables = store.list_by_function("$"); 23 | for v in root_variables.iter() { 24 | let functions = store.list_by_variable(v); 25 | assert_eq!(functions.len(), 1); 26 | } 27 | 28 | definitions.insert(String::new(), root_variables); 29 | let statements = resolve_statements(statements, &extensions); 30 | (statements, definitions) 31 | } 32 | 33 | fn resolve_statement( 34 | statement: &Statement, 35 | extensions: &BTreeMap>, 36 | ) -> Statement { 37 | match statement { 38 | Statement::Assign(Assign { target, value }) => { 39 | let target = resolve_expr(target, extensions); 40 | let value = resolve_expr(value, extensions); 41 | Statement::Assign(Assign { target, value }) 42 | } 43 | Statement::Expression(expr) => Statement::Expression(resolve_expr(expr, extensions)), 44 | Statement::If(If { test, body, orelse }) => { 45 | let test = resolve_expr(test, extensions); 46 | let body = resolve_statements(body, extensions); 47 | let orelse = resolve_statements(orelse, extensions); 48 | Statement::If(If { test, body, orelse }) 49 | } 50 | Statement::Func(Func { name, args, body }) => { 51 | let variables = extensions.get(name).expect("invalid"); 52 | let mut args = args.clone(); 53 | args.extend(variables.clone()); 54 | let body = resolve_statements(body, extensions); 55 | Statement::Func(Func { 56 | name: name.to_string(), 57 | args, 58 | body, 59 | }) 60 | } 61 | Statement::Return(expr) => { 62 | Statement::Return(expr.as_ref().map(|e| resolve_expr(e, extensions))) 63 | } 64 | Statement::While(While { test, body }) => { 65 | let test = resolve_expr(test, extensions); 66 | let body = resolve_statements(body, extensions); 67 | Statement::While(While { test, body }) 68 | } 69 | Statement::Break 70 | | Statement::Continue 71 | | Statement::Import(_) 72 | | Statement::FromImport(_) => statement.clone(), 73 | } 74 | } 75 | 76 | fn resolve_statements( 77 | statements: &[Statement], 78 | extensions: &BTreeMap>, 79 | ) -> Vec { 80 | statements 81 | .iter() 82 | .map(|s| resolve_statement(s, extensions)) 83 | .collect() 84 | } 85 | 86 | fn resolve_expr(expr: &Expr, extensions: &BTreeMap>) -> Expr { 87 | match expr { 88 | Expr::CallFunction(CallFunction { name, args }) => { 89 | let variables = match extensions.get(name) { 90 | Some(v) => v, 91 | None => { 92 | return Expr::CallFunction(CallFunction { 93 | name: name.to_string(), 94 | args: resolve_exprs(args, extensions), 95 | }) 96 | } 97 | }; 98 | let mut args = resolve_exprs(args, extensions); 99 | args.extend( 100 | variables 101 | .iter() 102 | .map(|name| Expr::VariableName(name.to_string())), 103 | ); 104 | Expr::CallFunction(CallFunction { 105 | name: name.to_string(), 106 | args, 107 | }) 108 | } 109 | Expr::CallMethod(CallMethod { value, name, args }) => { 110 | let value = resolve_expr(value, extensions); 111 | let args = resolve_exprs(args, extensions); 112 | Expr::CallMethod(CallMethod { 113 | value: Box::new(value), 114 | name: name.to_string(), 115 | args, 116 | }) 117 | } 118 | Expr::Tuple(exprs) => Expr::Tuple(resolve_exprs(exprs, extensions)), 119 | Expr::BoolOperation(BoolOperation { op, conditions }) => { 120 | let conditions = resolve_exprs(conditions, extensions); 121 | Expr::BoolOperation(BoolOperation { 122 | op: *op, 123 | conditions, 124 | }) 125 | } 126 | Expr::Compare(Compare { left, right, op }) => { 127 | let left = resolve_expr(left, extensions); 128 | let right = resolve_expr(right, extensions); 129 | Expr::Compare(Compare { 130 | left: Box::new(left), 131 | right: Box::new(right), 132 | op: *op, 133 | }) 134 | } 135 | Expr::BinaryOperation(BinaryOperation { left, right, op }) => { 136 | let left = resolve_expr(left, extensions); 137 | let right = resolve_expr(right, extensions); 138 | Expr::BinaryOperation(BinaryOperation { 139 | left: Box::new(left), 140 | right: Box::new(right), 141 | op: *op, 142 | }) 143 | } 144 | Expr::ConstantString(_) 145 | | Expr::ConstantBoolean(_) 146 | | Expr::ConstantNumber(_) 147 | | Expr::VariableName(_) 148 | | Expr::None => expr.clone(), 149 | Expr::UnaryOperation(UnaryOperation { value, op }) => { 150 | let value = Box::new(resolve_expr(value, extensions)); 151 | Expr::UnaryOperation(UnaryOperation { value, op: *op }) 152 | } 153 | Expr::Index(Index { value, index }) => { 154 | let value = Box::new(resolve_expr(value, extensions)); 155 | let index = Box::new(resolve_expr(index, extensions)); 156 | Expr::Index(Index { value, index }) 157 | } 158 | Expr::List(list) => { 159 | let list = resolve_exprs(list, extensions); 160 | Expr::List(list) 161 | } 162 | Expr::Dict(Dict { pairs }) => { 163 | let pairs = pairs 164 | .iter() 165 | .map(|(key, value)| { 166 | ( 167 | resolve_expr(key, extensions), 168 | resolve_expr(value, extensions), 169 | ) 170 | }) 171 | .collect(); 172 | Expr::Dict(Dict { pairs }) 173 | } 174 | } 175 | } 176 | 177 | fn resolve_exprs(exprs: &[Expr], extensions: &BTreeMap>) -> Vec { 178 | exprs.iter().map(|e| resolve_expr(e, extensions)).collect() 179 | } 180 | 181 | fn collect_extension( 182 | statements: &[Statement], 183 | store: &mut ReferenceStore, 184 | definitions: &mut BTreeMap>, 185 | extensions: &mut BTreeMap>, 186 | ) { 187 | for statement in statements { 188 | match statement { 189 | Statement::Func(Func { name, args, body }) => { 190 | collect_extension(body, store, definitions, extensions); 191 | let mut external = BTreeSet::new(); 192 | let mut internal = BTreeSet::new(); 193 | let variables = store.list_by_function(name); 194 | for v in variables { 195 | let functions = store.list_by_variable(&v); 196 | if functions.len() == 1 { 197 | internal.insert(v); 198 | } else { 199 | external.insert(v); 200 | } 201 | } 202 | store.remove_function(name); 203 | for arg in args { 204 | assert!( 205 | internal.remove(arg), 206 | "no arg={} in internal={:?} in function={}", 207 | arg, 208 | internal, 209 | name 210 | ); 211 | } 212 | definitions.insert(name.clone(), internal); 213 | extensions.insert(name.clone(), external); 214 | } 215 | Statement::If(If { body, orelse, .. }) => { 216 | collect_extension(body, store, definitions, extensions); 217 | collect_extension(orelse, store, definitions, extensions); 218 | } 219 | Statement::While(While { body, .. }) => { 220 | collect_extension(body, store, definitions, extensions); 221 | } 222 | Statement::Break 223 | | Statement::Continue 224 | | Statement::Assign(_) 225 | | Statement::Expression(_) 226 | | Statement::Return(_) 227 | | Statement::Import(_) 228 | | Statement::FromImport(_) => {} 229 | } 230 | } 231 | } 232 | 233 | fn list_variable_contexts( 234 | statements: &[Statement], 235 | function_name: &str, 236 | store: &mut ReferenceStore, 237 | ) { 238 | for statement in statements { 239 | match statement { 240 | Statement::Assign(Assign { target, value }) => { 241 | list_from_expr(target, function_name, store); 242 | list_from_expr(value, function_name, store); 243 | } 244 | Statement::Expression(expr) => { 245 | list_from_expr(expr, function_name, store); 246 | } 247 | Statement::Return(expr) => { 248 | if let Some(expr) = expr { 249 | list_from_expr(expr, function_name, store); 250 | } 251 | } 252 | Statement::If(If { test, body, orelse }) => { 253 | list_from_expr(test, function_name, store); 254 | list_variable_contexts(body, function_name, store); 255 | list_variable_contexts(orelse, function_name, store); 256 | } 257 | Statement::Func(Func { name, args, body }) => { 258 | list_variable_contexts(body, name, store); 259 | for arg in args { 260 | store.record(arg, name); 261 | } 262 | } 263 | Statement::While(While { test, body }) => { 264 | list_from_expr(test, function_name, store); 265 | list_variable_contexts(body, function_name, store); 266 | } 267 | Statement::Break 268 | | Statement::Continue 269 | | Statement::Import(_) 270 | | Statement::FromImport(_) => {} 271 | } 272 | } 273 | } 274 | 275 | fn list_from_expr(expr: &Expr, function_name: &str, store: &mut ReferenceStore) { 276 | match expr { 277 | Expr::CallFunction(CallFunction { name: _, args }) => { 278 | list_from_exprs(args, function_name, store); 279 | } 280 | Expr::CallMethod(CallMethod { 281 | value, 282 | name: _, 283 | args, 284 | }) => { 285 | list_from_expr(value, function_name, store); 286 | list_from_exprs(args, function_name, store); 287 | } 288 | Expr::Tuple(values) => { 289 | list_from_exprs(values, function_name, store); 290 | } 291 | Expr::VariableName(name) => { 292 | store.record(name, function_name); 293 | } 294 | Expr::BoolOperation(BoolOperation { op: _, conditions }) => { 295 | list_from_exprs(conditions, function_name, store); 296 | } 297 | Expr::Compare(Compare { left, right, op: _ }) => { 298 | list_from_expr(left, function_name, store); 299 | list_from_expr(right, function_name, store); 300 | } 301 | Expr::BinaryOperation(BinaryOperation { left, right, op: _ }) => { 302 | list_from_expr(left, function_name, store); 303 | list_from_expr(right, function_name, store); 304 | } 305 | Expr::Index(Index { value, index }) => { 306 | list_from_expr(value, function_name, store); 307 | list_from_expr(index, function_name, store); 308 | } 309 | Expr::List(list) => { 310 | list_from_exprs(list, function_name, store); 311 | } 312 | Expr::Dict(Dict { pairs }) => { 313 | for (key, value) in pairs { 314 | list_from_expr(key, function_name, store); 315 | list_from_expr(value, function_name, store); 316 | } 317 | } 318 | Expr::ConstantNumber(_) 319 | | Expr::ConstantString(_) 320 | | Expr::ConstantBoolean(_) 321 | | Expr::None => {} 322 | Expr::UnaryOperation(UnaryOperation { value, op: _ }) => { 323 | list_from_expr(value, function_name, store); 324 | } 325 | } 326 | } 327 | fn list_from_exprs(exprs: &[Expr], function_name: &str, store: &mut ReferenceStore) { 328 | for expr in exprs { 329 | list_from_expr(expr, function_name, store); 330 | } 331 | } 332 | 333 | #[cfg(test)] 334 | mod tests { 335 | use optpy_parser::{parse, CallFunction, Number}; 336 | 337 | use crate::{resolve, util::StripMargin}; 338 | 339 | use super::*; 340 | 341 | #[test] 342 | fn test_non_variable_function() { 343 | let code = r" 344 | |def f(): 345 | | return 1 346 | |print(f())" 347 | .strip_margin(); 348 | 349 | let ast = parse(code).unwrap(); 350 | let (statements, definitions) = resolve(&ast); 351 | 352 | assert_eq!( 353 | statements, 354 | [ 355 | Statement::Func(Func { 356 | name: "__f0".into(), 357 | args: vec![], 358 | body: vec![Statement::Return(Some(Expr::ConstantNumber(Number::Int( 359 | "1".into() 360 | ))))] 361 | }), 362 | Statement::Expression(Expr::CallFunction(CallFunction { 363 | name: "print_values__macro__".into(), 364 | args: vec![Expr::CallFunction(CallFunction { 365 | name: "__f0".into(), 366 | args: vec![] 367 | })] 368 | })) 369 | ] 370 | ); 371 | 372 | assert_eq!( 373 | definitions, 374 | BTreeMap::from([ 375 | ("".into(), BTreeSet::from([])), 376 | ("__f0".into(), BTreeSet::from([])), 377 | ]) 378 | ); 379 | } 380 | } 381 | --------------------------------------------------------------------------------