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 untrusted user: 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.
### 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
...and pseudo-instructions

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

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

@ -1,15 +1,16 @@
use sexp::SourcePosition;
use sexp::{SourcePosition, Sexp, Atom};
use crate::asm::error::CrsnError;
use crate::asm::instr::op::OpKind;
use crate::asm::parse::arg_parser::TokenParser;
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::parse::parse_instructions;
use crate::builtin::defs::BuiltinOp;
use crate::asm::instr::flatten::jumps_to_skips;
use crate::asm::data::{Rd, RdData};
use crate::asm::parse::parse_data::parse_label;
pub mod defs;
pub mod exec;
@ -47,7 +48,7 @@ impl CrsnExtension for BuiltinOps {
/// Parse a generic S-expression (non-op) that started with the given keyword
///
/// 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>
{
if keyword == "crit" || keyword == "critical" {
@ -91,19 +92,59 @@ impl CrsnExtension for BuiltinOps {
Box::new(Op {
kind: OpKind::BuiltIn(BuiltinOp::CriticalBegin),
cond: None,
pos: pos.clone()
pos: pos.clone(),
}),
Box::new(flattened),
Box::new(Op {
kind: OpKind::BuiltIn(BuiltinOp::CriticalEnd),
cond: None,
pos: pos.clone()
})
pos: pos.clone(),
}),
];
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))
}
}

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