forked from MightyPork/crsn
parent
e3fe3c6d72
commit
9d7f3d25c8
@ -0,0 +1,11 @@ |
|||||||
|
[package] |
||||||
|
name = "crsn_stdio" |
||||||
|
version = "0.1.0" |
||||||
|
authors = ["Ondřej Hruška <ondra@ondrovo.com>"] |
||||||
|
edition = "2018" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
crsn = { path = "../crsn" } |
||||||
|
console = "0.12.0" |
@ -0,0 +1,107 @@ |
|||||||
|
use crsn::asm::data::literal::Value; |
||||||
|
use crsn::asm::error::CrsnError; |
||||||
|
use crsn::asm::instr::op::OpKind; |
||||||
|
use crsn::asm::parse::arg_parser::TokenParser; |
||||||
|
use crsn::module::{CrsnExtension, ParseRes, CrsnUniq}; |
||||||
|
use crsn::runtime::fault::Fault; |
||||||
|
use crsn::runtime::run_thread::{RunState, ThreadInfo}; |
||||||
|
use crsn::sexp::SourcePosition; |
||||||
|
use crsn::asm::data::Mask; |
||||||
|
use std::io::{Read, stdin, stdout, Write}; |
||||||
|
use std::convert::TryFrom; |
||||||
|
use crsn::asm::instr::Cond; |
||||||
|
use console::Term; |
||||||
|
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub struct StdioOps { |
||||||
|
hdl_stdin : Value, |
||||||
|
hdl_stdout : Value, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
struct StdioData { |
||||||
|
console: Term, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for StdioData { |
||||||
|
fn default() -> Self { |
||||||
|
Self { |
||||||
|
console : Term::stdout() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl StdioOps { |
||||||
|
pub fn new() -> Box<dyn CrsnExtension> { |
||||||
|
Box::new(Self { |
||||||
|
hdl_stdin: 0, |
||||||
|
hdl_stdout: 0 |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl CrsnExtension for StdioOps { |
||||||
|
fn init(&mut self, uniq: &CrsnUniq) { |
||||||
|
self.hdl_stdin = uniq.unique_handle(); |
||||||
|
self.hdl_stdout = uniq.unique_handle(); |
||||||
|
} |
||||||
|
|
||||||
|
/// Get value of an extension-provided constant.
|
||||||
|
/// This constant may be an object handle, or a constant value used as argument in some other instruction.
|
||||||
|
fn get_constant_value<'a>(&self, name: &str) -> Option<Value> |
||||||
|
{ |
||||||
|
match name { |
||||||
|
"stdin" => Some(self.hdl_stdin), |
||||||
|
"stdout" => Some(self.hdl_stdout), |
||||||
|
_ => None |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn name(&self) -> &'static str { |
||||||
|
"stdio" |
||||||
|
} |
||||||
|
|
||||||
|
fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> { |
||||||
|
Ok(ParseRes::Unknown(args)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Run-time method called to read an object (using the object handle syntax)
|
||||||
|
fn read_obj(&self, state: &mut RunState, handle: Value, _mask: Mask) |
||||||
|
-> 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)); |
||||||
|
} |
||||||
|
|
||||||
|
if handle == self.hdl_stdout { |
||||||
|
return Err(Fault::NotAllowed("Cannot read stdout".into())); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(None) |
||||||
|
} |
||||||
|
|
||||||
|
/// Run-time method called to write an object (using the object handle syntax)
|
||||||
|
fn write_obj(&self, state: &mut RunState, handle: Value, _mask: Mask, value: Value) -> Result<Option<()>, Fault> |
||||||
|
{ |
||||||
|
state.clear_status(); |
||||||
|
|
||||||
|
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"); |
||||||
|
} else { |
||||||
|
state.set_flag(Cond::Invalid, true); |
||||||
|
} |
||||||
|
return Ok(Some(())); |
||||||
|
} |
||||||
|
|
||||||
|
if handle == self.hdl_stdin { |
||||||
|
return Err(Fault::NotAllowed("Cannot write stdin".into())); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(None) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
( |
||||||
|
; we don't have strings yet 👌 |
||||||
|
(ld @stdout 'H') (ld @stdout 'e') (ld @stdout 'l') (ld @stdout 'l') (ld @stdout 'o') |
||||||
|
(ld @stdout 32) (ld @stdout 'c') (ld @stdout 'r') (ld @stdout 's') (ld @stdout 'n') |
||||||
|
(ld @stdout '…') (ld @stdout 32) |
||||||
|
|
||||||
|
(:loop) |
||||||
|
(ld r0 @stdin) |
||||||
|
(cmp r0 'a' (<? (j :badchar))) |
||||||
|
(cmp r0 'z' (>? (j :badchar))) |
||||||
|
(sub r0 32) ; to uppercase |
||||||
|
(ld @stdout r0) |
||||||
|
(j :loop) |
||||||
|
(:badchar) |
||||||
|
(ld @stdout '🐈') |
||||||
|
(j :loop) |
||||||
|
) |
Loading…
Reference in new issue