Skip to content

Commit

Permalink
fix #26: add parameter integer types
Browse files Browse the repository at this point in the history
  • Loading branch information
hlorenzi committed Feb 20, 2020
1 parent d195785 commit 1b3d436
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "customasm"
version = "0.10.5"
version = "0.10.6"
edition = "2018"
authors = ["Henrique Lorenzi <hlorenzi12@gmail.com>"]

Expand Down
49 changes: 48 additions & 1 deletion src/asm/assembler.rs
Expand Up @@ -5,6 +5,7 @@ use crate::asm::BankDef;
use crate::asm::Bank;
use crate::asm::BinaryBlock;
use crate::asm::cpudef::CpuDef;
use crate::asm::cpudef::RuleParameterType;
use crate::util::FileServer;
use num_bigint::ToBigInt;

Expand Down Expand Up @@ -427,6 +428,48 @@ impl AssemblerState

Ok(())
}


pub fn check_expr_constraint(&self, report: RcReport, value: &ExpressionValue, typ: &RuleParameterType, span: &Span) -> Result<(), ()>
{
use crate::expr::bigint_bits;

if let RuleParameterType::Unsigned(width) = typ
{
if let ExpressionValue::Integer(value_int) = value
{
if value_int.sign() == num_bigint::Sign::Minus ||
bigint_bits(&value_int) > *width
{ return Err(report.error_span(&format!("argument out of range for type `u{}`", width), &span)); }
}
else
{ return Err(report.error_span(&format!("wrong argument for type `u{}`", width), &span)); }
}
else if let RuleParameterType::Signed(width) = typ
{
if let ExpressionValue::Integer(value_int) = value
{
if (value_int.sign() == num_bigint::Sign::NoSign && *width == 0) ||
(value_int.sign() == num_bigint::Sign::Plus && bigint_bits(&value_int) >= *width) ||
(value_int.sign() == num_bigint::Sign::Minus && bigint_bits(&value_int) > *width)
{ return Err(report.error_span(&format!("argument out of range for type `s{}`", width), &span)); }
}
else
{ return Err(report.error_span(&format!("wrong argument for type `s{}`", width), &span)); }
}
else if let RuleParameterType::Integer(width) = typ
{
if let ExpressionValue::Integer(value_int) = value
{
if bigint_bits(&value_int) > *width
{ return Err(report.error_span(&format!("argument out of range for type `i{}`", width), &span)); }
}
else
{ return Err(report.error_span(&format!("wrong argument for type `i{}`", width), &span)); }
}

Ok(())
}


pub fn output_parsed_instr(&mut self, report: RcReport, instr: &mut ParsedInstruction) -> Result<(), ()>
Expand All @@ -442,7 +485,11 @@ impl AssemblerState
let rule = &self.cpudef.as_ref().unwrap().rules[instr.rule_index];
let mut args_eval_ctx = ExpressionEvalContext::new();
for i in 0..instr.args.len()
{ args_eval_ctx.set_local(rule.params[i].name.clone(), instr.args[i].clone().unwrap()); }
{
let arg = instr.args[i].clone().unwrap();
self.check_expr_constraint(report.clone(), &arg, &rule.params[i].typ, &instr.exprs[i].span())?;
args_eval_ctx.set_local(rule.params[i].name.clone(), arg);
}

// Output binary representation.
let (left, right) = rule.production.slice().unwrap();
Expand Down
53 changes: 37 additions & 16 deletions src/asm/cpudef/cpudef.rs
Expand Up @@ -292,22 +292,7 @@ impl<'t> CpuDefParser<'t>

let (is_expr_parameter, typ) =
if self.parser.maybe_expect(TokenKind::Colon).is_some()
{
let tk_type = self.parser.expect(TokenKind::Identifier)?;
let typename = tk_type.excerpt.unwrap().clone();

let mut tokendef_index = None;
for i in 0..self.custom_token_defs.len()
{
if typename == self.custom_token_defs[i].name
{ tokendef_index = Some(i); }
}

if tokendef_index.is_none()
{ return Err(self.parser.report.error_span("unknown parameter type", &tk_type.span)); }

(false, RuleParameterType::CustomTokenDef(tokendef_index.unwrap()))
}
{ (false, self.parse_rule_parameter_type()?) }
else
{ (true, RuleParameterType::Expression) };

Expand All @@ -317,6 +302,42 @@ impl<'t> CpuDefParser<'t>

Ok(is_expr_parameter)
}


