Start working on switching to language-rust as a generator, for fun.

This commit is contained in:
2019-10-22 20:12:08 -07:00
parent d7665acf64
commit 2400b10fbc
9 changed files with 723 additions and 404 deletions

View File

@@ -1,11 +1,16 @@
{-# LANGUAGE QuasiQuotes #-}
module Conversions(
conversions
)
where
import Data.List(intercalate)
import File
import Gen
import Gen(Gen,toLit,out)
import Language.Rust.Data.Ident
import Language.Rust.Data.Position
import Language.Rust.Quote
import Language.Rust.Pretty
import Language.Rust.Syntax
conversions :: File
conversions = File {
@@ -16,83 +21,203 @@ conversions = File {
declareConversions :: Word -> Gen ()
declareConversions bitsize =
do let name = "U" ++ show bitsize
entries = bitsize `div` 64
out "use core::convert::{From,TryFrom};"
out "#[cfg(test)]"
out "use quickcheck::quickcheck;"
out ("use super::" ++ name ++ ";")
blank
buildUnsignedPrimConversions name entries "u8" >> blank
buildUnsignedPrimConversions name entries "u16" >> blank
buildUnsignedPrimConversions name entries "u32" >> blank
buildUnsignedPrimConversions name entries "u64" >> blank
buildUnsignedPrimConversions name entries "usize" >> blank
buildSignedPrimConversions name entries "i8" >> blank
buildSignedPrimConversions name entries "i16" >> blank
buildSignedPrimConversions name entries "i32" >> blank
buildSignedPrimConversions name entries "i64" >> blank
buildSignedPrimConversions name entries "isize"
blank
out ("#[cfg(test)]")
wrapIndent "quickcheck!" $
do roundTripTest name "u8" >> blank
roundTripTest name "u16" >> blank
roundTripTest name "u32" >> blank
roundTripTest name "u64" >> blank
roundTripTest name "usize"
do let sname = mkIdent ("U" ++ show bitsize)
entries = bitsize `div` 64
u8_prims = buildPrimitives sname (mkIdent "u8") entries
u16_prims = buildPrimitives sname (mkIdent "u16") entries
u32_prims = buildPrimitives sname (mkIdent "u32") entries
u64_prims = buildPrimitives sname (mkIdent "u64") entries
u128_prims = generateU128Primitives sname entries
i8_prims = generateSignedPrims sname (mkIdent "u8") (mkIdent "i8")
i16_prims = generateSignedPrims sname (mkIdent "u16") (mkIdent "i16")
i32_prims = generateSignedPrims sname (mkIdent "u32") (mkIdent "i32")
i64_prims = generateSignedPrims sname (mkIdent "u64") (mkIdent "i64")
i128_prims = generateI128Primitives sname
out $ show $ pretty' $ [sourceFile|
use core::convert::{From,TryFrom};
use core::num::TryFromIntError;
#[cfg(test)]
use quickcheck::quickcheck;
use super::$$sname;
use crate::ConversionError;
buildUnsignedPrimConversions :: String -> Word -> String -> Gen ()
buildUnsignedPrimConversions name entries primtype =
do implFor ("From<" ++ primtype ++ ">") name $
wrapIndent ("fn from(x: " ++ primtype ++ ") -> Self") $
do let zeroes = replicate (fromIntegral (entries - 1)) "0,"
values = ("x as u64," : zeroes)
out (name ++ " { value: [ ")
indent $ printBy 8 values
out ("] }")
blank
implFor ("From<" ++ name ++ ">") primtype $
wrapIndent ("fn from(x: " ++ name ++ ") -> Self") $
out ("x.value[0] as " ++ primtype)
blank
implFor' ("From<&'a " ++ name ++ ">") primtype $
wrapIndent ("fn from(x: &" ++ name ++ ") -> Self") $
out ("x.value[0] as " ++ primtype)
$@{u8_prims}
$@{u16_prims}
$@{u32_prims}
$@{u64_prims}
$@{u128_prims}
buildSignedPrimConversions :: String -> Word -> String -> Gen ()
buildSignedPrimConversions name entries primtype =
do implFor ("TryFrom<" ++ primtype ++ ">") name $
do out ("type Error = &'static str;")
blank
wrapIndent ("fn try_from(x: " ++ primtype ++ ") -> Result<Self,Self::Error>") $
do wrapIndent ("if x < 0") $
out ("return Err(\"Attempt to convert negative number to " ++
name ++ ".\");")
blank
let zeroes = replicate (fromIntegral (entries - 1)) "0,"
values = ("x as u64," : zeroes)
out ("Ok(" ++ name ++ " { value: [ ")
indent $ printBy 8 values
out ("] })")
blank
implFor ("From<" ++ name ++ ">") primtype $
wrapIndent ("fn from(x: " ++ name ++ ") -> Self") $
out ("x.value[0] as " ++ primtype)
blank
implFor' ("From<&'a " ++ name ++ ">") primtype $
wrapIndent ("fn from(x: &" ++ name ++ ") -> Self") $
out ("x.value[0] as " ++ primtype)
$@{i8_prims}
$@{i16_prims}
$@{i32_prims}
$@{i64_prims}
$@{i128_prims}
|]
roundTripTest :: String -> String -> Gen ()
roundTripTest name primtype =
wrapIndent ("fn " ++ primtype ++ "_roundtrips(x: " ++ primtype ++ ") -> bool") $
do out ("let big = " ++ name ++ "::from(x);");
out ("let small = " ++ primtype ++ "::from(big);")
out ("x == small")
generateU128Primitives :: Ident -> Word -> [Item Span]
generateU128Primitives sname entries = [
[item|impl From<u128> for $$sname {
fn from(x: u128) -> Self {
let mut res = $$sname::zero;
res[0] = x as u64;
res[1] = (x >> 64) as u64;
res
}
}|]
, [item|impl TryFrom<$$sname> for u128 {
type Error = ConversionError;
printBy :: Int -> [String] -> Gen ()
printBy amt xs
| length xs <= amt = out (intercalate " " xs)
| otherwise = printBy amt (take amt xs) >>
printBy amt (drop amt xs)
fn try_from(x: $$sname) -> Result<u128,ConversionError> {
let mut goodConversion = true;
let mut res = 0;
res = (x.values[1] as u128) << 64;
res |= x.values[0] as u128;
$@{testZeros}
if goodConversion {
Ok(res)
} else {
Err(ConversionError::Overflow);
}
}
}|]
, [item|impl<'a> TryFrom<&'a $$sname> for u128 {
type Error = ConversionError;
fn try_from(x: &$$sname) -> Result<u128,ConversionError> {
let mut goodConversion = true;
let mut res = 0;
res = (x.values[1] as u128) << 64;
res |= x.values[0] as u128;
$@{testZeros}
if goodConversion {
Ok(res)
} else {
Err(ConversionError::Overflow());
}
}
}|]
]
where
testZeros = map (zeroTest . toLit) [2..entries-1]
zeroTest i =
[stmt| goodConversion &= x.values[$$(i)] == 0; |]
buildPrimitives :: Ident -> Ident -> Word -> [Item Span]
buildPrimitives sname tname entries = [
[item|impl From<$$tname> for $$sname {
fn from(x: $$tname) -> Self {
let mut res = $$sname::zero();
res.values[0] = x as u64;
res
}
}|]
, [item|impl TryFrom<$$sname> for $$tname {
type Error = ConversionError;
fn try_from(x: $$sname) -> Result<Self,ConversionError> {
let mut goodConversion = true;
let mut res = 0;
res = x.values[0] as $$tname;
$@{testZeros}
if goodConversion {
Ok(res)
} else {
Err(ConversionError::Overflow)
}
}
}|]
, [item|impl<'a> TryFrom<&'a $$sname> for $$tname {
type Error = ConversionError;
fn try_from(x: &$$sname) -> Result<Self,ConversionError> {
let mut goodConversion = true;
let mut res = 0;
res = x.values[0] as $$tname;
$@{testZeros}
if goodConversion {
Ok(res)
} else {
Err(ConversionError::Overflow)
}
}
}|]
]
where
testZeros = map (zeroTest . toLit) [1..entries-1]
zeroTest i =
[stmt| goodConversion &= x.values[$$(i)] == 0; |]
generateSignedPrims :: Ident -> Ident -> Ident -> [Item Span]
generateSignedPrims sname unsigned signed = [
[item|impl TryFrom<$$signed> for $$sname {
type Error = ConversionError;
fn try_from(x: $$signed) -> Result<Self,ConversionError> {
let mut res = $$sname::zero();
res.values[0] = x as u64;
if x < 0 {
Err(ConversionError::NegativeToUnsigned)
} else {
Ok(res)
}
}
}|]
, [item|impl TryFrom<$$sname> for $$signed {
type Error = ConversionError;
fn try_from(x: $$sname) -> Result<Self,ConversionError> {
let uns = $$unsigned::from(x)?;
Ok($$signed::try_from(uns)?)
}
}|]
, [item|impl<'a> TryFrom<&'a $$sname> for $$signed {
type Error = ConversionError;
fn try_from(x: &$$sname) -> Result<Self,ConversionError> {
let uns = $$unsigned::from(x)?;
Ok($$signed::try_from(uns)?)
}
}|]
]
generateI128Primitives :: Ident -> [Item Span]
generateI128Primitives sname = [
[item|impl TryFrom<i128> for $$sname {
type Error = ConversionError;
fn try_from(x: i128) -> Result<Self,ConversionError> {
let mut res = $$sname::zero();
res.values[0] = x as u64;
res.values[1] = ((x as u128) >> 64) as u64;
if x < 0 {
Err(ConversionError::NegativeToUnsigned)
} else {
Ok(res)
}
}
}|]
, [item|impl TryFrom<$$sname> for i128 {
type Error = ConversionError;
fn try_from(x: $$sname) -> Result<Self,ConversionError> {
let uns = u128::from(x)?;
Ok(i128::try_from(uns)?)
}
}|]
, [item|impl<'a> TryFrom<&'a $$sname> for i128 {
type Error = ConversionError;
fn try_from(x: &$$sname) -> Result<Self,ConversionError> {
let uns = u128::from(x)?;
Ok(i128::try_from(uns)?)
}
}|]
]