add stdio module

pull/21/head
Ondřej Hruška 3 years ago
parent e3fe3c6d72
commit 9d7f3d25c8
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 51
      Cargo.lock
  2. 1
      Cargo.toml
  3. 21
      crsn/src/asm/parse/parse_data.rs
  4. 11
      crsn_stdio/Cargo.toml
  5. 107
      crsn_stdio/src/lib.rs
  6. 17
      examples/stdio.csn
  7. 1
      launcher/Cargo.toml
  8. 2
      launcher/src/main.rs

51
Cargo.lock generated

@ -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"

@ -5,4 +5,5 @@ members = [
"crsn_arith",
"crsn_stacks",
"crsn_screen",
"crsn_stdio",
]

@ -85,9 +85,24 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnE
if s.starts_with('\'') && s.ends_with('\'') {
if s.chars().count() == 3 {
return Ok(DataDisp::Immediate(s.chars().nth(1).unwrap() as u64));
let ch = s.chars().nth(1).unwrap();
if ch == '\'' {
return Err(CrsnError::Parse("Use '\\'' for apos".into(), pos));
}
return Ok(DataDisp::Immediate(ch as u64));
} else if s.chars().count() == 4 && s.chars().nth(1).unwrap() == '\\' {
return Ok(DataDisp::Immediate(s.chars().nth(2).unwrap() as u64));
let ch = s.chars().nth(2).unwrap();
let ch = match ch {
'\\' => '\\',
'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<DataDisp, CrsnE
if let Some(reference) = s.strip_prefix('@') {
/* extension constants (pre-defined handles) */
for p in pcx.parsers {
if let Some(val) = p.get_constant_value(&s) {
if let Some(val) = p.get_constant_value(reference) {
return Ok(DataDisp::ImmObject(val));
}
}

@ -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)
)

@ -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"

@ -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 {

Loading…
Cancel
Save