generalize parsing to allow extensions to add arbitrary syntax; add 'proc' keyword to introduce a routine.

pull/21/head
Ondřej Hruška 4 years ago
parent d47d0f2345
commit 4be5b4e05b
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 25
      crsn/src/asm/instr/flatten.rs
  2. 6
      crsn/src/asm/instr/mod.rs
  3. 9
      crsn/src/asm/parse/arg_parser.rs
  4. 16
      crsn/src/asm/parse/mod.rs
  5. 6
      crsn/src/asm/parse/parse_cond.rs
  6. 57
      crsn/src/asm/parse/parse_instr.rs
  7. 10
      crsn/src/asm/parse/parse_op.rs
  8. 23
      crsn/src/asm/parse/parse_routines.rs
  9. 14
      crsn/src/builtin/parse.rs
  10. 45
      crsn/src/module/mod.rs
  11. 6
      crsn_arith/src/lib.rs
  12. 10
      crsn_arith/src/parse.rs
  13. 6
      crsn_screen/src/lib.rs
  14. 10
      crsn_screen/src/parse.rs
  15. 6
      crsn_stacks/src/lib.rs
  16. 10
      crsn_stacks/src/parse.rs
  17. 4
      examples/factorial.csn
  18. 22
      examples/screen_bounce.csn

@ -9,11 +9,11 @@ use crate::builtin::defs::BuiltinOp;
/// A trait for something that can turn into multiple instructions
pub trait Flatten {
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError>;
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError>;
}
impl Flatten for Instr {
fn flatten(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];
if let Some(branches) = self.branches {
@ -31,10 +31,7 @@ impl Flatten for Instr {
Label::unique(label_num)
};
ops.push(BuiltinOp::JumpIf(!cond, next_lbl.clone()).into());
for branch_instr in branch {
ops.extend(branch_instr.flatten(label_num)?);
}
ops.extend(branch.flatten(label_num)?);
if cnt != branch_count - 1 {
ops.push(BuiltinOp::Jump(end_lbl.clone()).into());
@ -48,15 +45,23 @@ impl Flatten for Instr {
}
}
impl Flatten for Vec<Box<dyn Flatten>> {
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let mut ops = vec![];
for item in self.into_iter() {
ops.extend(item.flatten(label_num)?);
}
Ok(ops)
}
}
impl Flatten for Routine {
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
let mut ops = vec![
BuiltinOp::Routine(self.name.clone()).into(),
];
for instr in self.body {
ops.extend(instr.flatten(label_num)?);
}
ops.extend(self.body.flatten(label_num)?);
ops.push(BuiltinOp::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into())).into());

@ -9,15 +9,13 @@ pub mod cond;
mod flatten;
/// A higher-level instruction
#[derive(Debug)]
pub struct Instr {
pub op: Op,
pub branches: Option<Vec<(Cond, Vec<Instr>)>>,
pub branches: Option<Vec<(Cond, Box<dyn Flatten>)>>,
}
/// A routine
pub struct Routine {
pub name: RoutineName,
pub body: Vec<Instr>,
pub body: Box<dyn Flatten>,
}

