plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
50effc5
— Peter John
2026-07-19T19:31:02+05:30
feat(plum-cli): add compile subcommand wiring checker and codegen
- plum-cli/Cargo.toml +4 -0
- plum-cli/src/main.rs +46 -0
- plum-cli/tests/compile_tests.rs +24 -0
- test/simple_add.plum +2 -0
plum-cli/Cargo.toml
CHANGED
|
@@ -9,5 +9,9 @@ path = "src/main.rs"
|
|
|
9
9
|
|
|
10
10
|
[dependencies]
|
|
11
11
|
plum-core = { path = "../plum-core" }
|
|
12
|
+
plum-checker = { path = "../plum-checker" }
|
|
13
|
+
plum-wasm-codegen = { path = "../plum-wasm-codegen" }
|
|
12
14
|
clap = { version = "4", features = ["derive"] }
|
|
13
15
|
anyhow = "1"
|
|
16
|
+
tree-sitter = "0.26"
|
|
17
|
+
tree-sitter-plum = { path = "../tooling/tree-sitter-plum" }
|
plum-cli/src/main.rs
CHANGED
|
@@ -6,6 +6,7 @@ use anyhow::{Context, Result};
|
|
|
6
6
|
use clap::{Parser, Subcommand};
|
|
7
7
|
|
|
8
8
|
use plum_core::format_source;
|
|
9
|
+
use plum_core::AstParser;
|
|
9
10
|
|
|
10
11
|
#[derive(Parser)]
|
|
11
12
|
#[command(name = "plum", about = "The Plum language toolchain")]
|
|
@@ -27,6 +28,14 @@ enum Command {
|
|
|
27
28
|
#[arg(long)]
|
|
28
29
|
stdin: bool,
|
|
29
30
|
},
|
|
31
|
+
/// Compile a Plum source file to WASM
|
|
32
|
+
Compile {
|
|
33
|
+
/// Source file to compile
|
|
34
|
+
file: std::path::PathBuf,
|
|
35
|
+
/// Output path (default: input with .wasm extension)
|
|
36
|
+
#[arg(short, long)]
|
|
37
|
+
output: Option<std::path::PathBuf>,
|
|
38
|
+
},
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
fn main() {
|
|
@@ -40,6 +49,7 @@ fn run() -> Result<()> {
|
|
|
40
49
|
let cli = Cli::parse();
|
|
41
50
|
match cli.command {
|
|
42
51
|
Command::Format { file, check, stdin } => cmd_format(file, check, stdin),
|
|
52
|
+
Command::Compile { file, output } => cmd_compile(file, output),
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
55
|
|
|
@@ -80,3 +90,39 @@ fn cmd_format(
|
|
|
80
90
|
}
|
|
81
91
|
Ok(())
|
|
82
92
|
}
|
|
93
|
+
|
|
94
|
+
fn cmd_compile(file: std::path::PathBuf, output: Option<std::path::PathBuf>) -> Result<()> {
|
|
95
|
+
let source = fs::read_to_string(&file)
|
|
96
|
+
.with_context(|| format!("failed to read {}", file.display()))?;
|
|
97
|
+
|
|
98
|
+
// Parse
|
|
99
|
+
let mut parser = tree_sitter::Parser::new();
|
|
100
|
+
parser
|
|
101
|
+
.set_language(&tree_sitter_plum::LANGUAGE.into())
|
|
102
|
+
.map_err(|e| anyhow::anyhow!("language error: {e}"))?;
|
|
103
|
+
let tree = parser
|
|
104
|
+
.parse(&source, None)
|
|
105
|
+
.ok_or_else(|| anyhow::anyhow!("parse failed"))?;
|
|
106
|
+
let ap = AstParser::new(&source);
|
|
107
|
+
let ast = ap.parse_source(tree.root_node());
|
|
108
|
+
|
|
109
|
+
// Type check
|
|
110
|
+
if let Err(errors) = plum_checker::check_source(&ast) {
|
|
111
|
+
for e in &errors {
|
|
112
|
+
eprintln!("type error: {}", e.message);
|
|
113
|
+
}
|
|
114
|
+
process::exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Codegen
|
|
118
|
+
let wasm_bytes = plum_wasm_codegen::compile_source(&ast)
|
|
119
|
+
.map_err(|e| anyhow::anyhow!("codegen error: {e}"))?;
|
|
120
|
+
|
|
121
|
+
// Write output
|
|
122
|
+
let out_path = output.unwrap_or_else(|| file.with_extension("wasm"));
|
|
123
|
+
fs::write(&out_path, &wasm_bytes)
|
|
124
|
+
.with_context(|| format!("failed to write {}", out_path.display()))?;
|
|
125
|
+
|
|
126
|
+
eprintln!("compiled {} → {}", file.display(), out_path.display());
|
|
127
|
+
Ok(())
|
|
128
|
+
}
|
plum-cli/tests/compile_tests.rs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
use std::process::Command;
|
|
2
|
+
|
|
3
|
+
fn plum_bin() -> std::path::PathBuf {
|
|
4
|
+
let mut path = std::env::current_exe().unwrap();
|
|
5
|
+
path.pop(); // remove test binary
|
|
6
|
+
if path.ends_with("deps") {
|
|
7
|
+
path.pop();
|
|
8
|
+
}
|
|
9
|
+
path.push("plum");
|
|
10
|
+
path
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#[test]
|
|
14
|
+
fn compile_simple_add_produces_wasm() {
|
|
15
|
+
let src_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../test/simple_add.plum");
|
|
16
|
+
let out_path = "/tmp/add_test_output.wasm";
|
|
17
|
+
let status = Command::new(plum_bin())
|
|
18
|
+
.args(["compile", src_path, "-o", out_path])
|
|
19
|
+
.status()
|
|
20
|
+
.expect("failed to run plum compile");
|
|
21
|
+
assert!(status.success(), "plum compile exited with: {}", status);
|
|
22
|
+
let bytes = std::fs::read(out_path).expect("output wasm not found");
|
|
23
|
+
assert_eq!(&bytes[0..4], b"\0asm");
|
|
24
|
+
}
|
test/simple_add.plum
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
add(a: Int, b: Int) -> Int =
|
|
2
|
+
a + b
|