Merge branch 'spanned-sexp' into master

pull/21/head
Ondřej Hruška 4 years ago
commit 017ec53b14
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 5
      Cargo.lock
  2. 2
      crsn/Cargo.toml
  3. 2
      crsn/src/asm/data/rd.rs
  4. 19
      crsn/src/asm/data/reg.rs
  5. 2
      crsn/src/asm/data/wr.rs
  6. 41
      crsn/src/asm/error.rs
  7. 6
      crsn/src/asm/instr/cond.rs
  8. 59
      crsn/src/asm/instr/flatten.rs
  9. 3
      crsn/src/asm/instr/mod.rs
  10. 8
      crsn/src/asm/instr/op.rs
  11. 6
      crsn/src/asm/mod.rs
  12. 41
      crsn/src/asm/parse/arg_parser.rs
  13. 7
      crsn/src/asm/parse/mod.rs
  14. 8
      crsn/src/asm/parse/parse_cond.rs
  15. 114
      crsn/src/asm/parse/parse_data.rs
  16. 21
      crsn/src/asm/parse/parse_instr.rs
  17. 15
      crsn/src/asm/parse/parse_op.rs
  18. 25
      crsn/src/asm/parse/parse_routine.rs
  19. 56
      crsn/src/asm/parse/sexp_expect.rs
  20. 61
      crsn/src/asm/patches/mod.rs
  21. 23
      crsn/src/asm/patches/sexp_is_a.rs
  22. 17
      crsn/src/asm/patches/try_remove.rs
  23. 9
      crsn/src/builtin/defs.rs
  24. 7
      crsn/src/builtin/exec.rs
  25. 12
      crsn/src/builtin/mod.rs
  26. 97
      crsn/src/builtin/parse.rs
  27. 9
      crsn/src/module/mod.rs
  28. 23
      crsn/src/runtime/program.rs
  29. 11
      crsn/src/utils/mod.rs
  30. 10
      crsn_arith/src/exec.rs
  31. 5
      crsn_arith/src/lib.rs
  32. 33
      crsn_arith/src/parse.rs
  33. 16
      crsn_screen/src/exec.rs
  34. 5
      crsn_screen/src/lib.rs
  35. 9
      crsn_screen/src/parse.rs
  36. 12
      crsn_stacks/src/exec.rs
  37. 5
      crsn_stacks/src/lib.rs
  38. 3
      crsn_stacks/src/parse.rs
  39. 25
      examples/screen_bounce.csn
  40. 2
      launcher/src/main.rs
  41. 2
      lib/spanned_sexp/.gitignore
  42. 14
      lib/spanned_sexp/Cargo.toml
  43. 19
      lib/spanned_sexp/LICENSE
  44. 9
      lib/spanned_sexp/README.md
  45. 85
      lib/spanned_sexp/src/error.rs
  46. 324
      lib/spanned_sexp/src/lib.rs
  47. 70
      lib/spanned_sexp/src/test.rs

5
Cargo.lock generated

@ -879,8 +879,9 @@ dependencies = [
[[package]]
name = "sexp"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8fa7ac9df84000b0238cf497cb2d3056bac2ff2a7d8cf179d2803b4b58571f"
dependencies = [
"log",
]
[[package]]
name = "sha-1"

@ -6,7 +6,7 @@ edition = "2018"
publish = false
[dependencies]
sexp = "1.1.4"
sexp = { path = "../lib/spanned_sexp" }
thiserror = "1.0.20"
anyhow = "1.0.32"
dyn-clonable = "0.9.0"

@ -1,4 +1,4 @@
use std::fmt::{Debug, Formatter, Display};
use std::fmt::{Debug, Display, Formatter};
use std::fmt;
use crate::asm::data::{DataDisp, Mask, RdData, Register};

@ -1,6 +1,9 @@
use std::fmt::{self, Display, Formatter};
use sexp::SourcePosition;
use crate::asm::error::CrsnError;
use crate::asm::patches::ErrWithPos;
/// Register name
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
@ -23,27 +26,27 @@ impl Display for Register {
}
}
pub fn parse_reg(name: &str) -> anyhow::Result<Register> {
pub fn parse_reg(name: &str, at: &SourcePosition) -> Result<Register, CrsnError> {
// TODO deduplicate code
if let Some(rn) = name.strip_prefix("arg") {
if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() {
Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?;
return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at.clone()))?;
}
let val: u8 = rn.parse()?;
let val: u8 = rn.parse().err_pos(at)?;
Ok(Register::Arg(val))
} else if let Some(rn) = name.strip_prefix("res") {
if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() {
Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?;
return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at.clone()))?;
}
let val: u8 = rn.parse()?;
let val: u8 = rn.parse().err_pos(at)?;
Ok(Register::Res(val))
} else if let Some(rn) = name.strip_prefix("r") {
if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() {
Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?;
return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at.clone()))?;
}
let val: u8 = rn.parse()?;
let val: u8 = rn.parse().err_pos(at)?;
Ok(Register::Gen(val))
} else {
Err(CrsnError::Parse(format!("Bad reg name: {}", name).into()))?
Err(CrsnError::Parse(format!("Bad reg name: {}", name).into(), at.clone()))?
}
}

@ -1,4 +1,4 @@
use std::fmt::{Debug, Formatter, Display};
use std::fmt::{Debug, Display, Formatter};
use std::fmt;
use crate::asm::data::{DataDisp, Mask, Rd, WrData};

