Add "sym" and "def" instructions, add argument naming

pull/21/head
Ondřej Hruška 4 years ago
parent afd412f22a
commit e580a2b679
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 1
      Cargo.lock
  2. 1
      crsn/Cargo.toml
  3. 3
      crsn/src/asm/data/literal.rs
  4. 12
      crsn/src/asm/data/reg.rs
  5. 9
      crsn/src/asm/instr/flatten.rs
  6. 13
      crsn/src/asm/mod.rs
  7. 23
      crsn/src/asm/parse/arg_parser.rs
  8. 25
      crsn/src/asm/parse/mod.rs
  9. 4
      crsn/src/asm/parse/parse_cond.rs
  10. 86
      crsn/src/asm/parse/parse_data.rs
  11. 46
      crsn/src/asm/parse/parse_instr.rs
  12. 9
      crsn/src/asm/parse/parse_op.rs
  13. 47
      crsn/src/asm/parse/parse_routine.rs
  14. 59
      crsn/src/builtin/parse.rs
  15. 18
      crsn/src/module/mod.rs
  16. 2
      crsn_arith/src/lib.rs
  17. 2
      crsn_arith/src/parse.rs
  18. 2
      crsn_screen/src/lib.rs
  19. 2
      crsn_screen/src/parse.rs
  20. 2
      crsn_stacks/src/lib.rs
  21. 2
      crsn_stacks/src/parse.rs
  22. 13
      examples/aliases.csn
  23. 9
      examples/factorial.csn
  24. 36
      examples/screen_bounce.csn

1
Cargo.lock generated

@ -176,6 +176,7 @@ dependencies = [
"dyn-clonable", "dyn-clonable",
"log", "log",
"num-traits", "num-traits",
"parking_lot",
"sexp", "sexp",
"thiserror", "thiserror",
] ]

@ -12,3 +12,4 @@ anyhow = "1.0.32"
dyn-clonable = "0.9.0" dyn-clonable = "0.9.0"
log = "0.4.11" log = "0.4.11"
num-traits = "0.2.12" num-traits = "0.2.12"
parking_lot = "0.11.0"

@ -124,3 +124,6 @@ impl From<String> for RoutineName {
Self(n) Self(n)
} }
} }
pub type RegisterAlias = String;
pub type ConstantName = String;

