Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lang/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ Cargo.lock
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
#.idea/

# Modu
.modu/

# Other
tmp
4 changes: 4 additions & 0 deletions lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ bat = "0.24.0"
chrono = "0.4.39"
logos = "0.15.0"
rand = "0.8.5"
reqwest = { version = "0.12.11", features = ["blocking", "json"] }
rouille = "3.6.2"
serde_json = "1.0.134"
toml = "0.8.19"
zip = "2.2.2"
1 change: 1 addition & 0 deletions lang/examples/library/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.test
8 changes: 8 additions & 0 deletions lang/examples/library/lib.modu
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn hello() {
print("Hello, world!")
}

fn new_feature(a) {
print("yo this is 2.0.0!!!! new features (rea)")
return a;
}
4 changes: 4 additions & 0 deletions lang/examples/library/project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[package]
name = "test"
version = "2.0.7"
description = "A package im using to test the package system, this is not a package for units tests or shit"
20 changes: 20 additions & 0 deletions lang/examples/library/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# cool package
> very rea

this has cool featureas like
1. potatoes
2. cookies
3. cheese

install with
```bash
modu install test
```

very cool right

**yo**
*yo*
__yo__
_yo_
~~yo~~
7 changes: 7 additions & 0 deletions lang/examples/library/tests/equals.modu
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn equals(a,b) {
if a == b {
return true;
}

return false;
}
26 changes: 25 additions & 1 deletion lang/examples/nesting.modu
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ fn example(a) {
print("HELLO, WORLD");
}
}

if a != 3 {
print("Goodbye, World");

if a - 1 - -1 == 3 {
print("GOODBYE, WORLD");
}
}
}

