fixes, safeguards and docs for "compile-time arithmetics"

This commit is contained in:
2020-10-28 18:24:51 +01:00
parent 83996348cb
commit 02f73a21f4
8 changed files with 98 additions and 41 deletions
+7
View File
@@ -65,4 +65,11 @@ impl OpTrait for Op {
se
}
fn is_compile_time_evaluable(&self) -> bool {
match &self.kind {
OpKind::BuiltIn(op) => op.is_compile_time_evaluable(),
OpKind::Ext(op) => op.is_compile_time_evaluable()
}
}
}
+29 -36
View File
@@ -3,7 +3,7 @@ use std::convert::TryFrom;
use sexp::{Atom, Sexp, SourcePosition};
use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData, RdWr};
use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData, RdWr, Register};
use crate::asm::data::literal::{ConstantName, Label, RegisterAlias, Value};
use crate::asm::error::CrsnError;
use crate::asm::parse::{ParserContext, parse_instructions};
@@ -75,6 +75,7 @@ pub fn parse_label_str(name: &str, pos: &SourcePosition) -> Result<Label, CrsnEr
pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
// trace!("parse data: {:?}", tok);
match tok {
// immediates are okay in const eval - no tests needed
Sexp::Atom(Atom::I(val), _pos) => {
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) }))
}
@@ -92,10 +93,12 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnE
}
Sexp::List(list, pos) => {
if let Some(Sexp::Atom(Atom::S(s), s_pos)) = list.first() {
// Const eval
let parsing_expr = pcx.state.borrow().parsing_expr;
if !s.starts_with('=') && !parsing_expr {
return Err(CrsnError::Parse(format!("Invalid syntax, did you mean \"={}\"?", s).into(), s_pos.clone()));
return Err(CrsnError::Parse(format!("Invalid syntax, did you mean \"={}\" (a compile-time expression)?", s).into(), s_pos.clone()));
}
// start expr parsing mode
@@ -116,6 +119,10 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnE
let op = expr_ops.remove(0);
if !op.is_compile_time_evaluable() {
return Err(CrsnError::Parse(format!("Instruction {} cannot be used in an expression!", op.to_sexp()).into(), pos.clone()));
}
let mut state_mut = pcx.state.borrow_mut();
state_mut.const_eval.clear_all();
@@ -144,12 +151,16 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnE
}
/// Parse data disp from a string token (a &str is used so the string can be preprocessed)
pub fn parse_data_disp_from_str(s: &str, pos: &SourcePosition, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
fn parse_data_disp_from_str(s: &str, pos: &SourcePosition, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
if s == "_" {
return Ok(DataDisp::Discard);
}
if let Some(reference) = s.strip_prefix('@') {
if pcx.state.borrow().parsing_expr {
return Err(CrsnError::Parse("Object handles cannot be used in expressions!".into(), pos.clone()));
}
/* extension constants (pre-defined handles) */
for p in pcx.parsers {
if let Some(val) = p.get_constant_value(reference) {
@@ -188,7 +199,7 @@ pub fn parse_data_disp_from_str(s: &str, pos: &SourcePosition, pcx: &ParserConte
return Ok(DataDisp::Immediate(*val));
}
/* register aliases */
/* register aliases - no need to check for const_eval, the aliases could never be defined */
if let Some(val) = pstate.reg_aliases.get(s) {
return Ok(DataDisp::Register(*val));
} else if let Some(val) = pstate.global_reg_aliases.get(s) {
@@ -215,6 +226,14 @@ pub fn parse_data_disp_from_str(s: &str, pos: &SourcePosition, pcx: &ParserConte
}
}
if pcx.state.borrow().parsing_expr {
if s == "=result" {
return Ok(DataDisp::Register(Register::Gen(0)));
} else {
return Err(CrsnError::Parse("Registers cannot be used in expressions!".into(), pos.clone()));
}
}
/* register */
let reg = reg::parse_reg(&s, &pos)?;
@@ -231,39 +250,13 @@ pub fn parse_data_disp_from_str(s: &str, pos: &SourcePosition, pcx: &ParserConte
/// Parse immediate value
pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result<(Value, SourcePosition), CrsnError> {
match tok {
Sexp::Atom(Atom::I(val), pos) => {
Ok((unsafe { std::mem::transmute(val) }, pos))
}
Sexp::Atom(Atom::U(val), pos) => {
Ok((val, pos))
}
Sexp::Atom(Atom::C(val), pos) => {
Ok((val as u64, pos))
}
Sexp::Atom(Atom::QS(_), pos) => {
Err(CrsnError::Parse("quoted string not expected here".into(), pos))
}
Sexp::Atom(Atom::F(val), pos) => {
Ok((unsafe { std::mem::transmute(val) }, pos))
}
Sexp::Atom(Atom::S(s), pos) => {
let pstate = pcx.state.borrow();
if let Some(val) = pstate.constants.get(&s) {
return Ok((*val, pos));
}
let pos = tok.pos().clone();
let parsed = parse_rd(tok, pcx)?;
/* extension constants */
for p in pcx.parsers {
if let Some(val) = p.get_constant_value(&s) {
return Ok((val, pos));
}
}
Err(CrsnError::Parse(format!("unknown constant: {}", s).into(), pos))
}
Sexp::List(_, pos) => {
Err(CrsnError::Parse("expected a value".into(), pos))
match parsed {
Rd(RdData::Immediate(value)) => Ok((value, pos)),
_ => {
Err(CrsnError::Parse("expected an immediate value".into(), pos))
}
}
}
+1 -1
View File
@@ -95,7 +95,7 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pos: &SourcePosition,
if parsing_expr {
// hackity hack for const expr eval
arg_parser.prepend(atom_s("r0"));
arg_parser.prepend(atom_s("=result"));
}
if let Some(op) = parse_op(name.as_str(), arg_parser, &namepos)? {
+7
View File
@@ -204,6 +204,13 @@ impl OpTrait for BuiltinOp {
Ok(res)
}
fn is_compile_time_evaluable(&self) -> bool {
match self {
BuiltinOp::Load { .. } | BuiltinOp::LoadBits { .. } => true,
_ => false
}
}
fn to_sexp(&self) -> Sexp {
super::parse::to_sexp(self)
}
+7
View File
@@ -48,6 +48,13 @@ pub trait OpTrait: Debug + Send + Sync + 'static {
/// Turn into an S-expression that produces this instruction when parsed
fn to_sexp(&self) -> Sexp;
/// Check if an operation is safe to use in compile-time arithmetics - has no side effects
/// outside the input and output registers (and flags), has the form (op Wr ...), and does not
/// use object handles or extension data.
fn is_compile_time_evaluable(&self) -> bool {
false
}
}
/// CRSN initializer object.