Add loop syntactic sugar; errors now use print instead of debug; add check for duplicate labels.

master
Ondřej Hruška 4 years ago
parent fd90480cec
commit daff23dd98
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 30
      README.md
  2. 12
      crsn/src/asm/error.rs
  3. 4
      crsn/src/asm/instr/flatten.rs
  4. 2
      crsn/src/asm/parse/arg_parser.rs
  5. 53
      crsn/src/builtin/mod.rs
  6. 3
      crsn_stdio/src/lib.rs
  7. 43
      examples/loop.csn

@ -408,6 +408,36 @@ to a varying number of skips and conditional instructions by the assembler. Only
Jumping to a label is always safer than a manual skip. Jumping to a label is always safer than a manual skip.
### Loop syntactic sugar
Infinite loops are a very common construct, so there is special syntax added for them.
These are converted by the assembler to an anonymous label and a jump to it.
```
(loop
...ops
)
```
If you want to have a convenient jump target, give the loop a name. This lets you easily "break" and "continue"
by jumping to the labels.
```
(loop :label
...ops
)
```
becomes
```
(:label)
...ops
(j :label)
(:label-end)
```
## Built-in Instructions ## Built-in Instructions
...and pseudo-instructions ...and pseudo-instructions

@ -12,13 +12,13 @@ use crate::asm::instr::Cond;
/// csn_asm unified error type /// csn_asm unified error type
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum CrsnError { pub enum CrsnError {
#[error("S-expression parsing error: {0:?}")] #[error("S-expression parsing error: {0}")]
Sexp(#[from] Box<sexp::Error>), Sexp(#[from] Box<sexp::Error>),
#[error("Parse error: {0:?} at {1:?}")] #[error("Parse error: {0} at {1}")]
Parse(Cow<'static, str>, SourcePosition), Parse(Cow<'static, str>, SourcePosition),
#[error("Parse error: {0:?} at {1:?}")] #[error("Parse error: {0} at {1}")]
ParseOther(Box<dyn Error + Send + Sync>, SourcePosition), ParseOther(Box<dyn Error + Send + Sync>, SourcePosition),
#[error("Assembler error: {0:?} at {1:?}")] #[error("Assembler error: {0} at {1}")]
Asm(AsmError, SourcePosition), Asm(AsmError, SourcePosition),
} }
@ -33,8 +33,10 @@ pub enum AsmError {
DiscardAsValue, DiscardAsValue,
#[error("Conditional branch already defined for \"{0}\"")] #[error("Conditional branch already defined for \"{0}\"")]
ConditionalAlreadyUsed(Cond), ConditionalAlreadyUsed(Cond),
#[error("Label \"{0:?}\" not defined")] #[error("Label \"{0}\" not defined")]
LabelNotDefined(Label), LabelNotDefined(Label),
#[error("Label \"{0}\" already defined in this scope")]
LabelDuplicate(Label),
#[error("Bad register type: {0}")] #[error("Bad register type: {0}")]
BadRegisterType(Register), BadRegisterType(Register),
} }

@ -174,6 +174,10 @@ pub fn jumps_to_skips(ops: Vec<Op>) -> Result<Vec<Op>, CrsnError> {
let mut label_positions = HashMap::<Label, usize>::new(); let mut label_positions = HashMap::<Label, usize>::new();
for (n, op) in ops.iter().enumerate() { for (n, op) in ops.iter().enumerate() {
if let OpKind::BuiltIn(BuiltinOp::Label(name)) = &op.kind { if let OpKind::BuiltIn(BuiltinOp::Label(name)) = &op.kind {
if label_positions.contains_key(name) {
return Err(CrsnError::Asm(AsmError::LabelDuplicate(name.clone()), op.pos.clone()));
}
label_positions.insert(name.clone(), n - label_positions.len()); label_positions.insert(name.clone(), n - label_positions.len());
} }
} }

@ -109,7 +109,7 @@ impl<'a> TokenParser<'a> {
} }
/// Look at the next entry /// Look at the next entry
pub fn peek(&mut self) -> Option<&Sexp> { pub fn peek(&self) -> Option<&Sexp> {
self.args.last() self.args.last()
} }

