forked from MightyPork/crsn
add stdio module
This commit is contained in:
Generated
+51
@@ -168,6 +168,23 @@ dependencies = [
|
|||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "crsn"
|
name = "crsn"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -207,6 +224,14 @@ dependencies = [
|
|||||||
"crsn",
|
"crsn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crsn_stdio"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"crsn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
@@ -249,6 +274,12 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c53dc3a653e0f64081026e4bf048d48fec9fce90c66e8326ca7292df0ff2d82"
|
checksum = "4c53dc3a653e0f64081026e4bf048d48fec9fce90c66e8326ca7292df0ff2d82"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -348,6 +379,7 @@ dependencies = [
|
|||||||
"crsn_arith",
|
"crsn_arith",
|
||||||
"crsn_screen",
|
"crsn_screen",
|
||||||
"crsn_stacks",
|
"crsn_stacks",
|
||||||
|
"crsn_stdio",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
@@ -964,6 +996,25 @@ dependencies = [
|
|||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ members = [
|
|||||||
"crsn_arith",
|
"crsn_arith",
|
||||||
"crsn_stacks",
|
"crsn_stacks",
|
||||||
"crsn_screen",
|
"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.starts_with('\'') && s.ends_with('\'') {
|
||||||
if s.chars().count() == 3 {
|
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() == '\\' {
|
} 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 {
|
} else {
|
||||||
return Err(CrsnError::Parse(format!("Invalid character literal synax {}", s).into(), pos));
|
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('@') {
|
if let Some(reference) = s.strip_prefix('@') {
|
||||||
/* extension constants (pre-defined handles) */
|
/* extension constants (pre-defined handles) */
|
||||||
for p in pcx.parsers {
|
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));
|
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_arith = { path = "../crsn_arith" }
|
||||||
crsn_stacks = { path = "../crsn_stacks" }
|
crsn_stacks = { path = "../crsn_stacks" }
|
||||||
crsn_screen = { path = "../crsn_screen" }
|
crsn_screen = { path = "../crsn_screen" }
|
||||||
|
crsn_stdio = { path = "../crsn_stdio" }
|
||||||
|
|
||||||
simple_logger = "1.9.0"
|
simple_logger = "1.9.0"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use crsn::runtime::run_thread::{RunThread, ThreadToken, ThreadParams};
|
|||||||
use crsn_arith::ArithOps;
|
use crsn_arith::ArithOps;
|
||||||
use crsn_screen::ScreenOps;
|
use crsn_screen::ScreenOps;
|
||||||
use crsn_stacks::StackOps;
|
use crsn_stacks::StackOps;
|
||||||
|
use crsn_stdio::StdioOps;
|
||||||
|
|
||||||
mod read_file;
|
mod read_file;
|
||||||
mod serde_duration_millis;
|
mod serde_duration_millis;
|
||||||
@@ -127,6 +128,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
ArithOps::new(),
|
ArithOps::new(),
|
||||||
StackOps::new(),
|
StackOps::new(),
|
||||||
ScreenOps::new(),
|
ScreenOps::new(),
|
||||||
|
StdioOps::new(),
|
||||||
])?;
|
])?;
|
||||||
|
|
||||||
if config.asm_only {
|
if config.asm_only {
|
||||||
|
|||||||
Reference in New Issue
Block a user