From e580a2b679592a32def38a473bd8f36f42060732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 1 Oct 2020 23:26:06 +0200 Subject: [PATCH] Add "sym" and "def" instructions, add argument naming --- Cargo.lock | 1 + crsn/Cargo.toml | 1 + crsn/src/asm/data/literal.rs | 3 + crsn/src/asm/data/reg.rs | 12 +++- crsn/src/asm/instr/flatten.rs | 9 ++- crsn/src/asm/mod.rs | 13 ++++- crsn/src/asm/parse/arg_parser.rs | 23 +++++--- crsn/src/asm/parse/mod.rs | 25 ++++++++- crsn/src/asm/parse/parse_cond.rs | 4 +- crsn/src/asm/parse/parse_data.rs | 86 +++++++++++++++++++++++++++-- crsn/src/asm/parse/parse_instr.rs | 46 +++++++-------- crsn/src/asm/parse/parse_op.rs | 9 +-- crsn/src/asm/parse/parse_routine.rs | 47 ++++++++++++++++ crsn/src/builtin/parse.rs | 59 ++++++++++++++++++-- crsn/src/module/mod.rs | 18 ++++-- crsn_arith/src/lib.rs | 2 +- crsn_arith/src/parse.rs | 2 +- crsn_screen/src/lib.rs | 2 +- crsn_screen/src/parse.rs | 2 +- crsn_stacks/src/lib.rs | 2 +- crsn_stacks/src/parse.rs | 2 +- examples/aliases.csn | 13 +++++ examples/factorial.csn | 9 ++- examples/screen_bounce.csn | 36 ++++++------ 24 files changed, 333 insertions(+), 93 deletions(-) create mode 100644 crsn/src/asm/parse/parse_routine.rs create mode 100644 examples/aliases.csn diff --git a/Cargo.lock b/Cargo.lock index 58833dc..f18d411 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,6 +176,7 @@ dependencies = [ "dyn-clonable", "log", "num-traits", + "parking_lot", "sexp", "thiserror", ] diff --git a/crsn/Cargo.toml b/crsn/Cargo.toml index c060343..369e7aa 100644 --- a/crsn/Cargo.toml +++ b/crsn/Cargo.toml @@ -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" diff --git a/crsn/src/asm/data/literal.rs b/crsn/src/asm/data/literal.rs index 4ed46e0..7bf98e5 100644 --- a/crsn/src/asm/data/literal.rs +++ b/crsn/src/asm/data/literal.rs @@ -124,3 +124,6 @@ impl From for RoutineName { Self(n) } } + +pub type RegisterAlias = String; +pub type ConstantName = String; diff --git a/crsn/src/asm/data/reg.rs b/crsn/src/asm/data/reg.rs index 25d2d11..159c38e 100644 --- a/crsn/src/asm/data/reg.rs +++ b/crsn/src/asm/data/reg.rs @@ -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 { + // 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 { diff --git a/crsn/src/asm/instr/flatten.rs b/crsn/src/asm/instr/flatten.rs index 3fc3bab..7ef054b 100644 --- a/crsn/src/asm/instr/flatten.rs +++ b/crsn/src/asm/instr/flatten.rs @@ -12,6 +12,12 @@ pub trait Flatten { fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError>; } +impl Flatten for () { + fn flatten(self: Box, _label_num: &AtomicU32) -> Result, CrsnError> { + Ok(vec![]) + } +} + impl Flatten for Instr { fn flatten(self: Box, label_num: &AtomicU32) -> Result, CrsnError> { let mut ops = vec![self.op]; @@ -58,12 +64,13 @@ impl Flatten for Vec> { impl Flatten for Routine { fn flatten(self: Box, label_num: &AtomicU32) -> Result, 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) } diff --git a/crsn/src/asm/mod.rs b/crsn/src/asm/mod.rs index faab7d8..6ce4670 100644 --- a/crsn/src/asm/mod.rs +++ b/crsn/src/asm/mod.rs @@ -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>>) -> Result, 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() { diff --git a/crsn/src/asm/parse/arg_parser.rs b/crsn/src/asm/parse/arg_parser.rs index 111b7e1..4ea19f0 100644 --- a/crsn/src/asm/parse/arg_parser.rs +++ b/crsn/src/asm/parse/arg_parser.rs @@ -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, + pub pcx: &'a ParserContext<'a>, } -impl IntoIterator for TokenParser { +impl<'a> IntoIterator for TokenParser<'a> { type Item = Sexp; type IntoIter = std::vec::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.into_iter() } } -impl TokenParser { +impl<'a> TokenParser<'a> { /// Create a new argument parser - pub fn new(mut args: Vec) -> Self { + pub fn new(mut args: Vec, 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 { - 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 { - 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 { - 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 { - parse_wr(self.next()) + parse_wr(self.next(), self.pcx) } } diff --git a/crsn/src/asm/parse/mod.rs b/crsn/src/asm/parse/mod.rs index ceaf8fa..c1b45f1 100644 --- a/crsn/src/asm/parse/mod.rs +++ b/crsn/src/asm/parse/mod.rs @@ -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]) -> Result, CrsnError> { +pub struct ParserContext<'a> { + /// Extension modules + pub parsers: &'a [Box], + /// Mutable state + pub state: RefCell, +} + +pub struct ParserState { + /// Register aliases within the routine + pub reg_aliases: HashMap, + + /// The old reg aliases map is pushed here when entering a routine definition + pub reg_alias_stack: Vec>, + + /// Global constants + pub constants: HashMap, +} + +pub fn parse(source: &str, parsers: &ParserContext) -> Result, CrsnError> { let items = expect_list(Some(sexp::parse(source)?), true)?; let label_num = AtomicU32::new(0); diff --git a/crsn/src/asm/parse/parse_cond.rs b/crsn/src/asm/parse/parse_cond.rs index fee3789..6ddc64b 100644 --- a/crsn/src/asm/parse/parse_cond.rs +++ b/crsn/src/asm/parse/parse_cond.rs @@ -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]) -> Result<(Cond, Box), CrsnError> { +pub fn parse_cond_branch(tok: Sexp, parsers: &ParserContext) -> Result<(Cond, Box), CrsnError> { let mut list = expect_list(Some(tok), false)?; let kw = expect_string_atom(list.try_remove(0))?; diff --git a/crsn/src/asm/parse/parse_data.rs b/crsn/src/asm/parse/parse_data.rs index 815ddf1..fb3e63d 100644 --- a/crsn/src/asm/parse/parse_data.rs +++ b/crsn/src/asm/parse/parse_data.rs @@ -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) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { +pub fn parse_data_disp(tok: Option, pcx: &ParserContext) -> Result { + trace!("parse data: {:?}", tok); + let tok = if let Some(tok) = tok { tok } else { @@ -33,6 +69,18 @@ pub fn parse_data_disp(tok: Option) -> Result { 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) -> Result { } } +/// Parse immediate value +pub fn parse_value(tok: Option) -> Result { + 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 { + 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 { } pub fn parse_i64(literal: &str) -> anyhow::Result { + 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 { } } -pub fn parse_rd(tok: Option) -> anyhow::Result { - Ok(Rd::new(RdData::try_from(parse_data_disp(tok)?)?)) +pub fn parse_rd(tok: Option, pcx: &ParserContext) -> anyhow::Result { + Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?)?)) } -pub fn parse_wr(tok: Option) -> anyhow::Result { - Ok(Wr::new(WrData::try_from(parse_data_disp(tok)?)?)) +pub fn parse_wr(tok: Option, pcx: &ParserContext) -> anyhow::Result { + Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?)?)) } diff --git a/crsn/src/asm/parse/parse_instr.rs b/crsn/src/asm/parse/parse_instr.rs index 98c58af..a63b0e6 100644 --- a/crsn/src/asm/parse/parse_instr.rs +++ b/crsn/src/asm/parse/parse_instr.rs @@ -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, parsers: &[Box]) -> Result, CrsnError> { +pub fn parse_instructions(items: impl Iterator, pcx: &ParserContext) -> Result, 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, parsers: &[Box 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, parsers: &[Box, parsers: &[Box, parsers: &[Box + Clone, parsers: &[Box]) -> Result, 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, - })); -} diff --git a/crsn/src/asm/parse/parse_op.rs b/crsn/src/asm/parse/parse_op.rs index 7b64b80..1375f66 100644 --- a/crsn/src/asm/parse/parse_op.rs +++ b/crsn/src/asm/parse/parse_op.rs @@ -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]) -> Result { +pub fn parse_op<'a>(keyword: &str, mut arg_tokens: TokenParser<'a>) -> Result, 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); diff --git a/crsn/src/asm/parse/parse_routine.rs b/crsn/src/asm/parse/parse_routine.rs new file mode 100644 index 0000000..c2a5be2 --- /dev/null +++ b/crsn/src/asm/parse/parse_routine.rs @@ -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 + Clone, pcx: &ParserContext) -> Result, 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, + })); +} diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index dd1d40b..75b231d 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -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, CrsnError> { + fn parse_op<'a>(&self, keyword: &str, mut args: TokenParser<'a>) -> Result, 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 == ®ister).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) diff --git a/crsn/src/module/mod.rs b/crsn/src/module/mod.rs index 0bed824..05207ba 100644 --- a/crsn/src/module/mod.rs +++ b/crsn/src/module/mod.rs @@ -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 { +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 { +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, CrsnError>; + /// + /// pcx is available on the arg_tokens parser + fn parse_op<'a>(&self, keyword: &str, arg_tokens: TokenParser<'a>) -> Result, CrsnError>; /// Parse a generic S-expression (non-op) - fn parse_syntax(&self, keyword: &str, tokens: TokenParser, parsers: &[Box]) - -> Result>, CrsnError> + /// + /// pcx is available on the arg_tokens parser + fn parse_syntax<'a>(&self, keyword: &str, tokens: TokenParser<'a>) + -> Result>, CrsnError> { Ok(ParseRes::Unknown(tokens)) } diff --git a/crsn_arith/src/lib.rs b/crsn_arith/src/lib.rs index 6f2eb58..51095a4 100644 --- a/crsn_arith/src/lib.rs +++ b/crsn_arith/src/lib.rs @@ -21,7 +21,7 @@ impl CrsnExtension for ArithOps { "arith" } - fn parse_op(&self, keyword: &str, args: TokenParser) -> Result, CrsnError> { + fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse(keyword, args) } } diff --git a/crsn_arith/src/parse.rs b/crsn_arith/src/parse.rs index 5aae72f..5e1ce93 100644 --- a/crsn_arith/src/parse.rs +++ b/crsn_arith/src/parse.rs @@ -6,7 +6,7 @@ use crsn::module::ParseRes; use crate::defs::ArithOp; -pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result, CrsnError> { +pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "cmp" => { ArithOp::Compare { diff --git a/crsn_screen/src/lib.rs b/crsn_screen/src/lib.rs index af8d674..cd87ba8 100644 --- a/crsn_screen/src/lib.rs +++ b/crsn_screen/src/lib.rs @@ -24,7 +24,7 @@ impl CrsnExtension for ScreenOps { "screen" } - fn parse_op(&self, keyword: &str, args: TokenParser) -> Result, CrsnError> { + fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse(keyword, args) } } diff --git a/crsn_screen/src/parse.rs b/crsn_screen/src/parse.rs index a6fcaa1..e912f4a 100644 --- a/crsn_screen/src/parse.rs +++ b/crsn_screen/src/parse.rs @@ -6,7 +6,7 @@ use crsn::module::ParseRes; use crate::defs::ScreenOp; -pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result, CrsnError> { +pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "sc-init" => { ScreenOp::ScreenInit { diff --git a/crsn_stacks/src/lib.rs b/crsn_stacks/src/lib.rs index da6a728..f20a6f5 100644 --- a/crsn_stacks/src/lib.rs +++ b/crsn_stacks/src/lib.rs @@ -24,7 +24,7 @@ impl CrsnExtension for StackOps { "stacks" } - fn parse_op(&self, keyword: &str, args: TokenParser) -> Result, CrsnError> { + fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse(keyword, args) } diff --git a/crsn_stacks/src/parse.rs b/crsn_stacks/src/parse.rs index f9192ff..2b39f54 100644 --- a/crsn_stacks/src/parse.rs +++ b/crsn_stacks/src/parse.rs @@ -5,7 +5,7 @@ use crsn::module::ParseRes; use crate::defs::StackOp; -pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result, CrsnError> { +pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "stack" => { StackOp::NewStack { diff --git a/examples/aliases.csn b/examples/aliases.csn new file mode 100644 index 0000000..86845c8 --- /dev/null +++ b/examples/aliases.csn @@ -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) + ) +) diff --git a/examples/factorial.csn b/examples/factorial.csn index e4ebf93..27073a1 100644 --- a/examples/factorial.csn +++ b/examples/factorial.csn @@ -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))) diff --git a/examples/screen_bounce.csn b/examples/screen_bounce.csn index cea432e..a1c24a0 100644 --- a/examples/screen_bounce.csn +++ b/examples/screen_bounce.csn @@ -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") )