Croissant Runtime
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
crsn/crsn_stdio/src/lib.rs

248 lines
7.3 KiB

4 years ago
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};
4 years ago
use crsn::sexp::SourcePosition;
use std::convert::TryFrom;
use std::io;
use crsn::asm::instr::cond::Flag;
use std::fmt;
4 years ago
mod console {
use std::{io};
use std::os::unix::io::RawFd;
4 years ago
use std::ffi::c_void;
use std::mem::{self, MaybeUninit};
fn setup_fd(fd: RawFd) -> io::Result<libc::termios> {
use libc::*;
let mut tio = MaybeUninit::uninit();
if 0 != unsafe { tcgetattr(fd, tio.as_mut_ptr()) } {
return Err(io::Error::last_os_error());
}
let mut tio = unsafe { MaybeUninit::assume_init(tio) };
let old_tio : termios = unsafe { mem::transmute_copy(&tio) };
tio.c_iflag &= !(/*BRKINT |*/ /*ICRNL |*/ INPCK | ISTRIP | IXON);
tio.c_oflag |= ONLCR;
tio.c_cflag |= CS8;
tio.c_lflag &= !(ECHO | ICANON | IEXTEN /*| ISIG*/);
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
if 0 != unsafe { tcsetattr(fd, TCSANOW, &tio) } {
return Err(io::Error::last_os_error());
}
Ok(old_tio)
}
pub fn init_io() -> io::Result<libc::termios> {
setup_fd(libc::STDIN_FILENO)
}
4 years ago
pub fn read_byte() -> io::Result<u8> {
let mut buf = 0u8;
let len = unsafe { libc::read(libc::STDIN_FILENO, &mut buf as *mut u8 as *mut c_void, 1) };
if len <= 0 {
Err(io::Error::last_os_error())
} else {
Ok(buf as u8)
4 years ago
}
}
pub fn write_byte(b : u8) -> io::Result<()> {
let len = unsafe { libc::write(libc::STDOUT_FILENO, &b as *const u8 as *const c_void, 1) };
if len <= 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
pub fn write_char(c : char) -> io::Result<()> {
let mut buf = [0u8; 4];
for b in c.encode_utf8(&mut buf).as_bytes() {
write_byte(*b)?;
}
Ok(())
}
pub fn read_char() -> io::Result<char> {
let first = read_byte()?;
if first & 0x80 == 0 {
return Ok(first as char);
}
let mut bytes = [first, 0, 0, 0];
let remain = if first & 0b1110_0000 == 0b1100_0000 {
1
} else if first & 0b1111_0000 == 0b1110_0000 {
2
} else /*if first & 0b1111_1000 == 0b1111_0000*/ {
3
};
for n in 1..=remain {
bytes[n] = read_byte()?;
}
std::str::from_utf8(&bytes[..=remain])
.map(|s| s.chars().nth(0).unwrap())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
}
#[derive(Clone)]
pub struct StdioOps {
old_tio: Option<libc::termios>,
hdl_stdin : Value,
hdl_stdin_raw : Value,
hdl_stdout : Value,
hdl_stdout_raw : Value,
4 years ago
}
impl fmt::Debug for StdioOps {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StdioOps")
.field("hdl_stdin", &format_args!("0x{:08x}", self.hdl_stdin))
.field("hdl_stdin_raw", &format_args!("0x{:08x}", self.hdl_stdin_raw))
.field("hdl_stdout", &format_args!("0x{:08x}", self.hdl_stdout))
.field("hdl_stdout_raw", &format_args!("0x{:08x}", self.hdl_stdout_raw))
.finish()
}
}
4 years ago
impl StdioOps {
pub fn new() -> Box<dyn CrsnExtension> {
Box::new(Self {
old_tio: None,
4 years ago
hdl_stdin: 0,
hdl_stdin_raw: 0,
hdl_stdout: 0,
hdl_stdout_raw: 0,
4 years ago
})
}
}
impl Drop for StdioOps {
fn drop(&mut self) {
// Un-break the terminal
if let Some(tio) = self.old_tio.take() {
let _ = unsafe { libc::tcsetattr(libc::STDIN_FILENO, libc::TCSANOW, &tio) };
}
}
}
4 years ago
impl CrsnExtension for StdioOps {
fn init(&mut self, uniq: &CrsnUniq) {
self.hdl_stdin = uniq.unique_handle();
self.hdl_stdin_raw = uniq.unique_handle();
4 years ago
self.hdl_stdout = uniq.unique_handle();
self.hdl_stdout_raw = uniq.unique_handle();
// This can fail if the input is not a tty
if let Ok(tio) = console::init_io() {
self.old_tio = Some(tio);
}
4 years ago
}
/// 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 {
"cin" => Some(self.hdl_stdin),
"cin_r" => Some(self.hdl_stdin_raw),
"cout" => Some(self.hdl_stdout),
"cout_r" => Some(self.hdl_stdout_raw),
4 years ago
_ => None
}
}
fn name(&self) -> &'static str {
"stdio"
}
fn parse_op<'a>(&self, _pos: &SourcePosition, _keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
4 years ago
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)
4 years ago
-> Result<Option<Value>, Fault>
{
if handle == self.hdl_stdin {
match console::read_char() {
Ok(c) => {
return Ok(Some(c as Value));
}
Err(e) => {
state.set_flag(Flag::Invalid, true);
if e.kind() != io::ErrorKind::InvalidData {
state.set_flag(Flag::Eof, true);
}
return Ok(Some(0));
}
}
4 years ago
}
if handle == self.hdl_stdin_raw {
match console::read_byte() {
Ok(b) => {
return Ok(Some(b as Value));
}
Err(_e) => {
state.set_flag(Flag::Invalid, true);
state.set_flag(Flag::Eof, true);
return Ok(Some(0));
}
}
}
if handle == self.hdl_stdout || handle == self.hdl_stdout_raw {
state.set_flag(Flag::Invalid, true);
return Ok(Some(0));
4 years ago
}
Ok(None)
}
/// Run-time method called to write an object (using the object handle syntax)
fn write_obj(&self, state: &mut RunState, handle: Value, value: Value) -> Result<Option<()>, Fault>
4 years ago
{
state.clear_status();
if handle == self.hdl_stdout {
if let Ok(a_char) = char::try_from((value & 0xFFFF_FFFF) as u32) {
if console::write_char(a_char).is_err() {
state.set_flag(Flag::Eof, true);
}
4 years ago
} else {
state.set_flag(Flag::Invalid, true);
4 years ago
}
return Ok(Some(()));
}
if handle == self.hdl_stdout_raw {
if console::write_byte((value & 0xFF) as u8).is_err() {
state.set_flag(Flag::Eof, true);
}
return Ok(Some(()));
}
if handle == self.hdl_stdin || handle == self.hdl_stdin_raw {
state.set_flag(Flag::Invalid, true);
return Ok(Some(()));
4 years ago
}
Ok(None)
}
}