diff --git a/pikascript-compiler-rust/.gitignore b/pikascript-compiler-rust/.gitignore new file mode 100644 index 000000000..d67b347e9 --- /dev/null +++ b/pikascript-compiler-rust/.gitignore @@ -0,0 +1,2 @@ +/target +/pikascript-api diff --git a/pikascript-compiler-rust/.vscode/launch.json b/pikascript-compiler-rust/.vscode/launch.json new file mode 100644 index 000000000..f04884af9 --- /dev/null +++ b/pikascript-compiler-rust/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/target/debug/rust-msc", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/pikascript-compiler-rust/Arm2D.py b/pikascript-compiler-rust/Arm2D.py new file mode 100644 index 000000000..76e7f6eb6 --- /dev/null +++ b/pikascript-compiler-rust/Arm2D.py @@ -0,0 +1,11 @@ +from PikaObj import * + +class Line(TinyObj): + def new(): + pass + + def delete(): + pass + + def moveTo(x: int, y: int): + pass diff --git a/pikascript-compiler-rust/Cargo.lock b/pikascript-compiler-rust/Cargo.lock new file mode 100644 index 000000000..b05243cad --- /dev/null +++ b/pikascript-compiler-rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust-msc" +version = "0.1.0" diff --git a/pikascript-compiler-rust/Cargo.toml b/pikascript-compiler-rust/Cargo.toml new file mode 100644 index 000000000..5f99377dd --- /dev/null +++ b/pikascript-compiler-rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rust-msc" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] diff --git a/pikascript-compiler-rust/PikaObj.py b/pikascript-compiler-rust/PikaObj.py new file mode 100644 index 000000000..ab97533ba --- /dev/null +++ b/pikascript-compiler-rust/PikaObj.py @@ -0,0 +1,14 @@ +class TinyObj: + pass + + +class BaseObj(TinyObj): + pass + + +def print(val: any): + pass + + +def set(argPath: str, val: any): + pass diff --git a/pikascript-compiler-rust/PikaStdLib.py b/pikascript-compiler-rust/PikaStdLib.py new file mode 100644 index 000000000..9340d1855 --- /dev/null +++ b/pikascript-compiler-rust/PikaStdLib.py @@ -0,0 +1,27 @@ +from PikaObj import * + + +class MemChecker(BaseObj): + def max(): + pass + + def now(): + pass + + def resetMax(): + pass + + +class SysObj(BaseObj): + + def type(argPath: str): + pass + + def ls(objPath: str): + pass + + def remove(argPath: str): + pass + + def new(objPath: str, classPath: str): + pass \ No newline at end of file diff --git a/pikascript-compiler-rust/PyInfo.py b/pikascript-compiler-rust/PyInfo.py new file mode 100644 index 000000000..a8135597f --- /dev/null +++ b/pikascript-compiler-rust/PyInfo.py @@ -0,0 +1,39 @@ +class PyObj(BaseObj): + + def importClass(className: str, fp: str): + pass + + def newObj(objName: str, className: str, fp: str): + pass + + def makeInlcude(fp: str): + pass + + def makeNewObj(fp: str): + pass + + def getInclude() -> str: + pass + + +class PyMethod (SysObj): + def makeMethodFun(fp: str): + pass + + def makeMethodDeclear(fp: str): + pass + + def makeMethodDefine(fp: str): + pass + +class PyClass(SysObj): + obj = PyObj() + PyMethod() + def setSuper(superClassName: str): + pass + + def makeApi(path: str): + pass + + def makeHead(path: str): + pass diff --git a/pikascript-compiler-rust/README.md b/pikascript-compiler-rust/README.md new file mode 100644 index 000000000..1884b79af --- /dev/null +++ b/pikascript-compiler-rust/README.md @@ -0,0 +1,2 @@ +# pikascript-compiler-rust + diff --git a/pikascript-compiler-rust/application.py b/pikascript-compiler-rust/application.py new file mode 100644 index 000000000..1e7b32c62 --- /dev/null +++ b/pikascript-compiler-rust/application.py @@ -0,0 +1,22 @@ +import PyInfo +import Arm2D +import PikaStdLib +from PikaObj import * + +class Compiler(PikaStdLib.SysObj): + obj = PyInfo.PyObj() + PyInfo.PyMethod() + PyInfo.PyClass() + line = Arm2D.Line() + + def build(pythonApiPath: str, outputPath: str) -> int: + pass + + def analyzestr(pythonApiPath: str) -> int: + pass + + def analyzeLine(line: str) -> int: + pass + +class MyRoot(PikaStdLib.SysObj): + compiler = Compiler() diff --git a/pikascript-compiler-rust/init.sh b/pikascript-compiler-rust/init.sh new file mode 100644 index 000000000..2587c3139 --- /dev/null +++ b/pikascript-compiler-rust/init.sh @@ -0,0 +1,2 @@ +rm pikascript-api -rf +mkdir pikascript-api diff --git a/pikascript-compiler-rust/main.py b/pikascript-compiler-rust/main.py new file mode 100644 index 000000000..fbc49d391 --- /dev/null +++ b/pikascript-compiler-rust/main.py @@ -0,0 +1,7 @@ +import Arm2D +import PyInfo + +line = Arm2D.Line() +res = line.on() +line.off() +line.moveTo(20) diff --git a/pikascript-compiler-rust/src/arg_list.rs b/pikascript-compiler-rust/src/arg_list.rs new file mode 100644 index 000000000..7bda6d79b --- /dev/null +++ b/pikascript-compiler-rust/src/arg_list.rs @@ -0,0 +1,96 @@ +use crate::my_string; +use crate::py_arg::PyArg; +use std::collections::BTreeMap; +#[derive(Debug)] +pub struct ArgList { + py_arg_list: String, + list: BTreeMap, +} + +impl ArgList { + pub fn to_string(&self) -> String { + return self.py_arg_list.clone(); + } + pub fn new(py_arg_list: &Option) -> Option { + let py_arg_list = match py_arg_list { + Some(x) => x, + None => return None, + }; + let mut arg_list = ArgList { + py_arg_list: py_arg_list.clone(), + list: BTreeMap::new(), + }; + let py_arg_list = py_arg_list.replace(" ", ""); + let py_arg_list: Vec<&str> = py_arg_list.split(",").collect(); + for arg_define in py_arg_list.iter() { + let arg_name = match my_string::get_first_token(&arg_define.to_string(), ':') { + Some(name) => name, + None => return None, + }; + let type_name = match my_string::get_last_token(&arg_define.to_string(), ':') { + Some(name) => name, + None => return None, + }; + arg_list + .list + .entry(arg_name.clone()) + .or_insert(PyArg::new(&arg_name, &type_name)); + } + return Some(arg_list); + } + pub fn to_c(&self) -> String { + let mut arg_list_in_c = String::from(""); + for (i, (_, py_arg)) in self.list.iter().enumerate() { + let arg_name = py_arg.name(); + let type_name_in_c = py_arg.c_type(); + arg_list_in_c.push_str(&type_name_in_c); + arg_list_in_c.push_str(" "); + arg_list_in_c.push_str(&arg_name); + if i < self.list.len() - 1 { + arg_list_in_c.push_str(", "); + } + } + return arg_list_in_c; + } + pub fn call_arg_list(&self) -> String { + let mut call_arg_list = "".to_string(); + for (i, (_, py_arg)) in self.list.iter().enumerate() { + let arg_name = py_arg.name(); + call_arg_list.push_str(&arg_name); + if i < self.list.len() - 1 { + call_arg_list.push_str(", "); + } + } + return call_arg_list; + } + pub fn get_local_args(&self) -> String { + let mut get_local_args = "".to_string(); + for (_, py_arg) in self.list.iter() { + get_local_args.push_str(&py_arg.get_local_arg()); + } + return get_local_args; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_arg_list() { + let arg_list = + ArgList::new(&Some(String::from("arg1: str, arg2: int, arg3: FILE"))).unwrap(); + let arg_list_in_c = arg_list.to_c(); + let call_arg_list = arg_list.call_arg_list(); + let get_local_args = arg_list.get_local_args(); + assert_eq! {arg_list_in_c,"char * arg1, int arg2, void * arg3"}; + assert_eq! {call_arg_list,"arg1, arg2, arg3"}; + assert_eq! {get_local_args," char * arg1 = args_getStr(args, \"arg1\");\n int arg2 = args_getInt(args, \"arg2\");\n void * arg3 = args_getPtr(args, \"arg3\");\n"}; + } + #[test] + fn test_arg_list_one_arg() { + let arg_list = ArgList::new(&Some(String::from("argtest: str"))).unwrap(); + let arg_list_in_c = arg_list.to_c(); + assert_eq! {arg_list_in_c,"char * argtest"}; + } +} diff --git a/pikascript-compiler-rust/src/class_info.rs b/pikascript-compiler-rust/src/class_info.rs new file mode 100644 index 000000000..30607843d --- /dev/null +++ b/pikascript-compiler-rust/src/class_info.rs @@ -0,0 +1,272 @@ +use crate::import_info::ImportInfo; +use crate::method_info::MethodInfo; +use crate::my_string; +use crate::object_info::ObjectInfo; +use crate::script::Script; +use std::collections::BTreeMap; +#[derive(Debug)] +pub struct ClassInfo { + pub this_class_name: String, + pub super_class_name: String, + pub method_list: BTreeMap, + pub object_list: BTreeMap, + pub import_list: BTreeMap, + pub script_list: Script, +} + +impl ClassInfo { + pub fn add_file_profix(file_name: &String, class_name: &String) -> String { + if file_name == "main" { + return class_name.clone(); + } else if class_name == "BaseObj" || class_name == "TinyObj" { + return class_name.clone(); + } else { + return format!("{}_{}", file_name, class_name); + } + } + + pub fn new(file_name: &String, define: &String) -> Option { + let define = define.strip_prefix("class ").unwrap().to_string(); + let define = define.replace(" ", ""); + let super_class_name = match my_string::cut(&define, '(', ')') { + Some(s) => s, + None => return None, + }; + let super_class_name = match super_class_name.find(".") { + None => ClassInfo::add_file_profix(&file_name, &super_class_name), + Some(x) => super_class_name.replace(".", "_"), + }; + let mut this_calss_name = match my_string::get_first_token(&define, '(') { + Some(s) => s, + None => return None, + }; + this_calss_name = ClassInfo::add_file_profix(&file_name, &this_calss_name); + let new_class_info = ClassInfo { + this_class_name: this_calss_name, + super_class_name: super_class_name, + method_list: BTreeMap::new(), + object_list: BTreeMap::new(), + import_list: BTreeMap::new(), + script_list: Script::new(), + }; + return Some(new_class_info); + } + pub fn push_method(&mut self, method_define: String) { + let method_info = match MethodInfo::new(&self.this_class_name, method_define) { + Some(method) => method, + None => return, + }; + self.method_list + .entry(method_info.name.clone()) + .or_insert(method_info); + } + pub fn push_import(&mut self, import_define: String, file_name: &String) { + let import_info = match ImportInfo::new(&self.this_class_name, import_define, &file_name) { + Some(import) => import, + None => return, + }; + self.import_list + .entry(import_info.import_class_name.clone()) + .or_insert(import_info); + } + pub fn push_object(&mut self, object_define: String, file_name: &String) { + let object_info = match ObjectInfo::new(&self.this_class_name, object_define, &file_name) { + Some(object) => object, + None => return, + }; + self.object_list + .entry(object_info.name.clone()) + .or_insert(object_info); + } + + pub fn include(&self) -> String { + let mut include = String::new(); + include.push_str(&format!("#include \"{}.h\"\n", self.this_class_name)); + include.push_str(&format!("#include \"{}.h\"\n", self.super_class_name)); + for (_, import_info) in self.import_list.iter() { + include.push_str(&format!( + "#include \"{}.h\"\n", + import_info.import_class_name + )); + } + for (_, object_info) in self.object_list.iter() { + include.push_str(&format!( + "#include \"{}.h\"\n", + object_info.import_class_name + )); + } + return include; + } + + pub fn method_api_fn(&self) -> String { + let mut method_impl = String::new(); + for (_, method_info) in self.method_list.iter() { + method_impl.push_str(&method_info.method_fn_impl()); + } + return method_impl; + } + + pub fn script_fn(&self) -> String { + let mut script_fn = String::new(); + script_fn.push_str("PikaObj * pikaScriptInit(){\r\n"); + script_fn.push_str(" PikaObj * pikaMain = newRootObj(\"pikaMain\", New_PikaMain);\r\n"); + script_fn.push_str(&self.script_list.content); + script_fn.push_str(" return pikaMain;\r\n"); + script_fn.push_str("}\r\n\r\n"); + return script_fn; + } + + pub fn new_class_fn(&self) -> String { + let mut new_class_fn = String::new(); + let new_class_fn_head = format!("{}{{\n", self.new_class_fn_name()); + new_class_fn.push_str(&new_class_fn_head); + let derive = format!(" PikaObj *self = New_{}(args);\n", self.super_class_name); + new_class_fn.push_str(&derive); + for (_, import_info) in self.import_list.iter() { + new_class_fn.push_str(&import_info.import_fn()); + } + + for (_, object_info) in self.object_list.iter() { + new_class_fn.push_str(&object_info.new_object_fn()); + } + + for (_, method_info) in self.method_list.iter() { + new_class_fn.push_str(&method_info.get_define()); + } + + new_class_fn.push_str(" return self;\n"); + new_class_fn.push_str("}\n"); + return new_class_fn; + } + + pub fn new_class_fn_name(&self) -> String { + return format!("PikaObj *New_{}(Args *args)", self.this_class_name); + } + + pub fn method_impl_declear(&self) -> String { + let mut method_fn_declear = String::new(); + for (_, method_info) in self.method_list.iter() { + method_fn_declear.push_str(&method_info.method_impl_declear()); + } + return method_fn_declear; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_analize() { + assert_eq!( + ClassInfo::new( + &String::from("Pkg"), + &String::from("class Test(SuperTest):") + ) + .unwrap() + .this_class_name, + "Pkg_Test" + ); + assert_eq!( + ClassInfo::new( + &String::from("Pkg"), + &String::from("class Test(SuperTest):") + ) + .unwrap() + .super_class_name, + "Pkg_SuperTest" + ); + } + #[test] + fn test_push_method() { + let mut class_info = ClassInfo::new( + &String::from("Pkg"), + &String::from("class Test(SuperTest):"), + ) + .unwrap(); + class_info.push_method(String::from("def test(data: str)-> str:")); + assert_eq!( + class_info.method_list.get("test").unwrap().class_name, + "Pkg_Test" + ); + assert_eq!(class_info.method_list.get("test").unwrap().name, "test"); + assert_eq!( + class_info + .method_list + .get("test") + .as_ref() + .unwrap() + .return_type + .as_ref() + .unwrap() + .to_string(), + "str" + ); + assert_eq!( + class_info + .method_list + .get("test") + .as_ref() + .unwrap() + .arg_list + .as_ref() + .unwrap() + .to_string(), + "data:str" + ); + } + #[test] + fn test_push_object() { + let mut class_info = ClassInfo::new( + &String::from("Pkg"), + &String::from("class Test(SuperTest):"), + ) + .unwrap(); + class_info.push_object(String::from("testObj = TestObj()"), &"Pkg".to_string()); + assert_eq!( + class_info.object_list.get("testObj").unwrap().class_name, + "Pkg_Test" + ); + assert_eq!( + class_info.object_list.get("testObj").unwrap().name, + "testObj" + ); + assert_eq!( + class_info + .object_list + .get("testObj") + .unwrap() + .import_class_name, + "Pkg_TestObj" + ); + assert_eq!( + class_info.object_list.get("testObj").unwrap().name, + "testObj" + ); + } + #[test] + fn test_push_import() { + let mut class_info = ClassInfo::new( + &String::from("Pkg"), + &String::from("class Test(SuperTest):"), + ) + .unwrap(); + class_info.push_import(String::from("TestObj()"), &"Pkg".to_string()); + assert_eq!( + class_info + .import_list + .get("Pkg_TestObj") + .unwrap() + .class_name, + "Pkg_Test" + ); + assert_eq!( + class_info + .import_list + .get("Pkg_TestObj") + .unwrap() + .import_class_name, + "Pkg_TestObj" + ); + } +} diff --git a/pikascript-compiler-rust/src/compiler.rs b/pikascript-compiler-rust/src/compiler.rs new file mode 100644 index 000000000..76e189d7d --- /dev/null +++ b/pikascript-compiler-rust/src/compiler.rs @@ -0,0 +1,172 @@ +use crate::class_info::ClassInfo; +use crate::script::Script; +use std::collections::BTreeMap; +use std::fs::File; +use std::io::prelude::*; +#[derive(Debug)] +pub struct Compiler { + pub dist_path: String, + pub source_path: String, + pub class_list: BTreeMap, + pub class_now_name: Option, +} + +impl Compiler { + pub fn new(source_path: String, dist_path: String) -> Compiler { + let compiler = Compiler { + dist_path: dist_path.clone(), + source_path: source_path.clone(), + class_now_name: None, + class_list: BTreeMap::new(), + }; + return compiler; + } + pub fn analyze_main_line(mut compiler: Compiler, line: String) -> Compiler { + let file_name = "main".to_string(); + let class_name = "PikaMain".to_string(); + let class_now = match compiler.class_list.get_mut(&"PikaMain".to_string()) { + Some(class_now) => class_now, + None => compiler.class_list.entry(class_name.clone()).or_insert( + ClassInfo::new( + &file_name, + &"class PikaMain(PikaStdLib.SysObj):".to_string(), + ) + .unwrap(), + ), + }; + compiler.class_now_name = Some(class_name.clone()); + + if line.starts_with("def ") { + class_now.push_method(line); + return compiler; + } + if line.contains("(") && line.contains(")") && line.contains("=") { + if Script::assert(class_now, &line) { + class_now.script_list.add(&line); + return compiler; + } + class_now.push_object(line, &file_name); + return compiler; + } + if line.contains("(") && line.contains(")") { + class_now.script_list.add(&line); + } + return compiler; + } + + pub fn analyze_file(mut compiler: Compiler, file_name: String) -> Compiler { + println!("analyzing file: {}{}.py", compiler.source_path, file_name); + let mut file = File::open(format!("{}{}.py", compiler.source_path, file_name)).unwrap(); + let mut file_str = String::new(); + file.read_to_string(&mut file_str).unwrap(); + let lines: Vec<&str> = file_str.split('\n').collect(); + /* analyze each line of pikascript-api.py */ + for line in lines.iter() { + compiler = Compiler::analyze_line(compiler, line.to_string(), &file_name); + } + return compiler; + } + + pub fn analyze_line(mut compiler: Compiler, line: String, file_name: &String) -> Compiler { + let line = line.replace("\r", ""); + if line.starts_with("import ") { + let tokens: Vec<&str> = line.split(" ").collect(); + let file = tokens[1]; + return Compiler::analyze_file(compiler, file.to_string()); + } + + if line.starts_with("#") { + return compiler; + } + + if line.starts_with("class") { + let class_now = match ClassInfo::new(&file_name, &line) { + Some(s) => s, + None => return compiler, + }; + let class_name = class_now.this_class_name.clone(); + compiler + .class_list + .entry(class_name.clone()) + .or_insert(class_now); + compiler.class_now_name = Some(class_name.clone()); + return compiler; + } + if line.starts_with(" def ") { + let line = line.strip_prefix(" ").unwrap().to_string(); + let class_now = compiler + .class_list + .get_mut(&compiler.class_now_name.clone().unwrap()) + .unwrap(); + class_now.push_method(line); + return compiler; + } + if line.starts_with(" ") + && line.contains("(") + && line.contains(")") + && line.contains("=") + { + let line = line.strip_prefix(" ").unwrap().to_string(); + let class_now = compiler + .class_list + .get_mut(&compiler.class_now_name.clone().unwrap()) + .unwrap(); + class_now.push_object(line, &file_name); + return compiler; + } + if line.starts_with(" ") && line.contains("(") && line.contains(")") { + let line = line.strip_prefix(" ").unwrap().to_string(); + let class_now = compiler + .class_list + .get_mut(&compiler.class_now_name.clone().unwrap()) + .unwrap(); + class_now.push_import(line, &file_name); + return compiler; + } + + if file_name == "main" { + return Compiler::analyze_main_line(compiler, line); + } + + return compiler; + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_analyze() { + let compiler = Compiler::new(String::from(""), String::from("")); + let compiler = Compiler::analyze_line( + compiler, + String::from("class Test(SuperTest):"), + &"Pkg".to_string(), + ); + let compiler = + Compiler::analyze_line(compiler, String::from(" def test()"), &"Pkg".to_string()); + let compiler = Compiler::analyze_line( + compiler, + String::from(" testObj = TestObj()"), + &"Pkg".to_string(), + ); + let compiler = Compiler::analyze_line( + compiler, + String::from(" TestImport()"), + &"Pkg".to_string(), + ); + + let class_info = compiler.class_list.get("Pkg_Test").unwrap(); + let method_info = class_info.method_list.get("test").unwrap(); + let object_info = class_info.object_list.get("testObj").unwrap(); + let import_info = class_info.import_list.get("Pkg_TestImport").unwrap(); + assert_eq!(class_info.this_class_name, "Pkg_Test"); + assert_eq!(class_info.super_class_name, "Pkg_SuperTest"); + assert_eq!(method_info.name, "test"); + assert_eq!(method_info.class_name, "Pkg_Test"); + assert_eq!(object_info.name, "testObj"); + assert_eq!(object_info.class_name, "Pkg_Test"); + assert_eq!(import_info.class_name, "Pkg_Test"); + assert_eq!(import_info.import_class_name, "Pkg_TestImport"); + } +} diff --git a/pikascript-compiler-rust/src/import_info.rs b/pikascript-compiler-rust/src/import_info.rs new file mode 100644 index 000000000..0afc124fd --- /dev/null +++ b/pikascript-compiler-rust/src/import_info.rs @@ -0,0 +1,65 @@ +use crate::my_string; +use crate::class_info; +#[derive(Debug)] +pub struct ImportInfo { + pub class_name: String, + pub import_class_name: String, +} + +impl ImportInfo { + pub fn new( + class_name: &String, + input_define: String, + file_name: &String, + ) -> Option { + let define = input_define.replace(" ", ""); + let mut import_class_name = match my_string::get_first_token(&define, '(') { + Some(token) => token, + None => return None, + }; + import_class_name = match import_class_name.find(".") { + None => class_info::ClassInfo::add_file_profix(&file_name, &import_class_name), + Some(x) => import_class_name.replace(".", "_"), + }; + import_class_name = import_class_name.replace(".", "_"); + return Some(ImportInfo { + class_name: class_name.clone(), + import_class_name: import_class_name, + }); + } + pub fn import_fn(&self) -> String { + return format!( + " obj_import(self, \"{}\", New_{});\n", + self.import_class_name, self.import_class_name + ); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_import_info() { + assert_eq!( + ImportInfo::new( + &String::from("Test"), + String::from("ImportTest()"), + &"Pkg".to_string() + ) + .unwrap() + .import_class_name, + String::from("Pkg_ImportTest") + ); + assert_eq!( + ImportInfo::new( + &String::from("Test"), + String::from("ImportTest()"), + &"Pkg".to_string() + ) + .unwrap() + .class_name, + String::from("Test") + ); + } +} diff --git a/pikascript-compiler-rust/src/main.rs b/pikascript-compiler-rust/src/main.rs new file mode 100644 index 000000000..f94e9c61d --- /dev/null +++ b/pikascript-compiler-rust/src/main.rs @@ -0,0 +1,106 @@ +mod arg_list; +mod class_info; +mod compiler; +mod import_info; +mod method_info; +mod my_string; +mod object_info; +mod py_arg; +mod py_type; +mod script; +use compiler::*; +use std::fs::File; +use std::io::prelude::*; + +fn main() { + let mut compiler = Compiler::new(String::from(""), String::from("pikascript-api/")); + compiler = Compiler::analyze_file(compiler, String::from("main")); + /* write to compiler-info about the info */ + let mut compiler_info_file = + File::create(format!("{}compiler-info.txt", compiler.dist_path)).unwrap(); + let compiler_info = format!("{:?}", compiler); + compiler_info_file.write(compiler_info.as_bytes()).unwrap(); + /* make the api.c file for each python class */ + for (_, class_info) in compiler.class_list.iter() { + let api_file_path = format!("{}{}-api.c", compiler.dist_path, class_info.this_class_name); + let mut f = File::create(api_file_path).unwrap(); + f.write("/* ******************************** */\n".as_bytes()) + .unwrap(); + f.write("/* Warning! Don't modify this file! */\n".as_bytes()) + .unwrap(); + f.write("/* ******************************** */\n".as_bytes()) + .unwrap(); + f.write(class_info.include().as_bytes()).unwrap(); + f.write("#include \n".as_bytes()).unwrap(); + f.write("#include \n".as_bytes()).unwrap(); + f.write("#include \"BaseObj.h\"\n".as_bytes()).unwrap(); + f.write("\n".as_bytes()).unwrap(); + f.write(class_info.method_api_fn().as_bytes()).unwrap(); + f.write(class_info.new_class_fn().as_bytes()).unwrap(); + } + /* make the .h file for each python class */ + for (_, class_info) in compiler.class_list.iter() { + let api_file_path = format!("{}{}.h", compiler.dist_path, class_info.this_class_name); + let mut f = File::create(api_file_path).unwrap(); + f.write("/* ******************************** */\n".as_bytes()) + .unwrap(); + f.write("/* Warning! Don't modify this file! */\n".as_bytes()) + .unwrap(); + f.write("/* ******************************** */\n".as_bytes()) + .unwrap(); + f.write(format!("#ifndef __{}__H\n", class_info.this_class_name).as_bytes()) + .unwrap(); + f.write(format!("#define __{}__H\n", class_info.this_class_name).as_bytes()) + .unwrap(); + f.write("#include \n".as_bytes()).unwrap(); + f.write("#include \n".as_bytes()).unwrap(); + f.write("#include \"PikaObj.h\"\n".as_bytes()).unwrap(); + f.write("\n".as_bytes()).unwrap(); + let new_class_fn_declear = format!("{};\n", class_info.new_class_fn_name()); + f.write(new_class_fn_declear.as_bytes()).unwrap(); + f.write("\n".as_bytes()).unwrap(); + f.write(class_info.method_impl_declear().as_bytes()) + .unwrap(); + f.write("\n".as_bytes()).unwrap(); + f.write("#endif\n".as_bytes()).unwrap(); + } + /* make the pikascript.c */ + let api_file_path = format!("{}pikaScript.c", compiler.dist_path); + let mut f = File::create(api_file_path).unwrap(); + f.write("/* ******************************** */\n".as_bytes()) + .unwrap(); + f.write("/* Warning! Don't modify this file! */\n".as_bytes()) + .unwrap(); + f.write("/* ******************************** */\n".as_bytes()) + .unwrap(); + f.write("#include \"PikaMain.h\"\n".as_bytes()).unwrap(); + f.write("#include \n".as_bytes()).unwrap(); + f.write("#include \n".as_bytes()).unwrap(); + f.write("\n".as_bytes()).unwrap(); + let pika_main = compiler + .class_list + .get_mut(&"PikaMain".to_string()) + .unwrap(); + f.write(pika_main.script_fn().as_bytes()).unwrap(); + /* make the pikascript.h */ + let api_file_path = format!("{}pikaScript.h", compiler.dist_path); + let mut f = File::create(api_file_path).unwrap(); + f.write("/* ******************************** */\n".as_bytes()) + .unwrap(); + f.write("/* Warning! Don't modify this file! */\n".as_bytes()) + .unwrap(); + f.write("/* ******************************** */\n".as_bytes()) + .unwrap(); + f.write(format!("#ifndef __{}__H\n", "pikaScript").as_bytes()) + .unwrap(); + f.write(format!("#define __{}__H\n", "pikaScript").as_bytes()) + .unwrap(); + f.write("#include \n".as_bytes()).unwrap(); + f.write("#include \n".as_bytes()).unwrap(); + f.write("#include \"PikaObj.h\"\n".as_bytes()).unwrap(); + f.write("#include \"PikaMain.h\"\n".as_bytes()).unwrap(); + f.write("\n".as_bytes()).unwrap(); + f.write("PikaObj * pikaScriptInit();\n".as_bytes()).unwrap(); + f.write("\n".as_bytes()).unwrap(); + f.write("#endif\n".as_bytes()).unwrap(); +} diff --git a/pikascript-compiler-rust/src/method_info.rs b/pikascript-compiler-rust/src/method_info.rs new file mode 100644 index 000000000..3b85e3f24 --- /dev/null +++ b/pikascript-compiler-rust/src/method_info.rs @@ -0,0 +1,237 @@ +use crate::arg_list::ArgList; +use crate::my_string; +use crate::py_type::PyType; + +#[derive(Debug)] +pub struct MethodInfo { + pub class_name: String, + pub name: String, + pub arg_list: Option, + pub return_type: Option, +} + +impl MethodInfo { + pub fn new(class_name: &String, input_define: String) -> Option { + let define = match input_define.strip_prefix("def ") { + Some(define) => define.to_string(), + None => return None, + }; + let define = define.replace(" ", ""); + let name = match my_string::get_first_token(&define, '(') { + Some(token) => token, + None => return None, + }; + let arg_list = my_string::cut(&define, '(', ')'); + let return_type = match my_string::cut(&define, '>', ':') { + Some(py_type) => Some(PyType::new(&py_type)), + None => None, + }; + let method_info = MethodInfo { + name: name, + arg_list: ArgList::new(&arg_list), + return_type: return_type, + class_name: class_name.clone(), + }; + return Some(method_info); + } + pub fn get_define(&self) -> String { + let return_token = match &self.return_type { + Some(s) => format!("->{}", s.to_string()), + None => String::from(""), + }; + let arg_list = match &self.arg_list { + Some(t) => t.to_string(), + None => String::from(""), + }; + let define = format!( + " class_defineMethod(self, \"{}({}){}\", {}_{}Method);\n", + self.name, arg_list, return_token, self.class_name, self.name + ); + return define; + } + pub fn method_api_name(&self) -> String { + return format!( + "void {}_{}Method(PikaObj *self, Args *args)", + self.class_name, self.name + ); + } + pub fn method_impl_declear(&self) -> String { + let return_type_in_c = match self.return_type.as_ref() { + Some(x) => x.to_c_type(), + None => String::from("void"), + }; + let arg_list_in_c = match self.arg_list.as_ref() { + Some(x) => format!(", {}", x.to_c()), + None => String::from(""), + }; + return format!( + "{} {}_{}(PikaObj *self{});\n", + return_type_in_c, self.class_name, self.name, arg_list_in_c, + ); + } + pub fn method_fn_impl(&self) -> String { + let mut method_fn_impl = "".to_string(); + let method_fn_name = format!("{}{{\n", self.method_api_name()); + let get_local_args = match &self.arg_list { + Some(x) => x.get_local_args(), + None => "".to_string(), + }; + let return_impl = match &self.return_type { + Some(x) => format!(" {}(args, res);\n", x.return_fn()), + None => "".to_string(), + }; + let return_type_in_c = match &self.return_type { + Some(x) => format!("{} res = ", x.to_c_type()), + None => "".to_string(), + }; + let call_arg_list = match &self.arg_list { + Some(x) => format!(", {}", x.call_arg_list()), + None => "".to_string(), + }; + let call_method = format!( + " {}{}_{}(self{});\n", + return_type_in_c, self.class_name, self.name, call_arg_list + ); + method_fn_impl.push_str(&method_fn_name); + method_fn_impl.push_str(&get_local_args); + method_fn_impl.push_str(&call_method); + method_fn_impl.push_str(&return_impl); + method_fn_impl.push_str("}\n\n"); + return method_fn_impl; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_local_declear() { + let method_info = MethodInfo::new( + &String::from("Test"), + String::from("def test(test:str, test2:int)->str:"), + ); + let define = method_info.as_ref().unwrap().method_impl_declear(); + let method_fn_impl = method_info.as_ref().unwrap().method_fn_impl(); + assert_eq!(define, "char * Test_test(PikaObj *self, char * test, int test2);\n"); + assert_eq!( + method_fn_impl, + "void Test_testMethod(PikaObj *self, Args *args){\n char * test = args_getStr(args, \"test\");\n int test2 = args_getInt(args, \"test2\");\n char * res = Test_test(self, test, test2);\n method_returnStr(args, res);\n}\n\n" + ); + } + + #[test] + fn test_analize() { + assert_eq!( + MethodInfo::new( + &String::from("Test"), + String::from("def test(test:str)->str:") + ) + .unwrap() + .name, + String::from("test") + ); + assert_eq!( + MethodInfo::new( + &String::from("Test"), + String::from("def test(test:str)->str:") + ) + .unwrap() + .return_type + .unwrap() + .to_string(), + String::from("str") + ); + assert_eq!( + MethodInfo::new( + &String::from("Test"), + String::from("def test(test:str)->str:") + ) + .unwrap() + .arg_list + .unwrap() + .to_string(), + String::from("test:str") + ); + assert_eq!( + MethodInfo::new( + &String::from("Test"), + String::from("def test(test: str) ->str:") + ) + .unwrap() + .name, + String::from("test") + ); + assert_eq!( + MethodInfo::new( + &String::from("Test"), + String::from("def test(test: str) ->str:") + ) + .unwrap() + .return_type + .unwrap() + .to_string(), + String::from("str") + ); + assert_eq!( + MethodInfo::new( + &String::from("Test"), + String::from("def test(test: str) ->str:") + ) + .unwrap() + .arg_list + .unwrap() + .to_string(), + String::from("test:str") + ); + assert_eq!( + MethodInfo::new( + &String::from("Test"), + String::from("def test(test: str, test2: int) ->str:") + ) + .unwrap() + .arg_list + .unwrap() + .to_string(), + String::from("test:str,test2:int") + ); + } + #[test] + fn test_get_define() { + let method_info = MethodInfo::new( + &String::from("Test"), + String::from("def test(test:str, test2:int)->str:"), + ); + let define = method_info.unwrap().get_define(); + assert_eq!(define, String::from(" class_defineMethod(self, \"test(test:str,test2:int)->str\", Test_testMethod);\n")); + } + #[test] + fn test_get_define_no_return() { + let method_info = MethodInfo::new( + &String::from("Test"), + String::from("def test(test:str, test2:int):"), + ); + let define = method_info.unwrap().get_define(); + assert_eq!( + define, + String::from( + " class_defineMethod(self, \"test(test:str,test2:int)\", Test_testMethod);\n" + ) + ); + } + #[test] + fn test_get_define_no_return_no_arg_list() { + let method_info = + MethodInfo::new(&String::from("Test"), String::from("def test():")).unwrap(); + let define = method_info.get_define(); + let method_fn_name = method_info.method_api_name(); + assert_eq!( + define, + String::from(" class_defineMethod(self, \"test()\", Test_testMethod);\n") + ); + assert_eq!( + method_fn_name, + String::from("void Test_testMethod(PikaObj *self, Args *args)") + ); + } +} diff --git a/pikascript-compiler-rust/src/my_string.rs b/pikascript-compiler-rust/src/my_string.rs new file mode 100644 index 000000000..9cce2f90a --- /dev/null +++ b/pikascript-compiler-rust/src/my_string.rs @@ -0,0 +1,53 @@ +pub fn cut(string: &String, start_char: char, end_char: char) -> Option { + let start_index = match string.find(start_char) { + Some(i) => i, + None => return None, + }; + let end_index = match string.rfind(end_char) { + Some(i) => i, + None => return None, + }; + if start_index + 1 == end_index { + return None; + } + return Some(string[start_index + 1..end_index].to_string()); +} + +pub fn get_first_token(string: &String, end_char: char) -> Option { + return match string.split_once(end_char) { + Some(s) => Some(s.0.to_string()), + None => None, + }; +} + +pub fn get_last_token(string: &String, end_char: char) -> Option { + return match string.rsplit_once(end_char) { + Some(s) => Some(s.1.to_string()), + None => None, + }; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_my_string() { + assert_eq!( + cut(&String::from("(test)"), '(', ')'), + Some(String::from("test")) + ); + assert_eq!( + get_first_token(&String::from("test,"), ',',), + Some(String::from("test")) + ); + assert_eq!( + get_first_token(&String::from("test("), '(',), + Some(String::from("test")) + ); + assert_eq!( + get_last_token(&String::from("test=Test"), '=',), + Some(String::from("Test")) + ); + } +} diff --git a/pikascript-compiler-rust/src/object_info.rs b/pikascript-compiler-rust/src/object_info.rs new file mode 100644 index 000000000..eb5153efc --- /dev/null +++ b/pikascript-compiler-rust/src/object_info.rs @@ -0,0 +1,88 @@ +use crate::class_info; +use crate::my_string; +#[derive(Debug)] +pub struct ObjectInfo { + pub class_name: String, + pub name: String, + pub import_class_name: String, +} + +impl ObjectInfo { + pub fn new( + class_name: &String, + input_define: String, + file_name: &String, + ) -> Option { + let define = input_define.replace(" ", ""); + let name = match my_string::get_first_token(&define, '=') { + Some(token) => token, + None => return None, + }; + let mut import_class_name = match my_string::cut(&define, '=', '(') { + Some(token) => token, + None => return None, + }; + import_class_name = match import_class_name.find(".") { + None => class_info::ClassInfo::add_file_profix(&file_name, &import_class_name), + Some(x) => import_class_name.replace(".", "_"), + }; + return Some(ObjectInfo { + class_name: class_name.clone(), + name: name, + import_class_name: import_class_name, + }); + } + pub fn new_object_fn(&self) -> String { + let mut new_object_fn = String::new(); + let import_fn = format!( + " obj_import(self, \"{}\", New_{});\n", + self.import_class_name, self.import_class_name + ); + new_object_fn.push_str(&import_fn); + let new_fn = format!( + " obj_newObj(self, \"{}\", \"{}\");\n", + self.name, self.import_class_name + ); + new_object_fn.push_str(&new_fn); + return new_object_fn; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_object_info() { + assert_eq!( + ObjectInfo::new( + &String::from("Test"), + String::from("test=ImportTest()"), + &"Pkg".to_string() + ) + .unwrap() + .import_class_name, + String::from("Pkg_ImportTest") + ); + assert_eq!( + ObjectInfo::new( + &String::from("Test"), + String::from("test=ImportTest()"), + &"Pkg".to_string() + ) + .unwrap() + .name, + String::from("test") + ); + assert_eq!( + ObjectInfo::new( + &String::from("Test"), + String::from("test=ImportTest()"), + &"Pkg".to_string() + ) + .unwrap() + .class_name, + String::from("Test") + ); + } +} diff --git a/pikascript-compiler-rust/src/py_arg.rs b/pikascript-compiler-rust/src/py_arg.rs new file mode 100644 index 000000000..e4f81c79c --- /dev/null +++ b/pikascript-compiler-rust/src/py_arg.rs @@ -0,0 +1,46 @@ +use crate::py_type::PyType; + +#[derive(Debug)] +pub struct PyArg { + py_type: PyType, + name: String, +} + +impl PyArg { + pub fn new(name: &String, type_name: &String) -> PyArg { + let py_arg = PyArg { + name: name.clone(), + py_type: PyType::new(type_name), + }; + return py_arg; + } + pub fn name(&self) -> String { + return self.name.clone(); + } + pub fn c_type(&self) -> String { + return self.py_type.to_c_type(); + } + pub fn get_local_arg(&self) -> String { + return format!( + " {} {} = {}(args, \"{}\");\n", + self.c_type(), + self.name(), + self.py_type.get_fn(), + self.name() + ); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_arg_to_local() { + let arg = PyArg::new(&"arg".to_string(), &"str".to_string()); + assert_eq!( + arg.get_local_arg(), + " char * arg = args_getStr(args, \"arg\");\n" + ); + } +} diff --git a/pikascript-compiler-rust/src/py_type.rs b/pikascript-compiler-rust/src/py_type.rs new file mode 100644 index 000000000..75ab73b69 --- /dev/null +++ b/pikascript-compiler-rust/src/py_type.rs @@ -0,0 +1,78 @@ +#[derive(Debug)] +pub struct PyType { + type_name: String, +} + +impl PyType { + pub fn to_c_type(&self) -> String { + if self.type_name == "int" { + return "int".to_string(); + } + if self.type_name == "float" { + return "float".to_string(); + } + if self.type_name == "pointer" { + return "void *".to_string(); + } + if self.type_name == "str" { + return "char *".to_string(); + } + if self.type_name == "" { + return "void".to_string(); + } + return "void *".to_string(); + } + pub fn to_string(&self) -> String { + return self.type_name.clone(); + } + pub fn new(type_name: &String) -> PyType { + return PyType { + type_name: type_name.clone(), + }; + } + pub fn return_fn(&self) -> String { + if self.type_name == "int" { + return "method_returnInt".to_string(); + } + if self.type_name == "float" { + return "method_returnFloat".to_string(); + } + if self.type_name == "pointer" { + return "method_returnPtr".to_string(); + } + if self.type_name == "str" { + return "method_returnStr".to_string(); + } + return "method_returnPtr".to_string(); + } + pub fn set_fn(&self) -> String { + if self.type_name == "int" { + return "args_setInt".to_string(); + } + if self.type_name == "float" { + return "args_setFloat".to_string(); + } + if self.type_name == "pointer" { + return "args_setPtr".to_string(); + } + if self.type_name == "str" { + return "args_setStr".to_string(); + } + return "args_setPtr".to_string(); + } + pub fn get_fn(&self) -> String { + if self.type_name == "int" { + return "args_getInt".to_string(); + } + if self.type_name == "float" { + return "args_getFloat".to_string(); + } + if self.type_name == "pointer" { + return "args_getPtr".to_string(); + } + if self.type_name == "str" { + return "args_getStr".to_string(); + } + return "args_getPtr".to_string(); + } +} diff --git a/pikascript-compiler-rust/src/script.rs b/pikascript-compiler-rust/src/script.rs new file mode 100644 index 000000000..42a8051a8 --- /dev/null +++ b/pikascript-compiler-rust/src/script.rs @@ -0,0 +1,35 @@ +use crate::class_info::ClassInfo; +use crate::my_string; +#[derive(Debug)] +pub struct Script { + pub content: String, +} + +impl Script { + pub fn new() -> Script { + return Script { + content: String::new(), + }; + } + pub fn add(&mut self, content: &String) { + self.content.push_str(&Script::obj_run(content)); + } + pub fn assert(class_info: &ClassInfo, content: &String) -> bool { + let cmd = my_string::cut(content, '=', '(').unwrap(); + let cmd = cmd.replace(" ", ""); + let called_first_object = match my_string::get_first_token(&cmd, '.') { + Some(token) => token, + None => cmd, + }; + for (_, obj_info) in class_info.object_list.iter() { + let obj_name = obj_info.name.clone(); + if called_first_object == obj_name { + return true; + } + } + return false; + } + pub fn obj_run(cmd: &String) -> String { + return format!(" obj_run(pikaMain, \"{}\");\r\n", cmd); + } +}