Skip to content

Commit

Permalink
add custom token defs; implement #2
Browse files Browse the repository at this point in the history
  • Loading branch information
hlorenzi committed May 20, 2018
1 parent fb3c225 commit f061fca
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 52 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "customasm"
version = "0.7.0"
version = "0.8.0"
authors = ["Henrique Lorenzi <[email protected]>"]

[lib]
Expand Down
11 changes: 2 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,16 @@ Also, [check out an example project](/examples/nes/) which targets the NES!
You can compile from source by simply doing `cargo build`. There's also a
battery of tests available at `cargo test`.

## Upgrading from `v0.4`

Starting from `v0.6`, if you don't want to `#include` a CPU file in the main assembly file,
you can specify separate files to process in the same assembly session
with the `-i` command line option. Just remember to enclose the old CPU definition in a
`#cpudef` directive.

## Command Line Usage

```
Usage: customasm [options] <asm-file>
Usage: customasm [options] <asm-file-1> ... <asm-file-N>
Options:
-f, --format FORMAT The format of the output file. Possible formats:
binary, binstr, hexstr, bindump, hexdump
-i, --include FILE Specifies an additional file for processing before the
main assembly.
given <asm-files>.
-o, --output FILE The name of the output file.
-p, --print Print output to stdout instead of writing to a file.
-q, --quiet Suppress progress reports.
Expand Down
37 changes: 36 additions & 1 deletion doc/cpudef.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defines mnemonics for its instruction set.
## Configurations

The syntax first expects a list of configuration directives, one per line.
The currently available configuration is:
The currently available configurations are:

- `#align <bit_num>`
Sets the number of bits per byte for the target machine.
Expand All @@ -32,6 +32,19 @@ Machine instructions must be aligned to a byte boundary,
hence the directive's name. So, with 8-bit bytes, valid
instruction sizes are 8 bits, 16 bits, 24 bits, and so on.

- `#tokendef <name>`
Creates a group of tokens with associated values, which can
be used in place of arguments (e.g. for named registers).
See below for usage in parameters. Syntax is as follows:
```asm
#tokendef reg
{
a = 1
b = 2
c = 3
}
```

## Rules

The first line not starting with a `#` begins the list of rules.
Expand All @@ -48,6 +61,9 @@ The pattern part of a rule defines its mnemonic and/or parameter slots.
The pattern is a sequence of tokens:
- For mnemonics, text, or punctuation: just write them out verbatim.
- For parameter slots: write them as `{x}`, with `x` being any valid name.
- For custom token groups declared with `#tokendef`, write them as `{x: name}`,
with `name` being the name given at the `#tokendef` declaration (`reg` in the
example above).

### Output

