Add support for enumerated settings.
The EnumSetting objects can take one of 256 named values.
This commit is contained in:
@@ -4,10 +4,22 @@ Cretonne shared settings.
|
||||
This module defines settings are are relevant for all code generators.
|
||||
"""
|
||||
|
||||
from . import SettingGroup, BoolSetting
|
||||
from . import SettingGroup, BoolSetting, EnumSetting
|
||||
|
||||
group = SettingGroup('shared')
|
||||
|
||||
enable_simd = BoolSetting("Enable the use of SIMD instructions", default=True)
|
||||
opt_level = EnumSetting(
|
||||
"""
|
||||
Optimization level:
|
||||
|
||||
- default: Very profitable optimizations enabled, none slow.
|
||||
- best: Enable all optimizations
|
||||
- fastest: Optimize for compile time by disabling most optimizations.
|
||||
""",
|
||||
'default', 'best', 'fastest')
|
||||
|
||||
enable_simd = BoolSetting(
|
||||
"""Enable the use of SIMD instructions.""",
|
||||
default=True)
|
||||
|
||||
group.close(globals())
|
||||
|
||||
@@ -5,7 +5,7 @@ Generate sources with settings.
|
||||
import srcgen
|
||||
from unique_table import UniqueSeqTable
|
||||
import constant_hash
|
||||
from cretonne import BoolSetting, NumSetting, settings
|
||||
from cretonne import camel_case, BoolSetting, NumSetting, EnumSetting, settings
|
||||
|
||||
|
||||
def layout_group(sgrp):
|
||||
@@ -40,11 +40,25 @@ def layout_group(sgrp):
|
||||
return next_byte
|
||||
|
||||
|
||||
def gen_enum_types(sgrp, fmt):
|
||||
"""
|
||||
Emit enum types for any enum settings.
|
||||
"""
|
||||
for setting in sgrp.settings:
|
||||
if not isinstance(setting, EnumSetting):
|
||||
continue
|
||||
ty = camel_case(setting.name)
|
||||
fmt.line('#[derive(Debug, PartialEq, Eq)]')
|
||||
fmt.line(
|
||||
'pub enum {} {{ {} }}'
|
||||
.format(ty, ", ".join(camel_case(v) for v in setting.values)))
|
||||
|
||||
|
||||
def gen_getter(setting, fmt):
|
||||
"""
|
||||
Emit a getter function for `setting`.
|
||||
"""
|
||||
fmt.doc_comment(setting.__doc__ + '.')
|
||||
fmt.doc_comment(setting.__doc__)
|
||||
|
||||
if isinstance(setting, BoolSetting):
|
||||
proto = 'pub fn {}(&self) -> bool'.format(setting.name)
|
||||
@@ -56,6 +70,15 @@ def gen_getter(setting, fmt):
|
||||
proto = 'pub fn {}(&self) -> u8'.format(setting.name)
|
||||
with fmt.indented(proto + ' {', '}'):
|
||||
fmt.line('self.bytes[{}]'.format(setting.byte_offset))
|
||||
elif isinstance(setting, EnumSetting):
|
||||
ty = camel_case(setting.name)
|
||||
proto = 'pub fn {}(&self) -> {}'.format(setting.name, ty)
|
||||
with fmt.indented(proto + ' {', '}'):
|
||||
with fmt.indented(
|
||||
'match self.bytes[{}] {{'.format(setting.byte_offset), '}'):
|
||||
for i, v in enumerate(setting.values):
|
||||
fmt.line( '{} => {}::{},'.format(i, ty, camel_case(v)))
|
||||
fmt.line( '_ => panic!("Invalid enum value")')
|
||||
else:
|
||||
raise AssertionError("Unknown setting kind")
|
||||
|
||||
@@ -108,6 +131,11 @@ def gen_descriptors(sgrp, fmt):
|
||||
.format(setting.bit_offset))
|
||||
elif isinstance(setting, NumSetting):
|
||||
fmt.line('detail: Detail::Num,')
|
||||
elif isinstance(setting, EnumSetting):
|
||||
offs = enums.add(setting.values)
|
||||
fmt.line(
|
||||
'detail: Detail::Enum {{ last: {}, enumerators: {} }},'
|
||||
.format(len(setting.values)-1, offs))
|
||||
else:
|
||||
raise AssertionError("Unknown setting kind")
|
||||
|
||||
@@ -147,8 +175,8 @@ def gen_stringwise(sgrp, fmt):
|
||||
|
||||
with fmt.indented('impl Stringwise for Settings {', '}'):
|
||||
with fmt.indented(
|
||||
'fn lookup_mut(&mut self, name: &str)' +
|
||||
'-> Result<(Detail, &mut u8)> {',
|
||||
'fn lookup(&self, name: &str)' +
|
||||
'-> Result<(usize, Detail)> {',
|
||||
'}'):
|
||||
fmt.line('use simple_hash::simple_hash;')
|
||||
fmt.line('let tlen = HASH_TABLE.len();')
|
||||
@@ -162,9 +190,8 @@ def gen_stringwise(sgrp, fmt):
|
||||
fmt.line('return Err(Error::BadName)')
|
||||
with fmt.indented('if DESCRIPTORS[entry].name == name {', '}'):
|
||||
fmt.line(
|
||||
'return Ok((DESCRIPTORS[entry].detail, ' +
|
||||
'&mut self.bytes[DESCRIPTORS[entry].offset ' +
|
||||
'as usize]))')
|
||||
'return Ok((DESCRIPTORS[entry].offset as usize, ' +
|
||||
'DESCRIPTORS[entry].detail))')
|
||||
fmt.line('step += 1;')
|
||||
fmt.line('assert!(step < tlen);')
|
||||
fmt.line('idx += step;')
|
||||
@@ -175,6 +202,8 @@ def gen_stringwise(sgrp, fmt):
|
||||
'}'):
|
||||
fmt.line('ENUMERATORS[enums as usize + value as usize]')
|
||||
|
||||
with fmt.indented('fn raw_bytes_mut(&mut self) -> &mut [u8] {', '}'):
|
||||
fmt.line('&mut self.bytes')
|
||||
|
||||
def gen_display(sgrp, fmt):
|
||||
"""
|
||||
@@ -204,6 +233,7 @@ def gen_group(sgrp, fmt):
|
||||
with fmt.indented('pub struct Settings {', '}'):
|
||||
fmt.line('bytes: [u8; {}],'.format(byte_size))
|
||||
|
||||
gen_enum_types(sgrp, fmt)
|
||||
gen_getters(sgrp, fmt)
|
||||
gen_default(sgrp, byte_size, fmt)
|
||||
gen_descriptors(sgrp, fmt)
|
||||
|
||||
@@ -8,6 +8,7 @@ source code.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
class Formatter(object):
|
||||
@@ -108,8 +109,9 @@ class Formatter(object):
|
||||
self.line('// ' + s)
|
||||
|
||||
def doc_comment(self, s):
|
||||
"""Add a documentation comment line."""
|
||||
self.line('/// ' + s)
|
||||
"""Add a (multi-line) documentation comment."""
|
||||
s = re.sub('^', self.indent + '/// ', s, flags=re.M) + '\n'
|
||||
self.lines.append(s)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
|
||||
@@ -59,16 +59,26 @@ pub enum Error {
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
fn parse_bool_value(value: &str) -> Result<bool> {
|
||||
match value {
|
||||
"true" | "on" | "yes" | "1" => Ok(true),
|
||||
"false" | "off" | "no" | "0" => Ok(false),
|
||||
_ => Err(Error::BadValue),
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface for working with a group of settings as strings.
|
||||
pub trait Stringwise {
|
||||
/// Look up a setting by name, return the details of the setting along with a reference to the
|
||||
/// byte holding the value of the setting.
|
||||
fn lookup_mut(&mut self, name: &str) -> Result<(Detail, &mut u8)>;
|
||||
/// Look up a setting by name, return the byte offset and details of the setting.
|
||||
fn lookup(&self, name: &str) -> Result<(usize, Detail)>;
|
||||
|
||||
/// Get an enumerator string from the `Detail::enumerators` value and an offset.
|
||||
fn enumerator(&self, enums: u16, value: u8) -> &'static str;
|
||||
|
||||
/// Format a setting value as a TOML string. This is mostly for use by the generateed `Display`
|
||||
/// Get the underlying byte array used to store settings.
|
||||
fn raw_bytes_mut(&mut self) -> &mut [u8];
|
||||
|
||||
/// Format a setting value as a TOML string. This is mostly for use by the generated `Display`
|
||||
/// implementation.
|
||||
fn format_toml_value(&self, detail: Detail, byte: u8, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match detail {
|
||||
@@ -86,9 +96,10 @@ pub trait Stringwise {
|
||||
|
||||
/// Set a boolean setting by name.
|
||||
fn set_bool(&mut self, name: &str, value: bool) -> Result<()> {
|
||||
let (detail, byte) = try!(self.lookup_mut(name));
|
||||
let (offset, detail) = try!(self.lookup(name));
|
||||
if let Detail::Bool { bit } = detail {
|
||||
let mask = 1 << bit;
|
||||
let byte = &mut self.raw_bytes_mut()[offset];
|
||||
if value {
|
||||
*byte |= mask;
|
||||
} else {
|
||||
@@ -99,6 +110,43 @@ pub trait Stringwise {
|
||||
Err(Error::BadType)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the string value of a named setting.
|
||||
///
|
||||
/// For boolean settings, any of the values accepted by `parse_bool_value` above are accepted
|
||||
/// (true/false, on/off, yes/no, 1/0).
|
||||
///
|
||||
/// For enumerated settings, the value must match one of the allowed values exactly.
|
||||
fn set(&mut self, name: &str, value: &str) -> Result<()> {
|
||||
let (offset, detail) = try!(self.lookup(name));
|
||||
match detail {
|
||||
Detail::Bool { bit } => {
|
||||
let mask = 1 << bit;
|
||||
let byte = &mut self.raw_bytes_mut()[offset];
|
||||
if try!(parse_bool_value(value)) {
|
||||
*byte |= mask;
|
||||
} else {
|
||||
*byte &= !mask;
|
||||
}
|
||||
}
|
||||
Detail::Num => {
|
||||
self.raw_bytes_mut()[offset] = try!(value.parse().map_err(|_| Error::BadValue));
|
||||
}
|
||||
Detail::Enum { last, enumerators } => {
|
||||
// Linear search..
|
||||
for i in 0.. {
|
||||
if value == self.enumerator(enumerators, i) {
|
||||
self.raw_bytes_mut()[offset] = i;
|
||||
break;
|
||||
}
|
||||
if i == last {
|
||||
return Err(Error::BadValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Include code generated by `meta/gen_settings.py`. This file contains a public `Settings` struct
|
||||
@@ -116,6 +164,7 @@ mod tests {
|
||||
let s = Settings::default();
|
||||
assert_eq!(s.to_string(),
|
||||
"[shared]\n\
|
||||
opt_level = \"default\"\n\
|
||||
enable_simd = true\n");
|
||||
}
|
||||
|
||||
@@ -131,4 +180,22 @@ mod tests {
|
||||
assert_eq!(s.set_bool("enable_simd", false), Ok(()));
|
||||
assert_eq!(s.enable_simd(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modify_string() {
|
||||
let mut s = Settings::default();
|
||||
assert_eq!(s.enable_simd(), true);
|
||||
assert_eq!(s.opt_level(), super::OptLevel::Default);
|
||||
|
||||
assert_eq!(s.set("not_there", "true"), Err(BadName));
|
||||
assert_eq!(s.set("enable_simd", ""), Err(BadValue));
|
||||
assert_eq!(s.set("enable_simd", "best"), Err(BadValue));
|
||||
assert_eq!(s.set("opt_level", "true"), Err(BadValue));
|
||||
|
||||
assert_eq!(s.set("enable_simd", "no"), Ok(()));
|
||||
assert_eq!(s.enable_simd(), false);
|
||||
|
||||
assert_eq!(s.set("opt_level", "best"), Ok(()));
|
||||
assert_eq!(s.opt_level(), super::OptLevel::Best);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user