Hyphae: add assembler and disassembler
Some checks failed
per-push tests / timed-decomposer-parse (push) Blocked by required conditions
per-push tests / test-backend (push) Blocked by required conditions
per-push tests / build (push) Successful in 1m50s
per-push tests / test-utility (push) Has been cancelled
per-push tests / test-frontend (push) Has been cancelled
Some checks failed
per-push tests / timed-decomposer-parse (push) Blocked by required conditions
per-push tests / test-backend (push) Blocked by required conditions
per-push tests / build (push) Successful in 1m50s
per-push tests / test-utility (push) Has been cancelled
per-push tests / test-frontend (push) Has been cancelled
This commit adds hyphae binaries for an assembler and a disassembler As well as some fixes for observed misbehavior during manual verification. The new binaries are hphc for compiling assmbly files and hphdump for inspecting compiled hyphae binary. Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
parent
141ba43362
commit
ad39e26945
5 changed files with 185 additions and 24 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
|
@ -60,9 +60,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.38"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -70,9 +70,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.38"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -82,9 +82,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.32"
|
||||
version = "4.5.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
|
@ -142,6 +142,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||
name = "hyphae"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"num",
|
||||
"organelle",
|
||||
"serde",
|
||||
|
|
|
|||
|
|
@ -3,9 +3,28 @@ name = "hyphae"
|
|||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
name = "hyphae"
|
||||
crate-type = ["lib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hphc"
|
||||
path = "src/bin/hphc.rs"
|
||||
required-features = ["cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "hphdump"
|
||||
path = "src/bin/hphdump.rs"
|
||||
required-features = ["cli"]
|
||||
|
||||
[features]
|
||||
cli = ["clap"]
|
||||
|
||||
[dependencies]
|
||||
organelle = { path = "../organelle" }
|
||||
num = { version = "0.4.3", features = ["alloc"] }
|
||||
clap = { version = "4.5.53", features = ["derive"], optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
serde = { version = "1.0", features = ["alloc", "derive"] }
|
||||
|
|
|
|||
77
hyphae/src/bin/hphc.rs
Normal file
77
hyphae/src/bin/hphc.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* Mycelium Scheme
|
||||
* Copyright (C) 2025 Ava Affine
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use hyphae;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::fs::{read, File};
|
||||
use std::io::Write;
|
||||
use std::error::Error;
|
||||
use std::str::FromStr;
|
||||
|
||||
const HPHC_SUFFIX: &str= "hph";
|
||||
const HPHC_DEFAULT: &str = "out";
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about = "HyphaeVM assembly compiler")]
|
||||
struct Args {
|
||||
/// path to input program assembly
|
||||
compile: PathBuf,
|
||||
|
||||
/// path for output bytecode
|
||||
#[arg(short = 'o')]
|
||||
output: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// parse arguments
|
||||
let args = Args::parse();
|
||||
|
||||
// set output filepath
|
||||
let outfile = args.output
|
||||
.or(Some({
|
||||
let mut c = args.compile.clone();
|
||||
if c.as_path().is_dir() || c.as_path().file_name().is_none() {
|
||||
c = String::from(HPHC_DEFAULT).into();
|
||||
} else {
|
||||
c = c.as_path().file_name().unwrap().into();
|
||||
}
|
||||
|
||||
c.set_extension(HPHC_SUFFIX);
|
||||
c
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
// check input validity
|
||||
if args.compile.as_path().is_dir() {
|
||||
panic!("will not compile directory!");
|
||||
}
|
||||
|
||||
// compile input program
|
||||
let input_bytes = read(args.compile)?;
|
||||
let input = str::from_utf8(input_bytes.as_slice())?;
|
||||
let program = hyphae::serializer::Program::from_str(input)?;
|
||||
|
||||
// dump program bytecode to output
|
||||
let bytecode: Vec<u8> = program.into();
|
||||
let mut out_file = File::create(outfile)?;
|
||||
out_file.write_all(&bytecode)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
52
hyphae/src/bin/hphdump.rs
Normal file
52
hyphae/src/bin/hphdump.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/* Mycelium Scheme
|
||||
* Copyright (C) 2025 Ava Affine
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use hyphae;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::fs::read;
|
||||
use std::error::Error;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about = "HyphaeVM compiled binary inspector")]
|
||||
struct Args {
|
||||
/// path to input program
|
||||
decompile: PathBuf,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// parse arguments
|
||||
let args = Args::parse();
|
||||
|
||||
// check input validity
|
||||
if args.decompile.as_path().is_dir() {
|
||||
panic!("cannot dump directory!");
|
||||
}
|
||||
|
||||
// load input program
|
||||
let program: hyphae::serializer::Program;
|
||||
program = read(args.decompile)?
|
||||
.as_slice()
|
||||
.try_into()?;
|
||||
|
||||
// Display
|
||||
println!("{}", program);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -153,16 +153,16 @@ impl FromStr for Operand {
|
|||
"$oper4" => Ok(Operand(Address::Oper4, 0)),
|
||||
"true" => Ok(Operand(Address::Bool, 1)),
|
||||
"false" => Ok(Operand(Address::Bool, 0)),
|
||||
a if a.chars().nth(0).unwrap() == '%' &&
|
||||
a.len() > 1 &&
|
||||
a if a.len() > 1 &&
|
||||
a.chars().nth(0).unwrap() == '%' &&
|
||||
a[1..].parse::<usize>().is_ok() =>
|
||||
Ok(Operand(Address::Stack, a[1..].parse::<usize>().unwrap())),
|
||||
a if a.chars().nth(0).unwrap() == '@' &&
|
||||
a.len() > 1 &&
|
||||
a if a.len() > 1 &&
|
||||
a.chars().nth(0).unwrap() == '@' &&
|
||||
a[1..].parse::<usize>().is_ok() =>
|
||||
Ok(Operand(Address::Instr, a[1..].parse::<usize>().unwrap())),
|
||||
a if a.chars().nth(0).unwrap() == '\'' &&
|
||||
a.len() == 3 &&
|
||||
a if a.len() == 3 &&
|
||||
a.chars().nth(0).unwrap() == '\'' &&
|
||||
a.chars().nth(2).unwrap() == '\'' =>
|
||||
Ok(Operand(Address::Char, a.chars().nth(1).unwrap() as usize)),
|
||||
a if a.parse::<usize>().is_ok() =>
|
||||
|
|
@ -232,7 +232,11 @@ impl Into<Vec<u8>> for Instruction {
|
|||
impl FromStr for Instruction {
|
||||
type Err = &'static str;
|
||||
fn from_str(v: &str) -> Result<Self, Self::Err> {
|
||||
let toks: Vec<&str> = v.trim().split(' ').collect();
|
||||
let toks: Vec<&str> = v
|
||||
.trim()
|
||||
.split(' ')
|
||||
.filter(|x| x.len() > 0)
|
||||
.collect();
|
||||
if toks.len() < 1 {
|
||||
return Err("empty string");
|
||||
}
|
||||
|
|
@ -304,6 +308,7 @@ impl TryFrom<&[u8]> for Program {
|
|||
}
|
||||
cur += 1;
|
||||
while cur < value.len() {
|
||||
|
||||
let instruction: Instruction = value[cur..].try_into()?;
|
||||
cur += instruction.byte_length() as usize;
|
||||
prog.push(instruction);
|
||||
|
|
@ -318,9 +323,13 @@ impl TryFrom<&[u8]> for Program {
|
|||
|
||||
impl Into<Vec<u8>> for Program {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
for instr in self.0 {
|
||||
res.append(&mut instr.into())
|
||||
let mut res: Vec<u8> = vec![DeserializerControlCode::DataChunk as u8];
|
||||
for dat in self.0 {
|
||||
res.append(&mut dat.into());
|
||||
}
|
||||
res.push(DeserializerControlCode::CodeChunk as u8);
|
||||
for instr in self.1 {
|
||||
res.append(&mut instr.into());
|
||||
}
|
||||
res
|
||||
}
|
||||
|
|
@ -337,12 +346,12 @@ impl Display for Program {
|
|||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||
write!(f, "DATA:\n")?;
|
||||
for i in self.0.iter() {
|
||||
write!(f, " {}", i)?;
|
||||
write!(f, " {}\n", i)?;
|
||||
}
|
||||
|
||||
write!(f, "\nCODE:\n")?;
|
||||
write!(f, "CODE:\n")?;
|
||||
for i in self.1.iter() {
|
||||
write!(f, " {}", i)?;
|
||||
write!(f, " {}\n", i)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -354,7 +363,10 @@ impl FromStr for Program {
|
|||
fn from_str(val: &str) -> Result<Self, Self::Err> {
|
||||
//let mut datum = vec![];
|
||||
let mut instrs = vec![];
|
||||
let lines: Vec<&str> = val.split('\n').collect();
|
||||
let lines: Vec<&str> = val
|
||||
.split('\n')
|
||||
.filter(|x| x.len() > 0)
|
||||
.collect();
|
||||
let mut cur = 0;
|
||||
let mut toggle = 0;
|
||||
|
||||
|
|
@ -368,7 +380,7 @@ impl FromStr for Program {
|
|||
match lines[cur] {
|
||||
"DATA:" => return Err("datum parser unimplemented"),
|
||||
"CODE:" => toggle = 1,
|
||||
a => return Err("unknown section in document: "),
|
||||
_ => return Err("unknown section in document: "),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue