forked from MightyPork/crsn
add stdio module
This commit is contained in:
Generated
+51
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user