implement "lds" for objects (buffers, cin)

pull/21/head
Ondřej Hruška 3 years ago
parent 1e6e45ea72
commit 2d4f6b4af1
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 4
      README.md
  2. 33
      crsn/src/asm/data/rd.rs
  3. 2
      crsn/src/asm/parse/arg_parser.rs
  4. 13
      crsn/src/builtin/defs.rs
  5. 36
      crsn/src/builtin/exec.rs
  6. 48
      crsn/src/builtin/parse.rs
  7. 11
      crsn/src/module/mod.rs
  8. 2
      crsn/src/runtime/run_thread/state.rs
  9. 16
      crsn_buf/src/exec.rs
  10. 8
      crsn_buf/src/lib.rs
  11. 49
      crsn_stdio/src/lib.rs
  12. 7
      examples/lds_buf.csn
  13. 8
      examples/lds_cin.csn

@ -331,6 +331,10 @@ Jumping to a label is always safer than a manual skip.
; Functionally, this instruction is equivalent to a sequence of "ld"
(lds Wr (Rd...)) ; example - (lds @cout (65 66 67))
(lds Wr "string")
; Some objects can be used as the source for "lds":
; - @cin = read all to EOF or the first fault (invalid utf8)
; - @buffer = read all items in a buffer, first to last, without consuming it
(lds Wr @Obj)
; Exchange two register's values
(xch RW RW)