fn test_nesting(a, b, c) {
Expand All @@ -14,16 +22,32 @@ fn test_nesting(a, b, c) {

if c == 3 {
example(c);

if a == 2 {
example(5);
}

if a != 2 {
example(a);
}
}
}

if a != b {
print("a is not equal to b");

if c == 3 {
if a == 1 {
example(c);
}
}

if a != c {
print("a is not equal to c");

if b == 3 {
example(b);
}
}
}

test_nesting(1, 1, 3)
6 changes: 6 additions & 0 deletions lang/examples/using_packages/main.modu
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import "test" as test
import "basic_loops" as loops

print(test.new_feature("abc"))

loops.loop(print, 0, 5);
7 changes: 7 additions & 0 deletions lang/examples/using_packages/project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[dependencies]
basic_loops = "1.0.1"
test = "2.0.7"

[package]
name = "using_packages"
version = "1.0.0"
2 changes: 2 additions & 0 deletions lang/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ pub enum AST {

Rparen,

RBracket,

Comma,

Dot,
Expand Down
85 changes: 85 additions & 0 deletions lang/src/cli/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
pub fn init() {
use std::io::Write;

let mut package_name = String::new();
let mut package_version = String::new();

let mut is_library = String::new();

let mut file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.append(false)
.open("project.toml")
.unwrap();

if file.metadata().unwrap().len() > 0 {
println!("Project already initialized");
print!("Overwrite? (y/N) ");
std::io::stdout().flush().unwrap();

let mut overwrite = String::new();

std::io::stdin().read_line(&mut overwrite).unwrap();

if overwrite.trim() == "y" {
file.set_len(0).unwrap();
} else {
println!("Aborted");
return;
}
}

print!("Enter package name: ");
std::io::stdout().flush().unwrap();
std::io::stdin().read_line(&mut package_name).unwrap();

package_name = package_name.trim().to_string();

if package_name.is_empty() {
println!("Package name cannot be empty");
return;
}

if !package_name.chars().all(|c| c.is_alphanumeric() || c == '_') {
println!("Package name can only contain alphanumeric characters and underscores");
return;
}

print!("Enter package version: ");
std::io::stdout().flush().unwrap();
std::io::stdin().read_line(&mut package_version).unwrap();

if package_version.trim().is_empty() {
println!("Package version cannot be empty");
return;
}

print!("Is this a library? (y/N) ");
std::io::stdout().flush().unwrap();
std::io::stdin().read_line(&mut is_library).unwrap();

let package_name = package_name.trim();
let package_version = package_version.trim();

file.write_all(format!("[package]\nname = \"{}\"\nversion = \"{}\"", package_name, package_version).as_bytes()).unwrap();

let mut file_name = "main.modu";

if is_library.trim() == "y" {
file_name = "lib.modu";
}

let mut main_file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.append(false)
.open(file_name)
.unwrap();

if is_library.trim() == "y" {
main_file.write_all(b"fn hello() {\n print(\"Hello, world!\")\n}").unwrap();
} else {
main_file.write_all("print(\"Hello, world!\")".as_bytes()).unwrap();
}
}
141 changes: 141 additions & 0 deletions lang/src/cli/install.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use std::io::{Read, Write};

use toml;

fn install_package(name: &str, version: &str) -> Result<serde_json::Value, String> {
let mut client = reqwest::blocking::Client::new();

let response = client.get(&format!("https://modu-packages.vercel.app/api/v1/packages/{}/{}?isDownload=true", name, version)).send().unwrap();

if response.status().as_u16() != 200 {
let text = response.text().unwrap();

println!("Error: {}", text);

return Err(text);
}

let package = response.json::<serde_json::Value>().unwrap();

println!("Installing package {}@{}", name, package["version"].as_str().unwrap());
println!("> {}", match package["description"].as_str() {
Some(description) => description,
None => "No description"
});

let zip = client.get(package["zipUrl"].as_str().unwrap()).send().unwrap().bytes().unwrap();

let mut archive = zip::ZipArchive::new(std::io::Cursor::new(zip)).unwrap();

if std::fs::exists(".modu/packages/".to_string() + name).unwrap() {
std::fs::remove_dir_all(".modu/packages/".to_string() + name).unwrap();
}

std::fs::create_dir_all(".modu/packages/".to_string() + name).unwrap();

for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let path = ".modu/packages/".to_string() + name + "/" + file.name();

if file.name().contains("/") || file.name().contains("\\") {
if file.name().contains("/") {
let mut path = path.split("/").collect::<Vec<&str>>();
path.pop();

std::fs::create_dir_all(path.join("/")).unwrap();
} else {
let mut path = path.split("\\").collect::<Vec<&str>>();
path.pop();

std::fs::create_dir_all(path.join("\\")).unwrap();
}

#[cfg(windows)]
let path = (".modu/packages/".to_string() + name + "/" + file.name()).replace("/", "\\");

#[cfg(not(windows))]
let path = (".modu/packages/".to_string() + name + "/" + file.name()).replace("\\", "/");

let mut out = std::fs::File::create(path).unwrap();
std::io::copy(&mut file, &mut out).unwrap();

} else {
#[cfg(windows)]
let path = path.replace("/", "\\");

#[cfg(not(windows))]
let path = path.replace("\\", "/");

let mut out = std::fs::File::create(path).unwrap();
std::io::copy(&mut file, &mut out).unwrap();
}
}

Ok(package)
}

pub fn install() {
let mut content = String::new();
let file = std::fs::File::open("project.toml");

if file.is_err() {
println!("No project.toml found. Run `modu init` to create a new project");
return;
}

file.unwrap().read_to_string(&mut content).unwrap();

let args = std::env::args().collect::<Vec<String>>();


let toml = toml::from_str::<toml::Value>(&content).unwrap();
let mut toml = toml.as_table().unwrap().clone();

let dependencies = match toml.get_mut("dependencies") {
Some(dependencies) => dependencies.as_table_mut().unwrap(),

None => {
toml.insert("dependencies".to_string(), toml::Value::Table(toml::value::Table::new()));
toml.get_mut("dependencies").unwrap().as_table_mut().unwrap()
}
};

if args.len() < 3 {
for (name, version) in dependencies.iter() {
let package = match install_package(name, version.as_str().unwrap()) {
Ok(package) => {
println!("Package {} installed\n", name);
package
},
Err(_) => {
println!("Failed to install package {}", name);
return;
}
};
}

return;
}

let name = &(args[2].clone().split("@").collect::<Vec<&str>>()[0].to_string());

let version = match args[2].clone().split("@").collect::<Vec<&str>>().len() {
1 => "latest".to_string(),
_ => args[2].clone().split("@").collect::<Vec<&str>>()[1].to_string()
};

let package = match install_package(name, &version) {
Ok(package) => package,
Err(_) => {
println!("Failed to install package {}", name);
return;
}
};

dependencies.insert(name.clone(), toml::Value::String(package["version"].as_str().unwrap().to_string()));

let mut file = std::fs::File::create("project.toml").unwrap();
file.write_all(toml::to_string(&toml).unwrap().as_bytes()).unwrap();

println!("Package {} installed", name);
}
Loading
Loading