fn parse_rule_parameter_type(&mut self) -> Result<RuleParameterType, ()>
{
let tk_type = self.parser.expect(TokenKind::Identifier)?;
let typename = tk_type.excerpt.unwrap().clone();
let typename_first_char = typename.chars().next();

if typename_first_char == Some('u') ||
typename_first_char == Some('s') ||
typename_first_char == Some('i')
{
if let Ok(type_width) = usize::from_str_radix(&typename[1..], 10)
{
match typename_first_char
{
Some('u') => return Ok(RuleParameterType::Unsigned(type_width)),
Some('s') => return Ok(RuleParameterType::Signed(type_width)),
Some('i') => return Ok(RuleParameterType::Integer(type_width)),
_ => unreachable!()
}
}
}

let mut tokendef_index = None;
for i in 0..self.custom_token_defs.len()
{
if typename == self.custom_token_defs[i].name
{ tokendef_index = Some(i); }
}

if let Some(tokendef_index) = tokendef_index
{ Ok(RuleParameterType::CustomTokenDef(tokendef_index)) }
else
{ Err(self.parser.report.error_span("unknown parameter type", &tk_type.span)) }
}


fn parse_rule_production(&mut self, rule: &mut Rule) -> Result<(), ()>
Expand Down
5 changes: 4 additions & 1 deletion src/asm/cpudef/rule.rs
Expand Up @@ -25,7 +25,10 @@ pub enum RulePatternPart
pub enum RuleParameterType
{
Expression,
CustomTokenDef(usize)
CustomTokenDef(usize),
Unsigned(usize),
Signed(usize),
Integer(usize),
}


Expand Down
12 changes: 10 additions & 2 deletions src/asm/parser.rs
Expand Up @@ -469,12 +469,20 @@ impl<'a> AssemblerParser<'a>
// Evaluate the instruction's production and check whether it
// generates an error, in which case, just try the next instruction in the series.
let rule = &self.state.cpudef.as_ref().unwrap().rules[instr_match.rule_indices[best_match]];

let report = RcReport::new();

let mut args_eval_ctx = ExpressionEvalContext::new();
for i in 0..args.len()
{ args_eval_ctx.set_local(rule.params[i].name.clone(), args[i].clone().unwrap()); }
{
let arg = args[i].clone().unwrap();
self.state.check_expr_constraint(report.clone(), &arg, &rule.params[i].typ, &Span::new_dummy()).ok();
args_eval_ctx.set_local(rule.params[i].name.clone(), arg);
}

self.state.expr_eval(report.clone(), &ctx, &rule.production, &mut args_eval_ctx).ok();

if self.state.expr_eval(RcReport::new(), &ctx, &rule.production, &mut args_eval_ctx).is_ok()
if !report.has_errors()
{ break; }

best_match += 1;
Expand Down
2 changes: 1 addition & 1 deletion src/expr/eval.rs
Expand Up @@ -313,7 +313,7 @@ impl ExpressionValue
}


fn bigint_bits(x: &BigInt) -> usize
pub fn bigint_bits(x: &BigInt) -> usize
{
if x.is_zero()
{ return 1; }
Expand Down
3 changes: 2 additions & 1 deletion src/expr/mod.rs
Expand Up @@ -8,4 +8,5 @@ pub use self::expression::Expression;
pub use self::expression::ExpressionValue;
pub use self::expression::UnaryOp;
pub use self::expression::BinaryOp;
pub use self::eval::ExpressionEvalContext;
pub use self::eval::ExpressionEvalContext;
pub use self::eval::bigint_bits;
142 changes: 142 additions & 0 deletions src/test/asm.rs
Expand Up @@ -154,6 +154,88 @@ fn test_parameters()
}


