Merge branch 'nat-master' into master

This commit is contained in:
2020-10-22 21:55:16 +02:00
8 changed files with 78 additions and 11 deletions
+2
View File
@@ -98,6 +98,7 @@ These keywords (among others) are used in conditional branches to specify flag t
- `nem`, `nempty` … Not empty - `nem`, `nempty` … Not empty
- `eof` … EOF - `eof` … EOF
- `neof` … Not EOF - `neof` … Not EOF
- `else` … Always true, may be used in the last branch
# Syntax # Syntax
@@ -207,6 +208,7 @@ Conditonal branches are written like this:
then only one branch is taken - there is no fall-through. then only one branch is taken - there is no fall-through.
- The definition order is preserved, i.e. if the `inval` flag is to be checked, it should be done - The definition order is preserved, i.e. if the `inval` flag is to be checked, it should be done
before checking e.g. `nz`, which is, incidentally, true by default, because most flags are cleared by instructions that affects flags. before checking e.g. `nz`, which is, incidentally, true by default, because most flags are cleared by instructions that affects flags.
- `else` can be used as a final choice of branch that will always be taken.
## Routines ## Routines
+6
View File
@@ -19,6 +19,12 @@ pub struct SourcePosition {
pub index: u32, pub index: u32,
} }
impl fmt::Display for SourcePosition {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{}:{}", self.line, self.column)
}
}
/// Since errors are the uncommon case, they're boxed. This keeps the size of /// Since errors are the uncommon case, they're boxed. This keeps the size of
/// structs down, which helps performance in the common case. /// structs down, which helps performance in the common case.
/// ///
+3 -2
View File
@@ -331,10 +331,11 @@ fn parse_sexp(s: &str, pos: &mut usize) -> ERes<Sexp> {
//trace!("parse_sexp {}", pos); //trace!("parse_sexp {}", pos);
zspace(s, pos)?; zspace(s, pos)?;
let (c, _) = peek(s, *pos)?; let (c, _) = peek(s, *pos)?;
let pos0 = *pos;
let r = if c == '(' { let r = if c == '(' {
Ok(Sexp::List(parse_list(s, pos)?, spos(s, *pos))) Ok(Sexp::List(parse_list(s, pos)?, spos(s, pos0)))
} else { } else {
Ok(Sexp::Atom(parse_atom(s, pos)?, spos(s, *pos))) Ok(Sexp::Atom(parse_atom(s, pos)?, spos(s, pos0)))
}; };
zspace(s, pos)?; zspace(s, pos)?;
r r
+10
View File
@@ -106,6 +106,10 @@ pub enum Cond {
Eof, Eof,
/// Not empty /// Not empty
NotEof, NotEof,
// Always true, for eg. (else? ...)
True,
/// Always false
False,
} }
pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result<Cond, CrsnError> { pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result<Cond, CrsnError> {
@@ -134,6 +138,7 @@ pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result<Cond, CrsnError> {
"neof" => Cond::NotEof, "neof" => Cond::NotEof,
"ov" => Cond::Overflow, "ov" => Cond::Overflow,
"nov" => Cond::NotOverflow, "nov" => Cond::NotOverflow,
"else" => Cond::True,
_ => { _ => {
return Err(CrsnError::Parse(format!("Unknown cond: {}", text).into(), pos.clone())); return Err(CrsnError::Parse(format!("Unknown cond: {}", text).into(), pos.clone()));
} }
@@ -174,6 +179,8 @@ impl Display for Cond {
Cond::Valid => "valid", Cond::Valid => "valid",
Cond::Eof => "eof", Cond::Eof => "eof",
Cond::NotEof => "neof", Cond::NotEof => "neof",
Cond::True => "else",
Cond::False => "never",
}) })
} }
} }
@@ -214,6 +221,9 @@ impl Not for Cond {
Cond::NotEof => Cond::Eof, Cond::NotEof => Cond::Eof,
Cond::Eof => Cond::NotEof, Cond::Eof => Cond::NotEof,
Cond::True => Cond::False,
Cond::False => Cond::True,
} }
} }
} }
+6
View File
@@ -49,6 +49,10 @@ impl Flatten for InstrWithBranches {
return Err(CrsnError::Asm(AsmError::ConditionalAlreadyUsed(cond), branch.pos())); return Err(CrsnError::Asm(AsmError::ConditionalAlreadyUsed(cond), branch.pos()));
} }
if cnt != branch_count - 1 && cond == Cond::True {
warn!("\"Else\" conditional used in non-final branch at {}", branch.pos());
}
let next_lbl = if cnt == branch_count - 1 { let next_lbl = if cnt == branch_count - 1 {
end_lbl.clone() end_lbl.clone()
} else { } else {
@@ -64,11 +68,13 @@ impl Flatten for InstrWithBranches {
// optimization for single-branch conditionals with a single instruction // optimization for single-branch conditionals with a single instruction
ops.push(Op { cond: Some(cond), pos: pos.clone(), kind: flattened.remove(0).kind }); ops.push(Op { cond: Some(cond), pos: pos.clone(), kind: flattened.remove(0).kind });
} else { } else {
if cond != Cond::True { // evoid emiting `op.never`
ops.push(Op { ops.push(Op {
kind: OpKind::BuiltIn(BuiltinOp::Jump(next_lbl.clone())), kind: OpKind::BuiltIn(BuiltinOp::Jump(next_lbl.clone())),
pos: pos.clone(), pos: pos.clone(),
cond: Some(!cond), cond: Some(!cond),
}); });
}
ops.extend(flattened); ops.extend(flattened);
} }
+4
View File
@@ -50,7 +50,10 @@ impl OpTrait for Op {
OpKind::Ext(op) => op.to_sexp() OpKind::Ext(op) => op.to_sexp()
}; };
// TODO rewrite to be more readable?
if let Some(cond) = self.cond { if let Some(cond) = self.cond {
// "true" is used for "else" branches, it has no effect - just omit it
if cond != Cond::True {
if let Sexp::List(items, _) = &mut se { if let Sexp::List(items, _) = &mut se {
if let Some(Sexp::Atom(Atom::S(s), _)) = &mut items.get_mut(0) { if let Some(Sexp::Atom(Atom::S(s), _)) = &mut items.get_mut(0) {
s.push('.'); s.push('.');
@@ -58,6 +61,7 @@ impl OpTrait for Op {
} }
} }
} }
}
se se
} }
+2
View File
@@ -115,6 +115,8 @@ impl StatusFlags {
Cond::NotEmpty => !self.empty, Cond::NotEmpty => !self.empty,
Cond::Eof => self.eof, Cond::Eof => self.eof,
Cond::NotEof => !self.eof, Cond::NotEof => !self.eof,
Cond::True => true,
Cond::False => false,
} }
} }
+36
View File
@@ -0,0 +1,36 @@
(
; test else
(cmp 0 5
(eq? (fault))
(else? (ld r0 15))
(lt? (fault)) ; This should produce a warning
)
(cmp 15 r0
(ne? (fault "else did not run")))
(ld r0 0)
(cmp 0 5
(lt? (nop))
(else? (fault "fallthrough to else"))
)
;
(ld r8 0)
(ld r0 1
(else? (add r8 1))
)
(sub r0 1
(pos? (fault))
(else? (add r8 1))
)
(cmp r8 2
(ne? (fault))
)
)