@ -3,7 +3,7 @@ use std::fmt::{self, Display, Formatter};
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
/// Register name /// Register name
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Register { pub enum Register {
/// Argument register, read-only /// Argument register, read-only
Arg(u8), Arg(u8),
@ -24,13 +24,23 @@ impl Display for Register {
} }
pub fn parse_reg(name: &str) -> anyhow::Result<Register> { pub fn parse_reg(name: &str) -> anyhow::Result<Register> {
// TODO deduplicate code
if let Some(rn) = name.strip_prefix("arg") { 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()))?;
}
let val: u8 = rn.parse()?; let val: u8 = rn.parse()?;
Ok(Register::Arg(val)) Ok(Register::Arg(val))
} else if let Some(rn) = name.strip_prefix("res") { } 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()))?;
}
let val: u8 = rn.parse()?; let val: u8 = rn.parse()?;
Ok(Register::Res(val)) Ok(Register::Res(val))
} else if let Some(rn) = name.strip_prefix("r") { } 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()))?;
}
let val: u8 = rn.parse()?; let val: u8 = rn.parse()?;
Ok(Register::Gen(val)) Ok(Register::Gen(val))
} else { } else {

@ -12,6 +12,12 @@ pub trait Flatten {
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError>; fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError>;
} }
impl Flatten for () {
fn flatten(self: Box<Self>, _label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
Ok(vec![])
}
}
impl Flatten for Instr { impl Flatten for Instr {
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> { fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let mut ops = vec![self.op]; let mut ops = vec![self.op];
@ -58,12 +64,13 @@ impl Flatten for Vec<Box<dyn Flatten>> {
impl Flatten for Routine { impl Flatten for Routine {
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> { fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let mut ops = vec![ let mut ops = vec![
BuiltinOp::Barrier(Some(format!("Routine \"{}\" start", self.name).into())).into(),
BuiltinOp::Routine(self.name.clone()).into(), BuiltinOp::Routine(self.name.clone()).into(),
]; ];
ops.extend(self.body.flatten(label_num)?); ops.extend(self.body.flatten(label_num)?);
ops.push(BuiltinOp::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into())).into()); ops.push(BuiltinOp::Barrier(Some(format!("Routine \"{}\" end", self.name).into())).into());
numbered_labels_to_skips(ops) numbered_labels_to_skips(ops)
} }

@ -1,5 +1,7 @@
use std::cell::RefCell;
use std::sync::Arc; use std::sync::Arc;
use crate::asm::parse::{ParserContext, ParserState};
use crate::module::CrsnExtension; use crate::module::CrsnExtension;
use crate::runtime::program::Program; use crate::runtime::program::Program;
@ -11,7 +13,16 @@ pub mod patches;
/// Parse a program from string and assemble a low level instruction sequence from it. /// Parse a program from string and assemble a low level instruction sequence from it.
pub fn assemble(source: &str, parsers: Arc<Vec<Box<dyn CrsnExtension>>>) -> Result<Arc<Program>, error::CrsnError> { pub fn assemble(source: &str, parsers: Arc<Vec<Box<dyn CrsnExtension>>>) -> Result<Arc<Program>, error::CrsnError> {
let ops = parse::parse(source, &parsers)?; let pcx = ParserContext {
parsers: &parsers,
state: RefCell::new(ParserState {
reg_aliases: Default::default(),
reg_alias_stack: vec![],
constants: Default::default(),
}),
};
let ops = parse::parse(source, &pcx)?;
trace!("--- Compiled program ---"); trace!("--- Compiled program ---");
for (n, op) in ops.iter().enumerate() { for (n, op) in ops.iter().enumerate() {

@ -2,31 +2,36 @@ use sexp::Sexp;
use crate::asm::data::{Mask, Rd, RdData, RdObj, Wr}; use crate::asm::data::{Mask, Rd, RdData, RdObj, Wr};
use crate::asm::parse::parse_data::{parse_rd, parse_wr}; use crate::asm::parse::parse_data::{parse_rd, parse_wr};
use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::expect_string_atom; use crate::asm::parse::sexp_expect::expect_string_atom;
/// Utility for argument parsing /// Utility for argument parsing
pub struct TokenParser { pub struct TokenParser<'a> {
orig_len: usize, orig_len: usize,
args: Vec<Sexp>, args: Vec<Sexp>,
pub pcx: &'a ParserContext<'a>,
} }
impl IntoIterator for TokenParser { impl<'a> IntoIterator for TokenParser<'a> {
type Item = Sexp; type Item = Sexp;
type IntoIter = std::vec::IntoIter<Sexp>; type IntoIter = std::vec::IntoIter<Sexp>;
fn into_iter(mut self) -> Self::IntoIter { fn into_iter(mut self) -> Self::IntoIter {
// The vec is reversed so we can call "pop", but here we will iterate it as slice so it
// must be reversed to its original direction.
self.args.reverse(); self.args.reverse();
self.args.into_iter() self.args.into_iter()
} }
} }
impl TokenParser { impl<'a> TokenParser<'a> {
/// Create a new argument parser /// Create a new argument parser
pub fn new(mut args: Vec<Sexp>) -> Self { pub fn new(mut args: Vec<Sexp>, pcx: &'a ParserContext) -> Self {
args.reverse(); args.reverse();
Self { Self {
orig_len: args.len(), orig_len: args.len(),
args, args,
pcx,
} }
} }
@ -61,17 +66,19 @@ impl TokenParser {
/// Get the next string entry /// Get the next string entry
pub fn next_string(&mut self) -> anyhow::Result<String> { pub fn next_string(&mut self) -> anyhow::Result<String> {
Ok(expect_string_atom(self.next())?) let next = self.next();
let esa = expect_string_atom(next)?;
Ok(esa)
} }
/// Get the next entry as read location /// Get the next entry as read location
pub fn next_rd(&mut self) -> anyhow::Result<Rd> { pub fn next_rd(&mut self) -> anyhow::Result<Rd> {
parse_rd(self.next()) parse_rd(self.next(), self.pcx)
} }
/// Get the next entry as read location /// Get the next entry as read location
pub fn next_rdobj(&mut self) -> anyhow::Result<RdObj> { pub fn next_rdobj(&mut self) -> anyhow::Result<RdObj> {
match parse_rd(self.next())? { match parse_rd(self.next(), self.pcx)? {
Rd(RdData::ObjectPtr(reg), Mask::FULL) => { Rd(RdData::ObjectPtr(reg), Mask::FULL) => {
return Ok(RdObj::new(reg)); return Ok(RdObj::new(reg));
} }
@ -83,6 +90,6 @@ impl TokenParser {
/// Get the next entry as write location /// Get the next entry as write location
pub fn next_wr(&mut self) -> anyhow::Result<Wr> { pub fn next_wr(&mut self) -> anyhow::Result<Wr> {
parse_wr(self.next()) parse_wr(self.next(), self.pcx)
} }
} }

@ -1,7 +1,11 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::atomic::AtomicU32; use std::sync::atomic::AtomicU32;
pub use parse_instr::parse_instructions; pub use parse_instr::parse_instructions;
use crate::asm::data::literal::{ConstantName, RegisterAlias, Value};
use crate::asm::data::Register;
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::instr::Op; use crate::asm::instr::Op;
use crate::asm::parse::sexp_expect::expect_list; use crate::asm::parse::sexp_expect::expect_list;
@ -13,8 +17,27 @@ pub mod parse_data;
pub mod sexp_expect; pub mod sexp_expect;
pub mod parse_op; pub mod parse_op;
pub mod arg_parser; pub mod arg_parser;
pub mod parse_routine;
pub fn parse(source: &str, parsers: &[Box<dyn CrsnExtension>]) -> Result<Vec<Op>, CrsnError> { pub struct ParserContext<'a> {
/// Extension modules
pub parsers: &'a [Box<dyn CrsnExtension>],
/// Mutable state
pub state: RefCell<ParserState>,
}
pub struct ParserState {
/// Register aliases within the routine
pub reg_aliases: HashMap<RegisterAlias, Register>,
/// The old reg aliases map is pushed here when entering a routine definition
pub reg_alias_stack: Vec<HashMap<RegisterAlias, Register>>,
/// Global constants
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)?; let items = expect_list(Some(sexp::parse(source)?), true)?;
let label_num = AtomicU32::new(0); let label_num = AtomicU32::new(0);

@ -3,11 +3,11 @@ use sexp::Sexp;
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::instr::{Cond, cond, Flatten}; use crate::asm::instr::{Cond, cond, Flatten};
use crate::asm::parse::parse_instr::parse_instructions; use crate::asm::parse::parse_instr::parse_instructions;
use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::TryRemove; use crate::asm::patches::TryRemove;
use crate::module::CrsnExtension;
pub fn parse_cond_branch(tok: Sexp, parsers: &[Box<dyn CrsnExtension>]) -> Result<(Cond, Box<dyn Flatten>), CrsnError> { pub fn parse_cond_branch(tok: Sexp, parsers: &ParserContext) -> Result<(Cond, Box<dyn Flatten>), CrsnError> {
let mut list = expect_list(Some(tok), false)?; let mut list = expect_list(Some(tok), false)?;
let kw = expect_string_atom(list.try_remove(0))?; let kw = expect_string_atom(list.try_remove(0))?;

@ -4,18 +4,54 @@ use std::convert::TryFrom;
use sexp::{Atom, Sexp}; use sexp::{Atom, Sexp};
use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData}; use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData};
use crate::asm::data::literal::Label; use crate::asm::data::literal::{ConstantName, Label, RegisterAlias, Value};
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::expect_string_atom; use crate::asm::parse::sexp_expect::expect_string_atom;
fn is_valid_identifier(name: &str) -> bool {
name.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_')
&& name.chars().find(|c| !c.is_ascii_alphanumeric() && *c != '_').is_none()
}
/// Parse register alias
pub fn parse_reg_alias(name: Option<Sexp>) -> Result<RegisterAlias, CrsnError> {
trace!("parse reg alias: {:?}", name);
let name = expect_string_atom(name)?;
if !is_valid_identifier(&name) {
return Err(CrsnError::Parse(format!("\"{}\" is not an allowed register alias.", name).into()));
}
Ok(name)
}
/// Parse constant name
pub fn parse_constant_name(name: Option<Sexp>) -> Result<ConstantName, CrsnError> {
trace!("parse const name: {:?}", name);
let name = expect_string_atom(name)?;
if !is_valid_identifier(&name) {
return Err(CrsnError::Parse(format!("\"{}\" is not an allowed constant name.", name).into()));
}
Ok(name)
}
/// Parse a label /// Parse a label
pub fn parse_label(name: Option<Sexp>) -> Result<Label, CrsnError> { pub fn parse_label(name: Option<Sexp>) -> Result<Label, CrsnError> {
trace!("parse label: {:?}", name);
let name = expect_string_atom(name)?; let name = expect_string_atom(name)?;
Ok(Label::Named(name.trim_start_matches(':').into())) Ok(Label::Named(name.trim_start_matches(':').into()))
} }
/// Parse data disposition (address/value, without the read/write restriction) /// Parse data disposition (address/value, without the read/write restriction)
pub fn parse_data_disp(tok: Option<Sexp>) -> Result<DataDisp, CrsnError> { pub fn parse_data_disp(tok: Option<Sexp>, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
trace!("parse data: {:?}", tok);
let tok = if let Some(tok) = tok { let tok = if let Some(tok) = tok {
tok tok
} else { } else {
@ -33,6 +69,18 @@ pub fn parse_data_disp(tok: Option<Sexp>) -> Result<DataDisp, CrsnError> {
return Ok(DataDisp::Discard); return Ok(DataDisp::Discard);
} }
// check if we have an alias defined
{
let pstate = pcx.state.borrow();
if let Some(val) = pstate.constants.get(s) {
return Ok(DataDisp::Immediate(*val));
}
if let Some(val) = pstate.reg_aliases.get(s) {
return Ok(DataDisp::Register(*val));
}
}
if let Some(reference) = s.strip_prefix('@') { if let Some(reference) = s.strip_prefix('@') {
Ok(DataDisp::ObjectPtr(reg::parse_reg(reference)?)) Ok(DataDisp::ObjectPtr(reg::parse_reg(reference)?))
} else if s.starts_with(|c: char| c.is_ascii_digit()) { } else if s.starts_with(|c: char| c.is_ascii_digit()) {
@ -47,7 +95,32 @@ pub fn parse_data_disp(tok: Option<Sexp>) -> Result<DataDisp, CrsnError> {
} }
} }
/// Parse immediate value
pub fn parse_value(tok: Option<Sexp>) -> 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) })
}
Sexp::Atom(Atom::S(s)) => {
Ok(unsafe { std::mem::transmute(parse_i64(s)?) })
}
_ => {
Err(CrsnError::Parse(format!("bad value format: {:?}", tok).into()))
}
}
}
pub fn parse_u64(literal: &str) -> anyhow::Result<u64> { pub fn parse_u64(literal: &str) -> anyhow::Result<u64> {
trace!("parse u64 from {}", literal);
let mut without_underscores = Cow::Borrowed(literal); let mut without_underscores = Cow::Borrowed(literal);
if without_underscores.contains('_') { if without_underscores.contains('_') {
without_underscores = without_underscores.replace('_', "").into(); without_underscores = without_underscores.replace('_', "").into();
@ -63,6 +136,7 @@ pub fn parse_u64(literal: &str) -> anyhow::Result<u64> {
} }
pub fn parse_i64(literal: &str) -> anyhow::Result<i64> { pub fn parse_i64(literal: &str) -> anyhow::Result<i64> {
trace!("parse i64 from {}", literal);
if let Some(_value) = literal.strip_prefix("-") { if let Some(_value) = literal.strip_prefix("-") {
Ok(-1 * i64::try_from(parse_u64(literal)?)?) Ok(-1 * i64::try_from(parse_u64(literal)?)?)
} else { } else {
@ -70,10 +144,10 @@ pub fn parse_i64(literal: &str) -> anyhow::Result<i64> {
} }
} }
pub fn parse_rd(tok: Option<Sexp>) -> anyhow::Result<Rd> { pub fn parse_rd(tok: Option<Sexp>, pcx: &ParserContext) -> anyhow::Result<Rd> {
Ok(Rd::new(RdData::try_from(parse_data_disp(tok)?)?)) Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?)?))
} }
pub fn parse_wr(tok: Option<Sexp>) -> anyhow::Result<Wr> { pub fn parse_wr(tok: Option<Sexp>, pcx: &ParserContext) -> anyhow::Result<Wr> {
Ok(Wr::new(WrData::try_from(parse_data_disp(tok)?)?)) Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?)?))
} }

@ -1,17 +1,21 @@
use sexp::Sexp; use sexp::Sexp;
use crate::asm::data::literal::RoutineName; use crate::asm::data::literal::RoutineName;
use crate::asm::data::Register;
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::instr::{Flatten, Instr, Routine}; use crate::asm::instr::{Flatten, Instr, Routine};
use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::arg_parser::TokenParser;
use crate::asm::parse::parse_cond::parse_cond_branch; use crate::asm::parse::parse_cond::parse_cond_branch;
use crate::asm::parse::parse_data::parse_reg_alias;
use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom}; use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::SexpIsA; use crate::asm::patches::SexpIsA;
use crate::module::{CrsnExtension, ParseRes}; use crate::module::ParseRes;
use super::parse_op::parse_op; use super::parse_op::parse_op;
use crate::asm::parse::parse_routine::parse_routine;
pub fn parse_instructions(items: impl Iterator<Item=Sexp>, parsers: &[Box<dyn CrsnExtension>]) -> Result<Box<dyn Flatten>, CrsnError> { pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pcx: &ParserContext) -> Result<Box<dyn Flatten>, CrsnError> {
let mut parsed = vec![]; let mut parsed = vec![];
for expr in items { for expr in items {
let tokens = expect_list(Some(expr), false)?; let tokens = expect_list(Some(expr), false)?;
@ -20,14 +24,15 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, parsers: &[Box<dyn Cr
let name = expect_string_atom(toki.next())?; let name = expect_string_atom(toki.next())?;
if name == "proc" { if name == "proc" {
parsed.push(parse_routine(toki, parsers)?); parsed.push(parse_routine(toki, pcx)?);
continue; continue;
} }
let mut token_parser = TokenParser::new(toki.collect()); let mut token_parser = TokenParser::new(toki.collect(), pcx);
for p in parsers { for p in pcx.parsers {
token_parser = match p.parse_syntax(&name, token_parser, parsers) { token_parser = match p.parse_syntax(&name, token_parser) {
Ok(ParseRes::Parsed(op)) => return Ok(op), Ok(ParseRes::Parsed(op)) => return Ok(op),
Ok(ParseRes::ParsedNone) => return Ok(Box::new(())),
Ok(ParseRes::Unknown(to_reuse)) => { Ok(ParseRes::Unknown(to_reuse)) => {
if to_reuse.parsing_started() { if to_reuse.parsing_started() {
panic!("Module \"{}\" started parsing syntax, but returned Unknown!", p.name()); panic!("Module \"{}\" started parsing syntax, but returned Unknown!", p.name());
@ -43,7 +48,7 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, parsers: &[Box<dyn Cr
// Get back the original iterator // Get back the original iterator
let toki = token_parser.into_iter(); let toki = token_parser.into_iter();
let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect()); let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), pcx);
let branch_tokens = toki let branch_tokens = toki
.skip_while(|e| e.is_atom()) .skip_while(|e| e.is_atom())
.take_while(|e| e.is_list()); .take_while(|e| e.is_list());
@ -51,7 +56,7 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, parsers: &[Box<dyn Cr
let branches = { let branches = {
let mut branches = vec![]; let mut branches = vec![];
for t in branch_tokens { for t in branch_tokens {
branches.push(parse_cond_branch(t, parsers)?); branches.push(parse_cond_branch(t, pcx)?);
} }
if branches.is_empty() { if branches.is_empty() {
None None
@ -60,25 +65,12 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, parsers: &[Box<dyn Cr
} }
}; };
parsed.push(Box::new(Instr { if let Some(op) = parse_op(name.as_str(), arg_tokens)? {
op: parse_op(name.as_str(), arg_tokens, parsers)?, parsed.push(Box::new(Instr {
branches, op,
})); branches,
}));
}
} }
Ok(Box::new(parsed)) Ok(Box::new(parsed))
} }
fn parse_routine(mut toki: impl Iterator<Item=Sexp> + Clone, parsers: &[Box<dyn CrsnExtension>]) -> Result<Box<dyn Flatten>, CrsnError> {
let name = expect_string_atom(toki.next())?;
let _arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect());
// TODO parse arg tokens
let body = parse_instructions(toki, parsers)?;
return Ok(Box::new(Routine {
name: RoutineName(name),
body,
}));
}

@ -2,15 +2,16 @@ use crate::asm::error::CrsnError;
use crate::asm::instr::Op; use crate::asm::instr::Op;
use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::arg_parser::TokenParser;
use crate::builtin::parse::BuiltinOps; use crate::builtin::parse::BuiltinOps;
use crate::module::{CrsnExtension, ParseRes}; use crate::module::ParseRes;
pub fn parse_op(keyword: &str, mut arg_tokens: TokenParser, parsers: &[Box<dyn CrsnExtension>]) -> Result<Op, CrsnError> { pub fn parse_op<'a>(keyword: &str, mut arg_tokens: TokenParser<'a>) -> Result<Option<Op>, CrsnError> {
// Include built-in instructions // Include built-in instructions
let builtins = [BuiltinOps::new()]; let builtins = [BuiltinOps::new()];
for p in builtins.iter().chain(parsers) { 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(keyword, arg_tokens) {
Ok(ParseRes::Parsed(op)) => return Ok(op), Ok(ParseRes::Parsed(op)) => return Ok(Some(op)),
Ok(ParseRes::ParsedNone) => return Ok(None),
Ok(ParseRes::Unknown(to_reuse)) => { Ok(ParseRes::Unknown(to_reuse)) => {
if to_reuse.parsing_started() { if to_reuse.parsing_started() {
panic!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword); panic!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword);

@ -0,0 +1,47 @@
use sexp::Sexp;
use crate::asm::parse::{ParserContext, parse_instructions};
use crate::asm::instr::{Flatten, Routine};
use crate::asm::error::CrsnError;
use crate::asm::parse::sexp_expect::expect_string_atom;
use crate::asm::parse::arg_parser::TokenParser;
use crate::asm::patches::SexpIsA;
use crate::asm::parse::parse_data::parse_reg_alias;
use crate::asm::data::Register;
use crate::asm::data::literal::RoutineName;
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 arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), pcx);
let toki = toki.skip_while(|e| e.is_atom());
{
let mut pstate = pcx.state.borrow_mut();
let old = std::mem::replace(&mut pstate.reg_aliases, Default::default());
pstate.reg_alias_stack.push(old);
for (n, tok) in arg_tokens.into_iter().enumerate() {
let alias = parse_reg_alias(Some(tok))?;
if pstate.constants.contains_key(&alias) {
return Err(CrsnError::Parse(format!("Symbol \"{}\" already used for a constant.", alias).into()));
}
pstate.reg_aliases.insert(alias, Register::Arg(n as u8));
}
}
let body = parse_instructions(toki, pcx)?;
{
let mut pstate = pcx.state.borrow_mut();
let old = pstate.reg_alias_stack.pop().unwrap();
pstate.reg_aliases = old;
}
return Ok(Box::new(Routine {
name: RoutineName(name),
body,
}));
}

@ -1,11 +1,12 @@
use sexp::{Atom, Sexp}; use sexp::{Atom, Sexp};
use crate::asm::data::literal::{Label, RoutineName}; use crate::asm::data::literal::{Label, RoutineName};
use crate::asm::data::reg::parse_reg;
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::instr::cond::parse_cond; use crate::asm::instr::cond::parse_cond;
use crate::asm::instr::Op; use crate::asm::instr::Op;
use crate::asm::parse::arg_parser::TokenParser; use crate::asm::parse::arg_parser::TokenParser;
use crate::asm::parse::parse_data::{parse_label, parse_rd}; use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_rd, parse_reg_alias, parse_value};
use crate::asm::parse::sexp_expect::expect_string_atom; use crate::asm::parse::sexp_expect::expect_string_atom;
use crate::builtin::defs::BuiltinOp; use crate::builtin::defs::BuiltinOp;
use crate::module::{CrsnExtension, ParseRes}; use crate::module::{CrsnExtension, ParseRes};
@ -28,7 +29,9 @@ impl CrsnExtension for BuiltinOps {
"builtin" "builtin"
} }
fn parse_op(&self, keyword: &str, mut args: TokenParser) -> Result<ParseRes<Op>, CrsnError> { fn parse_op<'a>(&self, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
let pcx = args.pcx;
Ok(ParseRes::Parsed(Op::BuiltIn(match keyword { Ok(ParseRes::Parsed(Op::BuiltIn(match keyword {
"nop" => { "nop" => {
BuiltinOp::Nop BuiltinOp::Nop
@ -44,6 +47,48 @@ impl CrsnExtension for BuiltinOps {
} }
} }
"sym" => {
let alias = parse_reg_alias(args.next())?;
trace!("alias={:?}", alias);
let register = parse_reg(&args.next_string()?)?;
trace!("register={:?}", alias);
let mut pstate = pcx.state.borrow_mut();
if pstate.reg_aliases.contains_key(&alias) {
return Err(CrsnError::Parse(format!("Register alias \"{}\" already defined!", alias).into()));
}
if pstate.constants.contains_key(&alias) {
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a constant!", alias).into()));
}
if pstate.reg_aliases.iter().find(|x| x.1 == &register).is_some() {
return Err(CrsnError::Parse(format!("Register \"{}\" already aliased!", register).into()));
}
pstate.reg_aliases.insert(alias, register);
return Ok(ParseRes::ParsedNone);
}
"def" => {
let name = parse_constant_name(args.next())?;
let value = parse_value(args.next())?;
let mut pstate = pcx.state.borrow_mut();
if pstate.constants.contains_key(&name) {
return Err(CrsnError::Parse(format!("Constant \"{}\" already defined!", name).into()));
}
if pstate.reg_aliases.contains_key(&name) {
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a register alias!", name).into()));
}
pstate.constants.insert(name, value);
return Ok(ParseRes::ParsedNone);
}
"j" => { "j" => {
let dest = parse_label(args.next())?; let dest = parse_label(args.next())?;
BuiltinOp::Jump(dest) BuiltinOp::Jump(dest)
@ -59,7 +104,7 @@ impl CrsnExtension for BuiltinOps {
let mut call_args = vec![]; let mut call_args = vec![];
for t in args { for t in args {
call_args.push(parse_rd(Some(t))?); call_args.push(parse_rd(Some(t), pcx)?);
} }
BuiltinOp::Call(dest, call_args) BuiltinOp::Call(dest, call_args)
} }
@ -67,7 +112,7 @@ impl CrsnExtension for BuiltinOps {
"ret" => { "ret" => {
let mut ret_vals = vec![]; let mut ret_vals = vec![];
for t in args { for t in args {
ret_vals.push(parse_rd(Some(t))?); ret_vals.push(parse_rd(Some(t), pcx)?);
} }
BuiltinOp::Ret(ret_vals) BuiltinOp::Ret(ret_vals)
} }
@ -77,11 +122,13 @@ impl CrsnExtension for BuiltinOps {
BuiltinOp::Routine(dest) BuiltinOp::Routine(dest)
} }
"s" => { "sk" => {
BuiltinOp::Skip(args.next_rd()?) BuiltinOp::Skip(args.next_rd()?)
} }
"sif" => { // TODO add jne-style names for jif ne ...
"skif" => {
let cond = parse_cond(&args.next_string()?)?; let cond = parse_cond(&args.next_string()?)?;
let offs = args.next_rd()?; let offs = args.next_rd()?;
BuiltinOp::SkipIf(cond, offs) BuiltinOp::SkipIf(cond, offs)

@ -16,14 +16,16 @@ use crate::runtime::run_thread::ThreadInfo;
mod eval_res; mod eval_res;
/// Result type returned from the op parser. This is the Ok variant of a Result. /// Result type returned from the op parser. This is the Ok variant of a Result.
pub enum ParseRes<T> { pub enum ParseRes<'a, T> {
/// Parsing successful. /// Parsing successful.
Parsed(T), Parsed(T),
/// Parsing successful, but did not yield any result
ParsedNone,
/// Instruction not recognized, but there was no error. /// Instruction not recognized, but there was no error.
Unknown(TokenParser), Unknown(TokenParser<'a>),
} }
impl ParseRes<Op> { impl<'a> ParseRes<'a, Op> {
/// Helper to construct an extension op /// Helper to construct an extension op
pub fn ext(op: impl OpTrait) -> Self { pub fn ext(op: impl OpTrait) -> Self {
Self::Parsed(Op::Ext(Box::new(op))) Self::Parsed(Op::Ext(Box::new(op)))
@ -43,11 +45,15 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static {
/// the argument list and either return Ok or Err. /// the argument list and either return Ok or Err.
/// ///
/// If the instruction keyword is not recognized, return Unknown with the unchanged argument list. /// If the instruction keyword is not recognized, return Unknown with the unchanged argument list.
fn parse_op(&self, keyword: &str, arg_tokens: TokenParser) -> Result<ParseRes<Op>, CrsnError>; ///
/// pcx is available on the arg_tokens parser
fn parse_op<'a>(&self, keyword: &str, arg_tokens: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError>;
/// Parse a generic S-expression (non-op) /// Parse a generic S-expression (non-op)
fn parse_syntax(&self, keyword: &str, tokens: TokenParser, parsers: &[Box<dyn CrsnExtension>]) ///
-> Result<ParseRes<Box<dyn Flatten>>, CrsnError> /// pcx is available on the arg_tokens parser
fn parse_syntax<'a>(&self, keyword: &str, tokens: TokenParser<'a>)
-> Result<ParseRes<'a, Box<dyn Flatten>>, CrsnError>
{ {
Ok(ParseRes::Unknown(tokens)) Ok(ParseRes::Unknown(tokens))
} }

@ -21,7 +21,7 @@ impl CrsnExtension for ArithOps {
"arith" "arith"
} }
fn parse_op(&self, keyword: &str, args: TokenParser) -> Result<ParseRes<Op>, CrsnError> { fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
parse::parse(keyword, args) parse::parse(keyword, args)
} }
} }

@ -6,7 +6,7 @@ use crsn::module::ParseRes;
use crate::defs::ArithOp; use crate::defs::ArithOp;
pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result<ParseRes<Op>, CrsnError> { pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
Ok(ParseRes::ext(match keyword { Ok(ParseRes::ext(match keyword {
"cmp" => { "cmp" => {
ArithOp::Compare { ArithOp::Compare {

@ -24,7 +24,7 @@ impl CrsnExtension for ScreenOps {
"screen" "screen"
} }
fn parse_op(&self, keyword: &str, args: TokenParser) -> Result<ParseRes<Op>, CrsnError> { fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
parse::parse(keyword, args) parse::parse(keyword, args)
} }
} }

@ -6,7 +6,7 @@ use crsn::module::ParseRes;
use crate::defs::ScreenOp; use crate::defs::ScreenOp;
pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result<ParseRes<Op>, CrsnError> { pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
Ok(ParseRes::ext(match keyword { Ok(ParseRes::ext(match keyword {
"sc-init" => { "sc-init" => {
ScreenOp::ScreenInit { ScreenOp::ScreenInit {

@ -24,7 +24,7 @@ impl CrsnExtension for StackOps {
"stacks" "stacks"
} }
fn parse_op(&self, keyword: &str, args: TokenParser) -> Result<ParseRes<Op>, CrsnError> { fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
parse::parse(keyword, args) parse::parse(keyword, args)
} }

@ -5,7 +5,7 @@ use crsn::module::ParseRes;
use crate::defs::StackOp; use crate::defs::StackOp;
pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result<ParseRes<Op>, CrsnError> { pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
Ok(ParseRes::ext(match keyword { Ok(ParseRes::ext(match keyword {
"stack" => { "stack" => {
StackOp::NewStack { StackOp::NewStack {

@ -0,0 +1,13 @@
(
(def FOO 123) ; define a constant
(call my-add 7 8)
(cmp res0 138 (ne? (fault "assert failed")))
(halt)
(proc my-add a b ; give arguments names
(sym q r0) ; give register a temporary alias
(add q a b)
(add q FOO)
(ret q)
)
)

@ -1,9 +1,8 @@
( (
(proc main (call fac 5)
(call fac 5) (ld r0 res0)
(ld r0 res0) (halt)
(halt)
)
(proc fac (proc fac
(cmp arg0 2 (cmp arg0 2
(eq? (ret 2))) (eq? (ret 2)))

@ -1,26 +1,24 @@
; Set log level to "info" or above for the best results! ; Set log level to "info" or above for the best results!
( (
(proc main (sc-init 800 600)
(sc-init 800 600) (sc-opt 1 1) ; auto blit
(sc-opt 1 1) ; auto blit (sc-opt 2 25) ; frame rate
(sc-opt 2 25) ; frame rate
(ld r0 5) ; x (ld r0 5) ; x
(ld r1 0) ; y (ld r1 0) ; y
(ld r2 1) ; dx (ld r2 1) ; dx
(ld r3 1) ; dy (ld r3 1) ; dy
(ld r5 0x3300ff) (ld r5 0x3300ff)
(:loop) (:loop)
(add r5 0x000001) (add r5 0x000001)
(sc-px r0 r1 r5) (sc-px r0 r1 r5)
(add r0 r2) (add r0 r2)
(add r1 r3) (add r1 r3)
(cmp r0 799 (eq? (ld r2 -1)) (ne? (cmp r0 0 (eq? (ld r2 1))))) (cmp r0 799 (eq? (ld r2 -1)) (ne? (cmp r0 0 (eq? (ld r2 1)))))
(cmp r1 599 (eq? (ld r3 -1)) (ne? (cmp r1 0 (eq? (ld r3 1))))) (cmp r1 599 (eq? (ld r3 -1)) (ne? (cmp r1 0 (eq? (ld r3 1)))))
(j :loop) (j :loop)
) (fault "unreachable")
) )

Loading…
Cancel
Save