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

pull/21/head
Ondřej Hruška 3 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",
"log",
"num-traits",
"parking_lot",
"sexp",
"thiserror",
]

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

@ -124,3 +124,6 @@ impl From<String> for RoutineName {
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;
/// Register name
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Register {
/// Argument register, read-only
Arg(u8),
@ -24,13 +24,23 @@ impl Display for Register {
}
pub fn parse_reg(name: &str) -> anyhow::Result<Register> {
// 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()))?;
}
let val: u8 = rn.parse()?;
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()))?;
}
let val: u8 = rn.parse()?;
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()))?;
}
let val: u8 = rn.parse()?;
Ok(Register::Gen(val))
} else {

@ -12,6 +12,12 @@ pub trait Flatten {
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 {
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let mut ops = vec![self.op];
@ -58,12 +64,13 @@ impl Flatten for Vec<Box<dyn Flatten>> {
impl Flatten for Routine {
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let mut ops = vec![
BuiltinOp::Barrier(Some(format!("Routine \"{}\" start", self.name).into())).into(),
BuiltinOp::Routine(self.name.clone()).into(),
];
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)
}

@ -1,5 +1,7 @@
use std::cell::RefCell;
use std::sync::Arc;
use crate::asm::parse::{ParserContext, ParserState};
use crate::module::CrsnExtension;
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.
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 ---");
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::parse::parse_data::{parse_rd, parse_wr};
use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::expect_string_atom;
/// Utility for argument parsing
pub struct TokenParser {
pub struct TokenParser<'a> {
orig_len: usize,
args: Vec<Sexp>,
pub pcx: &'a ParserContext<'a>,
}
impl IntoIterator for TokenParser {
impl<'a> IntoIterator for TokenParser<'a> {
type Item = Sexp;
type IntoIter = std::vec::IntoIter<Sexp>;
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.into_iter()
}
}
impl TokenParser {
impl<'a> TokenParser<'a> {
/// 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();
Self {
orig_len: args.len(),
args,
pcx,
}
}
@ -61,17 +66,19 @@ impl TokenParser {
/// Get the next string entry
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
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
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) => {
return Ok(RdObj::new(reg));
}
@ -83,6 +90,6 @@ impl TokenParser {
/// Get the next entry as write location
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;
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::instr::Op;
use crate::asm::parse::sexp_expect::expect_list;
@ -13,8 +17,27 @@ pub mod parse_data;
pub mod sexp_expect;
pub mod parse_op;
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 label_num = AtomicU32::new(0);

@ -3,11 +3,11 @@ use sexp::Sexp;
use crate::asm::error::CrsnError;
use crate::asm::instr::{Cond, cond, Flatten};
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::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 kw = expect_string_atom(list.try_remove(0))?;

@ -4,18 +4,54 @@ use std::convert::TryFrom;
use sexp::{Atom, Sexp};
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::parse::ParserContext;
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
pub fn parse_label(name: Option<Sexp>) -> Result<Label, CrsnError> {
trace!("parse label: {:?}", name);
let name = expect_string_atom(name)?;
Ok(Label::Named(name.trim_start_matches(':').into()))
}
/// 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 {
tok
} else {
@ -33,6 +69,18 @@ pub fn parse_data_disp(tok: Option<Sexp>) -> Result<DataDisp, CrsnError> {
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('@') {
Ok(DataDisp::ObjectPtr(reg::parse_reg(reference)?))
} 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> {
trace!("parse u64 from {}", literal);
let mut without_underscores = Cow::Borrowed(literal);
if without_underscores.contains('_') {
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> {
trace!("parse i64 from {}", literal);
if let Some(_value) = literal.strip_prefix("-") {
Ok(-1 * i64::try_from(parse_u64(literal)?)?)
} else {
@ -70,10 +144,10 @@ pub fn parse_i64(literal: &str) -> anyhow::Result<i64> {
}
}
pub fn parse_rd(tok: Option<Sexp>) -> anyhow::Result<Rd> {
Ok(Rd::new(RdData::try_from(parse_data_disp(tok)?)?))
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_wr(tok: Option<Sexp>) -> anyhow::Result<Wr> {
Ok(Wr::new(WrData::try_from(parse_data_disp(tok)?)?))
pub fn parse_wr(tok: Option<Sexp>, pcx: &ParserContext) -> anyhow::Result<Wr> {
Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?)?))
}

@ -1,17 +1,21 @@
use sexp::Sexp;
use crate::asm::data::literal::RoutineName;
use crate::asm::data::Register;
use crate::asm::error::CrsnError;
use crate::asm::instr::{Flatten, Instr, Routine};
use crate::asm::parse::arg_parser::TokenParser;
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::patches::SexpIsA;
use crate::module::{CrsnExtension, ParseRes};
use crate::module::ParseRes;
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![];
for expr in items {
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())?;
if name == "proc" {
parsed.push(parse_routine(toki, parsers)?);
parsed.push(parse_routine(toki, pcx)?);
continue;
}
let mut token_parser = TokenParser::new(toki.collect());
for p in parsers {
token_parser = match p.parse_syntax(&name, token_parser, parsers) {
let mut token_parser = TokenParser::new(toki.collect(), pcx);
for p in pcx.parsers {
token_parser = match p.parse_syntax(&name, token_parser) {
Ok(ParseRes::Parsed(op)) => return Ok(op),
Ok(ParseRes::ParsedNone) => return Ok(Box::new(())),
Ok(ParseRes::Unknown(to_reuse)) => {
if to_reuse.parsing_started() {
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
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
.skip_while(|e| e.is_atom())
.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 mut branches = vec![];
for t in branch_tokens {
branches.push(parse_cond_branch(t, parsers)?);
branches.push(parse_cond_branch(t, pcx)?);
}
if branches.is_empty() {
None
@ -60,25 +65,12 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, parsers: &[Box<dyn Cr
}
};
parsed.push(Box::new(Instr {
op: parse_op(name.as_str(), arg_tokens, parsers)?,
branches,
}));
if let Some(op) = parse_op(name.as_str(), arg_tokens)? {
parsed.push(Box::new(Instr {
op,
branches,
}));
}
}
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::parse::arg_parser::TokenParser;
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
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) {
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)) => {
if to_reuse.parsing_started() {
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 crate::asm::data::literal::{Label, RoutineName};
use crate::asm::data::reg::parse_reg;
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::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::builtin::defs::BuiltinOp;
use crate::module::{CrsnExtension, ParseRes};
@ -28,7 +29,9 @@ impl CrsnExtension for BuiltinOps {
"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 {
"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" => {
let dest = parse_label(args.next())?;
BuiltinOp::Jump(dest)
@ -59,7 +104,7 @@ impl CrsnExtension for BuiltinOps {
let mut call_args = vec![];
for t in args {
call_args.push(parse_rd(Some(t))?);
call_args.push(parse_rd(Some(t), pcx)?);
}
BuiltinOp::Call(dest, call_args)
}
@ -67,7 +112,7 @@ impl CrsnExtension for BuiltinOps {
"ret" => {
let mut ret_vals = vec![];
for t in args {
ret_vals.push(parse_rd(Some(t))?);
ret_vals.push(parse_rd(Some(t), pcx)?);
}
BuiltinOp::Ret(ret_vals)
}
@ -77,11 +122,13 @@ impl CrsnExtension for BuiltinOps {
BuiltinOp::Routine(dest)
}
"s" => {
"sk" => {
BuiltinOp::Skip(args.next_rd()?)
}
"sif" => {
// TODO add jne-style names for jif ne ...
"skif" => {
let cond = parse_cond(&args.next_string()?)?;
let offs = args.next_rd()?;
BuiltinOp::SkipIf(cond, offs)

@ -16,14 +16,16 @@ use crate::runtime::run_thread::ThreadInfo;
mod eval_res;
/// 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.
Parsed(T),
/// Parsing successful, but did not yield any result
ParsedNone,
/// 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
pub fn ext(op: impl OpTrait) -> Self {
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.
///
/// 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)
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))
}

@ -21,7 +21,7 @@ impl CrsnExtension for ArithOps {
"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)
}
}

@ -6,7 +6,7 @@ use crsn::module::ParseRes;
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 {
"cmp" => {
ArithOp::Compare {

@ -24,7 +24,7 @@ impl CrsnExtension for ScreenOps {
"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)
}
}

@ -6,7 +6,7 @@ use crsn::module::ParseRes;
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 {
"sc-init" => {
ScreenOp::ScreenInit {

@ -24,7 +24,7 @@ impl CrsnExtension for StackOps {
"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)
}

@ -5,7 +5,7 @@ use crsn::module::ParseRes;
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 {
"stack" => {
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)
(ld r0 res0)
(halt)
)
(call fac 5)
(ld r0 res0)
(halt)
(proc fac
(cmp arg0 2
(eq? (ret 2)))

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

Loading…
Cancel
Save