Expand Down Expand Up @@ -209,4 +225,23 @@ Rule | Used as | Output
nop -> 0b110
halt -> 0b111
}
```

```asm
#cpudef
{
; example with named registers
#align 8
#tokendef reg
{
r0 = 0
r1 = 1
r2 = 2
r3 = 3
}
mov {dest: reg}, {value} -> 0b111100 @ dest[1:0] @ value[7:0]
}
```
101 changes: 92 additions & 9 deletions src/asm/cpudef/cpudef.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use syntax::{Token, TokenKind, Parser};
use expr::Expression;
use asm::cpudef::{Rule, RulePatternMatcher};
use expr::{Expression, ExpressionValue};
use asm::cpudef::{Rule, RuleParameterType, RulePatternMatcher};
use num::BigInt;
use std::collections::HashMap;


#[derive(Debug)]
pub struct CpuDef
{
pub align: usize,
pub rules: Vec<Rule>,
pub pattern_matcher: RulePatternMatcher
pub pattern_matcher: RulePatternMatcher,
pub custom_token_defs: Vec<CustomTokenDef>
}


Expand All @@ -17,7 +20,16 @@ struct CpuDefParser<'t>
parser: &'t mut Parser,

align: Option<usize>,
rules: Vec<Rule>
rules: Vec<Rule>,
custom_token_defs: Vec<CustomTokenDef>
}


#[derive(Debug)]
pub struct CustomTokenDef
{
pub name: String,
pub excerpt_to_value_map: HashMap<String, ExpressionValue>
}


Expand All @@ -29,7 +41,8 @@ impl CpuDef
{
parser: parser,
align: None,
rules: Vec::new()
rules: Vec::new(),
custom_token_defs: Vec::new()
};

cpudef_parser.parse_directives()?;
Expand All @@ -39,13 +52,14 @@ impl CpuDef

cpudef_parser.parse_rules()?;

let pattern_matcher = RulePatternMatcher::new(&cpudef_parser.rules);
let pattern_matcher = RulePatternMatcher::new(&cpudef_parser.rules, &cpudef_parser.custom_token_defs);

let cpudef = CpuDef
{
align: cpudef_parser.align.unwrap(),
rules: cpudef_parser.rules,
pattern_matcher: pattern_matcher
pattern_matcher: pattern_matcher,
custom_token_defs: cpudef_parser.custom_token_defs
};

Ok(cpudef)
Expand All @@ -63,6 +77,7 @@ impl<'t> CpuDefParser<'t>
match tk_name.excerpt.as_ref().unwrap().as_ref()
{
"align" => self.parse_directive_align(&tk_name)?,
"tokendef" => self.parse_directive_tokendef(&tk_name)?,

_ => return Err(self.parser.report.error_span("unknown directive", &tk_name.span))
}
Expand All @@ -89,6 +104,53 @@ impl<'t> CpuDefParser<'t>
Ok(())
}


fn parse_directive_tokendef(&mut self, _tk_name: &Token) -> Result<(), ()>
{
let tk_defname = self.parser.expect(TokenKind::Identifier)?;

let defname = tk_defname.excerpt.unwrap().clone();

if self.custom_token_defs.iter().find(|def| def.name == defname).is_some()
{ return Err(self.parser.report.error_span("duplicate custom token def name", &tk_defname.span)); }

let mut tokendef = CustomTokenDef
{
name: defname,
excerpt_to_value_map: HashMap::new()
};

self.parser.expect(TokenKind::BraceOpen)?;

while !self.parser.is_over() && !self.parser.next_is(0, TokenKind::BraceClose)
{
let tk_token = self.parser.expect(TokenKind::Identifier)?;
let token_excerpt = tk_token.excerpt.unwrap().clone();

if tokendef.excerpt_to_value_map.contains_key(&token_excerpt)
{ return Err(self.parser.report.error_span("duplicate token in group", &tk_token.span)); }

self.parser.expect(TokenKind::Equal)?;
let value = ExpressionValue::Integer(BigInt::from(self.parser.expect_usize()?.1));

tokendef.excerpt_to_value_map.insert(token_excerpt, value);

if self.parser.maybe_expect_linebreak().is_some()
{ continue; }

if self.parser.next_is(0, TokenKind::BraceClose)
{ continue; }

self.parser.expect(TokenKind::Comma)?;
}

self.parser.expect(TokenKind::BraceClose)?;

self.custom_token_defs.push(tokendef);

Ok(())
}


fn parse_rules(&mut self) -> Result<(), ()>
{
Expand Down Expand Up @@ -188,7 +250,28 @@ impl<'t> CpuDefParser<'t>
if rule.param_exists(&name)
{ return Err(self.parser.report.error_span("duplicate parameter name", &tk_name.span)); }

rule.pattern_add_param(name);
let 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)); }

RuleParameterType::CustomTokenDef(tokendef_index.unwrap())
}
else
{ RuleParameterType::Expression };

rule.pattern_add_param(name, typ);

self.parser.expect(TokenKind::BraceClose)?;

Expand All @@ -207,7 +290,7 @@ impl<'t> CpuDefParser<'t>
};

if width % self.align.unwrap() != 0
{ return Err(self.parser.report.error_span(format!("production (width = {}) does not align with a word boundary", width), &expr.span())); }
{ return Err(self.parser.report.error_span(format!("binary representation (width = {}) does not align with a word boundary", width), &expr.span())); }

rule.production = expr;

Expand Down
2 changes: 2 additions & 0 deletions src/asm/cpudef/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ mod rule_pattern_matcher;


pub use self::cpudef::CpuDef;
pub use self::cpudef::CustomTokenDef;
pub use self::rule::Rule;
pub use self::rule::RulePatternPart;
pub use self::rule::RuleParameter;
pub use self::rule::RuleParameterType;
pub use self::rule_pattern_matcher::RulePatternMatcher;
16 changes: 13 additions & 3 deletions src/asm/cpudef/rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,19 @@ pub enum RulePatternPart
}


#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum RuleParameterType
{
Expression,
CustomTokenDef(usize)
}


#[derive(Debug)]
pub struct RuleParameter
{
pub name: String
pub name: String,
pub typ: RuleParameterType
}


Expand All @@ -47,7 +56,7 @@ impl Rule
}


pub fn pattern_add_param<S>(&mut self, name: S)
pub fn pattern_add_param<S>(&mut self, name: S, typ: RuleParameterType)
where S: Into<String>
{
let name_owned = name.into();
Expand All @@ -58,7 +67,8 @@ impl Rule

let param = RuleParameter
{
name: name_owned
name: name_owned,
typ: typ
};

self.params.push(param);
Expand Down
Loading

0 comments on commit f061fca

Please sign in to comment.