implement "lds" for objects (buffers, cin)

pull/21/head
Ondřej Hruška 4 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" ; Functionally, this instruction is equivalent to a sequence of "ld"
(lds Wr (Rd...)) ; example - (lds @cout (65 66 67)) (lds Wr (Rd...)) ; example - (lds @cout (65 66 67))
(lds Wr "string") (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 ; Exchange two register's values
(xch RW RW) (xch RW RW)

@ -3,6 +3,8 @@ use std::fmt;
use crate::asm::data::{RdData, Register}; use crate::asm::data::{RdData, Register};
use crate::asm::data::literal::Value; use crate::asm::data::literal::Value;
use crate::runtime::run_thread::RunState;
use crate::runtime::fault::Fault;
/// Data source argument (read-only) /// Data source argument (read-only)
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
@ -47,14 +49,25 @@ impl Debug for Rd {
/// Reference an object through a handle /// Reference an object through a handle
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub struct RdObj(Register); pub enum RdObj {
Reg(Register),
Imm(Value),
}
impl RdObj { impl RdObj {
pub const fn new(reg: Register) -> Self { pub const fn from_reg(reg: Register) -> Self {
RdObj(reg) 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 { impl Display for RdObj {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 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 { impl Debug for RdObj {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 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> { pub fn next_rdobj(&mut self) -> Result<RdObj, CrsnError> {
match parse_rd(self.next_or_err()?, self.pcx)? { match parse_rd(self.next_or_err()?, self.pcx)? {
Rd(RdData::RegObject(reg)) => { Rd(RdData::RegObject(reg)) => {
Ok(RdObj::new(reg)) Ok(RdObj::from_reg(reg))
} }
other => { other => {
Err(CrsnError::Parse( Err(CrsnError::Parse(

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

@ -2,7 +2,6 @@ use std::time::Duration;
use sexp::Sexp; use sexp::Sexp;
use crate::asm::data::{Rd, RdData};
use crate::asm::data::literal::{Addr, Value}; use crate::asm::data::literal::{Addr, Value};
use crate::builtin::defs::{Barrier, BuiltinOp, LdsValue}; use crate::builtin::defs::{Barrier, BuiltinOp, LdsValue};
use crate::module::{EvalRes, OpTrait}; use crate::module::{EvalRes, OpTrait};
@ -103,13 +102,13 @@ impl OpTrait for BuiltinOp {
let pc = state.get_pc(); let pc = state.get_pc();
program.validate_jump(pc, Addr((pc.0 as i64 + res.advance) as u64))?; program.validate_jump(pc, Addr((pc.0 as i64 + res.advance) as u64))?;
} }
BuiltinOp::Move { dst, src } => { BuiltinOp::Load { dst, src } => {
state.clear_status(); state.clear_status();
let val = state.read(src)?; let val = state.read(src)?;
state.update_status(val); state.update_status(val);
state.write(dst, val)?; state.write(dst, val)?;
} }
BuiltinOp::MoveMultiple { dst, src, count } => { BuiltinOp::LoadMultiple { dst, src, count } => {
state.clear_status(); state.clear_status();
let mut count = state.read(count)?; let mut count = state.read(count)?;
let mut last = 0; let mut last = 0;
@ -120,7 +119,7 @@ impl OpTrait for BuiltinOp {
} }
state.update_status(last); state.update_status(last);
} }
BuiltinOp::Move32 { dst, src } => { BuiltinOp::Load32 { dst, src } => {
state.clear_status(); state.clear_status();
let new = state.read(src)?; let new = state.read(src)?;
let old = state.read(dst)?; let old = state.read(dst)?;
@ -128,7 +127,7 @@ impl OpTrait for BuiltinOp {
state.update_status(val); state.update_status(val);
state.write(dst, val)?; state.write(dst, val)?;
} }
BuiltinOp::Move16 { dst, src } => { BuiltinOp::Load16 { dst, src } => {
state.clear_status(); state.clear_status();
let new = state.read(src)?; let new = state.read(src)?;
let old = state.read(dst)?; let old = state.read(dst)?;
@ -136,7 +135,7 @@ impl OpTrait for BuiltinOp {
state.update_status(val); state.update_status(val);
state.write(dst, val)?; state.write(dst, val)?;
} }
BuiltinOp::Move8 { dst, src } => { BuiltinOp::Load8 { dst, src } => {
state.clear_status(); state.clear_status();
let new = state.read(src)?; let new = state.read(src)?;
let old = state.read(dst)?; let old = state.read(dst)?;
@ -144,7 +143,7 @@ impl OpTrait for BuiltinOp {
state.update_status(val); state.update_status(val);
state.write(dst, val)?; state.write(dst, val)?;
} }
BuiltinOp::MoveSequence { dst, value } => { BuiltinOp::LoadSequence { dst, value } => {
match value { match value {
LdsValue::Values(vals) => { LdsValue::Values(vals) => {
for val in vals { for val in vals {
@ -157,6 +156,21 @@ impl OpTrait for BuiltinOp {
state.write(dst, val as Value)?; 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 } => { BuiltinOp::Exchange { a, b } => {
@ -177,17 +191,17 @@ impl OpTrait for BuiltinOp {
std::thread::sleep(Duration::from_micros(state.read(micros)? * unit_us.micros())) std::thread::sleep(Duration::from_micros(state.read(micros)? * unit_us.micros()))
} }
BuiltinOp::Delete(obj) => { 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; let mut dropped = false;
for ex in info.program.extensions.iter() { 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; dropped = true;
} }
} }
if !dropped { 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); 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::builtin::defs::{Barrier, BuiltinOp, SleepUnit, LdsValue};
use crate::module::ParseRes; use crate::module::ParseRes;
use crate::utils::A; 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> { pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
let pcx = args.pcx; let pcx = args.pcx;
@ -183,35 +184,35 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
} }
"ld" => { "ld" => {
BuiltinOp::Move { BuiltinOp::Load {
dst: args.next_wr()?, dst: args.next_wr()?,
src: args.next_rd()?, src: args.next_rd()?,
} }
} }
"ld32" => { "ld32" => {
BuiltinOp::Move32 { BuiltinOp::Load32 {
dst: args.next_rdwr()?, dst: args.next_rdwr()?,
src: args.next_rd()?, src: args.next_rd()?,
} }
} }
"ld16" => { "ld16" => {
BuiltinOp::Move16 { BuiltinOp::Load16 {
dst: args.next_rdwr()?, dst: args.next_rdwr()?,
src: args.next_rd()?, src: args.next_rd()?,
} }
} }
"ld8" => { "ld8" => {
BuiltinOp::Move8 { BuiltinOp::Load8 {
dst: args.next_rdwr()?, dst: args.next_rdwr()?,
src: args.next_rd()?, src: args.next_rd()?,
} }
} }
"ldn" => { "ldn" => {
BuiltinOp::MoveMultiple { BuiltinOp::LoadMultiple {
dst: args.next_wr()?, dst: args.next_wr()?,
src: args.next_rd()?, src: args.next_rd()?,
count: 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), _) => { Sexp::Atom(Atom::QS(s), _) => {
LdsValue::Chars(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, _) => { Sexp::List(list, _) => {
let mut vals = vec![]; let mut vals = vec![];
for v in list { 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) LdsValue::Values(vals)
} }
other => { 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" => { "xch" => {
@ -359,15 +378,15 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp {
} }
} }
BuiltinOp::Delete(obj) => sexp::list(&[A("del"), A(obj)]), BuiltinOp::Delete(obj) => sexp::list(&[A("del"), A(obj)]),
BuiltinOp::Move { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]), BuiltinOp::Load { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]),
BuiltinOp::Move32 { dst, src } => sexp::list(&[A("ld32"), A(dst), A(src)]), BuiltinOp::Load32 { dst, src } => sexp::list(&[A("ld32"), A(dst), A(src)]),
BuiltinOp::Move16 { dst, src } => sexp::list(&[A("ld16"), A(dst), A(src)]), BuiltinOp::Load16 { dst, src } => sexp::list(&[A("ld16"), A(dst), A(src)]),
BuiltinOp::Move8 { dst, src } => sexp::list(&[A("ld8"), A(dst), A(src)]), BuiltinOp::Load8 { 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::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::Exchange { a, b } => sexp::list(&[A("xch"), A(a), A(b)]),
BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]), BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]),
BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]), BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]),
BuiltinOp::MoveSequence { dst, value } => { BuiltinOp::LoadSequence { dst, value } => {
match value { match value {
LdsValue::Values(vals) => { LdsValue::Values(vals) => {
let mut vals: Vec<_> = vals.iter().map(A).collect(); let mut vals: Vec<_> = vals.iter().map(A).collect();
@ -378,6 +397,9 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp {
LdsValue::Chars(str) => { LdsValue::Chars(str) => {
sexp::list(&[A("lds"), A(dst), atom_qs(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 crate::runtime::run_thread::ThreadInfo;
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use crate::asm::data::Wr;
mod eval_res; mod eval_res;
@ -105,7 +106,7 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static {
Ok(None) 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) fn read_obj(&self, state: &mut RunState, handle: Value)
-> Result<Option<Value>, Fault> -> Result<Option<Value>, Fault>
{ {
@ -113,6 +114,14 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static {
Ok(None) 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) /// 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> fn write_obj(&self, state: &mut RunState, handle: Value, value: Value) -> Result<Option<()>, Fault>
{ {

@ -91,7 +91,7 @@ impl RunState {
/// Read object handle value /// Read object handle value
pub fn read_obj(&mut self, rdo: impl Into<RdObj>) -> Result<Value, Fault> { 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 /// Read a `Rd` value

@ -10,6 +10,7 @@ use crsn::sexp::{atom_qs, Sexp};
use crsn::utils::A; use crsn::utils::A;
use crate::defs::{BufIoMode, BufOps, BufValue}; use crate::defs::{BufIoMode, BufOps, BufValue};
use crsn::asm::data::Wr;
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
struct Buffer { 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) /// 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> 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::runtime::run_thread::{RunState, ThreadInfo};
use crsn::sexp::SourcePosition; use crsn::sexp::SourcePosition;
use crate::parse::{BUF_IOMODE_STACK, BUF_IOMODE_REVERSE_STACK, BUF_IOMODE_QUEUE, BUF_IOMODE_REVERSE_QUEUE}; 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 defs;
mod parse; mod parse;
@ -59,4 +60,11 @@ impl CrsnExtension for BufOps {
_ => None _ => 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 std::io;
use crsn::asm::instr::cond::Flag; use crsn::asm::instr::cond::Flag;
use std::fmt; use std::fmt;
use crsn::asm::data::Wr;
mod console { mod console {
use std::{io}; use std::{io};
@ -244,4 +245,52 @@ impl CrsnExtension for StdioOps {
Ok(None) 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