# It is not intended for manual editing.
version = 4
+[[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
[[package]]
name = "equivalent"
version = "1.0.2"
name = "hylo-interpret"
version = "0.1.0"
dependencies = [
+ "anyhow",
"hylo-ir",
"syscalls",
]
name = "hyloc"
version = "0.1.0"
dependencies = [
+ "anyhow",
"hylo-interpret",
"hylo-ir",
]
edition = "2024"
[dependencies]
+anyhow = "1.0.100"
hylo-ir = { workspace = true }
syscalls = "0.7.0"
use std::collections::HashMap;
use syscalls::*;
+use anyhow::{Result, anyhow};
pub struct Interpreter<'a> {
module: &'a IRModule,
}
impl<'a> Interpreter<'a> {
- pub fn new(ir_mod: &'a IRModule) -> Self {
+ pub fn new(ir_mod: &'a IRModule) -> Result<Self> {
let mut index = 0;
let mut labels = HashMap::new();
for token in ir_mod.text.iter() {
}
index += 1;
}
- let instruction_pointer = *labels.get("main").unwrap();
+ let instruction_pointer = *labels.get("main").ok_or(anyhow!("no main word found!"))?;
let strings = ir_mod.data.iter().filter_map(|s| {
match s {
}
}).collect();
- Self {
+ Ok(Self {
module: ir_mod,
data_stack: vec![],
instruction_pointer,
return_stack: vec![],
labels,
strings
- }
+ })
}
fn process_syscall_result(&mut self, result: Result<usize, Errno>) {
}
}
- pub fn run(&mut self) {
+ fn ds_pop(&mut self) -> Result<u64> {
+ self.data_stack.pop().ok_or(anyhow!("popping from empty data stack"))
+ }
+
+ pub fn run(&mut self) -> Result<()> {
let mut looking_for_endif = false;
loop {
if looking_for_endif {
}
_ => {}
}
-
} else {
match &self.module.text[self.instruction_pointer] {
IR::Label(_) => {},
IR::Call(name) => {
self.return_stack.push(self.instruction_pointer);
- self.instruction_pointer = *self.labels.get(name).unwrap();
+ self.instruction_pointer = *self.labels.get(name).ok_or(anyhow!("calling undefined word `{}`", name))?;
},
IR::Ret => {
if self.return_stack.len() == 0 {
- break;
+ return Ok(());
}
- self.instruction_pointer = self.return_stack.pop().unwrap();
+ self.instruction_pointer = self.return_stack.pop().ok_or(anyhow!("returning from top level"))?;
},
IR::StackPush(num) => {
self.data_stack.push(*num);
},
IR::AddU64 => {
- let a = self.data_stack.pop().unwrap();
- let b = self.data_stack.pop().unwrap();
+ let a = self.ds_pop()?;
+ let b = self.ds_pop()?;
self.data_stack.push(a + b);
},
IR::SubtractU64 => {
- let b = self.data_stack.pop().unwrap();
- let a = self.data_stack.pop().unwrap();
+ let b = self.ds_pop()?;
+ let a = self.ds_pop()?;
self.data_stack.push(a - b);
},
IR::PutN => {
- println!("{}", self.data_stack.last().unwrap());
+ println!("{}", self.data_stack.last().ok_or(anyhow!("empty data stack"))?);
},
IR::Dup => {
- self.data_stack.push(*self.data_stack.last().unwrap());
+ self.data_stack.push(*self.data_stack.last().ok_or(anyhow!("empty data stack"))?);
},
IR::Swap => {
- let a = self.data_stack.pop().unwrap();
- let b = self.data_stack.pop().unwrap();
+ let a = self.ds_pop()?;
+ let b = self.ds_pop()?;
self.data_stack.push(a);
self.data_stack.push(b);
},
self.data_stack.pop();
},
IR::Equals => {
- let a = self.data_stack.pop().unwrap();
- let b = self.data_stack.pop().unwrap();
+ let a = self.ds_pop()?;
+ let b = self.ds_pop()?;
self.data_stack.push(if a == b {
0
} else {
});
},
IR::GreaterThan => {
- let b = self.data_stack.pop().unwrap();
- let a = self.data_stack.pop().unwrap();
+ let b = self.ds_pop()?;
+ let a = self.ds_pop()?;
self.data_stack.push(if a > b {
0
} else {
},
IR::If => {
- if self.data_stack.pop().unwrap() != 0 {
+ if self.ds_pop()? != 0 {
looking_for_endif = true;
}
},
IR::EndIf => {},
IR::Sys0 => {
- let call_num = Sysno::from(self.data_stack.pop().unwrap() as i32);
+ let call_num = Sysno::from(self.ds_pop()? as i32);
self.process_syscall_result(unsafe { syscall!(call_num) });
},
IR::Sys1 => {
- let a1 = self.data_stack.pop().unwrap();
- let call_num = Sysno::from(self.data_stack.pop().unwrap() as i32);
+ let a1 = self.ds_pop()?;
+ let call_num = Sysno::from(self.ds_pop()? as i32);
self.process_syscall_result(unsafe { syscall!(call_num, a1) });
},
IR::Sys2 => {
- let a2 = self.data_stack.pop().unwrap();
- let a1 = self.data_stack.pop().unwrap();
- let call_num = Sysno::from(self.data_stack.pop().unwrap() as i32);
+ let a2 = self.ds_pop()?;
+ let a1 = self.ds_pop()?;
+ let call_num = Sysno::from(self.ds_pop()? as i32);
self.process_syscall_result(unsafe { syscall!(call_num, a1, a2) });
},
IR::Sys3 => {
- let a3 = self.data_stack.pop().unwrap();
- let a2 = self.data_stack.pop().unwrap();
- let a1 = self.data_stack.pop().unwrap();
- let call_num = Sysno::from(self.data_stack.pop().unwrap() as i32);
+ let a3 = self.ds_pop()?;
+ let a2 = self.ds_pop()?;
+ let a1 = self.ds_pop()?;
+ let call_num = Sysno::from(self.ds_pop()? as i32);
self.process_syscall_result(unsafe { syscall!(call_num, a1, a2, a3) });
},
IR::Sys4 => {
- let a4 = self.data_stack.pop().unwrap();
- let a3 = self.data_stack.pop().unwrap();
- let a2 = self.data_stack.pop().unwrap();
- let a1 = self.data_stack.pop().unwrap();
- let call_num = Sysno::from(self.data_stack.pop().unwrap() as i32);
+ let a4 = self.ds_pop()?;
+ let a3 = self.ds_pop()?;
+ let a2 = self.ds_pop()?;
+ let a1 = self.ds_pop()?;
+ let call_num = Sysno::from(self.ds_pop()? as i32);
self.process_syscall_result(unsafe { syscall!(call_num, a1, a2, a3, a4) });
},
IR::Sys5 => {
- let a5 = self.data_stack.pop().unwrap();
- let a4 = self.data_stack.pop().unwrap();
- let a3 = self.data_stack.pop().unwrap();
- let a2 = self.data_stack.pop().unwrap();
- let a1 = self.data_stack.pop().unwrap();
- let call_num = Sysno::from(self.data_stack.pop().unwrap() as i32);
+ let a5 = self.ds_pop()?;
+ let a4 = self.ds_pop()?;
+ let a3 = self.ds_pop()?;
+ let a2 = self.ds_pop()?;
+ let a1 = self.ds_pop()?;
+ let call_num = Sysno::from(self.ds_pop()? as i32);
self.process_syscall_result(unsafe { syscall!(call_num, a1, a2, a3, a4, a5) });
},
IR::Sys6 => {
- let a6 = self.data_stack.pop().unwrap();
- let a5 = self.data_stack.pop().unwrap();
- let a4 = self.data_stack.pop().unwrap();
- let a3 = self.data_stack.pop().unwrap();
- let a2 = self.data_stack.pop().unwrap();
- let a1 = self.data_stack.pop().unwrap();
- let call_num = Sysno::from(self.data_stack.pop().unwrap() as i32);
+ let a6 = self.ds_pop()?;
+ let a5 = self.ds_pop()?;
+ let a4 = self.ds_pop()?;
+ let a3 = self.ds_pop()?;
+ let a2 = self.ds_pop()?;
+ let a1 = self.ds_pop()?;
+ let call_num = Sysno::from(self.ds_pop()? as i32);
self.process_syscall_result(unsafe { syscall!(call_num, a1, a2, a3, a4, a5, a6) });
},
_ => {
[dependencies]
hylo-ir = { workspace = true }
hylo-interpret = { workspace = true }
+anyhow = "1.0.100"
use std::collections::HashSet;
use std::path::PathBuf;
+use anyhow::{Result, anyhow};
+
macro_rules! push_num {
($num:ident) => { IR::StackPush(*$num as u64) }
}
-fn import(importer_dir: &PathBuf, specifier: &str, imported: &mut HashSet<PathBuf>, is_entrypoint: bool) -> Option<ModuleWithImports> {
+fn import(importer_dir: &PathBuf, specifier: &str, imported: &mut HashSet<PathBuf>, is_entrypoint: bool) -> Result<Option<ModuleWithImports>> {
let mut path = PathBuf::from(specifier);
if path.is_relative() {
let mut new_path = importer_dir.clone();
new_path.push(path);
- path = new_path.canonicalize().unwrap();
+ path = new_path.canonicalize()?;
}
if imported.contains(&path) {
- return None;
+ return Ok(None);
}
- let contents = std::fs::read_to_string(&path).unwrap();
+ let contents = std::fs::read_to_string(&path)?;
- Some(generate_internal(path, &Module::parse(tokenize(&contents), is_entrypoint), imported))
+ Ok(Some(generate_internal(path, &Module::parse(tokenize(&contents)?, is_entrypoint)?, imported)))
}
-fn collapse_module(mut module_w: ModuleWithImports) -> IRModule {
- let mut module = module_w.module.take().unwrap();
+fn collapse_module(mut module_w: ModuleWithImports) -> Result<IRModule> {
+ let mut module = module_w.module.take().ok_or(anyhow!("no module to collapse"))?;
let mut data = std::mem::take(&mut module.data);
let mut prev_data_len = data.len();
let mut text = std::mem::take(&mut module.text);
- module_w.imports.take().unwrap().into_iter().for_each(|imported| {
- let mut ir_mod = collapse_module(imported);
+ module_w.imports.take().ok_or(anyhow!("no imports to collapse"))?.into_iter().try_for_each(|imported| -> Result<()>{
+ let mut ir_mod = collapse_module(imported)?;
let mut mod_data = std::mem::take(&mut ir_mod.data);
let mod_data_len = mod_data.len();
data.append(&mut mod_data);
text.append(&mut mod_text);
prev_data_len += mod_data_len;
- });
+
+ Ok(())
+ })?;
- IRModule {
+ Ok(IRModule {
data,
text,
- }
+ })
}
-pub fn compile(path: &str) -> IRModule {
+pub fn compile(path: &str) -> Result<IRModule> {
let mut imported = HashSet::new();
- let module = import(&std::env::current_dir().unwrap(), path, &mut imported, true).unwrap();
+ let dir = std::env::current_dir()?;
+ let module = import(&dir, path, &mut imported, true)?.expect("somehow we've already imported this");
collapse_module(module) // TODO remove unused words
}
},
Token::String(text) => {
if last_was_import {
- if let Some(module) = import(&path.parent().unwrap().to_path_buf(), text, imported, false) {
- imports.push(module);
+ if let Some(parent_path) = path.parent() {
+ match import(&parent_path.to_path_buf(), text, imported, false) {
+ Ok(module) => {
+ if let Some(module) = module {
+ imports.push(module);
+ }
+ },
+ Err(msg) => {
+ eprintln!("{}", msg);
+ }
+ }
}
IR::ImportString // This will be elided later
} else {
use hylo_interpret::Interpreter;
-fn main() {
+use anyhow::Result;
+
+fn main() -> Result<()> {
let filename = std::env::args().nth(1).expect("must provide a file to compile");
- let module = ir::compile(&filename);
- let mut interp = Interpreter::new(&module);
- interp.run();
+ let module = ir::compile(&filename)?;
+ let mut interp = Interpreter::new(&module)?;
+ interp.run()?;
+ Ok(())
}
use crate::tokenizer::Token;
+use anyhow::{Result, bail};
#[derive(Debug)]
pub struct WordDefinition<'a> {
}
impl<'a> Module<'a> {
- pub fn parse(input: Vec<Token<'a>>, is_entrypoint: bool) -> Self {
+ pub fn parse(input: Vec<Token<'a>>, is_entrypoint: bool) -> Result<Self> {
let mut result = vec![];
let mut main = vec![];
let mut current_word: Option<WordDefinition> = None;
about_to_start_word_def = false;
continue;
} else {
- panic!("{:?} is not a valid word name!", token);
+ bail!("{:?} is not a valid word name!", token);
}
} else if let Token::Word(word) = token {
if word == ":" {
if current_word.is_some() {
- panic!("can't define words inside word definitions!");
+ bail!("can't define words inside word definitions!");
}
about_to_start_word_def = true;
continue;
}
if word == ";" {
let word = current_word.take();
- result.push(word.unwrap());
- continue;
+ if let Some(word) = word {
+ result.push(word);
+ continue;
+ } else {
+ bail!("`;` must be at the end of a word definition");
+ }
}
}
if let Some(ref mut current_word) = current_word {
}
if about_to_start_word_def || current_word.is_some() {
- panic!("unfinished word definition!");
+ bail!("unfinished word definition!");
}
if is_entrypoint {
});
}
- Module { words: result }
+ Ok(Module { words: result })
}
#[cfg(test)]
: soup chicken 4.5 hello ;
hello soup
-"), true);
+").unwrap(), true).unwrap();
result.debug_print();
}
}
+use anyhow::{Result, anyhow};
+
#[derive(Debug)]
pub enum Token<'a> {
Word(&'a str),
}
impl<'a> Token<'a>{
- fn parse_word_or_num(input: &'a str) -> Token<'a> {
+ fn parse_word_or_num(input: &'a str) -> Result<Token<'a>> {
if input == "-" {
- return Token::Word(input)
+ return Ok(Token::Word(input))
}
// we're assuming any token starting with `-` with length greater than one
// is a negative number
- if input.starts_with('-') || input.chars().nth(0).unwrap().is_numeric() {
+ if input.starts_with('-') || input.chars().nth(0).map(|x| x.is_numeric()).unwrap_or(false) {
if input.contains(':') {
let mut splat = input.split(':');
- let num = splat.next().unwrap();
- let typ = splat.next().unwrap();
+ let num = splat.next().ok_or(anyhow!("no number found"))?;
+ let typ = splat.next().ok_or(anyhow!("no number type found"))?;
match typ {
- "u8" => Token::NumU8(num.parse().unwrap()),
- "i8" => Token::NumI8(num.parse().unwrap()),
- "u16" => Token::NumU16(num.parse().unwrap()),
- "i16" => Token::NumI16(num.parse().unwrap()),
- "u32" => Token::NumU32(num.parse().unwrap()),
- "i32" => Token::NumI32(num.parse().unwrap()),
- "u64" => Token::NumU64(num.parse().unwrap()),
- "i64" => Token::NumI64(num.parse().unwrap()),
- "f32" => Token::NumF32(num.parse().unwrap()),
- "f64" => Token::NumF64(num.parse().unwrap()),
+ "u8" => Ok(Token::NumU8(num.parse()?)),
+ "i8" => Ok(Token::NumI8(num.parse()?)),
+ "u16" => Ok(Token::NumU16(num.parse()?)),
+ "i16" => Ok(Token::NumI16(num.parse()?)),
+ "u32" => Ok(Token::NumU32(num.parse()?)),
+ "i32" => Ok(Token::NumI32(num.parse()?)),
+ "u64" => Ok(Token::NumU64(num.parse()?)),
+ "i64" => Ok(Token::NumI64(num.parse()?)),
+ "f32" => Ok(Token::NumF32(num.parse()?)),
+ "f64" => Ok(Token::NumF64(num.parse()?)),
_ => panic!("unknown number type")
}
} else {
if input.contains('.') {
- Token:: NumF64(input.parse().unwrap())
+ Ok(Token::NumF64(input.parse()?))
} else if input.starts_with('-') {
- Token::NumI64(input.parse().unwrap())
+ Ok(Token::NumI64(input.parse()?))
} else {
- Token::NumU64(input.parse().unwrap())
+ Ok(Token::NumU64(input.parse()?))
}
}
} else {
- Token::Word(input)
+ Ok(Token::Word(input))
}
}
}
// TODO really want an iterator, not a vector
-pub fn tokenize<'a>(input: &'a str) -> Vec<Token<'a>> {
+pub fn tokenize<'a>(input: &'a str) -> Result<Vec<Token<'a>>> {
let mut result = vec![];
let mut string_start: Option<usize> = None;
let mut word_or_num_start: Option<usize> = None;
if token == "(" {
in_comment = true;
} else {
- result.push(Token::parse_word_or_num(&input[start..index]));
+ result.push(Token::parse_word_or_num(&input[start..index])?);
}
word_or_num_start = None;
}
if index == input.len() - 1 {
if !last_is_whitespace && let Some(start) = word_or_num_start {
- result.push(Token::parse_word_or_num(&input[start..]));
+ result.push(Token::parse_word_or_num(&input[start..])?);
}
continue;
}
last_is_whitespace = false;
}
}
- result
+ Ok(result)
}
#[cfg(test)]