From 04090d05350c0fa4a7476e56bcc42acfb4420350 Mon Sep 17 00:00:00 2001 From: Henrique Lorenzi Date: Sun, 11 Apr 2021 22:22:24 -0300 Subject: [PATCH] add asm blocks --- Cargo.toml | 2 +- src/asm/parser/rule_invocation.rs | 116 ++++++++++++------- src/asm/state.rs | 118 ++++++++++++++++++-- src/expr/eval.rs | 77 +++++++------ src/expr/expression.rs | 6 +- src/expr/mod.rs | 3 +- src/expr/parser.rs | 20 ++++ src/syntax/parser.rs | 13 +++ src/syntax/token.rs | 15 ++- src/test/expr.rs | 3 +- tests/expr_asm.asm | 179 ++++++++++++++++++++++++++++++ 11 files changed, 454 insertions(+), 98 deletions(-) create mode 100644 tests/expr_asm.asm diff --git a/Cargo.toml b/Cargo.toml index 5aafdf14..f37ca851 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "customasm" -version = "0.11.5" +version = "0.11.6" edition = "2018" authors = ["hlorenzi "] description = "An assembler for custom, user-defined instruction sets!" diff --git a/src/asm/parser/rule_invocation.rs b/src/asm/parser/rule_invocation.rs index 31dea637..635943a9 100644 --- a/src/asm/parser/rule_invocation.rs +++ b/src/asm/parser/rule_invocation.rs @@ -10,21 +10,50 @@ pub fn parse_rule_invocation(state: &mut asm::parser::State) let mut subparser = state.parser.slice_until_linebreak(); subparser.suppress_reports(); + let ctx = state.asm_state.get_ctx(&state); + + if let Ok(invocation) = match_rule_invocation( + &mut state.asm_state, + subparser, + ctx, + state.fileserver, + state.report.clone()) + { + let bankdata = state.asm_state.get_bankdata(state.asm_state.cur_bank); + bankdata.check_writable(&state.asm_state, state.report.clone(), &invocation.span)?; + + let bankdata = state.asm_state.get_bankdata_mut(state.asm_state.cur_bank); + bankdata.push_invocation(invocation); + } + + state.parser.expect_linebreak()?; + Ok(()) +} + + +pub fn match_rule_invocation( + asm_state: &asm::State, + subparser: syntax::Parser, + ctx: asm::Context, + fileserver: &dyn util::FileServer, + report: diagn::RcReport) + -> Result +{ if DEBUG { println!(""); println!( "=== parse rule invocation `{}` ===", - state.fileserver.get_excerpt(&subparser.get_full_span())); + fileserver.get_excerpt(&subparser.get_full_span())); } - let mut candidates = match_active_rulesets(state, &subparser)?; + let mut candidates = match_active_rulesets(asm_state, &subparser, fileserver, report.clone())?; if candidates.len() != 0 { // Calculate specificity scores for candidate in &mut candidates { - candidate.specificity = candidate.calculate_specificity_score(&state.asm_state); + candidate.specificity = candidate.calculate_specificity_score(&asm_state); } // Sort candidates by specificity score @@ -36,12 +65,12 @@ pub fn parse_rule_invocation(state: &mut asm::parser::State) println!("final candidates:"); for candidate in &candidates { - let rule_group = &state.asm_state.rulesets[candidate.rule_ref.ruleset_ref.index]; + let rule_group = &asm_state.rulesets[candidate.rule_ref.ruleset_ref.index]; let rule = &rule_group.rules[candidate.rule_ref.index]; println!( " `{}`", - state.fileserver.get_excerpt(&rule.span)); + fileserver.get_excerpt(&rule.span)); } } @@ -56,7 +85,7 @@ pub fn parse_rule_invocation(state: &mut asm::parser::State) let mut invocation = asm::Invocation { - ctx: state.asm_state.get_ctx(&state), + ctx, size_guess: 0, span: subparser.get_full_span(), kind: asm::InvocationKind::Rule(asm::RuleInvocation @@ -65,13 +94,14 @@ pub fn parse_rule_invocation(state: &mut asm::parser::State) }) }; - let resolved = state.asm_state.resolve_rule_invocation( - state.report.clone(), + let resolved = asm_state.resolve_rule_invocation( + report.clone(), &invocation, - state.fileserver, - false); + fileserver, + false, + &mut expr::EvalContext::new()); - //println!("{} = {:?}", state.fileserver.get_excerpt(&invocation.span), &resolved); + //println!("{} = {:?}", fileserver.get_excerpt(&invocation.span), &resolved); // TODO: can provide an exact guess even if resolution fails, // if we have an exact candidate, and @@ -89,31 +119,27 @@ pub fn parse_rule_invocation(state: &mut asm::parser::State) _ => 0 }; - //println!("{} = {}", state.fileserver.get_excerpt(&invocation.span), invocation.size_guess); + //println!("{} = {}", fileserver.get_excerpt(&invocation.span), invocation.size_guess); - let bankdata = state.asm_state.get_bankdata(state.asm_state.cur_bank); - bankdata.check_writable(&state.asm_state, state.report.clone(), &invocation.span)?; - - let bankdata = state.asm_state.get_bankdata_mut(state.asm_state.cur_bank); - bankdata.push_invocation(invocation); + return Ok(invocation); } - state.parser.expect_linebreak()?; - - Ok(()) + Err(()) } pub fn match_active_rulesets( - state: &asm::parser::State, - subparser: &syntax::Parser) + asm_state: &asm::State, + subparser: &syntax::Parser, + fileserver: &dyn util::FileServer, + report: diagn::RcReport) -> Result, ()> { let mut candidates = Vec::new(); - for ruleset_ref in &state.asm_state.active_rulesets + for ruleset_ref in &asm_state.active_rulesets { - if let Ok(subcandidates) = match_ruleset(state, *ruleset_ref, &subparser, true) + if let Ok(subcandidates) = match_ruleset(asm_state, *ruleset_ref, &subparser, true, fileserver, report.clone()) { for candidate in subcandidates { @@ -124,12 +150,12 @@ pub fn match_active_rulesets( if candidates.len() == 0 { - state.report.error_span("no match for instruction found", &subparser.get_full_span()); + report.error_span("no match for instruction found", &subparser.get_full_span()); } //println!( // "rule candidates for `{}`:\n{:#?}", - // state.fileserver.get_excerpt(&subparser.get_full_span()), + // fileserver.get_excerpt(&subparser.get_full_span()), // candidates); Ok(candidates) @@ -137,13 +163,15 @@ pub fn match_active_rulesets( pub fn match_ruleset<'a>( - state: &asm::parser::State, + asm_state: &asm::State, ruleset_ref: asm::RulesetRef, subparser: &syntax::Parser<'a>, - must_consume_all_tokens: bool) + must_consume_all_tokens: bool, + fileserver: &dyn util::FileServer, + report: diagn::RcReport) -> Result)>, ()> { - let rule_group = &state.asm_state.rulesets[ruleset_ref.index]; + let rule_group = &asm_state.rulesets[ruleset_ref.index]; let mut candidates = Vec::new(); @@ -155,11 +183,11 @@ pub fn match_ruleset<'a>( index, }; - if let Ok(subcandidates) = match_rule(state, rule_ref, subparser) + if let Ok(subcandidates) = match_rule(asm_state, rule_ref, subparser, fileserver, report.clone()) { //println!( // "finish pattern with parser at `{}`", - // state.fileserver.get_excerpt(&subparser_clone.get_next_spans(10))); + // fileserver.get_excerpt(&subparser_clone.get_next_spans(10))); for subcandidate in subcandidates { @@ -185,12 +213,14 @@ struct ParsingBranch<'a> pub fn match_rule<'a>( - state: &asm::parser::State, + asm_state: &asm::State, rule_ref: asm::RuleRef, - subparser: &syntax::Parser<'a>) + subparser: &syntax::Parser<'a>, + fileserver: &dyn util::FileServer, + report: diagn::RcReport) -> Result)>, ()> { - let rule_group = &state.asm_state.rulesets[rule_ref.ruleset_ref.index]; + let rule_group = &asm_state.rulesets[rule_ref.ruleset_ref.index]; let rule = &rule_group.rules[rule_ref.index]; let mut parsing_branches = Vec::new(); @@ -206,10 +236,10 @@ pub fn match_rule<'a>( println!(""); println!( "> try match rule `{}`", - state.fileserver.get_excerpt(&rule.span)); + fileserver.get_excerpt(&rule.span)); println!( " parser at `{}`", - state.fileserver.get_excerpt(&subparser.get_next_spans(100))); + fileserver.get_excerpt(&subparser.get_next_spans(100))); } for (index, part) in rule.pattern.iter().enumerate() @@ -245,7 +275,7 @@ pub fn match_rule<'a>( { println!(" branch {}, exact matched! parser at `{}`", branch_index, - state.fileserver.get_excerpt(&branch.parser.get_next_spans(100))); + fileserver.get_excerpt(&branch.parser.get_next_spans(100))); } } } @@ -303,7 +333,7 @@ pub fn match_rule<'a>( " branch {}, parser {}at `{}`", branch_index, if expr_using_slice { "using slice " } else { "" }, - state.fileserver.get_excerpt(&expr_parser.get_next_spans(100))); + fileserver.get_excerpt(&expr_parser.get_next_spans(100))); } let expr = expr::Expr::parse(&mut expr_parser)?; @@ -325,7 +355,7 @@ pub fn match_rule<'a>( { println!(" branch {}, expr matched! parser at `{}`", branch_index, - state.fileserver.get_excerpt(&branch.parser.get_next_spans(100))); + fileserver.get_excerpt(&branch.parser.get_next_spans(100))); } } } @@ -339,10 +369,12 @@ pub fn match_rule<'a>( } let subcandidates = match_ruleset( - state, + asm_state, rule_group_ref, &mut branch.parser, - false)?; + false, + fileserver, + report.clone())?; if subcandidates.len() != 0 { @@ -392,7 +424,7 @@ pub fn match_rule<'a>( println!("= branch {}{}, candidate parser at `{}`", branch_index, if branch.dead { " (dead)" } else { "" }, - state.fileserver.get_excerpt(&branch.parser.get_next_spans(100))); + fileserver.get_excerpt(&branch.parser.get_next_spans(100))); } if branch.dead diff --git a/src/asm/state.rs b/src/asm/state.rs index 6692bccc..ab35b903 100644 --- a/src/asm/state.rs +++ b/src/asm/state.rs @@ -447,7 +447,8 @@ impl State report.clone(), &invoc, fileserver, - true)? + true, + &mut expr::EvalContext::new())? } asm::InvocationKind::Data(_) => @@ -628,7 +629,8 @@ impl State report: diagn::RcReport, invocation: &asm::Invocation, fileserver: &dyn util::FileServer, - final_pass: bool) + final_pass: bool, + asm_block_args: &mut expr::EvalContext) -> Result { self.resolve_rule_invocation_candidates( @@ -636,7 +638,8 @@ impl State invocation, &invocation.get_rule_invoc().candidates, fileserver, - final_pass) + final_pass, + asm_block_args) } @@ -646,7 +649,8 @@ impl State invocation: &asm::Invocation, candidates: &Vec, fileserver: &dyn util::FileServer, - final_pass: bool) + final_pass: bool, + asm_block_args: &mut expr::EvalContext) -> Result { if DEBUG_CANDIDATE_RESOLUTION @@ -668,7 +672,8 @@ impl State invocation, &candidates[0], fileserver, - final_pass); + final_pass, + asm_block_args); } let mut successful_candidates = Vec::new(); @@ -693,7 +698,8 @@ impl State invocation, candidate, fileserver, - final_pass) + final_pass, + asm_block_args) { Ok(resolved) => { @@ -733,7 +739,8 @@ impl State invocation, successful_candidates[0].0, fileserver, - final_pass) + final_pass, + asm_block_args) } else { @@ -769,7 +776,8 @@ impl State invocation: &asm::Invocation, candidate: &asm::RuleInvocationCandidate, fileserver: &dyn util::FileServer, - final_pass: bool) + final_pass: bool, + asm_block_args: &mut expr::EvalContext) -> Result { let rule = self.get_rule(candidate.rule_ref).unwrap(); @@ -785,7 +793,7 @@ impl State report.clone(), &expr, &invocation.ctx, - &mut expr::EvalContext::new(), + asm_block_args, fileserver, final_pass)?; @@ -807,7 +815,8 @@ impl State invocation, &inner_candidate, fileserver, - final_pass)?; + final_pass, + asm_block_args)?; let arg_name = &rule.parameters[arg_index].name; @@ -936,7 +945,8 @@ impl State report, eval_ctx, &|info| self.eval_var(ctx, info, fileserver, final_pass), - &|info| self.eval_fn(ctx, info, fileserver)) + &|info| self.eval_fn(ctx, info, fileserver), + &|info| self.eval_asm(ctx, info, fileserver)) } @@ -1147,4 +1157,90 @@ impl State _ => unreachable!() } } + + + fn eval_asm( + &self, + ctx: &Context, + info: &mut expr::EvalAsmInfo, + fileserver: &dyn util::FileServer) + -> Result + { + let mut result = util::BigInt::new(0, Some(0)); + + let mut parser = syntax::Parser::new(Some(info.report.clone()), info.tokens); + + //println!("asm block `{}`", fileserver.get_excerpt(&parser.get_full_span())); + + while !parser.is_over() + { + let mut subparser = parser.slice_until_linebreak(); + subparser.suppress_reports(); + + let subparser_span = subparser.get_full_span(); + + //println!("> instr `{}`", fileserver.get_excerpt(&subparser_span)); + + let matches = asm::parser::match_rule_invocation( + &self, + subparser, + ctx.clone(), + fileserver, + info.report.clone())?; + + let value = self.resolve_rule_invocation( + info.report.clone(), + &matches, + fileserver, + true, + info.args)?; + + let (bigint, size) = match value + { + expr::Value::Integer(bigint) => + { + match bigint.size + { + Some(size) => (bigint, size), + None => + { + info.report.error_span( + "cannot infer size of instruction", + &subparser_span); + + return Err(()); + } + } + } + + _ => + { + info.report.error_span( + "wrong type returned from instruction", + &subparser_span); + + return Err(()); + } + }; + + if size > 0 + { + if result.size.unwrap() == 0 + { + result = bigint; + } + else + { + result = result.concat( + (result.size.unwrap() - 1, 0), + &bigint, + (size - 1, 0)); + } + } + + parser.expect_linebreak()?; + } + + Ok(expr::Value::make_integer(result)) + } } diff --git a/src/expr/eval.rs b/src/expr/eval.rs index f01cfcf6..392c8d99 100644 --- a/src/expr/eval.rs +++ b/src/expr/eval.rs @@ -55,18 +55,29 @@ pub struct EvalFunctionInfo<'a> } +pub struct EvalAsmInfo<'a> +{ + pub report: diagn::RcReport, + pub tokens: &'a [syntax::Token], + pub span: &'a diagn::Span, + pub args: &'a mut EvalContext, +} + + impl expr::Expr { - pub fn eval( + pub fn eval( &self, report: diagn::RcReport, ctx: &mut EvalContext, eval_var: &FVar, - eval_fn: &FFn) + eval_fn: &FFn, + eval_asm: &FAsm) -> Result where FVar: Fn(&EvalVariableInfo) -> Result, - FFn: Fn(&EvalFunctionInfo) -> Result + FFn: Fn(&EvalFunctionInfo) -> Result, + FAsm: Fn(&mut EvalAsmInfo) -> Result { match self { @@ -106,7 +117,7 @@ impl expr::Expr &expr::Expr::UnaryOp(ref span, _, op, ref inner_expr) => { - match inner_expr.eval(report.clone(), ctx, eval_var, eval_fn)? + match inner_expr.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)? { expr::Value::Integer(ref x) => match op { @@ -136,7 +147,7 @@ impl expr::Expr { if hierarchy_level == 0 && hierarchy.len() == 1 { - let value = rhs_expr.eval(report.clone(), ctx, eval_var, eval_fn)?; + let value = rhs_expr.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)?; ctx.set_local(hierarchy[0].clone(), value); return Ok(expr::Value::Void); } @@ -150,7 +161,7 @@ impl expr::Expr else if op == expr::BinaryOp::LazyOr || op == expr::BinaryOp::LazyAnd { - let lhs = lhs_expr.eval(report.clone(), ctx, eval_var, eval_fn)?; + let lhs = lhs_expr.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)?; match (op, &lhs) { @@ -161,7 +172,7 @@ impl expr::Expr _ => return Err(report.error_span("invalid argument type to operator", &lhs_expr.span())) } - let rhs = rhs_expr.eval(report.clone(), ctx, eval_var, eval_fn)?; + let rhs = rhs_expr.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)?; match (op, &rhs) { @@ -175,7 +186,7 @@ impl expr::Expr else { - match (lhs_expr.eval(report.clone(), ctx, eval_var, eval_fn)?, rhs_expr.eval(report.clone(), ctx, eval_var, eval_fn)?) + match (lhs_expr.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)?, rhs_expr.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)?) { (expr::Value::Integer(ref lhs), expr::Value::Integer(ref rhs)) => { @@ -257,17 +268,17 @@ impl expr::Expr &expr::Expr::TernaryOp(_, ref cond, ref true_branch, ref false_branch) => { - match cond.eval(report.clone(), ctx, eval_var, eval_fn)? + match cond.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)? { - expr::Value::Bool(true) => true_branch.eval(report.clone(), ctx, eval_var, eval_fn), - expr::Value::Bool(false) => false_branch.eval(report.clone(), ctx, eval_var, eval_fn), + expr::Value::Bool(true) => true_branch.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm), + expr::Value::Bool(false) => false_branch.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm), _ => Err(report.error_span("invalid condition type", &cond.span())) } } &expr::Expr::BitSlice(ref span, _, left, right, ref inner) => { - match inner.eval(report.clone(), ctx, eval_var, eval_fn)? + match inner.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)? { expr::Value::Integer(ref x) => Ok(expr::Value::make_integer(x.slice(left, right))), _ => Err(report.error_span("invalid argument type to slice", &span)) @@ -276,7 +287,7 @@ impl expr::Expr &expr::Expr::SoftSlice(_, _, _, _, ref inner) => { - inner.eval(report, ctx, eval_var, eval_fn) + inner.eval(report, ctx, eval_var, eval_fn, eval_asm) } &expr::Expr::Block(_, ref exprs) => @@ -284,14 +295,14 @@ impl expr::Expr let mut result = expr::Value::Void; for expr in exprs - { result = expr.eval(report.clone(), ctx, eval_var, eval_fn)?; } + { result = expr.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)?; } Ok(result) } &expr::Expr::Call(ref span, ref target, ref arg_exprs) => { - let func = target.eval(report.clone(), ctx, eval_var, eval_fn)?; + let func = target.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)?; match func { @@ -299,7 +310,7 @@ impl expr::Expr { let mut args = Vec::new(); for expr in arg_exprs - { args.push(expr.eval(report.clone(), ctx, eval_var, eval_fn)?); } + { args.push(expr.eval(report.clone(), ctx, eval_var, eval_fn, eval_asm)?); } let info = EvalFunctionInfo { @@ -308,7 +319,7 @@ impl expr::Expr args, span, }; - + match eval_fn(&info) { Ok(value) => Ok(value), @@ -319,29 +330,25 @@ impl expr::Expr _ => Err(report.error_span("expression is not callable", &target.span())) } } - } - } - - - /*pub fn eval_slice(&self, report: RcReport, ctx: &mut ExpressionEvalContext, eval_var: &FVar, eval_fn: &FFn) -> Result - where - FVar: Fn(RcReport, &str, &Span) -> Result, - FFn: Fn(RcReport, usize, Vec, &Span) -> Result - { - match self - { - &expr::Expr::SoftSlice(ref span, _, left, right, ref inner) => + + &expr::Expr::Asm(ref span, ref tokens) => { - match inner.eval(report.clone(), ctx, eval_var, eval_fn)? + let mut info = EvalAsmInfo { - expr::Value::Integer(ref x) => Ok(expr::Value::make_integer(x.slice(left, right))), - _ => Err(report.error_span("invalid argument type to slice", &span)) + report: report.clone(), + tokens, + span, + args: ctx, + }; + + match eval_asm(&mut info) + { + Ok(value) => Ok(value), + Err(_) => Err(()) } } - - _ => self.eval(report, ctx, eval_var, eval_fn) } - }*/ + } } diff --git a/src/expr/expression.rs b/src/expr/expression.rs index 548bbe29..c65f84f6 100644 --- a/src/expr/expression.rs +++ b/src/expr/expression.rs @@ -12,7 +12,8 @@ pub enum Expr BitSlice(diagn::Span, diagn::Span, usize, usize, Box), SoftSlice(diagn::Span, diagn::Span, usize, usize, Box), Block(diagn::Span, Vec), - Call(diagn::Span, Box, Vec) + Call(diagn::Span, Box, Vec), + Asm(diagn::Span, Vec), } @@ -73,7 +74,8 @@ impl Expr &Expr::BitSlice (ref span, ..) => span.clone(), &Expr::SoftSlice(ref span, ..) => span.clone(), &Expr::Block (ref span, ..) => span.clone(), - &Expr::Call (ref span, ..) => span.clone() + &Expr::Call (ref span, ..) => span.clone(), + &Expr::Asm (ref span, ..) => span.clone(), } } } diff --git a/src/expr/mod.rs b/src/expr/mod.rs index 9c163678..8dd5c450 100644 --- a/src/expr/mod.rs +++ b/src/expr/mod.rs @@ -10,4 +10,5 @@ pub use self::expression::UnaryOp; pub use self::expression::BinaryOp; pub use self::eval::EvalContext; pub use self::eval::EvalVariableInfo; -pub use self::eval::EvalFunctionInfo; \ No newline at end of file +pub use self::eval::EvalFunctionInfo; +pub use self::eval::EvalAsmInfo; \ No newline at end of file diff --git a/src/expr/parser.rs b/src/expr/parser.rs index 5e2f0ac4..0fca3286 100644 --- a/src/expr/parser.rs +++ b/src/expr/parser.rs @@ -391,6 +391,9 @@ impl<'a, 'parser> ExpressionParser<'a, 'parser> else if self.parser.next_is(0, syntax::TokenKind::String) { self.parse_string() } + + else if self.parser.next_is(0, syntax::TokenKind::KeywordAsm) + { self.parse_asm() } else { @@ -520,4 +523,21 @@ impl<'a, 'parser> ExpressionParser<'a, 'parser> Ok(expr) } + + + fn parse_asm(&mut self) -> Result + { + let tk_asm = self.parser.expect(syntax::TokenKind::KeywordAsm)?; + self.parser.expect(syntax::TokenKind::BraceOpen)?; + + let contents = self.parser.slice_until_token(syntax::TokenKind::BraceClose); + + let tk_brace_close = self.parser.expect(syntax::TokenKind::BraceClose)?; + + let expr = expr::Expr::Asm( + tk_asm.span.clone().join(&tk_brace_close.span), + contents.get_cloned_tokens()); + + Ok(expr) + } } \ No newline at end of file diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs index 56cb924a..51480a52 100644 --- a/src/syntax/parser.rs +++ b/src/syntax/parser.rs @@ -76,6 +76,19 @@ impl<'a> Parser<'a> } + pub fn get_cloned_tokens(&self) -> Vec + { + let mut result = Vec::new(); + + for token in self.tokens + { + result.push(token.clone()); + } + + result + } + + pub fn get_next_spans(&self, count: usize) -> diagn::Span { if self.index >= self.tokens.len() diff --git a/src/syntax/token.rs b/src/syntax/token.rs index a26dfd1e..43131b51 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -21,6 +21,7 @@ pub enum TokenKind Identifier, Number, String, + KeywordAsm, ParenOpen, ParenClose, BracketOpen, @@ -85,6 +86,7 @@ impl TokenKind { self == TokenKind::Identifier || self == TokenKind::Number || + self == TokenKind::KeywordAsm || self == TokenKind::ParenOpen || self == TokenKind::ParenClose || self == TokenKind::BracketOpen || @@ -129,6 +131,7 @@ impl TokenKind TokenKind::Identifier => "identifier", TokenKind::Number => "number", TokenKind::String => "string", + TokenKind::KeywordAsm => "`asm` keyword", TokenKind::ParenOpen => "`(`", TokenKind::ParenClose => "`)`", TokenKind::BracketOpen => "`[`", @@ -188,6 +191,7 @@ impl Token { match self.kind { + TokenKind::KeywordAsm => "asm", TokenKind::ParenOpen => "(", TokenKind::ParenClose => ")", TokenKind::BracketOpen => "[", @@ -247,10 +251,10 @@ where S: Into let (kind, length) = check_for_whitespace(&src[index..]).unwrap_or_else(|| check_for_comment (&src[index..]).unwrap_or_else(|| + check_for_fixed (&src[index..]).unwrap_or_else(|| check_for_identifier(&src[index..]).unwrap_or_else(|| check_for_number (&src[index..]).unwrap_or_else(|| check_for_string (&src[index..]).unwrap_or_else(|| - check_for_fixed (&src[index..]).unwrap_or_else(|| (TokenKind::Error, 1))))))); let span = Span::new(filename.clone(), index, index + length); @@ -384,18 +388,18 @@ fn check_for_string(src: &[char]) -> Option<(TokenKind, usize)> { let mut length = 0; - if src[length] != '\"' // " + if src[length] != '\"' { return None; } length += 1; - while length < src.len() && src[length] != '\"' // " + while length < src.len() && src[length] != '\"' { length += 1; } if length >= src.len() { return None; } - if src[length] != '\"' // " + if src[length] != '\"' { return None; } length += 1; @@ -406,9 +410,10 @@ fn check_for_string(src: &[char]) -> Option<(TokenKind, usize)> fn check_for_fixed(src: &[char]) -> Option<(TokenKind, usize)> { - static POSSIBLE_TOKENS: [(&str, TokenKind); 40] = + static POSSIBLE_TOKENS: [(&str, TokenKind); 41] = [ ("\n", TokenKind::LineBreak), + ("asm", TokenKind::KeywordAsm), ("(", TokenKind::ParenOpen), (")", TokenKind::ParenClose), ("[", TokenKind::BracketOpen), diff --git a/src/test/expr.rs b/src/test/expr.rs index cc5ee1a2..95044a41 100644 --- a/src/test/expr.rs +++ b/src/test/expr.rs @@ -18,7 +18,8 @@ where S: Into> report.clone(), &mut expr::EvalContext::new(), &|_| Err(false), - &|_| Err(false))?; + &|_| Err(false), + &|_| Err(()))?; Ok(expr_value) } diff --git a/tests/expr_asm.asm b/tests/expr_asm.asm new file mode 100644 index 00000000..99b8ae68 --- /dev/null +++ b/tests/expr_asm.asm @@ -0,0 +1,179 @@ +; ::: +; simple +#ruledef +{ + emit {x: i8} => x + test {x} => asm { emit x } +} + +emit 0x12 ; = 0x12 +test 0x12 ; = 0x12 +test 0x10 + 2 ; = 0x12 + + +; ::: +; concat +#ruledef +{ + emit {x: i8} => x + test {x} => asm { emit x } @ asm { emit 0xff } +} + +emit 0x12 ; = 0x12 +test 0x12 ; = 0x12ff +test 0x10 + 2 ; = 0x12ff + + +; ::: +; multi-line +#ruledef +{ + emit {x: i8} => 0x11 @ x + load {x: i8} => 0x22 @ x + test {x} => asm + { + emit x + emit 0xff + load x + } +} + +test 0x12 ; = 0x1112_11ff_2212 + + +; ::: +; inner expression +#ruledef +{ + emit {x: i8} => x + test {x} => asm { emit x * 9 } +} + +test 2 ; = 0x12 +test 1 + 1 ; = 0x12 + + +; ::: +; multiple captured variables +#ruledef +{ + emit {x: i8}, {y: i8} => x @ y + test {x}, {y} => asm { emit x, y } +} + +test 0x12, 0x34 ; = 0x1234 +test 0x10 + 2, 0x30 + 4 ; = 0x1234 + + +; ::: +; captured local variable +#ruledef +{ + emit {x: i8} => x + test {x} => + { + y = 0x10 + asm { emit x + y } + } +} + +test 2 ; = 0x12 + + +; ::: +; from different ruledef blocks +#ruledef +{ + test {x} => asm { emit x } +} + +#ruledef +{ + emit {x: i8} => x +} + +emit 0x12 ; = 0x12 +test 0x12 ; = 0x12 + + +; ::: +; assert resolution +#ruledef +{ + emit {x: i8} => + { + assert(x < 0x10) + 0x11 @ x + } + emit {x: i8} => + { + assert(x >= 0x10) + 0x22 @ x + } + test {x} => asm { emit x } +} + +emit 0x08 ; = 0x1108 +emit 0x12 ; = 0x2212 +test 0x08 ; = 0x1108 +test 0x12 ; = 0x2212 + + +; ::: +; no match +#ruledef +{ + test {x} => asm { unknown x } +} + +test 0x12 ; error: failed / error: :4: no match + + +; ::: +; multiple matches +#ruledef +{ + emit {x: i8} => x + emit {x: i8} => x + test {x} => asm { emit x } +} + +test 0x12 ; error: failed / error: :6: multiple + + +; ::: +; inner error +#ruledef +{ + emit {x} => x / 0 + test {x} => asm { emit x } +} + +test 12 ; error: failed / error: :4: zero + + +; ::: +; inner error +#ruledef +{ + emit {x} => x + test {x} => asm { emit x } +} + +test 12 ; error: failed / error: :5: infer + + +; ::: +; from forward-referenced ruledef +#ruledef +{ + test {x} => asm { emit x } +} + +; weird error?! +test 0x12 ; error: converge + +#ruledef +{ + emit {x: i8} => x +} \ No newline at end of file