forked from MightyPork/crsn
rewrite the stdio module to be less broken. also add sehbang support
This commit is contained in:
Generated
+3
-45
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<Atom> {
|
||||
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<Atom> {
|
||||
}
|
||||
|
||||
fn parse_unquoted_atom(s: &str, pos: &mut usize) -> ERes<Atom> {
|
||||
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<Atom> {
|
||||
}
|
||||
|
||||
fn parse_atom(s: &str, pos: &mut usize) -> ERes<Atom> {
|
||||
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<Atom> {
|
||||
}
|
||||
|
||||
fn parse_list(s: &str, pos: &mut usize) -> ERes<Vec<Sexp>> {
|
||||
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<Vec<Sexp>> {
|
||||
}
|
||||
|
||||
fn parse_sexp(s: &str, pos: &mut usize) -> ERes<Sexp> {
|
||||
trace!("parse_sexp {}", pos);
|
||||
//trace!("parse_sexp {}", pos);
|
||||
zspace(s, pos)?;
|
||||
let (c, _) = peek(s, *pos)?;
|
||||
let r = if c == '(' {
|
||||
|
||||
@@ -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<Flag> 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<Cond, CrsnError> {
|
||||
@@ -77,6 +130,8 @@ pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result<Cond, CrsnError> {
|
||||
"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<Cond, CrsnError> {
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::<Cond, u32>::new();
|
||||
let branch_count = branches.len();
|
||||
let end_lbl = Label::unique(label_num);
|
||||
|
||||
@@ -23,6 +23,17 @@ pub fn assemble(source: &str, uniq : &CrsnUniq, mut parsers: Vec<Box<dyn CrsnExt
|
||||
p.init(uniq);
|
||||
}
|
||||
|
||||
// remove first line if it looks like a shebang
|
||||
let source = if source.starts_with("#!") {
|
||||
if let Some(nl) = source.find('\n') {
|
||||
&source[nl + 1..]
|
||||
} else {
|
||||
source
|
||||
}
|
||||
} else {
|
||||
source
|
||||
};
|
||||
|
||||
let pcx = ParserContext {
|
||||
parsers: &parsers,
|
||||
state: RefCell::new(ParserState {
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::asm::parse::ParserContext;
|
||||
use crate::asm::parse::sexp_expect::expect_string_atom;
|
||||
|
||||
/// Utility for argument parsing
|
||||
#[derive(Debug)]
|
||||
pub struct TokenParser<'a> {
|
||||
orig_len: usize,
|
||||
args: Vec<Sexp>,
|
||||
|
||||
@@ -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<dyn CrsnExtension>],
|
||||
@@ -27,7 +28,7 @@ pub struct ParserContext<'a> {
|
||||
pub state: RefCell<ParserState>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ParserState {
|
||||
/// Register aliases within the routine
|
||||
pub reg_aliases: HashMap<RegisterAlias, Register>,
|
||||
|
||||
@@ -63,10 +63,11 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pos: &SourcePosition,
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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() {
|
||||
|
||||
@@ -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<EvalRes, Fault> {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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<ThreadInfo>,
|
||||
@@ -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
|
||||
|
||||
+17
-17
@@ -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<EvalRes, Fault> {
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-12
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,4 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
crsn = { path = "../crsn" }
|
||||
console = "0.12.0"
|
||||
libc = "0.2.79"
|
||||
|
||||
+159
-30
@@ -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;
|
||||
|
||||
mod console {
|
||||
use std::{io};
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
|
||||
fn setup_fd(fd: RawFd) -> io::Result<libc::termios> {
|
||||
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<libc::termios> {
|
||||
setup_fd(libc::STDIN_FILENO)
|
||||
}
|
||||
|
||||
pub fn read_byte() -> io::Result<u8> {
|
||||
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<char> {
|
||||
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<libc::termios>,
|
||||
hdl_stdin : Value,
|
||||
hdl_stdin_raw : Value,
|
||||
hdl_stdout : Value,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StdioData {
|
||||
console: Term,
|
||||
}
|
||||
|
||||
impl Default for StdioData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
console : Term::stdout()
|
||||
}
|
||||
}
|
||||
hdl_stdout_raw : Value,
|
||||
}
|
||||
|
||||
impl StdioOps {
|
||||
pub fn new() -> Box<dyn CrsnExtension> {
|
||||
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<Value>
|
||||
{
|
||||
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<Option<Value>, Fault>
|
||||
{
|
||||
if handle == self.hdl_stdin {
|
||||
let data = state.ext_mut::<StdioData>();
|
||||
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::<StdioData>();
|
||||
|
||||
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)
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
(sym buf r7)
|
||||
(mkbf buf (r0 66 67 68 '\n'))
|
||||
|
||||
(bfrpop @stdout @buf)
|
||||
(bfrpop @cout @buf)
|
||||
(s.nem -1)
|
||||
)
|
||||
|
||||
+5
-5
@@ -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)))
|
||||
(cmp r0 'z' (>? (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)
|
||||
|
||||
@@ -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')
|
||||
)
|
||||
|
||||
Regular → Executable
+5
-2
@@ -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)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user