From ace92a641169374b8506b3c2c0f0a6c9a4d2c393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 10 Oct 2020 18:46:32 +0200 Subject: [PATCH] rewrite the stdio module to be less broken. also add sehbang support --- Cargo.lock | 48 +------ README.md | 8 +- crsn/crsn-sexp/src/lib.rs | 18 +-- crsn/src/asm/instr/cond.rs | 67 ++++++++++ crsn/src/asm/instr/flatten.rs | 2 + crsn/src/asm/mod.rs | 11 ++ crsn/src/asm/parse/arg_parser.rs | 1 + crsn/src/asm/parse/mod.rs | 3 +- crsn/src/asm/parse/parse_instr.rs | 3 +- crsn/src/builtin/exec.rs | 4 +- crsn/src/runtime/frame/status.rs | 63 +++++---- crsn/src/runtime/run_thread/state.rs | 8 +- crsn_arith/src/exec.rs | 34 ++--- crsn_buf/src/exec.rs | 18 +-- crsn_screen/src/exec.rs | 24 ++-- crsn_stdio/Cargo.toml | 2 +- crsn_stdio/src/lib.rs | 189 ++++++++++++++++++++++----- examples/number_array.csn | 2 +- examples/stdio.csn | 10 +- examples/stdio_rev.csn | 6 +- examples/stdio_rot13.csn | 7 +- 21 files changed, 357 insertions(+), 171 deletions(-) mode change 100644 => 100755 examples/stdio_rot13.csn diff --git a/Cargo.lock b/Cargo.lock index 79659a3..8a5d033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,23 +168,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "console" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b1aacfaffdbff75be81c15a399b4bedf78aaefe840e8af1d299ac2ade885d2" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "regex", - "terminal_size", - "termios", - "unicode-width", - "winapi", - "winapi-util", -] - [[package]] name = "crsn" version = "0.1.0" @@ -228,8 +211,8 @@ dependencies = [ name = "crsn_stdio" version = "0.1.0" dependencies = [ - "console", "crsn", + "libc", ] [[package]] @@ -274,12 +257,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c53dc3a653e0f64081026e4bf048d48fec9fce90c66e8326ca7292df0ff2d82" -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "env_logger" version = "0.7.1" @@ -394,9 +371,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.77" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" [[package]] name = "lock_api" @@ -996,25 +973,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a14cd9f8c72704232f0bfc8455c0e861f0ad4eb60cc9ec8a170e231414c1e13" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "termios" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0fcee7b24a25675de40d5bb4de6e41b0df07bc9856295e7e2b3a3600c400c2" -dependencies = [ - "libc", -] - [[package]] name = "textwrap" version = "0.11.0" diff --git a/README.md b/README.md index 98ff01a..4fe75ac 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,16 @@ It's probably faster than you need for most things, actually. You can slow it down using the `-C` argument, or using sleep instructions. -#### What if I don't enjoy writing assembly that looks like weird Lisp? +### What if I don't enjoy writing assembly that looks like weird Lisp? Maybe this is not for you +### Shebang? + +Yes! You can use crsn as a scripting language! + +The first line from a source file is skipped if it starts with `#!` + ### Contributing Yup, go ahead. You can also develop your own private *crsn* extensions, they work like plugins. diff --git a/crsn/crsn-sexp/src/lib.rs b/crsn/crsn-sexp/src/lib.rs index 838a550..24485c4 100644 --- a/crsn/crsn-sexp/src/lib.rs +++ b/crsn/crsn-sexp/src/lib.rs @@ -144,7 +144,7 @@ fn atom_of_string(s: String) -> Atom { // returns the char it found, and the new pos if you wish to consume that char fn peek(s: &str, pos: usize) -> ERes<(char, usize)> { - trace!("peek {}", pos); + //trace!("peek {}", pos); if pos == s.len() { return err("unexpected eof", s, pos); } if s.is_char_boundary(pos) { let ch = s[pos..].chars().next().unwrap(); @@ -158,7 +158,7 @@ fn peek(s: &str, pos: usize) -> ERes<(char, usize)> { // returns the char it found, and the new pos if you wish to consume that char fn peekn(s: &str, nth: usize, pos: usize) -> Option<(char, usize)> { - trace!("peekn {}", pos); + //trace!("peekn {}", pos); if nth == 0 { panic!("peekn with nth=0"); } @@ -183,7 +183,7 @@ fn peekn(s: &str, nth: usize, pos: usize) -> Option<(char, usize)> { } fn expect(s: &str, pos: &mut usize, c: char) -> ERes<()> { - trace!("expect {}", pos); + //trace!("expect {}", pos); let (ch, next) = peek(s, *pos)?; *pos = next; if ch == c { @@ -208,7 +208,7 @@ fn consume_until_newline(s: &str, pos: &mut usize) -> ERes<()> { // zero or more spaces fn zspace(s: &str, pos: &mut usize) -> ERes<()> { - trace!("zspace {}", pos); + //trace!("zspace {}", pos); loop { if *pos == s.len() { return Ok(()); @@ -226,7 +226,7 @@ fn zspace(s: &str, pos: &mut usize) -> ERes<()> { } fn parse_quoted_atom(s: &str, quote: char, pos: &mut usize) -> ERes { - trace!("parse_quoted_atom {}", pos); + //trace!("parse_quoted_atom {}", pos); let pos0 = *pos; let mut cs: String = String::new(); @@ -266,7 +266,7 @@ fn parse_quoted_atom(s: &str, quote: char, pos: &mut usize) -> ERes { } fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes { - trace!("parse_unquoted_atom {}", pos); + //trace!("parse_unquoted_atom {}", pos); let mut cs: String = String::new(); loop { @@ -288,7 +288,7 @@ fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes { } fn parse_atom(s: &str, pos: &mut usize) -> ERes { - trace!("parse_atom {}", pos); + //trace!("parse_atom {}", pos); let (ch, _) = peek(s, *pos)?; if ch == '"' { @@ -309,7 +309,7 @@ fn parse_atom(s: &str, pos: &mut usize) -> ERes { } fn parse_list(s: &str, pos: &mut usize) -> ERes> { - trace!("parse_list {}", pos); + //trace!("parse_list {}", pos); zspace(s, pos)?; expect(s, pos, '(')?; @@ -331,7 +331,7 @@ fn parse_list(s: &str, pos: &mut usize) -> ERes> { } fn parse_sexp(s: &str, pos: &mut usize) -> ERes { - trace!("parse_sexp {}", pos); + //trace!("parse_sexp {}", pos); zspace(s, pos)?; let (c, _) = peek(s, *pos)?; let r = if c == '(' { diff --git a/crsn/src/asm/instr/cond.rs b/crsn/src/asm/instr/cond.rs index b489e43..c3b86d3 100644 --- a/crsn/src/asm/instr/cond.rs +++ b/crsn/src/asm/instr/cond.rs @@ -5,6 +5,55 @@ use sexp::SourcePosition; use crate::asm::error::CrsnError; +/// Condition flag +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum Flag { + /// Equality + Equal, + /// Equal to zero + Zero, + /// A < B + Lower, + /// A > B + Greater, + /// A > 0 + Positive, + /// A < 0 + Negative, + /// Arithmetic operation caused an integer operand to overflow + Overflow, + /// Arithmetic (or other) operation was invalid. + /// Example: division by zero + Invalid, + /// Arithmetic carry + Carry, + // Full + Full, + // Empty + Empty, + // Empty + Eof, +} + +impl From for Cond { + fn from(flag: Flag) -> Self { + match flag { + Flag::Equal => Cond::Equal, + Flag::Zero => Cond::Zero, + Flag::Lower => Cond::Lower, + Flag::Greater => Cond::Greater, + Flag::Positive => Cond::Positive, + Flag::Negative => Cond::Negative, + Flag::Overflow => Cond::Overflow, + Flag::Invalid => Cond::Invalid, + Flag::Carry => Cond::Carry, + Flag::Full => Cond::Full, + Flag::Empty => Cond::Empty, + Flag::Eof => Cond::Eof, + } + } +} + /// Condition flag #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum Cond { @@ -53,6 +102,10 @@ pub enum Cond { Empty, /// Not empty NotEmpty, + // Empty + Eof, + /// Not empty + NotEof, } pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result { @@ -77,6 +130,8 @@ pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result { "nf" | "nfull" => Cond::NotFull, "ok" | "val" | "valid" => Cond::Valid, "inval" | "invalid" | "nval" | "nok" => Cond::Invalid, + "eof" => Cond::Eof, + "neof" => Cond::NotEof, "ov" => Cond::Overflow, "nov" => Cond::NotOverflow, _ => { @@ -85,6 +140,13 @@ pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result { }) } +impl Display for Flag { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let cond : Cond = (*self).into(); + write!(f, "{}", cond) + } +} + impl Display for Cond { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str(match self { @@ -110,6 +172,8 @@ impl Display for Cond { Cond::NotCarry => "nc", Cond::Invalid => "inval", Cond::Valid => "valid", + Cond::Eof => "eof", + Cond::NotEof => "neof", }) } } @@ -147,6 +211,9 @@ impl Not for Cond { Cond::Invalid => Cond::Valid, Cond::Valid => Cond::Invalid, + + Cond::NotEof => Cond::Eof, + Cond::Eof => Cond::NotEof, } } } diff --git a/crsn/src/asm/instr/flatten.rs b/crsn/src/asm/instr/flatten.rs index 07d03b7..b307b7c 100644 --- a/crsn/src/asm/instr/flatten.rs +++ b/crsn/src/asm/instr/flatten.rs @@ -39,6 +39,8 @@ impl Flatten for InstrWithBranches { let parent_pos = self.pos; if let Some(branches) = self.branches { + // trace!("Branches {:?}", branches); + let labels = HashMap::::new(); let branch_count = branches.len(); let end_lbl = Label::unique(label_num); diff --git a/crsn/src/asm/mod.rs b/crsn/src/asm/mod.rs index bbdf097..48d998b 100644 --- a/crsn/src/asm/mod.rs +++ b/crsn/src/asm/mod.rs @@ -23,6 +23,17 @@ pub fn assemble(source: &str, uniq : &CrsnUniq, mut parsers: Vec { orig_len: usize, args: Vec, diff --git a/crsn/src/asm/parse/mod.rs b/crsn/src/asm/parse/mod.rs index bcfbbd0..20c4bf0 100644 --- a/crsn/src/asm/parse/mod.rs +++ b/crsn/src/asm/parse/mod.rs @@ -20,6 +20,7 @@ pub mod parse_op; pub mod arg_parser; pub mod parse_routine; +#[derive(Debug)] pub struct ParserContext<'a> { /// Extension modules pub parsers: &'a [Box], @@ -27,7 +28,7 @@ pub struct ParserContext<'a> { pub state: RefCell, } -#[derive(Default)] +#[derive(Default, Debug)] pub struct ParserState { /// Register aliases within the routine pub reg_aliases: HashMap, diff --git a/crsn/src/asm/parse/parse_instr.rs b/crsn/src/asm/parse/parse_instr.rs index 8dfff40..ac276a8 100644 --- a/crsn/src/asm/parse/parse_instr.rs +++ b/crsn/src/asm/parse/parse_instr.rs @@ -63,10 +63,11 @@ pub fn parse_instructions(items: impl Iterator, pos: &SourcePosition, .collect::>(); let arg_tokens = TokenParser::new(toki.take(token_count - branch_tokens.len()).collect(), &listpos, pcx); + // debug!("branch_tokens: {:#?}", branch_tokens); let branches = { let mut branches = vec![]; - for t in branch_tokens { + for t in branch_tokens.into_iter().rev() { branches.push(parse_cond_branch(t, pcx)?); } if branches.is_empty() { diff --git a/crsn/src/builtin/exec.rs b/crsn/src/builtin/exec.rs index 0b814de..f226863 100644 --- a/crsn/src/builtin/exec.rs +++ b/crsn/src/builtin/exec.rs @@ -4,12 +4,12 @@ use sexp::Sexp; use crate::asm::data::{Rd, RdData}; use crate::asm::data::literal::Addr; -use crate::asm::instr::Cond; use crate::builtin::defs::{Barrier, BuiltinOp}; use crate::module::{EvalRes, OpTrait}; use crate::runtime::fault::Fault; use crate::runtime::frame::StackFrame; use crate::runtime::run_thread::{state::RunState, ThreadInfo}; +use crate::asm::instr::cond::Flag; impl OpTrait for BuiltinOp { fn execute(&self, info: &ThreadInfo, state: &mut RunState) -> Result { @@ -138,7 +138,7 @@ impl OpTrait for BuiltinOp { } if !dropped { warn!("Object {:#x} to del does not exist!", x); - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } } } diff --git a/crsn/src/runtime/frame/status.rs b/crsn/src/runtime/frame/status.rs index 7f1d2fa..b14d8c3 100644 --- a/crsn/src/runtime/frame/status.rs +++ b/crsn/src/runtime/frame/status.rs @@ -3,6 +3,7 @@ use std::fmt; use crate::asm::data::literal::{is_negative, is_positive, Value}; use crate::asm::instr::Cond; +use crate::asm::instr::cond::Flag; #[derive(Default, Clone, Debug)] pub struct StatusFlags { @@ -28,6 +29,8 @@ pub struct StatusFlags { pub full: bool, /// Buffer empty pub empty: bool, + /// Stream ended + pub eof: bool, } impl StatusFlags { @@ -49,6 +52,7 @@ impl StatusFlags { if self.carry { val |= 0x100; } if self.full { val |= 0x200; } if self.empty { val |= 0x400; } + if self.eof { val |= 0x800; } val } @@ -64,6 +68,7 @@ impl StatusFlags { if val & 0x100 != 0 { self.carry = true; } if val & 0x200 != 0 { self.full = true; } if val & 0x400 != 0 { self.empty = true; } + if val & 0x800 != 0 { self.eof = true; } } #[inline(always)] @@ -98,50 +103,49 @@ impl StatusFlags { Cond::NotFull => !self.full, Cond::Empty => self.empty, Cond::NotEmpty => !self.empty, + Cond::Eof => self.eof, + Cond::NotEof => !self.eof, } } #[inline(always)] - pub fn set(&mut self, cond: Cond) { + pub fn set(&mut self, cond: Flag, val : bool) { match cond { - Cond::Equal => { - self.equal = true; + Flag::Equal => { + self.equal = val; } - Cond::Zero => { - self.zero = true; + Flag::Zero => { + self.zero = val; } - Cond::Lower => { - self.lower = true; + Flag::Lower => { + self.lower = val; } - Cond::Greater => { - self.lower = false; - self.equal = false; - self.greater = true; + Flag::Greater => { + self.greater = val; } - Cond::Positive => { - self.positive = true; + Flag::Positive => { + self.positive = val; } - Cond::Negative => { - self.negative = true; + Flag::Negative => { + self.negative = val; } - Cond::Overflow => { - self.overflow = true; + Flag::Overflow => { + self.overflow = val; } - Cond::Invalid => { - self.invalid = true; + Flag::Invalid => { + self.invalid = val; } - Cond::Carry => { - self.carry = true; + Flag::Carry => { + self.carry = val; } - Cond::Empty => { - self.empty = true; + Flag::Empty => { + self.empty = val; } - Cond::Full => { - self.full = true; + Flag::Full => { + self.full = val; } - other => { - error!("Cannot set cond by {:?}", other); - // ...and do nothing. Don't panic, we don't want to crash the runtime! + Flag::Eof => { + self.eof = val; } } } @@ -183,6 +187,9 @@ impl Display for StatusFlags { if self.empty { f.write_str(" Emp")?; } + if self.eof { + f.write_str(" Eof")?; + } f.write_str(" ]")?; Ok(()) diff --git a/crsn/src/runtime/run_thread/state.rs b/crsn/src/runtime/run_thread/state.rs index e6e9b05..ca29bfc 100644 --- a/crsn/src/runtime/run_thread/state.rs +++ b/crsn/src/runtime/run_thread/state.rs @@ -9,6 +9,7 @@ use crate::runtime::frame::{CallStack, REG_COUNT, StackFrame}; use std::sync::Arc; use crate::runtime::run_thread::ThreadInfo; use nudge::{likely}; +use crate::asm::instr::cond::Flag; pub struct RunState { pub thread_info: Arc, @@ -51,10 +52,9 @@ impl RunState { /// Set a status flag. Only supports simple, positive conds (i.e. not GreaterOrEqual) #[inline(always)] - pub fn set_flag(&mut self, cond: Cond, set: bool) { - if set { - self.frame.status.set(cond); - } + pub fn set_flag(&mut self, flag: Flag, set: bool) { + trace!("Flag {} = {:?}", flag, set); + self.frame.status.set(flag, set); } /// Check status flags for a condition diff --git a/crsn_arith/src/exec.rs b/crsn_arith/src/exec.rs index 3adf778..09cb6e3 100644 --- a/crsn_arith/src/exec.rs +++ b/crsn_arith/src/exec.rs @@ -3,7 +3,6 @@ use std::ops::Rem; use num_traits::PrimInt; use crsn::asm::data::{Rd, Wr}; -use crsn::asm::instr::Cond; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; @@ -12,6 +11,7 @@ use crsn::sexp::Sexp; use crsn::utils::A; use crate::defs::ArithOp; +use crsn::asm::instr::cond::Flag; impl OpTrait for ArithOp { fn execute(&self, _ti: &ThreadInfo, state: &mut RunState) -> Result { @@ -26,9 +26,9 @@ impl OpTrait for ArithOp { state.clear_status(); let x = state.read(a)?; let y = state.read(b)?; - state.set_flag(Cond::Equal, x == y); - state.set_flag(Cond::Lower, x < y); - state.set_flag(Cond::Greater, x > y); + state.set_flag(Flag::Equal, x == y); + state.set_flag(Flag::Lower, x < y); + state.set_flag(Flag::Greater, x > y); // Test flags are set when both arguments have the property if x == y { state.update_status(x); @@ -39,9 +39,9 @@ impl OpTrait for ArithOp { let val = state.read(val)?; let a = state.read(a)?; let b = state.read(b)?; - state.set_flag(Cond::Equal, val >= a && val <= b); - state.set_flag(Cond::Lower, val < a); - state.set_flag(Cond::Greater, val > b); + state.set_flag(Flag::Equal, val >= a && val <= b); + state.set_flag(Flag::Lower, val < a); + state.set_flag(Flag::Greater, val > b); state.update_status(val); } ArithOp::Add { dst, a, b } => { @@ -54,7 +54,7 @@ impl OpTrait for ArithOp { (x.wrapping_add(y), true) }; state.update_status(res); - state.set_flag(Cond::Overflow, ov); + state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; } ArithOp::Sub { dst, a, b } => { @@ -67,7 +67,7 @@ impl OpTrait for ArithOp { (x.wrapping_sub(y), true) }; state.update_status(res); - state.set_flag(Cond::Overflow, ov); + state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; } ArithOp::Mul { dst, a, b } => { @@ -77,7 +77,7 @@ impl OpTrait for ArithOp { let res = if let Some(v) = x.checked_mul(y) { v } else { - state.set_flag(Cond::Overflow, true); + state.set_flag(Flag::Overflow, true); x.wrapping_mul(y) }; state.update_status(res); @@ -88,7 +88,7 @@ impl OpTrait for ArithOp { let x = state.read(a)?; let d = state.read(div)?; if d == 0 { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } else { let (res, remainder, ov) = if let Some(v) = x.checked_div(d) { (v, x.rem(d), false) @@ -96,7 +96,7 @@ impl OpTrait for ArithOp { (x.wrapping_div(d), x.wrapping_rem(d), true) }; state.update_status(res); - state.set_flag(Cond::Overflow, ov); + state.set_flag(Flag::Overflow, ov); state.write(dst, res)?; state.write(rem, remainder)?; } @@ -106,7 +106,7 @@ impl OpTrait for ArithOp { let x = state.read(a)?; let d = state.read(div)?; if d == 0 { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } else { let (remainder, ov) = if let Some(v) = x.checked_rem(d) { (v, false) @@ -114,7 +114,7 @@ impl OpTrait for ArithOp { (x.wrapping_rem(d), true) }; state.update_status(remainder); - state.set_flag(Cond::Overflow, ov); + state.set_flag(Flag::Overflow, ov); state.write(dst, remainder)?; } } @@ -154,7 +154,7 @@ impl OpTrait for ArithOp { let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } else { let res = x.rotate_left(y as u32); state.update_status(res); @@ -166,7 +166,7 @@ impl OpTrait for ArithOp { let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } else { let res = x.rotate_right(y as u32); state.update_status(res); @@ -194,7 +194,7 @@ impl OpTrait for ArithOp { let x = state.read(a)?; let y = state.read(n)?; if y > u32::MAX as u64 { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } else { let res = x.signed_shr(y as u32); state.update_status(res); diff --git a/crsn_buf/src/exec.rs b/crsn_buf/src/exec.rs index 0279248..2a20a39 100644 --- a/crsn_buf/src/exec.rs +++ b/crsn_buf/src/exec.rs @@ -1,7 +1,6 @@ use std::collections::{HashMap, VecDeque}; use crsn::asm::data::literal::Value; -use crsn::asm::instr::Cond; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; @@ -10,6 +9,7 @@ use crsn::sexp::{Sexp, atom_qs}; use crsn::utils::A; use crate::defs::{BufOps, BufValue}; +use crsn::asm::instr::cond::Flag; #[derive(Debug, Default)] struct ExtData { @@ -71,10 +71,10 @@ impl OpTrait for BufOps { let empty = stack.is_empty(); state.write(dst, val)?; state.update_status(val); - state.set_flag(Cond::Empty, empty); + state.set_flag(Flag::Empty, empty); } else { - state.set_flag(Cond::Empty, true); - state.set_flag(Cond::Overflow, true); + state.set_flag(Flag::Empty, true); + state.set_flag(Flag::Overflow, true); } } @@ -89,7 +89,7 @@ impl OpTrait for BufOps { state.update_status(val); state.write(dst, val)?; } else { - state.set_flag(Cond::Overflow, true); + state.set_flag(Flag::Overflow, true); } } @@ -115,7 +115,7 @@ impl OpTrait for BufOps { } else if idx == stack.len() { stack.push_back(val); } else { - state.set_flag(Cond::Overflow, true); + state.set_flag(Flag::Overflow, true); } } @@ -131,7 +131,7 @@ impl OpTrait for BufOps { } else if idx == stack.len() { stack.push_back(val); } else { - state.set_flag(Cond::Overflow, true); + state.set_flag(Flag::Overflow, true); } } @@ -146,9 +146,9 @@ impl OpTrait for BufOps { let empty = stack.is_empty(); state.update_status(val); state.write(dst, val)?; - state.set_flag(Cond::Empty, empty); + state.set_flag(Flag::Empty, empty); } else { - state.set_flag(Cond::Overflow, true); + state.set_flag(Flag::Overflow, true); } } diff --git a/crsn_screen/src/exec.rs b/crsn_screen/src/exec.rs index 4e36da8..2c52aec 100644 --- a/crsn_screen/src/exec.rs +++ b/crsn_screen/src/exec.rs @@ -4,7 +4,6 @@ use std::time::{Duration, Instant}; use minifb::{Key, MouseButton, MouseMode, ScaleMode, Window, WindowOptions}; use crsn::asm::data::literal::Value; -use crsn::asm::instr::Cond; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; @@ -13,6 +12,7 @@ use crsn::sexp::Sexp; use crsn::utils::A; use crate::defs::ScreenOp; +use crsn::asm::instr::cond::Flag; #[derive(Debug)] struct Opts { @@ -87,7 +87,7 @@ impl OpTrait for ScreenOp { } other => { warn!("Bad screen opt: {}", other); - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } } } @@ -115,7 +115,7 @@ impl OpTrait for ScreenOp { w.update(); } None => { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } } } @@ -129,7 +129,7 @@ impl OpTrait for ScreenOp { let backend: &mut Backend = state.ext_mut(); if x >= backend.width as u64 || y >= backend.height as u64 { - state.set_flag(Cond::Overflow, true); + state.set_flag(Flag::Overflow, true); return Ok(eres); } @@ -138,7 +138,7 @@ impl OpTrait for ScreenOp { let index = y * backend.width as u64 + x; if index as usize > backend.buffer.len() { warn!("Screen set pixel out of bounds"); - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } else { backend.buffer[index as usize] = color as u32; @@ -148,7 +148,7 @@ impl OpTrait for ScreenOp { } } None => { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } } } @@ -162,7 +162,7 @@ impl OpTrait for ScreenOp { debug!("mp = {:?}", mp); match mp { None => { - state.set_flag(Cond::Overflow, true); + state.set_flag(Flag::Overflow, true); } Some((xf, yf)) => { let xval = xf.round() as u64; @@ -174,7 +174,7 @@ impl OpTrait for ScreenOp { } } None => { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } } } @@ -187,7 +187,7 @@ impl OpTrait for ScreenOp { Some(w) => { match num { None => { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } Some(kn) => { let down = w.is_key_down(kn) as u64; @@ -197,7 +197,7 @@ impl OpTrait for ScreenOp { } } None => { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } } } @@ -210,7 +210,7 @@ impl OpTrait for ScreenOp { 1 => Some(MouseButton::Right), 2 => Some(MouseButton::Middle), _ => { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); None } }; @@ -225,7 +225,7 @@ impl OpTrait for ScreenOp { } } None => { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } } } diff --git a/crsn_stdio/Cargo.toml b/crsn_stdio/Cargo.toml index 0309aec..fa7469e 100644 --- a/crsn_stdio/Cargo.toml +++ b/crsn_stdio/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] crsn = { path = "../crsn" } -console = "0.12.0" +libc = "0.2.79" diff --git a/crsn_stdio/src/lib.rs b/crsn_stdio/src/lib.rs index 1d58cb6..a054a92 100644 --- a/crsn_stdio/src/lib.rs +++ b/crsn_stdio/src/lib.rs @@ -6,43 +6,139 @@ use crsn::module::{CrsnExtension, ParseRes, CrsnUniq}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{RunState}; use crsn::sexp::SourcePosition; -use std::io::{Write}; use std::convert::TryFrom; -use crsn::asm::instr::Cond; -use console::Term; +use std::io; +use crsn::asm::instr::cond::Flag; -#[derive(Debug, Clone)] -pub struct StdioOps { - hdl_stdin : Value, - hdl_stdout : Value, -} +mod console { + use std::{io}; + use std::os::unix::io::RawFd; -#[derive(Debug)] -struct StdioData { - console: Term, -} + use std::ffi::c_void; + use std::mem::{self, MaybeUninit}; + + fn setup_fd(fd: RawFd) -> io::Result { + use libc::*; + + let mut tio = MaybeUninit::uninit(); + if 0 != unsafe { tcgetattr(fd, tio.as_mut_ptr()) } { + return Err(io::Error::last_os_error()); + } + let mut tio = unsafe { MaybeUninit::assume_init(tio) }; + let old_tio : termios = unsafe { mem::transmute_copy(&tio) }; + + tio.c_iflag &= !(/*BRKINT |*/ /*ICRNL |*/ INPCK | ISTRIP | IXON); + tio.c_oflag |= ONLCR; + tio.c_cflag |= CS8; + tio.c_lflag &= !(ECHO | ICANON | IEXTEN /*| ISIG*/); + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if 0 != unsafe { tcsetattr(fd, TCSANOW, &tio) } { + return Err(io::Error::last_os_error()); + } + + Ok(old_tio) + } + + pub fn init_io() -> io::Result { + setup_fd(libc::STDIN_FILENO) + } -impl Default for StdioData { - fn default() -> Self { - Self { - console : Term::stdout() + pub fn read_byte() -> io::Result { + let mut buf = 0u8; + let len = unsafe { libc::read(libc::STDIN_FILENO, &mut buf as *mut u8 as *mut c_void, 1) }; + if len <= 0 { + Err(io::Error::last_os_error()) + } else { + Ok(buf as u8) } } + + pub fn write_byte(b : u8) -> io::Result<()> { + let len = unsafe { libc::write(libc::STDOUT_FILENO, &b as *const u8 as *const c_void, 1) }; + if len <= 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } + + pub fn write_char(c : char) -> io::Result<()> { + let mut buf = [0u8; 4]; + for b in c.encode_utf8(&mut buf).as_bytes() { + write_byte(*b)?; + } + Ok(()) + } + + pub fn read_char() -> io::Result { + let first = read_byte()?; + + if first & 0x80 == 0 { + return Ok(first as char); + } + + let mut bytes = [first, 0, 0, 0]; + + let remain = if first & 0b1110_0000 == 0b1100_0000 { + 1 + } else if first & 0b1111_0000 == 0b1110_0000 { + 2 + } else /*if first & 0b1111_1000 == 0b1111_0000*/ { + 3 + }; + + for n in 1..=remain { + bytes[n] = read_byte()?; + } + + std::str::from_utf8(&bytes[..=remain]) + .map(|s| s.chars().nth(0).unwrap()) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + } +} + +#[derive(Debug, Clone)] +pub struct StdioOps { + old_tio: Option, + hdl_stdin : Value, + hdl_stdin_raw : Value, + hdl_stdout : Value, + hdl_stdout_raw : Value, } impl StdioOps { pub fn new() -> Box { Box::new(Self { + old_tio: None, hdl_stdin: 0, - hdl_stdout: 0 + hdl_stdin_raw: 0, + hdl_stdout: 0, + hdl_stdout_raw: 0, }) } } +impl Drop for StdioOps { + fn drop(&mut self) { + // Un-break the terminal + if let Some(tio) = self.old_tio.take() { + let _ = unsafe { libc::tcsetattr(libc::STDIN_FILENO, libc::TCSANOW, &tio) }; + } + } +} + impl CrsnExtension for StdioOps { fn init(&mut self, uniq: &CrsnUniq) { self.hdl_stdin = uniq.unique_handle(); + self.hdl_stdin_raw = uniq.unique_handle(); self.hdl_stdout = uniq.unique_handle(); + self.hdl_stdout_raw = uniq.unique_handle(); + + // This can fail if the input is not a tty + if let Ok(tio) = console::init_io() { + self.old_tio = Some(tio); + } } /// Get value of an extension-provided constant. @@ -50,8 +146,10 @@ impl CrsnExtension for StdioOps { fn get_constant_value<'a>(&self, name: &str) -> Option { match name { - "stdin" => Some(self.hdl_stdin), - "stdout" => Some(self.hdl_stdout), + "cin" => Some(self.hdl_stdin), + "cin_r" => Some(self.hdl_stdin_raw), + "cout" => Some(self.hdl_stdout), + "cout_r" => Some(self.hdl_stdout_raw), _ => None } } @@ -69,12 +167,36 @@ impl CrsnExtension for StdioOps { -> Result, Fault> { if handle == self.hdl_stdin { - let data = state.ext_mut::(); - return Ok(Some(data.console.read_char().expect("stdin read") as u64)); + match console::read_char() { + Ok(c) => { + return Ok(Some(c as Value)); + } + Err(e) => { + state.set_flag(Flag::Invalid, true); + if e.kind() != io::ErrorKind::InvalidData { + state.set_flag(Flag::Eof, true); + } + return Ok(Some(0)); + } + } } - if handle == self.hdl_stdout { - return Err(Fault::NotAllowed("Cannot read stdout".into())); + if handle == self.hdl_stdin_raw { + match console::read_byte() { + Ok(b) => { + return Ok(Some(b as Value)); + } + Err(_e) => { + state.set_flag(Flag::Invalid, true); + state.set_flag(Flag::Eof, true); + return Ok(Some(0)); + } + } + } + + if handle == self.hdl_stdout || handle == self.hdl_stdout_raw { + state.set_flag(Flag::Invalid, true); + return Ok(Some(0)); } Ok(None) @@ -87,18 +209,25 @@ impl CrsnExtension for StdioOps { if handle == self.hdl_stdout { if let Ok(a_char) = char::try_from((value & 0xFFFF_FFFF) as u32) { - let data = state.ext_mut::(); - - let mut b = [0; 4]; - data.console.write(a_char.encode_utf8(&mut b).as_bytes()).expect("stdout write"); + if console::write_char(a_char).is_err() { + state.set_flag(Flag::Eof, true); + } } else { - state.set_flag(Cond::Invalid, true); + state.set_flag(Flag::Invalid, true); } return Ok(Some(())); } - if handle == self.hdl_stdin { - return Err(Fault::NotAllowed("Cannot write stdin".into())); + if handle == self.hdl_stdout_raw { + if console::write_byte((value & 0xFF) as u8).is_err() { + state.set_flag(Flag::Eof, true); + } + return Ok(Some(())); + } + + if handle == self.hdl_stdin || handle == self.hdl_stdin_raw { + state.set_flag(Flag::Invalid, true); + return Ok(Some(())); } Ok(None) diff --git a/examples/number_array.csn b/examples/number_array.csn index 146e6df..c1cbb92 100644 --- a/examples/number_array.csn +++ b/examples/number_array.csn @@ -3,6 +3,6 @@ (sym buf r7) (mkbf buf (r0 66 67 68 '\n')) - (bfrpop @stdout @buf) + (bfrpop @cout @buf) (s.nem -1) ) diff --git a/examples/stdio.csn b/examples/stdio.csn index 5a52cbe..36538d3 100644 --- a/examples/stdio.csn +++ b/examples/stdio.csn @@ -20,27 +20,27 @@ (:next) (bfrd ch @str pos) (ret.ov) - (ld @stdout ch) + (ld @cout ch) (inc pos) (j :next) ) (:loop) - (ld r0 @stdin) + (ld r0 @cin) (cmp r0 'q' - (eq? (ld @stdout '\n') + (eq? (ld @cout '\n') (halt))) ; uppercase ASCII (cmp r0 'a' (? (j :badchar))) (sub r0 ' ') - (ld @stdout r0) + (ld @cout r0) (j :loop) (:badchar) - (ld @stdout r0) + (ld @cout r0) (bfrd r0 @TXT T_UNK) (call print r0) (j :loop) diff --git a/examples/stdio_rev.csn b/examples/stdio_rev.csn index bd4e00a..bb09044 100644 --- a/examples/stdio_rev.csn +++ b/examples/stdio_rev.csn @@ -1,9 +1,9 @@ ( (mkbf r7) - (:r)(ld r0 @stdin) + (:r)(ld r0 @cin) (cmp r0 '\n' (eq? (j :q))) (bfpush @r7 r0) (j :r) - (:q)(bfpop @stdout @r7 (nem? (j :q))) - (ld @stdout '\n') + (:q)(bfpop @cout @r7 (nem? (j :q))) + (ld @cout '\n') ) diff --git a/examples/stdio_rot13.csn b/examples/stdio_rot13.csn old mode 100644 new mode 100755 index 6a5d141..258804c --- a/examples/stdio_rot13.csn +++ b/examples/stdio_rot13.csn @@ -1,6 +1,9 @@ +#!/usr/bin/env crsn ( (:again) - (ld r0 @stdin (inval? (halt))) + (ld r0 @cin + (eof? (halt)) + (inval? (j :again))) (rcmp r0 'A' 'Z' (eq? (ld r1 'A')) (ne? (rcmp r0 'a' 'z' @@ -11,6 +14,6 @@ (mod r0 26) (add r0 r1) (:print) - (ld @stdout r0) + (ld @cout r0) (j :again) )