#[test]
fn test_parameter_types()
{
test("load {a: u} -> 0x12 @ a[7:0]", "load 0x00", Fail(("cpu", 1, "type")));
test("load {a: u1f} -> 0x12 @ a[7:0]", "load 0x00", Fail(("cpu", 1, "type")));
test("load {a: u-1} -> 0x12 @ a[7:0]", "load 0x00", Fail(("cpu", 1, "type")));

test("load {a: u0} -> 0x12 @ a[7:0]", "load 0x00", Fail(("asm", 1, "out of range")));

test("load {a: u1} -> 0x12 @ a[7:0]", "load 0b00", Pass((4, "1200")));
test("load {a: u1} -> 0x12 @ a[7:0]", "load 0b01", Pass((4, "1201")));
test("load {a: u1} -> 0x12 @ a[7:0]", "load 0b10", Fail(("asm", 1, "out of range")));
test("load {a: u1} -> 0x12 @ a[7:0]", "load -0b01", Fail(("asm", 1, "out of range")));

test("load {a: u8} -> 0x12 @ a[7:0]", "load 0x00", Pass((4, "1200")));
test("load {a: u8} -> 0x12 @ a[7:0]", "load 0x34", Pass((4, "1234")));
test("load {a: u8} -> 0x12 @ a[7:0]", "load 0xff", Pass((4, "12ff")));
test("load {a: u8} -> 0x12 @ a[7:0]", "load 0x100", Fail(("asm", 1, "out of range")));
test("load {a: u8} -> 0x12 @ a[7:0]", "load -0x01", Fail(("asm", 1, "out of range")));

test("load {a: u16} -> 0x12 @ a[15:0]", "load 0x0000", Pass((4, "120000")));
test("load {a: u16} -> 0x12 @ a[15:0]", "load 0x3456", Pass((4, "123456")));
test("load {a: u16} -> 0x12 @ a[15:0]", "load 0xffff", Pass((4, "12ffff")));
test("load {a: u16} -> 0x12 @ a[15:0]", "load 0x10000", Fail(("asm", 1, "out of range")));
test("load {a: u16} -> 0x12 @ a[15:0]", "load -0x0001", Fail(("asm", 1, "out of range")));

test("load {a: s0} -> 0x12 @ a[7:0]", "load 0x00", Fail(("asm", 1, "out of range")));

test("load {a: s1} -> 0x12 @ a[7:0]", "load 0b00", Pass((4, "1200")));
test("load {a: s1} -> 0x12 @ a[7:0]", "load -0b01", Pass((4, "12ff")));
test("load {a: s1} -> 0x12 @ a[7:0]", "load 0b01", Fail(("asm", 1, "out of range")));
test("load {a: s1} -> 0x12 @ a[7:0]", "load -0b10", Fail(("asm", 1, "out of range")));

test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x00", Pass((4, "1200")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x34", Pass((4, "1234")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x7e", Pass((4, "127e")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x7f", Pass((4, "127f")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load -0x01", Pass((4, "12ff")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load -0x7f", Pass((4, "1281")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load -0x80", Pass((4, "1280")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x80", Fail(("asm", 1, "out of range")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load -0x81", Fail(("asm", 1, "out of range")));

test("load {a: s8} -> 0x12 @ a[15:0]", "load -0x01", Pass((4, "12ffff")));

test("load {a: i0} -> 0x12 @ a[7:0]", "load 0x00", Fail(("asm", 1, "out of range")));

test("load {a: i1} -> 0x12 @ a[7:0]", "load 0b00", Pass((4, "1200")));
test("load {a: i1} -> 0x12 @ a[7:0]", "load 0b01", Pass((4, "1201")));
test("load {a: i1} -> 0x12 @ a[7:0]", "load -0b01", Pass((4, "12ff")));
test("load {a: i1} -> 0x12 @ a[7:0]", "load 0b10", Fail(("asm", 1, "out of range")));
test("load {a: i1} -> 0x12 @ a[7:0]", "load -0b10", Fail(("asm", 1, "out of range")));

test("load {a: i8} -> 0x12 @ a[7:0]", "load 0x00", Pass((4, "1200")));
test("load {a: i8} -> 0x12 @ a[7:0]", "load 0x34", Pass((4, "1234")));
test("load {a: i8} -> 0x12 @ a[7:0]", "load 0xff", Pass((4, "12ff")));
test("load {a: i8} -> 0x12 @ a[7:0]", "load -0x01", Pass((4, "12ff")));
test("load {a: i8} -> 0x12 @ a[7:0]", "load -0x7f", Pass((4, "1281")));
test("load {a: i8} -> 0x12 @ a[7:0]", "load -0x80", Pass((4, "1280")));
test("load {a: i8} -> 0x12 @ a[7:0]", "load 0x100", Fail(("asm", 1, "out of range")));
test("load {a: i8} -> 0x12 @ a[7:0]", "load -0x81", Fail(("asm", 1, "out of range")));

test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x05 - 0x05", Pass((4, "1200")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x30 + 0x04", Pass((4, "1234")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x70 + 0x0f", Pass((4, "127f")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x00 - 0x01", Pass((4, "12ff")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load -0x70 - 0x0f", Pass((4, "1281")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load -0x70 - 0x10", Pass((4, "1280")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load 0x70 + 0x10", Fail(("asm", 1, "out of range")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load -0x70 - 0x11", Fail(("asm", 1, "out of range")));

test("load {a: s8} -> 0x12 @ a[7:0]", "load x \n x = 0x00", Pass((4, "1200")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load x \n x = 0x34", Pass((4, "1234")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load x \n x = 0x7f", Pass((4, "127f")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load x \n x = -0x01", Pass((4, "12ff")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load x \n x = -0x7f", Pass((4, "1281")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load x \n x = -0x80", Pass((4, "1280")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load x \n x = 0x80", Fail(("asm", 1, "out of range")));
test("load {a: s8} -> 0x12 @ a[7:0]", "load x \n x = -0x81", Fail(("asm", 1, "out of range")));
}


#[test]
fn test_tokendef()
{
Expand Down Expand Up @@ -565,6 +647,66 @@ fn test_cascading()
test(INSTRSET, " b = 0x15 \n add a, b \n a = 0x25", Pass((4, "dd2515")));
test(INSTRSET, " add a, b \n a = 0x07 \n b = 0x07", Pass((4, "dd0707")));
test(INSTRSET, " add a, b \n a = 0x25 \n b = 0x25", Pass((4, "dd2525")));

static INSTRSET2: &'static str = "
load {a: u4} -> 0x10 @ a[7:0] \n
load {a: u8} -> 0x20 @ a[7:0] \n
load {a} -> 0xff @ a[7:0] \n
store {a: u4 } -> 0x30 @ a[7:0] \n
store {a: u8 } -> 0x40 @ a[7:0] \n
store {a: u16} -> 0x50 @ a[7:0] \n
add {a: u4}, {b: u4} -> 0xaa @ a[7:0] @ b[7:0] \n
add {a: u8}, {b} -> 0xbb @ a[7:0] @ b[7:0] \n
add {a}, {b: u8} -> 0xcc @ a[7:0] @ b[7:0] \n
add {a}, {b} -> 0xdd @ a[7:0] @ b[7:0]";

test(INSTRSET2, "load 0x005", Pass((4, "1005")));
test(INSTRSET2, "load 0x015", Pass((4, "2015")));
test(INSTRSET2, "load 0x125", Pass((4, "ff25")));

test(INSTRSET2, "value = 0x005 \n load value", Pass((4, "1005")));
test(INSTRSET2, "value = 0x015 \n load value", Pass((4, "2015")));
test(INSTRSET2, "value = 0x125 \n load value", Pass((4, "ff25")));

test(INSTRSET2, "load value \n value = 0x005", Pass((4, "ff05")));
test(INSTRSET2, "load value \n value = 0x015", Pass((4, "ff15")));
test(INSTRSET2, "load value \n value = 0x125", Pass((4, "ff25")));

test(INSTRSET2, "store 0x00005", Pass((4, "3005")));
test(INSTRSET2, "store 0x00015", Pass((4, "4015")));
test(INSTRSET2, "store 0x00125", Pass((4, "5025")));
test(INSTRSET2, "store 0x11135", Fail(("asm", 1, "out of range")));

test(INSTRSET2, "value = 0x00005 \n store value", Pass((4, "3005")));
test(INSTRSET2, "value = 0x00015 \n store value", Pass((4, "4015")));
test(INSTRSET2, "value = 0x00125 \n store value", Pass((4, "5025")));
test(INSTRSET2, "value = 0x11135 \n store value", Fail(("asm", 2, "out of range")));

test(INSTRSET2, "store value \n value = 0x00005", Pass((4, "5005")));
test(INSTRSET2, "store value \n value = 0x00015", Pass((4, "5015")));
test(INSTRSET2, "store value \n value = 0x00125", Pass((4, "5025")));
test(INSTRSET2, "store value \n value = 0x11135", Fail(("asm", 1, "out of range")));

test(INSTRSET2, "add 0x005, 0x007", Pass((4, "aa0507")));
test(INSTRSET2, "add 0x015, 0x025", Pass((4, "bb1525")));
test(INSTRSET2, "add 0x125, 0x015", Pass((4, "cc2515")));
test(INSTRSET2, "add 0x125, 0x125", Pass((4, "dd2525")));

test(INSTRSET2, "a = 0x005 \n b = 0x007 \n add a, b", Pass((4, "aa0507")));
test(INSTRSET2, "a = 0x005 \n b = 0x125 \n add a, b", Pass((4, "bb0525")));
test(INSTRSET2, "a = 0x015 \n b = 0x007 \n add a, b", Pass((4, "bb1507")));
test(INSTRSET2, "a = 0x015 \n b = 0x125 \n add a, b", Pass((4, "bb1525")));
test(INSTRSET2, "a = 0x125 \n b = 0x015 \n add a, b", Pass((4, "cc2515")));
test(INSTRSET2, "a = 0x125 \n b = 0x125 \n add a, b", Pass((4, "dd2525")));

test(INSTRSET2, "a = 0x005 \n add a, b \n b = 0x007", Pass((4, "dd0507")));
test(INSTRSET2, "a = 0x015 \n add a, b \n b = 0x125", Pass((4, "dd1525")));
test(INSTRSET2, " b = 0x007 \n add a, b \n a = 0x005", Pass((4, "dd0507")));
test(INSTRSET2, " b = 0x015 \n add a, b \n a = 0x125", Pass((4, "dd2515")));
test(INSTRSET2, " add a, b \n a = 0x007 \n b = 0x007", Pass((4, "dd0707")));
test(INSTRSET2, " add a, b \n a = 0x125 \n b = 0x125", Pass((4, "dd2525")));
}


Expand Down

0 comments on commit 1b3d436

Please sign in to comment.