@ -1,15 +1,16 @@
use sexp::SourcePosition; use sexp::{SourcePosition, Sexp, Atom};
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::instr::op::OpKind; use crate::asm::instr::op::OpKind;
use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::arg_parser::TokenParser;
use crate::module::{CrsnExtension, ParseRes}; use crate::module::{CrsnExtension, ParseRes};
use crate::asm::data::literal::Value; use crate::asm::data::literal::{Value, Label};
use crate::asm::instr::{Flatten, Op}; use crate::asm::instr::{Flatten, Op};
use crate::asm::parse::parse_instructions; use crate::asm::parse::parse_instructions;
use crate::builtin::defs::BuiltinOp; use crate::builtin::defs::BuiltinOp;
use crate::asm::instr::flatten::jumps_to_skips; use crate::asm::instr::flatten::jumps_to_skips;
use crate::asm::data::{Rd, RdData}; use crate::asm::data::{Rd, RdData};
use crate::asm::parse::parse_data::parse_label;
pub mod defs; pub mod defs;
pub mod exec; pub mod exec;
@ -47,7 +48,7 @@ impl CrsnExtension for BuiltinOps {
/// Parse a generic S-expression (non-op) that started with the given keyword /// Parse a generic S-expression (non-op) that started with the given keyword
/// ///
/// pcx is available on the arg_tokens parser /// pcx is available on the arg_tokens parser
fn parse_syntax<'a>(&self, pos: &SourcePosition, keyword: &str, tokens: TokenParser<'a>) fn parse_syntax<'a>(&self, pos: &SourcePosition, keyword: &str, mut tokens: TokenParser<'a>)
-> Result<ParseRes<'a, Box<dyn Flatten>>, CrsnError> -> Result<ParseRes<'a, Box<dyn Flatten>>, CrsnError>
{ {
if keyword == "crit" || keyword == "critical" { if keyword == "crit" || keyword == "critical" {
@ -91,19 +92,59 @@ impl CrsnExtension for BuiltinOps {
Box::new(Op { Box::new(Op {
kind: OpKind::BuiltIn(BuiltinOp::CriticalBegin), kind: OpKind::BuiltIn(BuiltinOp::CriticalBegin),
cond: None, cond: None,
pos: pos.clone() pos: pos.clone(),
}), }),
Box::new(flattened), Box::new(flattened),
Box::new(Op { Box::new(Op {
kind: OpKind::BuiltIn(BuiltinOp::CriticalEnd), kind: OpKind::BuiltIn(BuiltinOp::CriticalEnd),
cond: None, cond: None,
pos: pos.clone() pos: pos.clone(),
}) }),
]; ];
return Ok(ParseRes::Parsed(Box::new(vec))); return Ok(ParseRes::Parsed(Box::new(vec)));
} }
if keyword == "loop" {
let mut end_label = None;
let label = if let Some(Sexp::Atom(Atom::S(_), _)) = tokens.peek() {
let label = parse_label(tokens.next().unwrap())?;
if let Label::Named(n) = &label {
end_label = Some(Label::Named(format!("{}-end", n)));
}
label
} else {
Label::unique(&tokens.pcx.state.borrow().label_num)
};
let pcx = tokens.pcx;
let inner = parse_instructions(tokens.into_iter(), pos, pcx)?;
let mut vec : Vec<Box<dyn Flatten>> = vec![
Box::new(Op {
kind: OpKind::BuiltIn(BuiltinOp::Label(label.clone())),
cond: None,
pos: pos.clone(),
}),
inner,
Box::new(Op {
kind: OpKind::BuiltIn(BuiltinOp::Jump(label)),
cond: None,
pos: pos.clone(),
}),
];
if let Some(el) = end_label {
vec.push(Box::new(Op {
kind: OpKind::BuiltIn(BuiltinOp::Label(el)),
cond: None,
pos: pos.clone(),
}));
}
return Ok(ParseRes::Parsed(Box::new(vec)))
}
Ok(ParseRes::Unknown(tokens)) Ok(ParseRes::Unknown(tokens))
} }
} }

@ -14,7 +14,6 @@ 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; use crsn::asm::data::Wr;
use std::time::Instant;
mod console { mod console {
use std::{io}; use std::{io};
@ -23,7 +22,7 @@ mod console {
use std::ffi::c_void; use std::ffi::c_void;
use std::mem::{self, MaybeUninit}; use std::mem::{self, MaybeUninit};
use crsn::runtime::fault::Fault; use crsn::runtime::fault::Fault;
use std::time::{Duration, Instant}; use std::time::{Instant};
struct ReadCharState { struct ReadCharState {
bytes: [u8; 4], bytes: [u8; 4],

@ -0,0 +1,43 @@
(
; Unlabeled loop. Can be exited by jumping out
(loop
(nop)
(nop)
(nop)
)
(barrier)
; The above is equivalent to:
(:label)
(nop)
(nop)
(nop)
(j :label)
(barrier)
; The label name can be specified.
; This adds a start label and ":label-end" at the end of the loop:
(loop :repeat
(nop)
(nop)
(j :repeat-end)
(nop)
)
(barrier)
; The above is equivalent to: (labels changed to avoid a compile error)
(:repeat2)
(nop)
(nop)
(j :repeat2-end)
(nop)
(j :repeat2)
(:repeat2-end)
)
Loading…
Cancel
Save