@ -3,6 +3,8 @@ use std::fmt;
use crate::asm::data::{RdData, Register};
use crate::asm::data::literal::Value;
use crate::runtime::run_thread::RunState;
use crate::runtime::fault::Fault;
/// Data source argument (read-only)
#[derive(Clone, Copy, Eq, PartialEq)]
@ -47,14 +49,25 @@ impl Debug for Rd {
/// Reference an object through a handle
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct RdObj(Register);
pub enum RdObj {
Reg(Register),
Imm(Value),
}
impl RdObj {
pub const fn new(reg: Register) -> Self {
RdObj(reg)
pub const fn from_reg(reg: Register) -> Self {
RdObj::Reg(reg)
}
pub const fn from_value(value: Value) -> Self {
RdObj::Imm(value)
}
pub const fn reg(self) -> Register {
self.0
pub fn read(self, state: &mut RunState) -> Result<Value, Fault> {
Ok(match self {
RdObj::Reg(r) => state.read(Rd::new(RdData::Register(r)))?,
RdObj::Imm(v) => v,
})
}
}
@ -66,12 +79,18 @@ impl From<&RdObj> for RdObj {
impl Display for RdObj {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "@{}", self.0)
match self {
RdObj::Reg(r) => write!(f, "@{}", r),
RdObj::Imm(v) => write!(f, "@{:#x}", v)
}
}
}
impl Debug for RdObj {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Obj({})", self.reg())
match self {
RdObj::Reg(r) => write!(f, "Obj({})", r),
RdObj::Imm(v) => write!(f, "Obj({:#x})", v)
}
}
}

@ -102,7 +102,7 @@ impl<'a> TokenParser<'a> {
pub fn next_rdobj(&mut self) -> Result<RdObj, CrsnError> {
match parse_rd(self.next_or_err()?, self.pcx)? {
Rd(RdData::RegObject(reg)) => {
Ok(RdObj::new(reg))
Ok(RdObj::from_reg(reg))
}
other => {
Err(CrsnError::Parse(

@ -33,6 +33,7 @@ impl SleepUnit {
#[derive(Clone, Debug, PartialEq)]
pub enum LdsValue {
Values(Vec<Rd>),
Handle(RdObj),
Chars(String),
}
@ -77,17 +78,17 @@ pub enum BuiltinOp {
/// The object is released and the handle becomes invalid.
Delete(RdObj),
/// Move a value
Move { dst: Wr, src: Rd },
Load { dst: Wr, src: Rd },
/// Move lower 32 bits
Move32 { dst: RdWr, src: Rd },
Load32 { dst: RdWr, src: Rd },
/// Move lower 16 bits
Move16 { dst: RdWr, src: Rd },
Load16 { dst: RdWr, src: Rd },
/// Move lower 8 bits
Move8 { dst: RdWr, src: Rd },
Load8 { dst: RdWr, src: Rd },
/// Move N values
MoveMultiple { dst: Wr, src: Rd, count: Rd },
LoadMultiple { dst: Wr, src: Rd, count: Rd },
/// Move values from a string or integer list
MoveSequence { dst: Wr, value: LdsValue },
LoadSequence { dst: Wr, value: LdsValue },
/// Swap two registers
Exchange { a: RdWr, b: RdWr },
/// Store runtime status to a register

@ -2,7 +2,6 @@ use std::time::Duration;
use sexp::Sexp;
use crate::asm::data::{Rd, RdData};
use crate::asm::data::literal::{Addr, Value};
use crate::builtin::defs::{Barrier, BuiltinOp, LdsValue};
use crate::module::{EvalRes, OpTrait};
@ -103,13 +102,13 @@ impl OpTrait for BuiltinOp {
let pc = state.get_pc();
program.validate_jump(pc, Addr((pc.0 as i64 + res.advance) as u64))?;
}
BuiltinOp::Move { dst, src } => {
BuiltinOp::Load { dst, src } => {
state.clear_status();
let val = state.read(src)?;
state.update_status(val);
state.write(dst, val)?;
}
BuiltinOp::MoveMultiple { dst, src, count } => {
BuiltinOp::LoadMultiple { dst, src, count } => {
state.clear_status();
let mut count = state.read(count)?;
let mut last = 0;
@ -120,7 +119,7 @@ impl OpTrait for BuiltinOp {
}
state.update_status(last);
}
BuiltinOp::Move32 { dst, src } => {
BuiltinOp::Load32 { dst, src } => {
state.clear_status();
let new = state.read(src)?;
let old = state.read(dst)?;
@ -128,7 +127,7 @@ impl OpTrait for BuiltinOp {
state.update_status(val);
state.write(dst, val)?;
}
BuiltinOp::Move16 { dst, src } => {
BuiltinOp::Load16 { dst, src } => {
state.clear_status();
let new = state.read(src)?;
let old = state.read(dst)?;
@ -136,7 +135,7 @@ impl OpTrait for BuiltinOp {
state.update_status(val);
state.write(dst, val)?;
}
BuiltinOp::Move8 { dst, src } => {
BuiltinOp::Load8 { dst, src } => {
state.clear_status();
let new = state.read(src)?;
let old = state.read(dst)?;
@ -144,7 +143,7 @@ impl OpTrait for BuiltinOp {
state.update_status(val);
state.write(dst, val)?;
}
BuiltinOp::MoveSequence { dst, value } => {
BuiltinOp::LoadSequence { dst, value } => {
match value {
LdsValue::Values(vals) => {
for val in vals {
@ -157,6 +156,21 @@ impl OpTrait for BuiltinOp {
state.write(dst, val as Value)?;
}
}
LdsValue::Handle(h) => {
let hv = h.read(state)?;
let mut found = false;
for ex in info.program.extensions.iter() {
if ex.read_obj_all(state, *dst, hv)?.is_some() {
found = true;
}
}
if !found {
warn!("Object {:#x} to read does not exist!", hv);
state.set_flag(Flag::Invalid, true);
}
}
}
}
BuiltinOp::Exchange { a, b } => {
@ -177,17 +191,17 @@ impl OpTrait for BuiltinOp {
std::thread::sleep(Duration::from_micros(state.read(micros)? * unit_us.micros()))
}
BuiltinOp::Delete(obj) => {
let x = state.read(Rd::new(RdData::Register(obj.reg())))?;
let hv = obj.read(state)?;
trace!("Del object: {:#x}", x);
trace!("Del object: {:#x}", hv);
let mut dropped = false;
for ex in info.program.extensions.iter() {
if ex.drop_obj(info, state, x)?.is_some() {
if ex.drop_obj(info, state, hv)?.is_some() {
dropped = true;
}
}
if !dropped {
warn!("Object {:#x} to del does not exist!", x);
warn!("Object {:#x} to del does not exist!", hv);
state.set_flag(Flag::Invalid, true);
}
}

@ -11,6 +11,7 @@ use crate::asm::patches::ErrWithPos;
use crate::builtin::defs::{Barrier, BuiltinOp, SleepUnit, LdsValue};
use crate::module::ParseRes;
use crate::utils::A;
use crate::asm::data::{Rd, RdData, RdObj};
pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
let pcx = args.pcx;
@ -183,35 +184,35 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
}
"ld" => {
BuiltinOp::Move {
BuiltinOp::Load {
dst: args.next_wr()?,
src: args.next_rd()?,
}
}
"ld32" => {
BuiltinOp::Move32 {
BuiltinOp::Load32 {
dst: args.next_rdwr()?,
src: args.next_rd()?,
}
}
"ld16" => {
BuiltinOp::Move16 {
BuiltinOp::Load16 {
dst: args.next_rdwr()?,
src: args.next_rd()?,
}
}
"ld8" => {
BuiltinOp::Move8 {
BuiltinOp::Load8 {
dst: args.next_rdwr()?,
src: args.next_rd()?,
}
}
"ldn" => {
BuiltinOp::MoveMultiple {
BuiltinOp::LoadMultiple {
dst: args.next_wr()?,
src: args.next_rd()?,
count: args.next_rd()?,
@ -225,6 +226,22 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
Sexp::Atom(Atom::QS(s), _) => {
LdsValue::Chars(s)
}
tok @ Sexp::Atom(Atom::S(_), _) => {
let pos = tok.pos().clone();
match parse_rd(tok, args.pcx)? {
Rd(RdData::ImmObject(v)) => {
LdsValue::Handle(RdObj::Imm(v))
}
Rd(RdData::RegObject(r)) => {
LdsValue::Handle(RdObj::Reg(r))
}
_ => {
return Err(CrsnError::Parse(
"Expected a handle, quoted string, or a tuple of values".into(),
pos));
}
}
}
Sexp::List(list, _) => {
let mut vals = vec![];
for v in list {
@ -233,11 +250,13 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
LdsValue::Values(vals)
}
other => {
return Err(CrsnError::Parse("Expected quoted string or a tuple of values".into(), other.pos().clone()));
return Err(CrsnError::Parse(
"Expected a handle, quoted string, or a tuple of values".into(),
other.pos().clone()));
}
};
BuiltinOp::MoveSequence { dst, value }
BuiltinOp::LoadSequence { dst, value }
}
"xch" => {
@ -359,15 +378,15 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp {
}
}
BuiltinOp::Delete(obj) => sexp::list(&[A("del"), A(obj)]),
BuiltinOp::Move { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]),
BuiltinOp::Move32 { dst, src } => sexp::list(&[A("ld32"), A(dst), A(src)]),
BuiltinOp::Move16 { dst, src } => sexp::list(&[A("ld16"), A(dst), A(src)]),
BuiltinOp::Move8 { dst, src } => sexp::list(&[A("ld8"), A(dst), A(src)]),
BuiltinOp::MoveMultiple { dst, src, count } => sexp::list(&[A("ldn"), A(dst), A(src), A(count)]),
BuiltinOp::Load { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]),
BuiltinOp::Load32 { dst, src } => sexp::list(&[A("ld32"), A(dst), A(src)]),
BuiltinOp::Load16 { dst, src } => sexp::list(&[A("ld16"), A(dst), A(src)]),
BuiltinOp::Load8 { dst, src } => sexp::list(&[A("ld8"), A(dst), A(src)]),
BuiltinOp::LoadMultiple { dst, src, count } => sexp::list(&[A("ldn"), A(dst), A(src), A(count)]),
BuiltinOp::Exchange { a, b } => sexp::list(&[A("xch"), A(a), A(b)]),
BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]),
BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]),
BuiltinOp::MoveSequence { dst, value } => {
BuiltinOp::LoadSequence { dst, value } => {
match value {
LdsValue::Values(vals) => {
let mut vals: Vec<_> = vals.iter().map(A).collect();
@ -378,6 +397,9 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp {
LdsValue::Chars(str) => {
sexp::list(&[A("lds"), A(dst), atom_qs(str)])
}
LdsValue::Handle(h) => {
sexp::list(&[A("lds"), A(dst), A(h)])
}
}
}
}

@ -15,6 +15,7 @@ use crate::runtime::run_thread::state::RunState;
use crate::runtime::run_thread::ThreadInfo;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use crate::asm::data::Wr;
mod eval_res;
@ -105,7 +106,7 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static {
Ok(None)
}
/// Run-time method called to read an object (using the object handle syntax)
/// Run-time method called to read an object ("ld" and its variants, using the object handle syntax)
fn read_obj(&self, state: &mut RunState, handle: Value)
-> Result<Option<Value>, Fault>
{
@ -113,6 +114,14 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static {
Ok(None)
}
/// Run-time method called to read all values from an object ("lds" using the object handle syntax)
fn read_obj_all(&self, state: &mut RunState, whandle: Wr, rhandle: Value)
-> Result<Option<()>, Fault>
{
// Default impl - we do not support reading this object
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>
{

@ -91,7 +91,7 @@ impl RunState {
/// Read object handle value
pub fn read_obj(&mut self, rdo: impl Into<RdObj>) -> Result<Value, Fault> {
self.read(Rd::new(RdData::Register(rdo.into().reg())))
rdo.into().read(self)
}
/// Read a `Rd` value

@ -10,6 +10,7 @@ use crsn::sexp::{atom_qs, Sexp};
use crsn::utils::A;
use crate::defs::{BufIoMode, BufOps, BufValue};
use crsn::asm::data::Wr;
#[derive(Debug, Default, Clone)]
struct Buffer {
@ -318,6 +319,21 @@ pub(crate) fn read_obj(state: &mut RunState, handle: Value)
}
}
/// Run-time method called to read all from an object (using the object handle syntax)
pub(crate) fn read_obj_all(state: &mut RunState, whandle: Wr, rhandle: Value)
-> Result<Option<()>, Fault>
{
let store: &mut ExtData = state.ext_mut();
if let Some(buf) = store.buffers.get_mut(&rhandle) {
for v in buf.data.clone() { // FIXME potentially needless clone (but allows "aliasing")
state.write(whandle, v)?;
}
Ok(Some(()))
} else {
Ok(None)
}
}
/// Run-time method called to write an object (using the object handle syntax)
pub(crate) fn write_obj(state: &mut RunState, handle: Value, value: Value) -> Result<Option<()>, Fault>
{

@ -7,6 +7,7 @@ use crsn::runtime::fault::Fault;
use crsn::runtime::run_thread::{RunState, ThreadInfo};
use crsn::sexp::SourcePosition;
use crate::parse::{BUF_IOMODE_STACK, BUF_IOMODE_REVERSE_STACK, BUF_IOMODE_QUEUE, BUF_IOMODE_REVERSE_QUEUE};
use crsn::asm::data::Wr;
mod defs;
mod parse;
@ -59,4 +60,11 @@ impl CrsnExtension for BufOps {
_ => None
}
}
/// Run-time method called to read all values from an object ("lds" using the object handle syntax)
fn read_obj_all(&self, state: &mut RunState, whandle: Wr, rhandle: Value)
-> Result<Option<()>, Fault>
{
exec::read_obj_all(state, whandle, rhandle)
}
}

@ -10,6 +10,7 @@ use std::convert::TryFrom;
use std::io;
use crsn::asm::instr::cond::Flag;
use std::fmt;
use crsn::asm::data::Wr;
mod console {
use std::{io};
@ -244,4 +245,52 @@ impl CrsnExtension for StdioOps {
Ok(None)
}
/// Run-time method called to read all values from an object ("lds" using the object handle syntax)
fn read_obj_all(&self, state: &mut RunState, whandle: Wr, rhandle: Value)
-> Result<Option<()>, Fault>
{
if rhandle == self.hdl_stdin {
loop {
match console::read_char() {
Ok(c) => {
state.write(whandle, c as Value)?;
}
Err(e) => {
if e.kind() != io::ErrorKind::InvalidData {
state.set_flag(Flag::Eof, true);
} else {
state.set_flag(Flag::Invalid, true);
}
return Ok(Some(()));
}
}
}
}
if rhandle == self.hdl_stdin_raw {
loop {
match console::read_byte() {
Ok(c) => {
state.write(whandle, c as Value)?;
}
Err(e) => {
if e.kind() != io::ErrorKind::InvalidData {
state.set_flag(Flag::Eof, true);
} else {
state.set_flag(Flag::Invalid, true);
}
return Ok(Some(()));
}
}
}
}
if rhandle == self.hdl_stdout || rhandle == self.hdl_stdout_raw {
state.set_flag(Flag::Invalid, true);
return Ok(Some(()));
}
Ok(None)
}
}

@ -0,0 +1,7 @@
(
(mkbf r0 "Lazy fox\n")
(lds @cout @r0) ; buffers implement lds as a non-consuming read from start to end
(lds @cout @r0)
(lds @cout @r0)
(halt)
)

@ -0,0 +1,8 @@
(
; Read all stdin and print it reversed
(mkbf r0)
(lds @r0 @cin) ; cin implements the "lds" operation
(bfrev @r0)
(lds @cout @r0)
(del @r0)
)
Loading…
Cancel
Save