@ -5,21 +5,22 @@ use crate::asm::parse::parse_data::{parse_rd, parse_wr};
use crate::asm::parse::sexp_expect::expect_string_atom;
/// Utility for argument parsing
pub struct ArgParser {
pub struct TokenParser {
orig_len: usize,
args: Vec<Sexp>,
}
impl IntoIterator for ArgParser {
impl IntoIterator for TokenParser {
type Item = Sexp;
type IntoIter = std::vec::IntoIter<Sexp>;
fn into_iter(self) -> Self::IntoIter {
fn into_iter(mut self) -> Self::IntoIter {
self.args.reverse();
self.args.into_iter()
}
}
impl ArgParser {
impl TokenParser {
/// Create a new argument parser
pub fn new(mut args: Vec<Sexp>) -> Self {
args.reverse();

@ -1,7 +1,6 @@
use std::sync::atomic::AtomicU32;
use std::sync::atomic::{AtomicU32, AtomicU64};
pub use parse_instr::parse_instructions;
use parse_routines::parse_routines;
use crate::asm::error::CrsnError;
use crate::asm::instr::{Flatten, Op, Routine};
@ -11,22 +10,15 @@ use crate::module::CrsnExtension;
mod parse_cond;
mod parse_instr;
pub mod parse_data;
mod parse_routines;
pub mod sexp_expect;
mod parse_op;
pub mod arg_parser;
pub fn parse(source: &str, parsers: &[Box<dyn CrsnExtension>]) -> Result<Vec<Op>, CrsnError> {
let root = sexp::parse(source)?;
let items = expect_list(Some(sexp::parse(source)?), true)?;
let subs: Vec<Routine> = parse_routines(expect_list(Some(root), true)?, parsers)?;
let mut combined = vec![];
let label_num = AtomicU32::new(0);
for sub in subs {
combined.extend(sub.flatten(&label_num)?);
}
Ok(combined)
parse_instructions(items.into_iter(), parsers)?
.flatten(&label_num)
}

@ -1,13 +1,13 @@
use sexp::Sexp;
use crate::asm::error::CrsnError;
use crate::asm::instr::{Cond, cond, Instr};
use crate::asm::instr::{Cond, cond, Instr, Flatten};
use crate::asm::parse::parse_instr::parse_instructions;
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, Vec<Instr>), CrsnError> {
pub fn parse_cond_branch(tok: Sexp, parsers: &[Box<dyn CrsnExtension>]) -> Result<(Cond, Box<dyn Flatten>), CrsnError> {
let mut list = expect_list(Some(tok), false)?;
let kw = expect_string_atom(list.try_remove(0))?;
@ -15,6 +15,6 @@ pub fn parse_cond_branch(tok: Sexp, parsers: &[Box<dyn CrsnExtension>]) -> Resul
return Err(CrsnError::Parse(format!("Condition must end with '?': {}", kw).into()));
}
Ok((cond::parse_cond(&kw)?, parse_instructions(list, parsers)?))
Ok((cond::parse_cond(&kw)?, parse_instructions(list.into_iter(), parsers)?))
}

@ -1,24 +1,49 @@
use sexp::Sexp;
use crate::asm::error::CrsnError;
use crate::asm::instr::Instr;
use crate::asm::parse::arg_parser::ArgParser;
use crate::asm::instr::{Instr, Flatten, Routine};
use crate::asm::parse::arg_parser::TokenParser;
use crate::asm::parse::parse_cond::parse_cond_branch;
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::SexpIsA;
use crate::module::CrsnExtension;
use crate::module::{CrsnExtension, ParseRes};
use super::parse_op::parse_op;
use crate::asm::data::literal::RoutineName;
pub fn parse_instructions(instrs: Vec<Sexp>, parsers: &[Box<dyn CrsnExtension>]) -> Result<Vec<Instr>, CrsnError> {
pub fn parse_instructions(items: impl Iterator<Item=Sexp>, parsers: &[Box<dyn CrsnExtension>]) -> Result<Box<dyn Flatten>, CrsnError> {
let mut parsed = vec![];
for expr in instrs {
for expr in items {
let tokens = expect_list(Some(expr), false)?;
let mut toki = tokens.into_iter();
let name = expect_string_atom(toki.next())?;
let arg_tokens = ArgParser::new(toki.clone().take_while(|e| e.is_atom()).collect());
if name == "proc" {
parsed.push(parse_routine(toki, parsers)?);
continue;
}
let mut token_parser = TokenParser::new(toki.collect());
for p in parsers {
token_parser = match p.parse_syntax(&name, token_parser, parsers) {
Ok(ParseRes::Parsed(op)) => return Ok(op),
Ok(ParseRes::Unknown(to_reuse)) => {
if to_reuse.parsing_started() {
panic!("Module \"{}\" started parsing syntax, but returned Unknown!", p.name());
}
to_reuse
}
Err(err) => {
return Err(err);
}
}
}
// 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 branch_tokens = toki
.skip_while(|e| e.is_atom())
.take_while(|e| e.is_list());
@ -35,11 +60,25 @@ pub fn parse_instructions(instrs: Vec<Sexp>, parsers: &[Box<dyn CrsnExtension>])
}
};
parsed.push(Instr {
parsed.push(Box::new(Instr {
op: parse_op(name.as_str(), arg_tokens, parsers)?,
branches,
});
}));
}
Ok(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
}))
}

@ -1,17 +1,17 @@
use crate::asm::error::CrsnError;
use crate::asm::instr::Op;
use crate::asm::parse::arg_parser::ArgParser;
use crate::asm::parse::arg_parser::TokenParser;
use crate::builtin::parse::BuiltinOps;
use crate::module::{CrsnExtension, ParseOpRes};
use crate::module::{CrsnExtension, ParseRes};
pub fn parse_op(keyword: &str, mut arg_tokens: ArgParser, parsers: &[Box<dyn CrsnExtension>]) -> Result<Op, CrsnError> {
pub fn parse_op(keyword: &str, mut arg_tokens: TokenParser, parsers: &[Box<dyn CrsnExtension>]) -> Result<Op, CrsnError> {
// Include built-in instructions
let builtins = [BuiltinOps::new()];
for p in builtins.iter().chain(parsers) {
arg_tokens = match p.parse_op(keyword, arg_tokens) {
Ok(ParseOpRes::Parsed(op)) => return Ok(op),
Ok(ParseOpRes::Unknown(to_reuse)) => {
Ok(ParseRes::Parsed(op)) => return Ok(op),
Ok(ParseRes::Unknown(to_reuse)) => {
if to_reuse.parsing_started() {
panic!("Module \"{}\" started parsing {}, but returned Unknown!", p.name(), keyword);
}

@ -1,23 +0,0 @@
use sexp::Sexp;
use crate::asm::data::literal::RoutineName;
use crate::asm::error::CrsnError;
use crate::asm::instr::Routine;
use crate::asm::parse::parse_instr::parse_instructions;
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::TryRemove;
use crate::module::CrsnExtension;
pub fn parse_routines(routines: Vec<Sexp>, parsers: &[Box<dyn CrsnExtension>]) -> Result<Vec<Routine>, CrsnError> {
let mut parsed = vec![];
for rt in routines {
let mut def = expect_list(Some(rt), false)?;
let name = expect_string_atom(def.try_remove(0))?;
let body = parse_instructions(def, parsers)?;
parsed.push(Routine {
name: RoutineName(name),
body,
})
}
Ok(parsed)
}

@ -4,11 +4,11 @@ use crate::asm::data::literal::{Label, RoutineName};
use crate::asm::error::CrsnError;
use crate::asm::instr::cond::parse_cond;
use crate::asm::instr::Op;
use crate::asm::parse::arg_parser::ArgParser;
use crate::asm::parse::arg_parser::TokenParser;
use crate::asm::parse::parse_data::{parse_label, parse_rd};
use crate::asm::parse::sexp_expect::expect_string_atom;
use crate::builtin::defs::BuiltinOp;
use crate::module::{CrsnExtension, ParseOpRes};
use crate::module::{CrsnExtension, ParseRes};
#[derive(Debug, Clone)]
pub struct BuiltinOps {
@ -28,8 +28,8 @@ impl CrsnExtension for BuiltinOps {
"builtin"
}
fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result<ParseOpRes<Op>, CrsnError> {
Ok(ParseOpRes::Parsed(Op::BuiltIn(match keyword {
fn parse_op(&self, keyword: &str, mut args: TokenParser) -> Result<ParseRes<Op>, CrsnError> {
Ok(ParseRes::Parsed(Op::BuiltIn(match keyword {
"nop" => {
BuiltinOp::Nop
}
@ -142,10 +142,10 @@ impl CrsnExtension for BuiltinOps {
let label = Label::Named(label.to_string());
BuiltinOp::FarLabel(label)
} else {
return Ok(ParseOpRes::Unknown(args));
return Ok(ParseRes::Unknown(args));
}
} else {
return Ok(ParseOpRes::Unknown(args));
return Ok(ParseRes::Unknown(args));
}
}
@ -154,7 +154,7 @@ impl CrsnExtension for BuiltinOps {
let label = Label::Named(label.to_string());
BuiltinOp::Label(label)
} else {
return Ok(ParseOpRes::Unknown(args));
return Ok(ParseRes::Unknown(args));
}
}
})))

@ -1,3 +1,5 @@
#![allow(unused_variables)]
use std::fmt::Debug;
pub use eval_res::EvalRes;
@ -5,8 +7,8 @@ pub use eval_res::EvalRes;
use crate::asm::data::literal::Value;
use crate::asm::data::Mask;
use crate::asm::error::CrsnError;
use crate::asm::instr::Op;
use crate::asm::parse::arg_parser::ArgParser;
use crate::asm::instr::{Op, Flatten};
use crate::asm::parse::arg_parser::TokenParser;
use crate::runtime::fault::Fault;
use crate::runtime::run_thread::state::RunState;
use crate::runtime::run_thread::ThreadInfo;
@ -14,14 +16,14 @@ 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 ParseOpRes<T> {
pub enum ParseRes<T> {
/// Parsing successful.
Parsed(T),
/// Instruction not recognized, but there was no error.
Unknown(ArgParser),
Unknown(TokenParser),
}
impl ParseOpRes<Op> {
impl ParseRes<Op> {
/// Helper to construct an extension op
pub fn ext(op: impl OpTrait) -> Self {
Self::Parsed(Op::Ext(Box::new(op)))
@ -41,36 +43,35 @@ 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: ArgParser) -> Result<ParseOpRes<Op>, CrsnError>;
fn parse_op(&self, keyword: &str, arg_tokens: TokenParser) -> Result<ParseRes<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>
{
Ok(ParseRes::Unknown(tokens))
}
/// Drop an object referenced by a handle
fn drop_obj(&self,
#[allow(unused)] ti: &ThreadInfo,
#[allow(unused)] state: &mut RunState,
#[allow(unused)] handle: Value) -> Result<Option<()>, Fault>
fn drop_obj(&self, ti: &ThreadInfo, state: &mut RunState, handle: Value)
-> Result<Option<()>, Fault>
{
// Default impl - we do not support dropping this object
Ok(None)
}
/// Run-time method called to read an object (using the object handle syntax)
fn read_obj(&self,
#[allow(unused)] ti: &ThreadInfo,
#[allow(unused)] state: &mut RunState,
#[allow(unused)] handle: Value,
#[allow(unused)] mask: Mask,
) -> Result<Option<Value>, Fault> {
fn read_obj(&self, ti: &ThreadInfo, state: &mut RunState, handle: Value, _mask: Mask)
-> Result<Option<Value>, Fault>
{
// Default impl - we do not support reading this object
Ok(None)
}
/// Run-time method called to write an object (using the object handle syntax)
fn write_obj(&self,
#[allow(unused)] ti: &ThreadInfo,
#[allow(unused)] state: &mut RunState,
#[allow(unused)] handle: Value,
#[allow(unused)] mask: Mask,
) -> Result<Option<()>, Fault> {
fn write_obj(&self, ti: &ThreadInfo, state: &mut RunState, handle: Value, mask: Mask)
-> Result<Option<()>, Fault>
{
// Default impl - we do not support writing this object
Ok(None)
}

@ -1,7 +1,7 @@
use crsn::asm::error::CrsnError;
use crsn::asm::instr::Op;
use crsn::asm::parse::arg_parser::ArgParser;
use crsn::module::{CrsnExtension, ParseOpRes};
use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::{CrsnExtension, ParseRes};
mod defs;
mod parse;
@ -21,7 +21,7 @@ impl CrsnExtension for ArithOps {
"arith"
}
fn parse_op(&self, keyword: &str, args: ArgParser) -> Result<ParseOpRes<Op>, CrsnError> {
fn parse_op(&self, keyword: &str, args: TokenParser) -> Result<ParseRes<Op>, CrsnError> {
parse::parse(keyword, args)
}
}

@ -1,13 +1,13 @@
use crsn::asm::data::{Rd, Wr};
use crsn::asm::error::CrsnError;
use crsn::asm::instr::Op;
use crsn::asm::parse::arg_parser::ArgParser;
use crsn::module::ParseOpRes;
use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::ParseRes;
use crate::defs::ArithOp;
pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result<ParseOpRes<Op>, CrsnError> {
Ok(ParseOpRes::ext(match keyword {
pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result<ParseRes<Op>, CrsnError> {
Ok(ParseRes::ext(match keyword {
"cmp" => {
ArithOp::Compare {
a: args.next_rd()?,
@ -430,7 +430,7 @@ pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result<ParseOpRes<Op>
}
_other => {
return Ok(ParseOpRes::Unknown(args));
return Ok(ParseRes::Unknown(args));
}
}))
}

@ -3,8 +3,8 @@ extern crate log;
use crsn::asm::error::CrsnError;
use crsn::asm::instr::Op;
use crsn::asm::parse::arg_parser::ArgParser;
use crsn::module::{CrsnExtension, ParseOpRes};
use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::{CrsnExtension, ParseRes};
mod defs;
mod parse;
@ -24,7 +24,7 @@ impl CrsnExtension for ScreenOps {
"screen"
}
fn parse_op(&self, keyword: &str, args: ArgParser) -> Result<ParseOpRes<Op>, CrsnError> {
fn parse_op(&self, keyword: &str, args: TokenParser) -> Result<ParseRes<Op>, CrsnError> {
parse::parse(keyword, args)
}
}

@ -1,13 +1,13 @@
use crsn::asm::data::Rd;
use crsn::asm::error::CrsnError;
use crsn::asm::instr::Op;
use crsn::asm::parse::arg_parser::ArgParser;
use crsn::module::ParseOpRes;
use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::ParseRes;
use crate::defs::ScreenOp;
pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result<ParseOpRes<Op>, CrsnError> {
Ok(ParseOpRes::ext(match keyword {
pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result<ParseRes<Op>, CrsnError> {
Ok(ParseRes::ext(match keyword {
"sc-init" => {
ScreenOp::ScreenInit {
width: args.next_rd()?,
@ -41,7 +41,7 @@ pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result<ParseOpRes<Op>
}
_other => {
return Ok(ParseOpRes::Unknown(args));
return Ok(ParseRes::Unknown(args));
}
}))
}

@ -1,8 +1,8 @@
use crsn::asm::data::literal::Value;
use crsn::asm::error::CrsnError;
use crsn::asm::instr::Op;
use crsn::asm::parse::arg_parser::ArgParser;
use crsn::module::{CrsnExtension, ParseOpRes};
use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::{CrsnExtension, ParseRes};
use crsn::runtime::fault::Fault;
use crsn::runtime::run_thread::{RunState, ThreadInfo};
@ -24,7 +24,7 @@ impl CrsnExtension for StackOps {
"stacks"
}
fn parse_op(&self, keyword: &str, args: ArgParser) -> Result<ParseOpRes<Op>, CrsnError> {
fn parse_op(&self, keyword: &str, args: TokenParser) -> Result<ParseRes<Op>, CrsnError> {
parse::parse(keyword, args)
}

@ -1,12 +1,12 @@
use crsn::asm::error::CrsnError;
use crsn::asm::instr::Op;
use crsn::asm::parse::arg_parser::ArgParser;
use crsn::module::ParseOpRes;
use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::ParseRes;
use crate::defs::StackOp;
pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result<ParseOpRes<Op>, CrsnError> {
Ok(ParseOpRes::ext(match keyword {
pub(crate) fn parse(keyword: &str, mut args: TokenParser) -> Result<ParseRes<Op>, CrsnError> {
Ok(ParseRes::ext(match keyword {
"stack" => {
StackOp::NewStack {
dst: args.next_wr()?,
@ -28,7 +28,7 @@ pub(crate) fn parse(keyword: &str, mut args: ArgParser) -> Result<ParseOpRes<Op>
}
_other => {
return Ok(ParseOpRes::Unknown(args));
return Ok(ParseRes::Unknown(args));
}
}))
}

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

@ -1,10 +1,10 @@
; Set log level to "info" or above for the best results!
(
(main
(proc main
(sc-init 800 600)
(sc-opt 1 0) ; auto blit
(sc-opt 2 60) ; frame rate
(sc-opt 1 1) ; auto blit
(sc-opt 2 25) ; frame rate
(ld r0 5) ; x
(ld r1 0) ; y
@ -15,16 +15,12 @@
(ld r5 0x3300ff)
(:loop)
(add r5 0x000001)
(and r5 0xffffff)
(sc-px r0 r1 r5)
(add r0 r2)
(add r1 r3)
(cmp r0 799 (eq? (ld r2 -1)))
(cmp r0 0 (eq? (ld r2 1)))
(cmp r1 599 (eq? (ld r3 -1)))
(cmp r1 0 (eq? (ld r3 1)))
(sc-blit 0)
(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)
)
)

Loading…
Cancel
Save