@ -1,8 +1,10 @@
use std::borrow::Cow;
use std::num::ParseIntError;
use std::error::Error;
use thiserror::Error;
use sexp::SourcePosition;
use crate::asm::data::{Mask, Register};
use crate::asm::data::literal::Label;
use crate::asm::instr::Cond;
@ -10,24 +12,14 @@ use crate::asm::instr::Cond;
/// csn_asm unified error type
#[derive(Error, Debug)]
pub enum CrsnError {
#[error("S-expression syntax error: {0:?}")]
PreParse(#[from] Box<sexp::Error>),
#[error("Parse error: {0:?}")]
Parse(Cow<'static, str>),
#[error("Parse error in {1:?}: {0:?}")]
ParseIn(Cow<'static, str>, sexp::Sexp),
#[error("Assembler error: {0:?}")]
Asm(AsmError),
#[error("Architecture error: {0:?}")]
Arch(ArchError),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
impl From<std::num::ParseIntError> for CrsnError {
fn from(e: ParseIntError) -> Self {
CrsnError::Other(anyhow::anyhow!(e))
}
#[error("S-expression parsing error: {0:?}")]
Sexp(#[from] Box<sexp::Error>),
#[error("Parse error: {0:?} at {1:?}")]
Parse(Cow<'static, str>, SourcePosition),
#[error("Parse error: {0:?} at {1:?}")]
ParseOther(Box<dyn Error + Send + Sync>, SourcePosition),
#[error("Assembler error: {0:?} at {1:?}")]
Asm(AsmError, SourcePosition),
}
/// Error from the assembler stage (after parsing S-expressions and basic validation)
@ -50,14 +42,3 @@ pub enum AsmError {
#[error("Bad register type: {0}")]
BadRegisterType(Register),
}
/// Architectural error - the code is syntactically OK, but cannot run
#[derive(Error, Debug)]
pub enum ArchError {
#[error("Register {0} does not exist")]
RegisterNotExist(Register),
#[error("Register {0} is not writable")]
RegisterNotWritable(Register),
#[error("Register {0} is not readable")]
RegisterNotReadable(Register),
}

@ -1,6 +1,8 @@
use std::fmt::{self, Display, Formatter};
use std::ops::Not;
use sexp::SourcePosition;
use crate::asm::error::CrsnError;
/// Condition flag
@ -45,7 +47,7 @@ pub enum Cond {
NotCarry,
}
pub fn parse_cond(text: &str) -> Result<Cond, CrsnError> {
pub fn parse_cond(text: &str, pos: &SourcePosition) -> Result<Cond, CrsnError> {
Ok(match text.trim_end_matches('?') {
"eq" | "=" | "==" => Cond::Equal,
"ne" | "<>" | "!=" | "≠" => Cond::NotEqual,
@ -66,7 +68,7 @@ pub fn parse_cond(text: &str) -> Result<Cond, CrsnError> {
"ov" | "^" => Cond::Overflow,
"nov" | "!ov" | "!^" => Cond::NotOverflow,
_ => {
return Err(CrsnError::Parse(format!("Unknown cond: {}", text).into()));
return Err(CrsnError::Parse(format!("Unknown cond: {}", text).into(), pos.clone()));
}
})
}

@ -1,5 +1,8 @@
use std::collections::HashMap;
use std::sync::atomic::{AtomicU32};
use std::fmt::Debug;
use std::sync::atomic::AtomicU32;
use sexp::SourcePosition;
use crate::asm::data::{Rd, RdData};
use crate::asm::data::literal::{Label, Value};
@ -8,22 +11,32 @@ use crate::asm::instr::{Cond, InstrWithBranches, Op, Routine};
use crate::asm::instr::op::OpKind;
use crate::builtin::defs::Barrier;
use crate::builtin::defs::BuiltinOp;
use std::fmt::Debug;
/// A trait for something that can turn into multiple instructions
pub trait Flatten : Debug {
pub trait Flatten: Debug {
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError>;
fn pos(&self) -> SourcePosition;
}
impl Flatten for () {
fn flatten(self: Box<Self>, _label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
Ok(vec![])
}
fn pos(&self) -> SourcePosition {
SourcePosition::default()
}
}
impl Flatten for InstrWithBranches {
fn pos(&self) -> SourcePosition {
self.pos.clone()
}
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let mut ops = vec![self.op];
let parent_pos = self.pos;
if let Some(branches) = self.branches {
let labels = HashMap::<Cond, u32>::new();
@ -31,7 +44,7 @@ impl Flatten for InstrWithBranches {
let end_lbl = Label::unique(label_num);
for (cnt, (cond, branch)) in branches.into_iter().enumerate() {
if labels.contains_key(&cond) {
return Err(CrsnError::Asm(AsmError::ConditionalAlreadyUsed(cond)));
return Err(CrsnError::Asm(AsmError::ConditionalAlreadyUsed(cond), branch.pos()));
}
let next_lbl = if cnt == branch_count - 1 {
@ -40,27 +53,29 @@ impl Flatten for InstrWithBranches {
Label::unique(label_num)
};
let pos = branch.pos().clone();
let mut flattened = branch.flatten(label_num)?;
if flattened.len() == 0 {
ops.push(Op { cond: Some(cond), kind: BuiltinOp::Jump(end_lbl.clone()).into() });
ops.push(Op { cond: Some(cond), pos: pos.clone(), kind: BuiltinOp::Jump(end_lbl.clone()).into() });
} else if flattened.len() == 1 && flattened[0].cond.is_none() && branch_count == 1 {
// optimization for single-branch conditionals with a single instruction
ops.push(Op { cond: Some(cond), kind: flattened.remove(0).kind });
ops.push(Op { cond: Some(cond), pos: pos.clone(), kind: flattened.remove(0).kind });
} else {
ops.push(Op {
kind: OpKind::BuiltIn(BuiltinOp::Jump(next_lbl.clone())),
pos: pos.clone(),
cond: Some(!cond),
});
ops.extend(flattened);
}
if cnt != branch_count - 1 {
ops.push(BuiltinOp::Jump(end_lbl.clone()).into());
ops.push(BuiltinOp::Label(next_lbl).into());
ops.push(BuiltinOp::Jump(end_lbl.clone()).into_op(pos.clone()));
ops.push(BuiltinOp::Label(next_lbl).into_op(pos.clone()));
}
}
ops.push(BuiltinOp::Label(end_lbl).into());
ops.push(BuiltinOp::Label(end_lbl).into_op(parent_pos));
}
Ok(ops)
@ -68,6 +83,17 @@ impl Flatten for InstrWithBranches {
}
impl Flatten for Vec<Box<dyn Flatten>> {
fn pos(&self) -> SourcePosition {
match self.first() {
None => {
Default::default()
}
Some(f) => {
f.pos()
}
}
}
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let mut ops = vec![];
for item in self.into_iter() {
@ -78,15 +104,21 @@ impl Flatten for Vec<Box<dyn Flatten>> {
}
impl Flatten for Routine {
fn pos(&self) -> SourcePosition {
self.pos.clone()
}
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let skip_label = Label::unique(label_num);
let self_pos = self.pos();
let mut ops: Vec<Op> = vec![
BuiltinOp::Barrier {
kind: Barrier::Open(skip_label.clone()),
msg: Some(format!("proc {} start", self.name).into()),
}.into(),
BuiltinOp::Routine(self.name.clone()).into(),
}.into_op(self.pos()),
BuiltinOp::Routine(self.name.clone()).into_op(self.pos()),
];
ops.extend(self.body.flatten(label_num)?);
@ -95,7 +127,7 @@ impl Flatten for Routine {
BuiltinOp::Barrier {
kind: Barrier::Close(skip_label.clone()),
msg: Some(format!("proc {} end", self.name).into()),
}.into()
}.into_op(self_pos)
);
labels_to_skips(ops)
@ -123,10 +155,11 @@ pub fn labels_to_skips(ops: Vec<Op>) -> Result<Vec<Op>, CrsnError> {
let skip = *dest as isize - n as isize + skipped;
cleaned.push(Op {
cond: op.cond,
pos: op.pos.clone(),
kind: OpKind::BuiltIn(BuiltinOp::Skip(Rd::new(RdData::Immediate(skip as Value)))),
});
} else {
return Err(CrsnError::Asm(AsmError::LabelNotDefined(target)));
return Err(CrsnError::Asm(AsmError::LabelNotDefined(target), op.pos));
}
}
_ => {

@ -1,6 +1,7 @@
pub use cond::Cond;
pub use flatten::Flatten;
pub use op::Op;
use sexp::SourcePosition;
use crate::asm::data::literal::RoutineName;
@ -12,6 +13,7 @@ pub mod flatten;
#[derive(Debug)]
pub struct InstrWithBranches {
pub op: Op,
pub pos: SourcePosition,
pub branches: Option<Vec<(Cond, Box<dyn Flatten>)>>,
}
@ -19,5 +21,6 @@ pub struct InstrWithBranches {
#[derive(Debug)]
pub struct Routine {
pub name: RoutineName,
pub pos: SourcePosition,
pub body: Box<dyn Flatten>,
}

@ -1,11 +1,12 @@
use std::fmt::Debug;
use sexp::{Atom, Sexp, SourcePosition};
use crate::asm::instr::Cond;
use crate::builtin::defs::BuiltinOp;
use crate::module::{EvalRes, OpTrait};
use crate::runtime::fault::Fault;
use crate::runtime::run_thread::{info::ThreadInfo, state::RunState};
use sexp::{Sexp, Atom};
/// A higher level simple opration
#[derive(Debug)]
@ -18,6 +19,7 @@ pub enum OpKind {
#[derive(Debug)]
pub struct Op {
pub cond: Option<Cond>,
pub pos: SourcePosition,
pub kind: OpKind,
}
@ -49,8 +51,8 @@ impl OpTrait for Op {
};
if let Some(cond) = self.cond {
if let Sexp::List(items) = &mut se {
if let Some(Sexp::Atom(Atom::S(s))) = &mut items.get_mut(0) {
if let Sexp::List(items, _) = &mut se {
if let Some(Sexp::Atom(Atom::S(s), _)) = &mut items.get_mut(0) {
s.push('.');
s.push_str(&cond.to_string());
}

@ -1,10 +1,12 @@
use std::cell::RefCell;
use std::sync::Arc;
use sexp::SourcePosition;
use crate::asm::instr::flatten::labels_to_skips;
use crate::asm::parse::{ParserContext, ParserState};
use crate::module::CrsnExtension;
use crate::runtime::program::Program;
use crate::asm::instr::flatten::labels_to_skips;
pub mod data;
pub mod error;
@ -23,7 +25,7 @@ pub fn assemble(source: &str, parsers: Arc<Vec<Box<dyn CrsnExtension>>>) -> Resu
}),
};
let ops = parse::parse(source, &pcx)?;
let ops = parse::parse(source, &SourcePosition::default(), &pcx)?;
let ops = labels_to_skips(ops)?;
Ok(Program::new(ops, parsers)?)

@ -1,6 +1,7 @@
use sexp::Sexp;
use sexp::{Sexp, SourcePosition};
use crate::asm::data::{Mask, Rd, RdData, RdObj, Wr};
use crate::asm::error::CrsnError;
use crate::asm::parse::parse_data::{parse_rd, parse_wr};
use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::expect_string_atom;
@ -9,6 +10,7 @@ use crate::asm::parse::sexp_expect::expect_string_atom;
pub struct TokenParser<'a> {
orig_len: usize,
args: Vec<Sexp>,
start_pos: &'a SourcePosition,
pub pcx: &'a ParserContext<'a>,
}
@ -26,10 +28,11 @@ impl<'a> IntoIterator for TokenParser<'a> {
impl<'a> TokenParser<'a> {
/// Create a new argument parser
pub fn new(mut args: Vec<Sexp>, pcx: &'a ParserContext) -> Self {
pub fn new(mut args: Vec<Sexp>, start_pos: &'a SourcePosition, pcx: &'a ParserContext) -> Self {
args.reverse();
Self {
orig_len: args.len(),
start_pos,
args,
pcx,
}
@ -59,37 +62,51 @@ impl<'a> TokenParser<'a> {
self.args.pop()
}
/// Get the next entry, or raise an error
pub fn next_or_err(&mut self) -> Result<Sexp, CrsnError> {
match self.next() {
None => {
Err(CrsnError::Parse("Unexpected end of token list".into(), self.start_pos.clone()))
}
Some(removed) => Ok(removed)
}
}
/// Look at the next entry
pub fn peek(&mut self) -> Option<&Sexp> {
self.args.last()
}
/// Get the next string entry
pub fn next_string(&mut self) -> anyhow::Result<String> {
let next = self.next();
pub fn next_string(&mut self) -> Result<(String, SourcePosition), CrsnError> {
let next = self.next_or_err()?;
let esa = expect_string_atom(next)?;
Ok(esa)
}
/// Get the next entry as read location
pub fn next_rd(&mut self) -> anyhow::Result<Rd> {
parse_rd(self.next(), self.pcx)
pub fn next_rd(&mut self) -> Result<Rd, CrsnError> {
let next = self.next_or_err()?;
parse_rd(next, self.pcx)
}
/// Get the next entry as read location
pub fn next_rdobj(&mut self) -> anyhow::Result<RdObj> {
match parse_rd(self.next(), self.pcx)? {
pub fn next_rdobj(&mut self) -> Result<RdObj, CrsnError> {
match parse_rd(self.next_or_err()?, self.pcx)? {
Rd(RdData::ObjectPtr(reg), Mask::FULL) => {
return Ok(RdObj::new(reg));
Ok(RdObj::new(reg))
}
other => {
anyhow::bail!("Not a valid object handle syntax: {:?}", other);
Err(CrsnError::Parse(
format!("Not a valid object handle syntax: {:?}", other).into(),
self.start_pos.clone(),
))
}
}
}
/// Get the next entry as write location
pub fn next_wr(&mut self) -> anyhow::Result<Wr> {
parse_wr(self.next(), self.pcx)
pub fn next_wr(&mut self) -> Result<Wr, CrsnError> {
parse_wr(self.next_or_err()?, self.pcx)
}
}

@ -3,6 +3,7 @@ use std::collections::HashMap;
use std::sync::atomic::AtomicU32;
pub use parse_instr::parse_instructions;
use sexp::SourcePosition;
use crate::asm::data::literal::{ConstantName, RegisterAlias, Value};
use crate::asm::data::Register;
@ -38,12 +39,12 @@ pub struct ParserState {
pub constants: HashMap<ConstantName, Value>,
}
pub fn parse(source: &str, parsers: &ParserContext) -> Result<Vec<Op>, CrsnError> {
let items = expect_list(Some(sexp::parse(source)?), true)?;
pub fn parse(source: &str, pos: &SourcePosition, parsers: &ParserContext) -> Result<Vec<Op>, CrsnError> {
let (items, _pos) = expect_list(sexp::parse(source)?, true)?;
/* numbered labels start with a weird high number
to avoid conflicts with user-defined numbered labels */
let label_num = AtomicU32::new(0x7890_0000);
parse_instructions(items.into_iter(), parsers)?
parse_instructions(items.into_iter(), pos, parsers)?
.flatten(&label_num)
}

@ -8,13 +8,13 @@ use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::TryRemove;
pub fn parse_cond_branch(tok: Sexp, parsers: &ParserContext) -> Result<(Cond, Box<dyn Flatten>), CrsnError> {
let mut list = expect_list(Some(tok), false)?;
let kw = expect_string_atom(list.try_remove(0))?;
let (mut list, pos) = expect_list(tok, false)?;
let (kw, kw_pos) = expect_string_atom(list.remove_or_err(0, &pos, "Missing \"cond?\" keyword in conditional branch")?)?;
if !kw.ends_with('?') {
return Err(CrsnError::Parse(format!("Condition must end with '?': {}", kw).into()));
return Err(CrsnError::Parse(format!("Condition must end with '?': {}", kw).into(), kw_pos));
}
Ok((cond::parse_cond(&kw)?, parse_instructions(list.into_iter(), parsers)?))
Ok((cond::parse_cond(&kw, &kw_pos)?, parse_instructions(list.into_iter(), &pos, parsers)?))
}

@ -1,13 +1,14 @@
use std::borrow::Cow;
use std::convert::TryFrom;
use sexp::{Atom, Sexp};
use sexp::{Atom, Sexp, SourcePosition};
use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData};
use crate::asm::data::literal::{ConstantName, Label, RegisterAlias, Value};
use crate::asm::error::CrsnError;
use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::expect_string_atom;
use crate::asm::patches::ErrWithPos;
fn is_valid_identifier(name: &str) -> bool {
name.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_')
@ -15,65 +16,60 @@ fn is_valid_identifier(name: &str) -> bool {
}
/// Parse register alias
pub fn parse_reg_alias(name: Option<Sexp>) -> Result<RegisterAlias, CrsnError> {
pub fn parse_reg_alias(name: Sexp) -> Result<(RegisterAlias, SourcePosition), CrsnError> {
// trace!("parse reg alias: {:?}", name);
let name = expect_string_atom(name)?;
let (name, namepos) = expect_string_atom(name)?;
if !is_valid_identifier(&name) {
return Err(CrsnError::Parse(format!("\"{}\" is not an allowed register alias.", name).into()));
return Err(CrsnError::Parse(format!("\"{}\" is not an allowed register alias.", name).into(), namepos));
}
Ok(name)
Ok((name, namepos))
}
/// Parse constant name
pub fn parse_constant_name(name: Option<Sexp>) -> Result<ConstantName, CrsnError> {
pub fn parse_constant_name(name: Sexp) -> Result<(ConstantName, SourcePosition), CrsnError> {
// trace!("parse const name: {:?}", name);
let name = expect_string_atom(name)?;
let (name, namepos) = expect_string_atom(name)?;
if !is_valid_identifier(&name) {
return Err(CrsnError::Parse(format!("\"{}\" is not an allowed constant name.", name).into()));
return Err(CrsnError::Parse(format!("\"{}\" is not an allowed constant name.", name).into(), namepos));
}
Ok(name)
Ok((name, namepos))
}
/// Parse a label
pub fn parse_label(name: Option<Sexp>) -> Result<Label, CrsnError> {
pub fn parse_label(name: Sexp) -> Result<Label, CrsnError> {
// trace!("parse label: {:?}", name);
let name = expect_string_atom(name)?;
Ok(parse_label_str(&name)?)
let (name, namepos) = expect_string_atom(name)?;
Ok(parse_label_str(&name, &namepos)?)
}
pub fn parse_label_str(name: &str) -> Result<Label, CrsnError> {
pub fn parse_label_str(name: &str, pos: &SourcePosition) -> Result<Label, CrsnError> {
let label = name.trim_start_matches(':');
Ok(if label.starts_with('#') {
Label::Numbered(u32::try_from(parse_u64(&label[1..])?).expect("numbered label fit in u32"))
let val = parse_u64(&label[1..], pos)?;
Label::Numbered(u32::try_from(val).err_pos(pos)?)
} else {
Label::Named(label.to_string())
})
}
/// Parse data disposition (address/value, without the read/write restriction)
pub fn parse_data_disp(tok: Option<Sexp>, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
// trace!("parse data: {:?}", tok);
let tok = if let Some(tok) = tok {
tok
} else {
return Err(CrsnError::Parse("Expected data disposition token".into()));
};
// TODO implement masks
match &tok {
Sexp::Atom(Atom::I(val)) => {
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(*val) }))
match tok {
Sexp::Atom(Atom::I(val), _pos) => {
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) }))
}
Sexp::Atom(Atom::S(s)) => {
Sexp::Atom(Atom::S(s), pos) => {
if s == "_" {
return Ok(DataDisp::Discard);
}
@ -81,11 +77,11 @@ pub fn parse_data_disp(tok: Option<Sexp>, pcx: &ParserContext) -> Result<DataDis
// check if we have an alias defined
{
let pstate = pcx.state.borrow();
if let Some(val) = pstate.constants.get(s) {
if let Some(val) = pstate.constants.get(&s) {
return Ok(DataDisp::Immediate(*val));
}
if let Some(val) = pstate.reg_aliases.get(s) {
if let Some(val) = pstate.reg_aliases.get(&s) {
return Ok(DataDisp::Register(*val));
}
}
@ -95,63 +91,55 @@ pub fn parse_data_disp(tok: Option<Sexp>, pcx: &ParserContext) -> Result<DataDis
if let Some(val) = pstate.reg_aliases.get(reference) {
Ok(DataDisp::ObjectPtr(*val))
} else {
let reg = reg::parse_reg(reference)?;
let reg = reg::parse_reg(reference, &pos)?;
if pstate.reg_aliases.values().find(|v| **v == reg).is_some() {
Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into()))
Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos))
} else {
Ok(DataDisp::ObjectPtr(reg))
}
}
} else if s.starts_with(|c: char| c.is_ascii_digit()) {
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(parse_i64(s)?) }))
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(parse_i64(&s, &pos)?) }))
} else {
let reg = reg::parse_reg(s)?;
let reg = reg::parse_reg(&s, &pos)?;
let pstate = pcx.state.borrow();
if pstate.reg_aliases.values().find(|v| **v == reg).is_some() {
Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into()))
Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos))
} else {
Ok(DataDisp::Register(reg))
}
}
}
_ => {
Err(CrsnError::Parse(format!("bad data disp: {:?}", tok).into()))
other => {
Err(CrsnError::Parse(format!("bad data disp: {:?}", other).into(), other.pos().clone()))
}
}
}
/// Parse immediate value
pub fn parse_value(tok: Option<Sexp>, pcx: &ParserContext) -> Result<Value, CrsnError> {
let tok = if let Some(tok) = tok {
tok
} else {
return Err(CrsnError::Parse("Expected value token".into()));
};
// trace!("parse value: {:?}", tok);
match &tok {
Sexp::Atom(Atom::I(val)) => {
Ok(unsafe { std::mem::transmute(*val) })
pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result<Value, CrsnError> {
match tok {
Sexp::Atom(Atom::I(val), _pos) => {
Ok(unsafe { std::mem::transmute(val) })
}
Sexp::Atom(Atom::S(s)) => {
Sexp::Atom(Atom::S(s), pos) => {
let pstate = pcx.state.borrow();
if let Some(val) = pstate.constants.get(s) {
if let Some(val) = pstate.constants.get(&s) {
return Ok(*val);
}
Ok(unsafe { std::mem::transmute(parse_i64(s)?) })
Ok(unsafe { std::mem::transmute(parse_i64(&s, &pos)?) })
}
_ => {
Err(CrsnError::Parse(format!("bad value format: {:?}", tok).into()))
other => {
Err(CrsnError::Parse(format!("bad value format: {:?}", other).into(), other.pos().clone()))
}
}
}
pub fn parse_u64(literal: &str) -> anyhow::Result<u64> {
pub fn parse_u64(literal: &str, pos: &SourcePosition) -> Result<u64, CrsnError> {
// trace!("parse u64 from {}", literal);
let mut without_underscores = Cow::Borrowed(literal);
if without_underscores.contains('_') {
@ -159,27 +147,29 @@ pub fn parse_u64(literal: &str) -> anyhow::Result<u64> {
}
if let Some(hex) = without_underscores.strip_prefix("0x") {
Ok(u64::from_str_radix(hex, 16)?)
Ok(u64::from_str_radix(hex, 16).err_pos(pos)?)
} else if let Some(hex) = without_underscores.strip_prefix("0b") {
Ok(u64::from_str_radix(hex, 2)?)
Ok(u64::from_str_radix(hex, 2).err_pos(pos)?)
} else {
Ok(u64::from_str_radix(&without_underscores, 10)?)
Ok(u64::from_str_radix(&without_underscores, 10).err_pos(pos)?)
}
}
pub fn parse_i64(literal: &str) -> anyhow::Result<i64> {
pub fn parse_i64(literal: &str, pos: &SourcePosition) -> Result<i64, CrsnError> {
// trace!("parse i64 from {}", literal);
if let Some(_value) = literal.strip_prefix("-") {
Ok(-1 * i64::try_from(parse_u64(literal)?)?)
Ok(-1 * i64::try_from(parse_u64(literal, pos)?).err_pos(pos)?)
} else {
Ok(i64::try_from(parse_u64(literal)?)?)
Ok(i64::try_from(parse_u64(literal, pos)?).err_pos(pos)?)
}
}
pub fn parse_rd(tok: Option<Sexp>, pcx: &ParserContext) -> anyhow::Result<Rd> {
Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?)?))
pub fn parse_rd(tok: Sexp, pcx: &ParserContext) -> Result<Rd, CrsnError> {
let pos = tok.pos().clone();
Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?))
}
pub fn parse_wr(tok: Option<Sexp>, pcx: &ParserContext) -> anyhow::Result<Wr> {
Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?)?))
pub fn parse_wr(tok: Sexp, pcx: &ParserContext) -> Result<Wr, CrsnError> {
let pos = tok.pos().clone();
Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?))
}

@ -1,4 +1,4 @@
use sexp::Sexp;
use sexp::{Sexp, SourcePosition};
use crate::asm::error::CrsnError;
use crate::asm::instr::{Flatten, InstrWithBranches};
@ -7,27 +7,27 @@ use crate::asm::parse::parse_cond::parse_cond_branch;
use crate::asm::parse::parse_routine::parse_routine;
use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::SexpIsA;
use crate::asm::patches::NextOrErr;
use crate::module::ParseRes;
use super::parse_op::parse_op;
pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pcx: &ParserContext) -> Result<Box<dyn Flatten>, CrsnError> {
pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pos: &SourcePosition, pcx: &ParserContext) -> Result<Box<dyn Flatten>, CrsnError> {
let mut parsed = vec![];
for expr in items {
let tokens = expect_list(Some(expr), false)?;
let (tokens, listpos) = expect_list(expr, false)?;
let mut toki = tokens.into_iter();
let name = expect_string_atom(toki.next())?;
let (name, namepos) = expect_string_atom(toki.next_or_err(listpos.clone(), "Expected instruction name token")?)?;
if name == "proc" {
parsed.push(parse_routine(toki, pcx)?);
parsed.push(parse_routine(toki, pos, pcx)?);
continue;
}
let mut token_parser = TokenParser::new(toki.collect(), pcx);
let mut token_parser = TokenParser::new(toki.collect(), &listpos, pcx);
for p in pcx.parsers {
token_parser = match p.parse_syntax(&name, token_parser) {
token_parser = match p.parse_syntax(pos, &name, token_parser) {
Ok(ParseRes::Parsed(op)) => return Ok(op),
Ok(ParseRes::ParsedNone) => return Ok(Box::new(())),
Ok(ParseRes::Unknown(to_reuse)) => {
@ -45,7 +45,7 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pcx: &ParserContext)
// Get back the original iterator
let toki = token_parser.into_iter();
let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), pcx);
let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), &listpos, pcx);
let branch_tokens = toki
.skip_while(|e| e.is_atom())
.take_while(|e| e.is_list());
@ -62,9 +62,10 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pcx: &ParserContext)
}
};
if let Some(op) = parse_op(name.as_str(), arg_tokens)? {
if let Some(op) = parse_op(name.as_str(), arg_tokens, &namepos)? {
parsed.push(Box::new(InstrWithBranches {
op,
pos: namepos,
branches,
}));
}

@ -1,30 +1,33 @@
use sexp::SourcePosition;
use crate::asm::error::CrsnError;
use crate::asm::instr::cond::parse_cond;
use crate::asm::instr::Op;
use crate::asm::parse::arg_parser::TokenParser;
use crate::module::ParseRes;
use crate::builtin::BuiltinOps;
use crate::module::ParseRes;
pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>) -> Result<Option<Op>, CrsnError> {
pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>, spos: &SourcePosition) -> Result<Option<Op>, CrsnError> {
// Include built-in instructions
let builtins = [BuiltinOps::new()];
let mut cond = None;
if let Some(pos) = keyword.find('.') {
cond = Some(parse_cond(&keyword[(pos + 1)..])?);
cond = Some(parse_cond(&keyword[(pos + 1)..], spos)?);
keyword = &keyword[..pos];
}
for p in builtins.iter().chain(arg_tokens.pcx.parsers) {
arg_tokens = match p.parse_op(keyword, arg_tokens) {
arg_tokens = match p.parse_op(spos, keyword, arg_tokens) {
Ok(ParseRes::Parsed(kind)) => return Ok(Some(Op {
cond,
pos: spos.clone(),
kind,
})),
Ok(ParseRes::ParsedNone) => return Ok(None),
Ok(ParseRes::Unknown(to_reuse)) => {
if to_reuse.parsing_started() {
panic!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword);
return Err(CrsnError::Parse(format!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword).into(), spos.clone()));
}
to_reuse
}
@ -34,5 +37,5 @@ pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>) -> Resul
}
}
return Err(CrsnError::Parse(format!("Unknown instruction: {}", keyword).into()));
return Err(CrsnError::Parse(format!("Unknown instruction: {}", keyword).into(), spos.clone()));
}

@ -1,4 +1,4 @@
use sexp::Sexp;
use sexp::{Sexp, SourcePosition};
use crate::asm::data::Register;
use crate::asm::error::CrsnError;
@ -7,14 +7,14 @@ use crate::asm::parse::{parse_instructions, ParserContext};
use crate::asm::parse::arg_parser::TokenParser;
use crate::asm::parse::parse_data::parse_reg_alias;
use crate::asm::parse::sexp_expect::expect_string_atom;
use crate::asm::patches::SexpIsA;
use crate::asm::patches::NextOrErr;
use crate::builtin::parse::parse_routine_name;
pub fn parse_routine(mut toki: impl Iterator<Item=Sexp> + Clone, pcx: &ParserContext) -> Result<Box<dyn Flatten>, CrsnError> {
let name = expect_string_atom(toki.next())?;
let mut name = parse_routine_name(name)?;
pub fn parse_routine(mut toki: impl Iterator<Item=Sexp> + Clone, rt_pos: &SourcePosition, pcx: &ParserContext) -> Result<Box<dyn Flatten>, CrsnError> {
let (name, namepos) = expect_string_atom(toki.next_or_err(rt_pos.clone(), "Expected routine name")?)?;
let mut name = parse_routine_name(name, &namepos)?;
let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), pcx);
let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), rt_pos, pcx);
// If arity is explicitly given, then either no named argument must be provided,
// or their count must match the arity. If no arity is given, then arity is determined
@ -22,7 +22,7 @@ pub fn parse_routine(mut toki: impl Iterator<Item=Sexp> + Clone, pcx: &ParserCon
if name.arity == 0 && arg_tokens.len() != 0 {
name.arity = arg_tokens.len() as u8;
} else if arg_tokens.len() != 0 && name.arity as usize != arg_tokens.len() {
return Err(CrsnError::Parse(format!("arity mismatch in routine {}", name.name).into()));
return Err(CrsnError::Parse(format!("arity mismatch in routine {}", name.name).into(), rt_pos.clone()));
}
let toki = toki.skip_while(|e| e.is_atom());
@ -34,17 +34,17 @@ pub fn parse_routine(mut toki: impl Iterator<Item=Sexp> + Clone, pcx: &ParserCon
pstate.reg_alias_stack.push(old);
for (n, tok) in arg_tokens.into_iter().enumerate() {
let alias = parse_reg_alias(Some(tok))?;
let alias = parse_reg_alias(tok)?;
if pstate.constants.contains_key(&alias) {
return Err(CrsnError::Parse(format!("Symbol \"{}\" already used for a constant.", alias).into()));
if pstate.constants.contains_key(&alias.0) {
return Err(CrsnError::Parse(format!("Symbol \"{}\" already used for a constant.", alias.0).into(), alias.1));
}
pstate.reg_aliases.insert(alias, Register::Arg(n as u8));
pstate.reg_aliases.insert(alias.0, Register::Arg(n as u8));
}
}
let body = parse_instructions(toki, pcx)?;
let body = parse_instructions(toki, rt_pos, pcx)?;
{
let mut pstate = pcx.state.borrow_mut();
@ -54,6 +54,7 @@ pub fn parse_routine(mut toki: impl Iterator<Item=Sexp> + Clone, pcx: &ParserCon
return Ok(Box::new(Routine {
name,
pos: rt_pos.clone(),
body,
}));
}

@ -1,53 +1,37 @@
use sexp::{Atom, Sexp};
use sexp::{Atom, Sexp, SourcePosition};
use crate::asm::error::CrsnError;
pub fn expect_list(expr: Option<Sexp>, allow_empty: bool) -> Result<Vec<Sexp>, CrsnError> {
if let Some(expr) = expr {
match &expr {
Sexp::Atom(_) => {
return Err(CrsnError::ParseIn("Expected a list".into(), expr));
pub fn expect_list(expr: Sexp, allow_empty: bool) -> Result<(Vec<Sexp>, SourcePosition), CrsnError> {
match expr {
Sexp::Atom(_, pos) => {
return Err(CrsnError::Parse("Expected a list".into(), pos));
}
Sexp::List(list, pos) => {
if !allow_empty && list.is_empty() {
return Err(CrsnError::Parse("Routine: Empty list".into(), pos));
}
Sexp::List(list) => {
if !allow_empty && list.is_empty() {
return Err(CrsnError::ParseIn("Routine: Empty list".into(), expr));
}
if let Sexp::List(list) = expr {
return Ok(list);
} else {
unreachable!();
}
}
Ok((list, pos))
}
}
Err(CrsnError::Parse("Expected a list, got nothing".into()))
}
pub fn expect_atom(expr: Option<Sexp>) -> Result<Atom, CrsnError> {
if let Some(expr) = expr {
match &expr {
Sexp::Atom(_atom) => {
if let Sexp::Atom(a) = expr {
return Ok(a);
} else {
unreachable!();
}
}
Sexp::List(_) => {
return Err(CrsnError::ParseIn("Expected atom got list".into(), expr));
}
pub fn expect_atom(expr: Sexp) -> Result<(Atom, SourcePosition), CrsnError> {
match expr {
Sexp::Atom(a, pos) => {
Ok((a, pos))
}
Sexp::List(_, pos) => {
return Err(CrsnError::Parse("Expected atom got list".into(), pos));
}
}
Err(CrsnError::Parse("Expected atom, got nothing".into()))
}
pub fn expect_string_atom(expr: Option<Sexp>) -> Result<String, CrsnError> {
pub fn expect_string_atom(expr: Sexp) -> Result<(String, SourcePosition), CrsnError> {
match expect_atom(expr) {
Ok(Atom::S(s)) => Ok(s),
Ok(atom) => Err(CrsnError::ParseIn("Expected string atom".into(), Sexp::Atom(atom))),
Ok((Atom::S(s), pos)) => Ok((s, pos)),
Ok((_, pos)) => Err(CrsnError::Parse("Expected string atom".into(), pos)),
Err(e) => Err(e),
}
}

@ -1,6 +1,59 @@
pub use sexp_is_a::SexpIsA;
pub use try_remove::TryRemove;
use sexp::SourcePosition;
mod try_remove;
mod sexp_is_a;
use crate::asm::error::CrsnError;
pub trait TryRemove {
type Item;
fn try_remove(&mut self, index: usize) -> Option<Self::Item>;
fn remove_or_err(&mut self, index: usize, pos: &SourcePosition, err: &'static str) -> Result<Self::Item, CrsnError>;
}
impl<T> TryRemove for Vec<T> {
type Item = T;
fn try_remove(&mut self, index: usize) -> Option<T> {
if self.is_empty() {
None
} else {
Some(self.remove(index))
}
}
fn remove_or_err(&mut self, index: usize, pos: &SourcePosition, err: &'static str) -> Result<Self::Item, CrsnError> {
match self.try_remove(index) {
None => {
Err(CrsnError::Parse(err.into(), pos.clone()))
}
Some(removed) => Ok(removed)
}
}
}
pub trait NextOrErr<T> {
fn next_or_err(&mut self, pos: SourcePosition, err: &'static str) -> Result<T, CrsnError>;
}
impl<T, K: Iterator<Item=T>> NextOrErr<T> for K {
fn next_or_err(&mut self, pos: SourcePosition, err: &'static str) -> Result<T, CrsnError> {
match self.next() {
None => {
Err(CrsnError::Parse(err.into(), pos))
}
Some(removed) => Ok(removed)
}
}
}