From 9d7f3d25c8738f5638756d88573e2b5bfa2eee40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 8 Oct 2020 00:42:24 +0200 Subject: [PATCH] add stdio module --- Cargo.lock | 51 +++++++++++++++ Cargo.toml | 1 + crsn/src/asm/parse/parse_data.rs | 21 +++++- crsn_stdio/Cargo.toml | 11 ++++ crsn_stdio/src/lib.rs | 107 +++++++++++++++++++++++++++++++ examples/stdio.csn | 17 +++++ launcher/Cargo.toml | 1 + launcher/src/main.rs | 2 + 8 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 crsn_stdio/Cargo.toml create mode 100644 crsn_stdio/src/lib.rs create mode 100644 examples/stdio.csn diff --git a/Cargo.lock b/Cargo.lock index ffa4093..c285660 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,6 +168,23 @@ 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" @@ -207,6 +224,14 @@ dependencies = [ "crsn", ] +[[package]] +name = "crsn_stdio" +version = "0.1.0" +dependencies = [ + "console", + "crsn", +] + [[package]] name = "digest" version = "0.8.1" @@ -249,6 +274,12 @@ 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" @@ -348,6 +379,7 @@ dependencies = [ "crsn_arith", "crsn_screen", "crsn_stacks", + "crsn_stdio", "log", "serde", "simple_logger", @@ -964,6 +996,25 @@ 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/Cargo.toml b/Cargo.toml index 3ec18f3..44573dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ members = [ "crsn_arith", "crsn_stacks", "crsn_screen", + "crsn_stdio", ] diff --git a/crsn/src/asm/parse/parse_data.rs b/crsn/src/asm/parse/parse_data.rs index b4f41f1..453acce 100644 --- a/crsn/src/asm/parse/parse_data.rs +++ b/crsn/src/asm/parse/parse_data.rs @@ -85,9 +85,24 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result '\\', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + '\'' => '\'', + _ => { + return Err(CrsnError::Parse(format!("Unknown escape sequence: {}", s).into(), pos)); + } + }; + return Ok(DataDisp::Immediate(ch as u64)); } else { return Err(CrsnError::Parse(format!("Invalid character literal synax {}", s).into(), pos)); } @@ -96,7 +111,7 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result"] +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" diff --git a/crsn_stdio/src/lib.rs b/crsn_stdio/src/lib.rs new file mode 100644 index 0000000..edc7208 --- /dev/null +++ b/crsn_stdio/src/lib.rs @@ -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 { + 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 + { + 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, 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, Fault> + { + if handle == self.hdl_stdin { + let data = state.ext_mut::(); + 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, 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::(); + + 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) + } +} diff --git a/examples/stdio.csn b/examples/stdio.csn new file mode 100644 index 0000000..8757637 --- /dev/null +++ b/examples/stdio.csn @@ -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))) + (sub r0 32) ; to uppercase + (ld @stdout r0) + (j :loop) + (:badchar) + (ld @stdout '🐈') + (j :loop) +) diff --git a/launcher/Cargo.toml b/launcher/Cargo.toml index e9487a1..e61cac8 100644 --- a/launcher/Cargo.toml +++ b/launcher/Cargo.toml @@ -11,6 +11,7 @@ crsn = { path = "../crsn" } crsn_arith = { path = "../crsn_arith" } crsn_stacks = { path = "../crsn_stacks" } crsn_screen = { path = "../crsn_screen" } +crsn_stdio = { path = "../crsn_stdio" } simple_logger = "1.9.0" log = "0.4.11" diff --git a/launcher/src/main.rs b/launcher/src/main.rs index e69e631..2cbed04 100644 --- a/launcher/src/main.rs +++ b/launcher/src/main.rs @@ -15,6 +15,7 @@ use crsn::runtime::run_thread::{RunThread, ThreadToken, ThreadParams}; use crsn_arith::ArithOps; use crsn_screen::ScreenOps; use crsn_stacks::StackOps; +use crsn_stdio::StdioOps; mod read_file; mod serde_duration_millis; @@ -127,6 +128,7 @@ fn main() -> anyhow::Result<()> { ArithOps::new(), StackOps::new(), ScreenOps::new(), + StdioOps::new(), ])?; if config.asm_only {