Rename libraries

libctonfile -> libreader.

This library will only provide .cton file reading/parsing services which are
not needed after deployment.

Code for writing .cton files lives in the main cretonne library because it is
fairly small, and because it is useful for extracting test cases from a
deployed library.
This commit is contained in:
Jakob Stoklund Olesen
2016-04-29 14:32:10 -07:00
parent ddea422ceb
commit 810a90e322
9 changed files with 11 additions and 11 deletions

12
src/libreader/Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
authors = ["The Cretonne Project Developers"]
name = "cretonne-reader"
version = "0.0.0"
publish = false
[lib]
name = "cton_reader"
path = "lib.rs"
[dependencies]
cretonne = { path = "../libcretonne" }

464
src/libreader/lexer.rs Normal file
View File

@@ -0,0 +1,464 @@
// ====--------------------------------------------------------------------------------------====//
//
// Lexical analysis for .cton files.
//
// ====--------------------------------------------------------------------------------------====//
use std::str::CharIndices;
use cretonne::types;
/// The location of a `Token` or `Error`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Location {
pub line_number: usize,
}
/// A Token returned from the `Lexer`.
///
/// Some variants may contains references to the original source text, so the `Token` has the same
/// lifetime as the source.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Token<'a> {
Comment(&'a str),
LPar, // '('
RPar, // ')'
LBrace, // '{'
RBrace, // '}'
Comma, // ','
Dot, // '.'
Colon, // ':'
Equal, // '='
Arrow, // '->'
Function, // 'function'
Entry, // 'entry'
Float(&'a str), // Floating point immediate
Integer(&'a str), // Integer immediate
Type(types::Type), // i32, f32, b32x4, ...
ValueDirect(u32), // v12
ValueExtended(u32), // vx7
Ebb(u32), // ebb3
StackSlot(u32), // ss3
Identifier(&'a str), // Unrecognized identifier (opcode, enumerator, ...)
}
/// A `Token` with an associated location.
#[derive(Debug, PartialEq, Eq)]
pub struct LocatedToken<'a> {
pub token: Token<'a>,
pub location: Location,
}
/// Wrap up a `Token` with the given location.
fn token<'a>(token: Token<'a>, loc: Location) -> Result<LocatedToken<'a>, LocatedError> {
Ok(LocatedToken {
token: token,
location: loc,
})
}
/// An error from the lexical analysis.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
InvalidChar,
}
/// An `Error` with an associated Location.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LocatedError {
pub error: Error,
pub location: Location,
}
/// Wrap up an `Error` with the given location.
fn error<'a>(error: Error, loc: Location) -> Result<LocatedToken<'a>, LocatedError> {
Err(LocatedError {
error: error,
location: loc,
})
}
/// Lexical analysis.
///
/// A `Lexer` reads text from a `&str` and provides a sequence of tokens.
///
/// Also keep track of a line number for error reporting.
///
pub struct Lexer<'a> {
// Complete source being processed.
source: &'a str,
// Iterator into `source`.
chars: CharIndices<'a>,
// Next character to be processed, or `None` at the end.
lookahead: Option<char>,
// Index into `source` of lookahead character.
pos: usize,
// Current line number.
line_number: usize,
}
impl<'a> Lexer<'a> {
pub fn new(s: &'a str) -> Lexer {
let mut lex = Lexer {
source: s,
chars: s.char_indices(),
lookahead: None,
pos: 0,
line_number: 1,
};
// Advance to the first char.
lex.next_ch();
lex
}
// Advance to the next character.
// Return the next lookahead character, or None when the end is encountered.
// Always update cur_ch to reflect
fn next_ch(&mut self) -> Option<char> {
if self.lookahead == Some('\n') {
self.line_number += 1;
}
match self.chars.next() {
Some((idx, ch)) => {
self.pos = idx;
self.lookahead = Some(ch);
}
None => {
self.pos = self.source.len();
self.lookahead = None;
}
}
self.lookahead
}
// Get the location corresponding to `lookahead`.
fn loc(&self) -> Location {
Location { line_number: self.line_number }
}
// Starting from `lookahead`, are we looking at `prefix`?
fn looking_at(&self, prefix: &str) -> bool {
self.source[self.pos..].starts_with(prefix)
}
// Scan a single-char token.
fn scan_char(&mut self, tok: Token<'a>) -> Result<LocatedToken<'a>, LocatedError> {
assert!(self.lookahead != None);
let loc = self.loc();
self.next_ch();
token(tok, loc)
}
// Scan a multi-char token.
fn scan_chars(&mut self,
count: usize,
tok: Token<'a>)
-> Result<LocatedToken<'a>, LocatedError> {
let loc = self.loc();
for _ in 0..count {
assert!(self.lookahead != None);
self.next_ch();
}
token(tok, loc)
}
// Scan a comment extending to the end of the current line.
fn scan_comment(&mut self) -> Result<LocatedToken<'a>, LocatedError> {
let begin = self.pos;
let loc = self.loc();
loop {
match self.next_ch() {
None | Some('\n') => {
let text = &self.source[begin..self.pos];
return token(Token::Comment(text), loc);
}
_ => {}
}
}
}
// Scan a number token which can represent either an integer or floating point number.
//
// Accept the following forms:
//
// - `10`: Integer
// - `-10`: Integer
// - `0xff_00`: Integer
// - `0.0`: Float
// - `0x1.f`: Float
// - `-0x2.4`: Float
// - `0x0.4p-34`: Float
//
// This function does not filter out all invalid numbers. It depends in the context-sensitive
// decoding of the text for that. For example, the number of allowed digits an an Ieee32` and
// an `Ieee64` constant are different.
fn scan_number(&mut self) -> Result<LocatedToken<'a>, LocatedError> {
let begin = self.pos;
let loc = self.loc();
let mut is_float = false;
// Skip a leading sign.
if self.lookahead == Some('-') {
self.next_ch();
}
// Check for NaNs with payloads.
if self.looking_at("NaN:") || self.looking_at("sNaN:") {
// Skip the `NaN:` prefix, the loop below won't accept it.
// We expect a hexadecimal number to follow the colon.
while self.next_ch() != Some(':') {}
is_float = true;
} else if self.looking_at("NaN") || self.looking_at("Inf") {
// This is Inf or a default quiet NaN.
is_float = true;
}
// Look for the end of this number. Detect the radix point if there is one.
loop {
match self.next_ch() {
Some('-') | Some('_') => {}
Some('.') => is_float = true,
Some(ch) if ch.is_alphanumeric() => {}
_ => break,
}
}
let text = &self.source[begin..self.pos];
if is_float {
token(Token::Float(text), loc)
} else {
token(Token::Integer(text), loc)
}
}
// Scan a 'word', which is an identifier-like sequence of characters beginning with '_' or an
// alphabetic char, followed by zero or more alphanumeric or '_' characters.
//
//
fn scan_word(&mut self) -> Result<LocatedToken<'a>, LocatedError> {
let begin = self.pos;
let loc = self.loc();
let mut trailing_digits = 0usize;
assert!(self.lookahead == Some('_') || self.lookahead.unwrap().is_alphabetic());
loop {
match self.next_ch() {
Some(ch) if ch.is_digit(10) => trailing_digits += 1,
Some('_') => trailing_digits = 0,
Some(ch) if ch.is_alphabetic() => trailing_digits = 0,
_ => break,
}
}
let text = &self.source[begin..self.pos];
match if trailing_digits == 0 {
Self::keyword(text)
} else {
// Look for numbered well-known entities like ebb15, v45, ...
let (prefix, suffix) = text.split_at(text.len() - trailing_digits);
Self::numbered_entity(prefix, suffix).or_else(|| Self::value_type(text, prefix, suffix))
} {
Some(t) => token(t, loc),
None => token(Token::Identifier(text), loc),
}
}
// Recognize a keyword.
fn keyword(text: &str) -> Option<Token<'a>> {
match text {
"function" => Some(Token::Function),
"entry" => Some(Token::Entry),
_ => None,
}
}
// If prefix is a well-known entity prefix and suffix is a valid entity number, return the
// decoded token.
fn numbered_entity(prefix: &str, suffix: &str) -> Option<Token<'a>> {
// Reject non-canonical numbers like v0001.
if suffix.len() > 1 && suffix.starts_with('0') {
return None;
}
let value: u32 = match suffix.parse() {
Ok(v) => v,
_ => return None,
};
match prefix {
"v" => Some(Token::ValueDirect(value)),
"vx" => Some(Token::ValueExtended(value)),
"ebb" => Some(Token::Ebb(value)),
"ss" => Some(Token::StackSlot(value)),
_ => None,
}
}
// Recognize a scalar or vector type.
fn value_type(text: &str, prefix: &str, suffix: &str) -> Option<Token<'a>> {
let is_vector = prefix.ends_with('x');
let scalar = if is_vector {
&prefix[0..prefix.len() - 1]
} else {
text
};
let base_type = match scalar {
"i8" => types::I8,
"i16" => types::I16,
"i32" => types::I32,
"i64" => types::I64,
"f32" => types::F32,
"f64" => types::F64,
"b1" => types::B1,
"b8" => types::B8,
"b16" => types::B16,
"b32" => types::B32,
"b64" => types::B64,
_ => return None,
};
if is_vector {
let lanes: u16 = match suffix.parse() {
Ok(v) => v,
_ => return None,
};
base_type.by(lanes).map(|t| Token::Type(t))
} else {
Some(Token::Type(base_type))
}
}
/// Get the next token or a lexical error.
///
/// Return None when the end of the source is encountered.
pub fn next(&mut self) -> Option<Result<LocatedToken<'a>, LocatedError>> {
loop {
let loc = self.loc();
return match self.lookahead {
None => None,
Some(';') => Some(self.scan_comment()),
Some('(') => Some(self.scan_char(Token::LPar)),
Some(')') => Some(self.scan_char(Token::RPar)),
Some('{') => Some(self.scan_char(Token::LBrace)),
Some('}') => Some(self.scan_char(Token::RBrace)),
Some(',') => Some(self.scan_char(Token::Comma)),
Some('.') => Some(self.scan_char(Token::Dot)),
Some(':') => Some(self.scan_char(Token::Colon)),
Some('=') => Some(self.scan_char(Token::Equal)),
Some('-') => {
if self.looking_at("->") {
Some(self.scan_chars(2, Token::Arrow))
} else {
Some(self.scan_number())
}
}
Some(ch) if ch.is_digit(10) => Some(self.scan_number()),
Some(ch) if ch.is_alphabetic() => Some(self.scan_word()),
Some(ch) if ch.is_whitespace() => {
self.next_ch();
continue;
}
_ => {
// Skip invalid char, return error.
self.next_ch();
Some(error(Error::InvalidChar, loc))
}
};
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use cretonne::types;
fn token<'a>(token: Token<'a>, line: usize) -> Option<Result<LocatedToken<'a>, LocatedError>> {
Some(super::token(token, Location { line_number: line }))
}
fn error<'a>(error: Error, line: usize) -> Option<Result<LocatedToken<'a>, LocatedError>> {
Some(super::error(error, Location { line_number: line }))
}
#[test]
fn make_lexer() {
let mut l1 = Lexer::new("");
let mut l2 = Lexer::new(" ");
let mut l3 = Lexer::new("\n ");
assert_eq!(l1.next(), None);
assert_eq!(l2.next(), None);
assert_eq!(l3.next(), None);
}
#[test]
fn lex_comment() {
let mut lex = Lexer::new("; hello");
assert_eq!(lex.next(), token(Token::Comment("; hello"), 1));
assert_eq!(lex.next(), None);
lex = Lexer::new("\n ;hello\n;foo");
assert_eq!(lex.next(), token(Token::Comment(";hello"), 2));
assert_eq!(lex.next(), token(Token::Comment(";foo"), 3));
assert_eq!(lex.next(), None);
// Scan a comment after an invalid char.
let mut lex = Lexer::new("#; hello");
assert_eq!(lex.next(), error(Error::InvalidChar, 1));
assert_eq!(lex.next(), token(Token::Comment("; hello"), 1));
assert_eq!(lex.next(), None);
}
#[test]
fn lex_chars() {
let mut lex = Lexer::new("(); hello\n = :{, }.");
assert_eq!(lex.next(), token(Token::LPar, 1));
assert_eq!(lex.next(), token(Token::RPar, 1));
assert_eq!(lex.next(), token(Token::Comment("; hello"), 1));
assert_eq!(lex.next(), token(Token::Equal, 2));
assert_eq!(lex.next(), token(Token::Colon, 2));
assert_eq!(lex.next(), token(Token::LBrace, 2));
assert_eq!(lex.next(), token(Token::Comma, 2));
assert_eq!(lex.next(), token(Token::RBrace, 2));
assert_eq!(lex.next(), token(Token::Dot, 2));
assert_eq!(lex.next(), None);
}
#[test]
fn lex_numbers() {
let mut lex = Lexer::new(" 0 2_000 -1,0xf -0x0 0.0 0x0.4p-34");
assert_eq!(lex.next(), token(Token::Integer("0"), 1));
assert_eq!(lex.next(), token(Token::Integer("2_000"), 1));
assert_eq!(lex.next(), token(Token::Integer("-1"), 1));
assert_eq!(lex.next(), token(Token::Comma, 1));
assert_eq!(lex.next(), token(Token::Integer("0xf"), 1));
assert_eq!(lex.next(), token(Token::Integer("-0x0"), 1));
assert_eq!(lex.next(), token(Token::Float("0.0"), 1));
assert_eq!(lex.next(), token(Token::Float("0x0.4p-34"), 1));
assert_eq!(lex.next(), None);
}
#[test]
fn lex_identifiers() {
let mut lex = Lexer::new("v0 v00 vx01 ebb1234567890 ebb5234567890 entry v1x vx1 vxvx4 \
function0 function b1 i32x4 f32x5");
assert_eq!(lex.next(), token(Token::ValueDirect(0), 1));
assert_eq!(lex.next(), token(Token::Identifier("v00"), 1));
assert_eq!(lex.next(), token(Token::Identifier("vx01"), 1));
assert_eq!(lex.next(), token(Token::Ebb(1234567890), 1));
assert_eq!(lex.next(), token(Token::Identifier("ebb5234567890"), 1));
assert_eq!(lex.next(), token(Token::Entry, 1));
assert_eq!(lex.next(), token(Token::Identifier("v1x"), 1));
assert_eq!(lex.next(), token(Token::ValueExtended(1), 1));
assert_eq!(lex.next(), token(Token::Identifier("vxvx4"), 1));
assert_eq!(lex.next(), token(Token::Identifier("function0"), 1));
assert_eq!(lex.next(), token(Token::Function, 1));
assert_eq!(lex.next(), token(Token::Type(types::B1), 1));
assert_eq!(lex.next(), token(Token::Type(types::I32.by(4).unwrap()), 1));
assert_eq!(lex.next(), token(Token::Identifier("f32x5"), 1));
assert_eq!(lex.next(), None);
}
}

