use std::cell::RefCell; use std::sync::Arc; use sexp::SourcePosition; use crate::asm::instr::flatten::jumps_to_skips; use crate::asm::parse::{ParserContext, ParserState}; use crate::module::{CrsnExtension, CrsnUniq}; use crate::runtime::program::Program; 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; pub(crate) fn read_source_file(path: impl AsRef) -> Result { trace!("Read source file: {}", path.as_ref().display()); let source = read_file(path)?; // remove first line if it looks like a shebang let s = if source.starts_with("#!") { if let Some(nl) = source.find('\n') { (&source[nl + 1..]).to_string() } else { source } } else { source }; Ok(s) } /// Parse a program from string and assemble a low level instruction sequence from it. pub fn assemble(path: impl AsRef, 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(), vec![path.clone()]).unwrap(), cycle_time: Default::default(), scheduler_interval: Default::default(), extensions: parsers_arc.clone(), }); /* numbered labels start with a weird high number to avoid conflicts with user-defined numbered labels */ let label_num = Arc::new(AtomicU32::new(0x7890_0000)); let pcx = ParserContext { parsers: &parsers_arc, state: RefCell::new(ParserState { reg_aliases: Default::default(), reg_alias_stack: vec![], global_reg_aliases: Default::default(), constants: Default::default(), // This is a fake thread to pass to constant expressions when evaluating them. // This allows to evaluate nearly all instructions at compile time. const_eval: RunState { thread_info: ti.clone(), cr: Default::default(), parked: Default::default(), global_regs: [0; REG_COUNT], ext_data: Default::default(), cr_deadline: None, critical_section: 0, }, const_eval_ti: ti, parsing_expr: false, label_num: label_num.clone(), files: vec![ path, ], active_file: 0 }), }; let res = do_parse(&source, &pcx, parsers_arc.clone()); if let Err(e) = &res { if let Some(pos) = e.pos() { let f = pcx.state.borrow().files[pos.file as usize].clone(); eprintln!("Error in source file: {}", f.display()); } } res } fn do_parse(source: &str, pcx : &ParserContext, parsers_arc : Arc>>) -> Result, error::CrsnError> { let ops = parse::parse(source, &SourcePosition::default(), pcx)?; let ops = jumps_to_skips(ops)?; Ok(Program::new(ops, parsers_arc, pcx.state.borrow_mut().files.split_off(0))?) }