From 9d4fef5222522683d11e6fd67c7543f9e58451cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Nov 2020 19:48:51 +0100 Subject: [PATCH 1/2] add file index field to SourcePosition, some refactor, put coroutines in Box --- crsn/crsn-sexp/src/error.rs | 64 ++------------------------- crsn/crsn-sexp/src/lib.rs | 6 ++- crsn/crsn-sexp/src/position.rs | 65 ++++++++++++++++++++++++++++ crsn/crsn-sexp/src/test.rs | 17 ++++---- crsn/src/runtime/program.rs | 1 + crsn/src/runtime/run_thread.rs | 4 +- crsn/src/runtime/run_thread/state.rs | 8 ++-- 7 files changed, 88 insertions(+), 77 deletions(-) create mode 100644 crsn/crsn-sexp/src/position.rs diff --git a/crsn/crsn-sexp/src/error.rs b/crsn/crsn-sexp/src/error.rs index 8a3ada2..640903a 100644 --- a/crsn/crsn-sexp/src/error.rs +++ b/crsn/crsn-sexp/src/error.rs @@ -1,5 +1,6 @@ -use std::{cmp, fmt}; -use std::fmt::{Formatter, Debug}; +use std::{fmt}; +use super::SourcePosition; +use position::get_line_and_column; /// The representation of an s-expression parse error. pub struct Error { @@ -9,38 +10,6 @@ pub struct Error { pub pos: SourcePosition, } -/// Position in the input string -#[derive(PartialEq, Clone, Default)] -pub struct SourcePosition { - /// The line number on which the error occurred. - pub line: u32, - /// The column number on which the error occurred. - pub column: u32, - /// The index in the given string which caused the error. - pub index: u32, -} - -impl fmt::Display for SourcePosition { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}:{}", self.line, self.column) - } -} - -impl Debug for SourcePosition { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if f.alternate() { - f.debug_struct("SourcePosition") - .field("line", &self.line) - .field("column", &self.column) - .field("index", &self.index) - .finish() - } else { - // shorter version - write!(f, "Pos({}:{})", self.line, self.column) - } - } -} - /// Since errors are the uncommon case, they're boxed. This keeps the size of /// structs down, which helps performance in the common case. /// @@ -66,24 +35,6 @@ impl fmt::Debug for Error { impl std::error::Error for Error {} -pub(crate) fn get_line_and_column(s: &str, pos: usize) -> SourcePosition { - let mut line: usize = 1; - let mut col: isize = -1; - for c in s.chars().take(pos + 1) { - if c == '\n' { - line += 1; - col = -1; - } else { - col += 1; - } - } - SourcePosition { - line: line as u32, - column: cmp::max(col, 0) as u32, - index: pos as u32, - } -} - #[cold] fn err_impl(message: &'static str, s: &str, pos: usize) -> Err { Box::new(Error { @@ -96,12 +47,3 @@ fn err_impl(message: &'static str, s: &str, pos: usize) -> Err { pub(crate) fn err(message: &'static str, s: &str, pos: usize) -> ERes { Err(err_impl(message, s, pos)) } - -/// Build a span -pub(crate) fn spos(s: &str, pos: usize) -> SourcePosition { - if pos >= s.len() { - Default::default() - } else { - get_line_and_column(s, pos) - } -} diff --git a/crsn/crsn-sexp/src/lib.rs b/crsn/crsn-sexp/src/lib.rs index 50815ab..f5d6bc8 100644 --- a/crsn/crsn-sexp/src/lib.rs +++ b/crsn/crsn-sexp/src/lib.rs @@ -8,14 +8,16 @@ use std::borrow::Cow; use std::fmt; use std::str::{self, FromStr}; -use error::{ERes, err, spos}; +use error::{ERes, err}; pub use error::Error; -pub use error::SourcePosition; +pub use position::SourcePosition; +use position::spos; #[cfg(test)] mod test; mod error; +mod position; /// A single data element in an s-expression. Floats are excluded to ensure /// atoms may be used as keys in ordered and hashed data structures. diff --git a/crsn/crsn-sexp/src/position.rs b/crsn/crsn-sexp/src/position.rs new file mode 100644 index 0000000..f54859c --- /dev/null +++ b/crsn/crsn-sexp/src/position.rs @@ -0,0 +1,65 @@ +use std::{fmt, cmp}; +use std::fmt::{Formatter, Debug}; + +/// Position in the input string +#[derive(PartialEq, Clone, Default)] +pub struct SourcePosition { + /// The line number on which the error occurred. + pub line: u32, + /// The column number on which the error occurred. + pub column: u32, + /// The index in the given string which caused the error. + pub index: u32, + /// File index if there are multiple files + pub file: u32, +} + +impl fmt::Display for SourcePosition { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}:{}", self.line, self.column) + } +} + +impl Debug for SourcePosition { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + if f.alternate() { + f.debug_struct("SourcePosition") + .field("line", &self.line) + .field("column", &self.column) + .field("index", &self.index) + .field("file", &self.file) + .finish() + } else { + // shorter version + write!(f, "Pos({}:{})", self.line, self.column) + } + } +} + +pub(crate) fn get_line_and_column(s: &str, pos: usize) -> SourcePosition { + let mut line: usize = 1; + let mut col: isize = -1; + for c in s.chars().take(pos + 1) { + if c == '\n' { + line += 1; + col = -1; + } else { + col += 1; + } + } + SourcePosition { + line: line as u32, + column: cmp::max(col, 0) as u32, + index: pos as u32, + file: 0 + } +} + +/// Build a span +pub(crate) fn spos(s: &str, pos: usize) -> SourcePosition { + if pos >= s.len() { + Default::default() + } else { + get_line_and_column(s, pos) + } +} diff --git a/crsn/crsn-sexp/src/test.rs b/crsn/crsn-sexp/src/test.rs index 3f71cb8..b7e8a86 100644 --- a/crsn/crsn-sexp/src/test.rs +++ b/crsn/crsn-sexp/src/test.rs @@ -1,5 +1,6 @@ use super::*; use super::error::get_line_and_column; +use super::position::get_line_and_column; #[test] fn test_hello_world() { @@ -67,16 +68,16 @@ fn show_an_error() { #[test] fn line_and_col_test() { let s = "0123456789\n0123456789\n\n6"; - assert_eq!(get_line_and_column(s, 4), SourcePosition { line: 1, column: 4, index: 4 }); + assert_eq!(get_line_and_column(s, 4), SourcePosition { line: 1, column: 4, index: 4, file: 0 }); - assert_eq!(get_line_and_column(s, 10), SourcePosition { line: 2, column: 0, index: 10 }); - assert_eq!(get_line_and_column(s, 11), SourcePosition { line: 2, column: 0, index: 11 }); - assert_eq!(get_line_and_column(s, 15), SourcePosition { line: 2, column: 4, index: 15 }); + assert_eq!(get_line_and_column(s, 10), SourcePosition { line: 2, column: 0, index: 10, file: 0 }); + assert_eq!(get_line_and_column(s, 11), SourcePosition { line: 2, column: 0, index: 11, file: 0 }); + assert_eq!(get_line_and_column(s, 15), SourcePosition { line: 2, column: 4, index: 15, file: 0 }); - assert_eq!(get_line_and_column(s, 21), SourcePosition { line: 3, column: 0, index: 21 }); - assert_eq!(get_line_and_column(s, 22), SourcePosition { line: 4, column: 0, index: 22 }); - assert_eq!(get_line_and_column(s, 23), SourcePosition { line: 4, column: 0, index: 23 }); - assert_eq!(get_line_and_column(s, 500), SourcePosition { line: 4, column: 0, index: 500 }); + assert_eq!(get_line_and_column(s, 21), SourcePosition { line: 3, column: 0, index: 21, file: 0 }); + assert_eq!(get_line_and_column(s, 22), SourcePosition { line: 4, column: 0, index: 22, file: 0 }); + assert_eq!(get_line_and_column(s, 23), SourcePosition { line: 4, column: 0, index: 23, file: 0 }); + assert_eq!(get_line_and_column(s, 500), SourcePosition { line: 4, column: 0, index: 500, file: 0 }); } #[test] diff --git a/crsn/src/runtime/program.rs b/crsn/src/runtime/program.rs index c2c1d05..e7f8599 100644 --- a/crsn/src/runtime/program.rs +++ b/crsn/src/runtime/program.rs @@ -100,6 +100,7 @@ impl Program { line: 0, column: 0, index: 0, + file: 0, }, cond: None, } diff --git a/crsn/src/runtime/run_thread.rs b/crsn/src/runtime/run_thread.rs index 89798d5..58a39a7 100644 --- a/crsn/src/runtime/run_thread.rs +++ b/crsn/src/runtime/run_thread.rs @@ -54,12 +54,12 @@ impl RunThread { let rs = RunState { thread_info: ti.clone(), - cr: CoroutineContext { + cr: Box::new(CoroutineContext { handle: 0, // this is the root frame: StackFrame::new(params.pc, params.args), call_stack: vec![], cr_state: Default::default(), - }, + }), parked: Default::default(), global_regs: [0; REG_COUNT], ext_data: Default::default(), diff --git a/crsn/src/runtime/run_thread/state.rs b/crsn/src/runtime/run_thread/state.rs index 81f8185..b0d86d8 100644 --- a/crsn/src/runtime/run_thread/state.rs +++ b/crsn/src/runtime/run_thread/state.rs @@ -17,9 +17,9 @@ use std::time::{Instant, Duration}; pub struct RunState { pub thread_info: Arc, /// The active coroutine - pub cr: CoroutineContext, + pub cr: Box, /// Parked coroutines - pub parked: VecDeque, + pub parked: VecDeque>, /// General purpose registers that stay valid for the entire run-time of the thread pub global_regs: [Value; REG_COUNT], /// Extension data @@ -87,12 +87,12 @@ impl RunState { trace!("Spawn cr {:#}", handle); // front - so it runs ASAP - self.parked.push_front(CoroutineContext { + self.parked.push_front(Box::new(CoroutineContext { handle, frame, call_stack: vec![], cr_state: Default::default() - }); + })); if self.cr_deadline.is_none() { // start context switching self.start_task_switching(); From 9f289cffd3b3d87084c36948a550288f7adac796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 1 Nov 2020 21:56:11 +0100 Subject: [PATCH 2/2] add includes --- README.md | 23 ++++++ crsn/crsn-sexp/src/error.rs | 2 +- crsn/crsn-sexp/src/position.rs | 9 ++- crsn/src/asm/error.rs | 16 ++++ crsn/src/asm/mod.rs | 55 ++++++++++--- crsn/src/asm/parse/mod.rs | 7 ++ crsn/src/asm/parse/parse_instr.rs | 87 ++++++++++++++++++++- crsn/src/asm/patches/mod.rs | 21 +++++ {launcher/src => crsn/src/asm}/read_file.rs | 2 +- crsn/src/runtime/program.rs | 10 ++- crsn/src/runtime/run_thread.rs | 8 +- examples/include/main.csn | 18 +++++ examples/include/other.csn | 3 + examples/include/utils/itoa.csn | 14 ++++ examples/include/utils/printnum.csn | 17 ++++ launcher/src/main.rs | 5 +- 16 files changed, 271 insertions(+), 26 deletions(-) rename {launcher/src => crsn/src/asm}/read_file.rs (79%) create mode 100644 examples/include/main.csn create mode 100644 examples/include/other.csn create mode 100644 examples/include/utils/itoa.csn create mode 100644 examples/include/utils/printnum.csn diff --git a/README.md b/README.md index be62722..8308fec 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,29 @@ The same program can be written in a compact form: ((ld r0 100)(:again)(sub r0 1 (nz? (j :again)))) ``` +### Includes + +Croissant allows program composition using includes. + +``` +(include path) +``` + +Path can be a filesystem absolute or relative path (with slashes). Relative path is always resolved from the current source file. +Use double quotes if needed. + +If the included file is missing an extension, `.csn` is appended automatically. + +Each included file must contain a list of instructions or procedures, just like the main file. + +Includes work at the S-expression level. The included file is parsed to instructions and inserted +in place of the include directive. Error reports will always show which file the line and column refer to. + +There are no special scoping rules. It is possible to define a label in a file and jump to it from another. +Defines and register aliases (sym) also work cross-file. + +Don't go too crazy with includes, it can get messy. Or do, I'm not your mom. + ## Instruction Instructions are written like this: diff --git a/crsn/crsn-sexp/src/error.rs b/crsn/crsn-sexp/src/error.rs index 640903a..febf27a 100644 --- a/crsn/crsn-sexp/src/error.rs +++ b/crsn/crsn-sexp/src/error.rs @@ -23,7 +23,7 @@ pub(crate) type ERes = Result; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}:{}: {}", self.pos.line, self.pos.column, self.message) + write!(f, "{}: {}", self.pos, self.message) } } diff --git a/crsn/crsn-sexp/src/position.rs b/crsn/crsn-sexp/src/position.rs index f54859c..7d2dc51 100644 --- a/crsn/crsn-sexp/src/position.rs +++ b/crsn/crsn-sexp/src/position.rs @@ -20,6 +20,13 @@ impl fmt::Display for SourcePosition { } } +impl SourcePosition { + pub fn with_file(mut self, file: u32) -> Self { + self.file = file; + self + } +} + impl Debug for SourcePosition { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if f.alternate() { @@ -31,7 +38,7 @@ impl Debug for SourcePosition { .finish() } else { // shorter version - write!(f, "Pos({}:{})", self.line, self.column) + write!(f, "Pos({}|{}:{})", self.file, self.line, self.column) } } } diff --git a/crsn/src/asm/error.rs b/crsn/src/asm/error.rs index bdfdbab..6f7d5ac 100644 --- a/crsn/src/asm/error.rs +++ b/crsn/src/asm/error.rs @@ -8,6 +8,7 @@ use sexp::SourcePosition; use crate::asm::data::{Register}; use crate::asm::data::literal::Label; use crate::asm::instr::Cond; +use std::io; /// csn_asm unified error type #[derive(Error, Debug)] @@ -20,6 +21,21 @@ pub enum CrsnError { ParseOther(Box, SourcePosition), #[error("Assembler error: {0} at {1}")] Asm(AsmError, SourcePosition), + #[error("IO error: {0}")] + IOError(#[from] io::Error), +} + +impl CrsnError { + /// Get error pos + pub fn pos(&self) -> Option<&SourcePosition> { + match self { + CrsnError::Parse(_, p) => Some(p), + CrsnError::ParseOther(_, p) => Some(p), + CrsnError::Asm(_, p) => Some(p), + CrsnError::Sexp(se) => Some(&se.pos), + CrsnError::IOError(_) =>None, + } + } } /// Error from the assembler stage (after parsing S-expressions and basic validation) diff --git a/crsn/src/asm/mod.rs b/crsn/src/asm/mod.rs index e4daa32..c099f37 100644 --- a/crsn/src/asm/mod.rs +++ b/crsn/src/asm/mod.rs @@ -11,26 +11,25 @@ use crate::builtin::BuiltinOps; use crate::runtime::run_thread::{RunState, ThreadInfo, ThreadToken}; use crate::runtime::frame::REG_COUNT; use std::sync::atomic::AtomicU32; +use std::path::{Path}; pub mod data; pub mod error; pub mod instr; pub mod parse; pub mod patches; +mod read_file; +use read_file::read_file; -/// Parse a program from string and assemble a low level instruction sequence from it. -pub fn assemble(source: &str, uniq : &CrsnUniq, mut parsers: Vec>) -> Result, error::CrsnError> { - parsers.insert(0, BuiltinOps::new()); - for p in &mut parsers { - p.init(uniq); - } +pub(crate) fn read_source_file(path: impl AsRef) -> Result { + trace!("Read source file: {}", path.as_ref().display()); - let parsers_arc = Arc::new(parsers); + let source = read_file(path)?; // remove first line if it looks like a shebang - let source = if source.starts_with("#!") { + let s = if source.starts_with("#!") { if let Some(nl) = source.find('\n') { - &source[nl + 1..] + (&source[nl + 1..]).to_string() } else { source } @@ -38,10 +37,25 @@ pub fn assemble(source: &str, uniq : &CrsnUniq, mut parsers: Vec, uniq : &CrsnUniq, mut parsers: Vec>) -> Result, error::CrsnError> { + parsers.insert(0, BuiltinOps::new()); + for p in &mut parsers { + p.init(uniq); + } + + let path = path.as_ref().canonicalize()?; + let source = read_source_file(&path)?; + + let parsers_arc = Arc::new(parsers); + let ti = Arc::new(ThreadInfo { id: ThreadToken(0), uniq: Default::default(), - program: Program::new(vec![], parsers_arc.clone()).unwrap(), + program: Program::new(vec![], parsers_arc.clone(), vec![path.clone()]).unwrap(), cycle_time: Default::default(), scheduler_interval: Default::default(), extensions: parsers_arc.clone(), @@ -72,12 +86,27 @@ pub fn assemble(source: &str, uniq : &CrsnUniq, mut parsers: Vec>>) -> Result, error::CrsnError> { + let ops = parse::parse(source, &SourcePosition::default(), pcx)?; let ops = jumps_to_skips(ops)?; - Ok(Program::new(ops, parsers_arc)?) + Ok(Program::new(ops, parsers_arc, pcx.state.borrow_mut().files.split_off(0))?) } diff --git a/crsn/src/asm/parse/mod.rs b/crsn/src/asm/parse/mod.rs index e1d4b62..fa8f4d9 100644 --- a/crsn/src/asm/parse/mod.rs +++ b/crsn/src/asm/parse/mod.rs @@ -13,6 +13,7 @@ use crate::asm::parse::sexp_expect::expect_list; use crate::module::CrsnExtension; use crate::runtime::run_thread::{ThreadInfo, RunState}; use std::sync::Arc; +use std::path::PathBuf; pub mod parse_cond; pub mod parse_instr; @@ -54,6 +55,12 @@ pub struct ParserState { /// Label numberer pub label_num : Arc, + + /// Numbered files + pub files : Vec, + + /// Active file number. Is backed up before descending into a sub-file, and restored afterwards. + pub active_file: usize, } impl ParserState { diff --git a/crsn/src/asm/parse/parse_instr.rs b/crsn/src/asm/parse/parse_instr.rs index 22048f4..f949ef3 100644 --- a/crsn/src/asm/parse/parse_instr.rs +++ b/crsn/src/asm/parse/parse_instr.rs @@ -6,14 +6,19 @@ use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::parse_cond::parse_cond_branch; use crate::asm::parse::parse_routine::parse_routine; use crate::asm::parse::ParserContext; -use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; +use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom, expect_any_string_atom}; use crate::asm::patches::NextOrErr; use crate::module::ParseRes; +use crate::asm::patches::ErrSetFile; use super::parse_op::parse_op; +use std::path::{PathBuf}; +use std::convert::TryFrom; +use crate::asm::read_source_file; +use crate::asm::instr::flatten::jumps_to_skips; pub fn parse_instructions(items: impl Iterator, pos: &SourcePosition, pcx: &ParserContext) -> Result, CrsnError> { - let mut parsed = vec![]; + let mut parsed: Vec> = vec![]; 'exprs: for expr in items { let (tokens, listpos) = expect_list(expr, false)?; @@ -28,6 +33,84 @@ pub fn parse_instructions(items: impl Iterator, pos: &SourcePosition, } } + if name == "include" { + // TODO move this to a separate function and get rid of err_file() + + if parsing_expr { + return Err(CrsnError::Parse("Illegal syntax in const expression".into(), pos.clone())); + } + + let (mut path, _namepos) = expect_any_string_atom(toki.next_or_err(listpos.clone(), "Expected file path or name to include")?)?; + + // add extension if missing + let last_piece = path.split('/').rev().next().unwrap(); + if !last_piece.contains('.') { + path.push_str(".csn"); + } + + trace!("*** include, raw path: {}", path); + + let state = pcx.state.borrow(); + let this_file = &state.files[state.active_file]; + + let new_file = PathBuf::try_from(path).unwrap(); + + let new_pb = if !new_file.is_absolute() { + let parent = this_file.parent().expect("file has parent"); + + let fixed = parent.join(&new_file); + trace!("Try to resolve: {}", fixed.display()); + + fixed.canonicalize()? + } else { + new_file.to_owned() + }; + drop(state); + drop(new_file); + + let (old_af, af) = { + let mut state = pcx.state.borrow_mut(); + let old_af = state.active_file; + let af = state.files.len(); + state.active_file = af; + state.files.push(new_pb.clone()); + (old_af, af as u32) + }; + + let loaded = read_source_file(&new_pb) + .err_file(af)?; + + let (items, _pos) = + expect_list( + sexp::parse(&loaded) + .map_err(|mut e| { + e.pos.file = af; + e + })?, + true) + .err_file(af)?; + + let sub_parsed = parse_instructions(items.into_iter(), pos, pcx) + .err_file(af)?; + + let mut flat = sub_parsed.flatten(&pcx.state.borrow().label_num) + .err_file(af)?; + + flat.iter_mut().for_each(|op| { + op.pos.file = af as u32; + }); + + trace!("inner falt {:#?}", flat); + + parsed.push(Box::new(flat)); + + { + let mut state = pcx.state.borrow_mut(); + state.active_file = old_af; + } + continue; + } + if name == "proc" { if parsing_expr { return Err(CrsnError::Parse("Illegal syntax in const expression".into(), pos.clone())); diff --git a/crsn/src/asm/patches/mod.rs b/crsn/src/asm/patches/mod.rs index b36202c..f1ba3cd 100644 --- a/crsn/src/asm/patches/mod.rs +++ b/crsn/src/asm/patches/mod.rs @@ -57,3 +57,24 @@ impl ErrWithPos for Result { + fn err_file(self, file: u32) -> Result; +} + +impl ErrSetFile for Result { + /// Set file context in the error + fn err_file(self, file: u32) -> Result { + match self { + Ok(v) => Ok(v), + Err(CrsnError::Parse(a, p)) => Err(CrsnError::Parse(a, p.with_file(file))), + Err(CrsnError::ParseOther(a, p)) => Err(CrsnError::ParseOther(a, p.with_file(file))), + Err(CrsnError::Asm(a, p)) => Err(CrsnError::Asm(a, p.with_file(file))), + Err(CrsnError::Sexp(mut se)) => { + se.pos.file = file; + Err(CrsnError::Sexp(se)) + }, + Err(other @ CrsnError::IOError(_)) => Err(other), + } + } +} diff --git a/launcher/src/read_file.rs b/crsn/src/asm/read_file.rs similarity index 79% rename from launcher/src/read_file.rs rename to crsn/src/asm/read_file.rs index 5ac24b8..9d755c8 100644 --- a/launcher/src/read_file.rs +++ b/crsn/src/asm/read_file.rs @@ -4,7 +4,7 @@ use std::io::Read; use std::path::Path; /// Read a file to string -pub fn read_file>(path: P) -> io::Result { +pub fn read_file(path: impl AsRef) -> io::Result { let path = path.as_ref(); let mut file = File::open(path)?; diff --git a/crsn/src/runtime/program.rs b/crsn/src/runtime/program.rs index e7f8599..96224a4 100644 --- a/crsn/src/runtime/program.rs +++ b/crsn/src/runtime/program.rs @@ -10,6 +10,7 @@ use crate::asm::instr::op::OpKind; use crate::builtin::defs::{Barrier, BuiltinOp}; use crate::module::CrsnExtension; use crate::runtime::fault::Fault; +use std::path::PathBuf; #[derive(Debug)] pub struct Program { @@ -20,16 +21,23 @@ pub struct Program { /// Barriers from-to (inclusive). /// Standalone barriers have both addresses the same. barriers: Vec<(Addr, Addr)>, + /// This is used at runtime to better report error locations when included files are used + pub(crate) file_names: Vec, } impl Program { - pub fn new(ops: Vec, extensions: Arc>>) -> Result, CrsnError> { + pub fn new( + ops: Vec, + extensions: Arc>>, + file_names: Vec, + ) -> Result, CrsnError> { let mut p = Self { ops, extensions, routines: Default::default(), far_labels: Default::default(), barriers: Default::default(), + file_names, }; p.scan()?; Ok(Arc::new(p)) diff --git a/crsn/src/runtime/run_thread.rs b/crsn/src/runtime/run_thread.rs index 58a39a7..c3b9241 100644 --- a/crsn/src/runtime/run_thread.rs +++ b/crsn/src/runtime/run_thread.rs @@ -279,12 +279,14 @@ impl RunThread { debug!("Thread ended."); } e => { + eprintln!("*** Program failed: {} ***", e); if let Some(instr) = self.info.program.ops.get(orig_pc.0 as usize) { - error!("Fault at {}: {}", instr.pos(), e); + eprintln!("Source location: {}, line {}, column {}", self.info.program.file_names[instr.pos().file as usize].display(), instr.pos().line, instr.pos().column); } else { - error!("Fault at PC {}: {}", orig_pc, e); + eprintln!("Instruction address: {}", orig_pc); } - warn!("Core dump: {:?}", self.state); + + debug!("\nCore dump: {:?}", self.state); } } } diff --git a/examples/include/main.csn b/examples/include/main.csn new file mode 100644 index 0000000..7129001 --- /dev/null +++ b/examples/include/main.csn @@ -0,0 +1,18 @@ +( + (include other) + (def BAR (=add FOO 1000)) + + (cmp BAR 1123 (ne? (fault))) + + (include utils/itoa.csn) + (include "utils/printnum") + + (mkbf r0) + (call itoa r0 BAR) + (lds @cout @r0) + (del @r0) + (ld @cout '\n') + + (call printnum BAR) + (ld @cout '\n') +) diff --git a/examples/include/other.csn b/examples/include/other.csn new file mode 100644 index 0000000..54d58b6 --- /dev/null +++ b/examples/include/other.csn @@ -0,0 +1,3 @@ +( + (def FOO 123) +) diff --git a/examples/include/utils/itoa.csn b/examples/include/utils/itoa.csn new file mode 100644 index 0000000..e3fcabd --- /dev/null +++ b/examples/include/utils/itoa.csn @@ -0,0 +1,14 @@ +( + (proc itoa buf num + (ld r1 num) + (tst r1 (<0? (mul r1 -1))) + (:next) + (mod r0 r1 10) + (add r0 '0') + (bfrpush @buf r0) + (div r1 10 (z? + (tst num (<0? (bfrpush @buf '-'))) + (ret))) + (j :next) + ) +) diff --git a/examples/include/utils/printnum.csn b/examples/include/utils/printnum.csn new file mode 100644 index 0000000..d68cc81 --- /dev/null +++ b/examples/include/utils/printnum.csn @@ -0,0 +1,17 @@ +( + (proc printnum num + (mkbf r15) + (ld r1 num) + (tst r1 (<0? (mul r1 -1))) + (:next) + (mod r0 r1 10) + (add r0 '0') + (bfrpush @r15 r0) + (div r1 10 (z? + (tst num (<0? (bfrpush @r15 '-'))) + (lds @cout @r15) + (del @r15) + (ret))) + (j :next) + ) +) diff --git a/launcher/src/main.rs b/launcher/src/main.rs index 080bd30..35ed975 100644 --- a/launcher/src/main.rs +++ b/launcher/src/main.rs @@ -17,7 +17,6 @@ use crsn_screen::ScreenOps; use crsn_stdio::StdioOps; use crsn_buf::BufOps; -mod read_file; mod serde_duration_millis; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -167,11 +166,9 @@ fn main() -> anyhow::Result<()> { debug!("Loading {}", config.program_file); - let source = read_file::read_file(&config.program_file)?; - let uniq = CrsnUniq::new(); - let parsed = crsn::asm::assemble(&source, &uniq, vec![ + let parsed = crsn::asm::assemble(&config.program_file, &uniq, vec![ ArithOps::new(), BufOps::new(), ScreenOps::new(),