16
src/libreader/lib.rs Normal file
View File

@@ -0,0 +1,16 @@
// ====------------------------------------------------------------------------------------==== //
//
// Cretonne file reader library.
//
// ====------------------------------------------------------------------------------------==== //
//
// The cton_reader library supports reading and writing .cton files. This functionality is needed
// for testing Cretonne, but is not essential for a JIT compiler.
//
// ====------------------------------------------------------------------------------------==== //
extern crate cretonne;
pub mod lexer;
pub mod parser;

412
src/libreader/parser.rs Normal file
View File

@@ -0,0 +1,412 @@
// ====--------------------------------------------------------------------------------------====//
//
// Parser for .cton files.
//
// ====--------------------------------------------------------------------------------------====//
use std::collections::HashMap;
use std::result;
use std::fmt::{self, Display, Formatter, Write};
use std::u32;
use lexer::{self, Lexer, Token};
use cretonne::types::{FunctionName, Signature, ArgumentType, ArgumentExtension};
use cretonne::immediates::Imm64;
use cretonne::repr::{Function, StackSlot, StackSlotData};
pub use lexer::Location;
/// A parse error is returned when the parse failed.
#[derive(Debug)]
pub struct Error {
pub location: Location,
pub message: String,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}: {}", self.location.line_number, self.message)
}
}
pub type Result<T> = result::Result<T, Error>;
pub struct Parser<'a> {
lex: Lexer<'a>,
lex_error: Option<lexer::Error>,
// Current lookahead token.
lookahead: Option<Token<'a>>,
// Location of lookahead.
location: Location,
}
// Context for resolving references when parsing a single function.
//
// Many entities like values, stack slots, and function signatures are referenced in the `.cton`
// file by number. We need to map these numbers to real references.
struct Context {
function: Function,
stack_slots: HashMap<u32, StackSlot>,
}
impl Context {
fn new(f: Function) -> Context {
Context {
function: f,
stack_slots: HashMap::new(),
}
}
fn add(&mut self, number: u32, data: StackSlotData, loc: &Location) -> Result<()> {
if self.stack_slots.insert(number, self.function.make_stack_slot(data)).is_some() {
Err(Error {
location: loc.clone(),
message: format!("duplicate stack slot: ss{}", number),
})
} else {
Ok(())
}
}
}
impl<'a> Parser<'a> {
/// Create a new `Parser` which reads `text`. The referenced text must outlive the parser.
pub fn new(text: &'a str) -> Parser {
Parser {
lex: Lexer::new(text),
lex_error: None,
lookahead: None,
location: Location { line_number: 0 },
}
}
/// Parse the entire string into a list of functions.
pub fn parse(text: &'a str) -> Result<Vec<Function>> {
Self::new(text).parse_function_list()
}
// Consume the current lookahead token and return it.
fn consume(&mut self) -> Token<'a> {
self.lookahead.take().expect("No token to consume")
}
// Get the current lookahead token, after making sure there is one.
fn token(&mut self) -> Option<Token<'a>> {
if self.lookahead == None {
match self.lex.next() {
Some(Ok(lexer::LocatedToken { token, location })) => {
self.lookahead = Some(token);
self.location = location;
}
Some(Err(lexer::LocatedError { error, location })) => {
self.lex_error = Some(error);
self.location = location;
}
None => {}
}
}
return self.lookahead;
}
// Generate an error.
fn error(&self, message: &str) -> Error {
Error {
location: self.location,
message:
// If we have a lexer error latched, report that.
match self.lex_error {
Some(lexer::Error::InvalidChar) => "invalid character".to_string(),
None => message.to_string(),
}
}
}
// Match and consume a token without payload.
fn match_token(&mut self, want: Token<'a>, err_msg: &str) -> Result<Token<'a>> {
if self.token() == Some(want) {
Ok(self.consume())
} else {
Err(self.error(err_msg))
}
}
// If the next token is a `want`, consume it, otherwise do nothing.
fn optional(&mut self, want: Token<'a>) -> bool {
if self.token() == Some(want) {
self.consume();
true
} else {
false
}
}
// Match and consume a specific identifier string.
// Used for pseudo-keywords like "stack_slot" that only appear in certain contexts.
fn match_identifier(&mut self, want: &'static str, err_msg: &str) -> Result<Token<'a>> {
if self.token() == Some(Token::Identifier(want)) {
Ok(self.consume())
} else {
Err(self.error(err_msg))
}
}
// Match and consume a stack slot reference.
fn match_ss(&mut self, err_msg: &str) -> Result<u32> {
if let Some(Token::StackSlot(ss)) = self.token() {
self.consume();
Ok(ss)
} else {
Err(self.error(err_msg))
}
}
// Match and consume an Imm64 immediate.
fn match_imm64(&mut self, err_msg: &str) -> Result<Imm64> {
if let Some(Token::Integer(text)) = self.token() {
self.consume();
// Lexer just gives us raw text that looks like an integer.
// Parse it as an Imm64 to check for overflow and other issues.
text.parse().map_err(|e| self.error(e))
} else {
Err(self.error(err_msg))
}
}
/// Parse a list of function definitions.
///
/// This is the top-level parse function matching the whole contents of a file.
pub fn parse_function_list(&mut self) -> Result<Vec<Function>> {
let mut list = Vec::new();
while self.token().is_some() {
list.push(try!(self.parse_function()));
}
Ok(list)
}
// Parse a whole function definition.
//
// function ::= * function-spec "{" preample function-body "}"
//
fn parse_function(&mut self) -> Result<Function> {
let (name, sig) = try!(self.parse_function_spec());
let mut ctx = Context::new(Function::with_name_signature(name, sig));
// function ::= function-spec * "{" preample function-body "}"
try!(self.match_token(Token::LBrace, "expected '{' before function body"));
// function ::= function-spec "{" * preample function-body "}"
try!(self.parse_preamble(&mut ctx));
// function ::= function-spec "{" preample function-body * "}"
try!(self.match_token(Token::RBrace, "expected '}' after function body"));
Ok(ctx.function)
}
// Parse a function spec.
//
// function-spec ::= * "function" name signature
//
fn parse_function_spec(&mut self) -> Result<(FunctionName, Signature)> {
try!(self.match_token(Token::Function, "expected 'function' keyword"));
// function-spec ::= "function" * name signature
let name = try!(self.parse_function_name());
// function-spec ::= "function" name * signature
let sig = try!(self.parse_signature());
Ok((name, sig))
}
// Parse a function name.
//
// function ::= "function" * name signature { ... }
//
fn parse_function_name(&mut self) -> Result<String> {
match self.token() {
Some(Token::Identifier(s)) => {
self.consume();
Ok(s.to_string())
}
_ => Err(self.error("expected function name")),
}
}
// Parse a function signature.
//
// signature ::= * "(" [arglist] ")" ["->" retlist] [call_conv]
//
fn parse_signature(&mut self) -> Result<Signature> {
let mut sig = Signature::new();
try!(self.match_token(Token::LPar, "expected function signature: ( args... )"));
// signature ::= "(" * [arglist] ")" ["->" retlist] [call_conv]
if self.token() != Some(Token::RPar) {
sig.argument_types = try!(self.parse_argument_list());
}
try!(self.match_token(Token::RPar, "expected ')' after function arguments"));
if self.optional(Token::Arrow) {
sig.return_types = try!(self.parse_argument_list());
}
// TBD: calling convention.
Ok(sig)
}
// Parse list of function argument / return value types.
//
// arglist ::= * arg { "," arg }
//
fn parse_argument_list(&mut self) -> Result<Vec<ArgumentType>> {
let mut list = Vec::new();
// arglist ::= * arg { "," arg }
list.push(try!(self.parse_argument_type()));
// arglist ::= arg * { "," arg }
while self.optional(Token::Comma) {
// arglist ::= arg { "," * arg }
list.push(try!(self.parse_argument_type()));
}
Ok(list)
}
// Parse a single argument type with flags.
fn parse_argument_type(&mut self) -> Result<ArgumentType> {
// arg ::= * type { flag }
let mut arg = if let Some(Token::Type(t)) = self.token() {
ArgumentType::new(t)
} else {
return Err(self.error("expected argument type"));
};
self.consume();
// arg ::= type * { flag }
while let Some(Token::Identifier(s)) = self.token() {
match s {
"uext" => arg.extension = ArgumentExtension::Uext,
"sext" => arg.extension = ArgumentExtension::Sext,
"inreg" => arg.inreg = true,
_ => break,
}
self.consume();
}
Ok(arg)
}
// Parse the function preamble.
//
// preamble ::= * { preamble-decl }
// preamble-decl ::= * stack-slot-decl
// * function-decl
// * signature-decl
//
// The parsed decls are added to `ctx` rather than returned.
fn parse_preamble(&mut self, ctx: &mut Context) -> Result<()> {
loop {
try!(match self.token() {
Some(Token::StackSlot(..)) => {
self.parse_stack_slot_decl()
.and_then(|(num, dat)| ctx.add(num, dat, &self.location))
}
// More to come..
_ => return Ok(()),
});
}
}
// Parse a stack slot decl, add to `func`.
//
// stack-slot-decl ::= * StackSlot(ss) "=" "stack_slot" Bytes {"," stack-slot-flag}
fn parse_stack_slot_decl(&mut self) -> Result<(u32, StackSlotData)> {
let number = try!(self.match_ss("expected stack slot number: ss«n»"));
try!(self.match_token(Token::Equal, "expected '=' in stack_slot decl"));
try!(self.match_identifier("stack_slot", "expected 'stack_slot'"));
// stack-slot-decl ::= StackSlot(ss) "=" "stack_slot" * Bytes {"," stack-slot-flag}
let bytes = try!(self.match_imm64("expected byte-size in stack_slot decl")).to_bits();
if bytes > u32::MAX as u64 {
return Err(self.error("stack slot too large"));
}
let data = StackSlotData::new(bytes as u32);
// TBD: stack-slot-decl ::= StackSlot(ss) "=" "stack_slot" Bytes * {"," stack-slot-flag}
Ok((number, data))
}
}
#[cfg(test)]
mod tests {
use super::*;
use cretonne::types::{self, ArgumentType, ArgumentExtension};
#[test]
fn argument_type() {
let mut p = Parser::new("i32 sext");
let arg = p.parse_argument_type().unwrap();
assert_eq!(arg,
ArgumentType {
value_type: types::I32,
extension: ArgumentExtension::Sext,
inreg: false,
});
let Error { location, message } = p.parse_argument_type().unwrap_err();
assert_eq!(location.line_number, 1);
assert_eq!(message, "expected argument type");
}
#[test]
fn signature() {
let sig = Parser::new("()").parse_signature().unwrap();
assert_eq!(sig.argument_types.len(), 0);
assert_eq!(sig.return_types.len(), 0);
let sig2 = Parser::new("(i8 inreg uext, f32, f64) -> i32 sext, f64")
.parse_signature()
.unwrap();
assert_eq!(sig2.to_string(),
"(i8 uext inreg, f32, f64) -> i32 sext, f64");
// `void` is not recognized as a type by the lexer. It should not appear in files.
assert_eq!(Parser::new("() -> void").parse_signature().unwrap_err().to_string(),
"1: expected argument type");
assert_eq!(Parser::new("i8 -> i8").parse_signature().unwrap_err().to_string(),
"1: expected function signature: ( args... )");
assert_eq!(Parser::new("(i8 -> i8").parse_signature().unwrap_err().to_string(),
"1: expected ')' after function arguments");
}
#[test]
fn stack_slot_decl() {
let func = Parser::new("function foo() {
ss3 = stack_slot 13
ss1 = stack_slot 1
}")
.parse_function()
.unwrap();
assert_eq!(func.name, "foo");
let mut iter = func.stack_slot_iter();
let ss0 = iter.next().unwrap();
assert_eq!(ss0.to_string(), "ss0");
assert_eq!(func[ss0].size, 13);
let ss1 = iter.next().unwrap();
assert_eq!(ss1.to_string(), "ss1");
assert_eq!(func[ss1].size, 1);
assert_eq!(iter.next(), None);
// Catch suplicate definitions.
assert_eq!(Parser::new("function bar() {
ss1 = stack_slot 13
ss1 = stack_slot 1
}")
.parse_function()
.unwrap_err()
.to_string(),
"3: duplicate stack slot: ss1");
}
}