Add support for casting.
This commit is contained in:
7
examples/basic/cast1.ngr
Normal file
7
examples/basic/cast1.ngr
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
x8 = 5u8;
|
||||||
|
x16 = <u16>x8 + 1u16;
|
||||||
|
print x16;
|
||||||
|
x32 = <u32>x16 + 1u32;
|
||||||
|
print x32;
|
||||||
|
x64 = <u64>x32 + 1u64;
|
||||||
|
print x64;
|
||||||
7
examples/basic/cast2.ngr
Normal file
7
examples/basic/cast2.ngr
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
x8 = 5i8;
|
||||||
|
x16 = <i16>x8 - 1i16;
|
||||||
|
print x16;
|
||||||
|
x32 = <i32>x16 - 1i32;
|
||||||
|
print x32;
|
||||||
|
x64 = <i64>x32 - 1i64;
|
||||||
|
print x64;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::backend::runtime::RuntimeFunctionError;
|
use crate::{backend::runtime::RuntimeFunctionError, eval::PrimitiveType, ir::Type};
|
||||||
use codespan_reporting::diagnostic::Diagnostic;
|
use codespan_reporting::diagnostic::Diagnostic;
|
||||||
use cranelift_codegen::{isa::LookupError, settings::SetError, CodegenError};
|
use cranelift_codegen::{isa::LookupError, settings::SetError, CodegenError};
|
||||||
use cranelift_module::ModuleError;
|
use cranelift_module::ModuleError;
|
||||||
@@ -39,6 +39,8 @@ pub enum BackendError {
|
|||||||
LookupError(#[from] LookupError),
|
LookupError(#[from] LookupError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Write(#[from] cranelift_object::object::write::Error),
|
Write(#[from] cranelift_object::object::write::Error),
|
||||||
|
#[error("Invalid type cast from {from} to {to}")]
|
||||||
|
InvalidTypeCast { from: PrimitiveType, to: Type },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BackendError> for Diagnostic<usize> {
|
impl From<BackendError> for Diagnostic<usize> {
|
||||||
@@ -64,6 +66,9 @@ impl From<BackendError> for Diagnostic<usize> {
|
|||||||
BackendError::Write(me) => {
|
BackendError::Write(me) => {
|
||||||
Diagnostic::error().with_message(format!("Cranelift object write error: {}", me))
|
Diagnostic::error().with_message(format!("Cranelift object write error: {}", me))
|
||||||
}
|
}
|
||||||
|
BackendError::InvalidTypeCast { from, to } => Diagnostic::error().with_message(
|
||||||
|
format!("Internal error trying to cast from {} to {}", from, to),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +108,17 @@ impl PartialEq for BackendError {
|
|||||||
BackendError::Write(b) => a == b,
|
BackendError::Write(b) => a == b,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
BackendError::InvalidTypeCast {
|
||||||
|
from: from1,
|
||||||
|
to: to1,
|
||||||
|
} => match other {
|
||||||
|
BackendError::InvalidTypeCast {
|
||||||
|
from: from2,
|
||||||
|
to: to2,
|
||||||
|
} => from1 == from2 && to1 == to2,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::ir::{Expression, Primitive, Program, Statement, Value, ValueOrRef};
|
use crate::eval::PrimitiveType;
|
||||||
|
use crate::ir::{Expression, Primitive, Program, Statement, Type, Value, ValueOrRef};
|
||||||
use crate::syntax::ConstantType;
|
use crate::syntax::ConstantType;
|
||||||
use cranelift_codegen::entity::EntityRef;
|
use cranelift_codegen::entity::EntityRef;
|
||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
@@ -307,6 +308,70 @@ impl Expression {
|
|||||||
Err(BackendError::VariableLookupFailure(name))
|
Err(BackendError::VariableLookupFailure(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expression::Cast(_, target_type, expr) => {
|
||||||
|
let (val, val_type) =
|
||||||
|
expr.into_crane(builder, local_variables, global_variables)?;
|
||||||
|
|
||||||
|
match (val_type, &target_type) {
|
||||||
|
(ConstantType::I8, Type::Primitive(PrimitiveType::I8)) => Ok((val, val_type)),
|
||||||
|
(ConstantType::I8, Type::Primitive(PrimitiveType::I16)) => {
|
||||||
|
Ok((builder.ins().sextend(types::I16, val), ConstantType::I16))
|
||||||
|
}
|
||||||
|
(ConstantType::I8, Type::Primitive(PrimitiveType::I32)) => {
|
||||||
|
Ok((builder.ins().sextend(types::I32, val), ConstantType::I32))
|
||||||
|
}
|
||||||
|
(ConstantType::I8, Type::Primitive(PrimitiveType::I64)) => {
|
||||||
|
Ok((builder.ins().sextend(types::I64, val), ConstantType::I64))
|
||||||
|
}
|
||||||
|
|
||||||
|
(ConstantType::I16, Type::Primitive(PrimitiveType::I16)) => Ok((val, val_type)),
|
||||||
|
(ConstantType::I16, Type::Primitive(PrimitiveType::I32)) => {
|
||||||
|
Ok((builder.ins().sextend(types::I32, val), ConstantType::I32))
|
||||||
|
}
|
||||||
|
(ConstantType::I16, Type::Primitive(PrimitiveType::I64)) => {
|
||||||
|
Ok((builder.ins().sextend(types::I64, val), ConstantType::I64))
|
||||||
|
}
|
||||||
|
|
||||||
|
(ConstantType::I32, Type::Primitive(PrimitiveType::I32)) => Ok((val, val_type)),
|
||||||
|
(ConstantType::I32, Type::Primitive(PrimitiveType::I64)) => {
|
||||||
|
Ok((builder.ins().sextend(types::I64, val), ConstantType::I64))
|
||||||
|
}
|
||||||
|
|
||||||
|
(ConstantType::I64, Type::Primitive(PrimitiveType::I64)) => Ok((val, val_type)),
|
||||||
|
|
||||||
|
(ConstantType::U8, Type::Primitive(PrimitiveType::U8)) => Ok((val, val_type)),
|
||||||
|
(ConstantType::U8, Type::Primitive(PrimitiveType::U16)) => {
|
||||||
|
Ok((builder.ins().uextend(types::I16, val), ConstantType::U16))
|
||||||
|
}
|
||||||
|
(ConstantType::U8, Type::Primitive(PrimitiveType::U32)) => {
|
||||||
|
Ok((builder.ins().uextend(types::I32, val), ConstantType::U32))
|
||||||
|
}
|
||||||
|
(ConstantType::U8, Type::Primitive(PrimitiveType::U64)) => {
|
||||||
|
Ok((builder.ins().uextend(types::I64, val), ConstantType::U64))
|
||||||
|
}
|
||||||
|
|
||||||
|
(ConstantType::U16, Type::Primitive(PrimitiveType::U16)) => Ok((val, val_type)),
|
||||||
|
(ConstantType::U16, Type::Primitive(PrimitiveType::U32)) => {
|
||||||
|
Ok((builder.ins().uextend(types::I32, val), ConstantType::U32))
|
||||||
|
}
|
||||||
|
(ConstantType::U16, Type::Primitive(PrimitiveType::U64)) => {
|
||||||
|
Ok((builder.ins().uextend(types::I64, val), ConstantType::U64))
|
||||||
|
}
|
||||||
|
|
||||||
|
(ConstantType::U32, Type::Primitive(PrimitiveType::U32)) => Ok((val, val_type)),
|
||||||
|
(ConstantType::U32, Type::Primitive(PrimitiveType::U64)) => {
|
||||||
|
Ok((builder.ins().uextend(types::I64, val), ConstantType::U64))
|
||||||
|
}
|
||||||
|
|
||||||
|
(ConstantType::U64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)),
|
||||||
|
|
||||||
|
_ => Err(BackendError::InvalidTypeCast {
|
||||||
|
from: val_type.into(),
|
||||||
|
to: target_type,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Expression::Primitive(_, prim, mut vals) => {
|
Expression::Primitive(_, prim, mut vals) => {
|
||||||
let mut values = vec![];
|
let mut values = vec![];
|
||||||
let mut first_type = None;
|
let mut first_type = None;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ fn main() {
|
|||||||
let args = CommandLineArguments::parse();
|
let args = CommandLineArguments::parse();
|
||||||
let mut compiler = ngr::Compiler::default();
|
let mut compiler = ngr::Compiler::default();
|
||||||
|
|
||||||
let output_file = args.output.unwrap_or("output.o".to_string());
|
let output_file = args.output.unwrap_or_else(|| "output.o".to_string());
|
||||||
|
|
||||||
if let Some(bytes) = compiler.compile(&args.file) {
|
if let Some(bytes) = compiler.compile(&args.file) {
|
||||||
std::fs::write(&output_file, bytes)
|
std::fs::write(&output_file, bytes)
|
||||||
|
|||||||
@@ -35,11 +35,13 @@
|
|||||||
//!
|
//!
|
||||||
mod env;
|
mod env;
|
||||||
mod primop;
|
mod primop;
|
||||||
|
mod primtype;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
use cranelift_module::ModuleError;
|
use cranelift_module::ModuleError;
|
||||||
pub use env::{EvalEnvironment, LookupError};
|
pub use env::{EvalEnvironment, LookupError};
|
||||||
pub use primop::PrimOpError;
|
pub use primop::PrimOpError;
|
||||||
|
pub use primtype::PrimitiveType;
|
||||||
pub use value::Value;
|
pub use value::Value;
|
||||||
|
|
||||||
use crate::backend::BackendError;
|
use crate::backend::BackendError;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::eval::primtype::PrimitiveType;
|
||||||
use crate::eval::value::Value;
|
use crate::eval::value::Value;
|
||||||
|
|
||||||
/// Errors that can occur running primitive operations in the evaluators.
|
/// Errors that can occur running primitive operations in the evaluators.
|
||||||
@@ -22,6 +23,13 @@ pub enum PrimOpError {
|
|||||||
BadArgCount(String, usize),
|
BadArgCount(String, usize),
|
||||||
#[error("Unknown primitive operation {0}")]
|
#[error("Unknown primitive operation {0}")]
|
||||||
UnknownPrimOp(String),
|
UnknownPrimOp(String),
|
||||||
|
#[error("Unsafe cast from {from} to {to}")]
|
||||||
|
UnsafeCast {
|
||||||
|
from: PrimitiveType,
|
||||||
|
to: PrimitiveType,
|
||||||
|
},
|
||||||
|
#[error("Unknown primitive type {0}")]
|
||||||
|
UnknownPrimType(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing primitives in an interpreter like this is *super* tedious,
|
// Implementing primitives in an interpreter like this is *super* tedious,
|
||||||
|
|||||||
121
src/eval/primtype.rs
Normal file
121
src/eval/primtype.rs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
use crate::{
|
||||||
|
eval::{PrimOpError, Value},
|
||||||
|
syntax::ConstantType,
|
||||||
|
};
|
||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum PrimitiveType {
|
||||||
|
U8,
|
||||||
|
U16,
|
||||||
|
U32,
|
||||||
|
U64,
|
||||||
|
I8,
|
||||||
|
I16,
|
||||||
|
I32,
|
||||||
|
I64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PrimitiveType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
PrimitiveType::I8 => write!(f, "i8"),
|
||||||
|
PrimitiveType::I16 => write!(f, "i16"),
|
||||||
|
PrimitiveType::I32 => write!(f, "i32"),
|
||||||
|
PrimitiveType::I64 => write!(f, "i64"),
|
||||||
|
PrimitiveType::U8 => write!(f, "u8"),
|
||||||
|
PrimitiveType::U16 => write!(f, "u16"),
|
||||||
|
PrimitiveType::U32 => write!(f, "u32"),
|
||||||
|
PrimitiveType::U64 => write!(f, "u64"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Value> for PrimitiveType {
|
||||||
|
fn from(value: &Value) -> Self {
|
||||||
|
match value {
|
||||||
|
Value::I8(_) => PrimitiveType::I8,
|
||||||
|
Value::I16(_) => PrimitiveType::I16,
|
||||||
|
Value::I32(_) => PrimitiveType::I32,
|
||||||
|
Value::I64(_) => PrimitiveType::I64,
|
||||||
|
Value::U8(_) => PrimitiveType::U8,
|
||||||
|
Value::U16(_) => PrimitiveType::U16,
|
||||||
|
Value::U32(_) => PrimitiveType::U32,
|
||||||
|
Value::U64(_) => PrimitiveType::U64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConstantType> for PrimitiveType {
|
||||||
|
fn from(value: ConstantType) -> Self {
|
||||||
|
match value {
|
||||||
|
ConstantType::I8 => PrimitiveType::I8,
|
||||||
|
ConstantType::I16 => PrimitiveType::I16,
|
||||||
|
ConstantType::I32 => PrimitiveType::I32,
|
||||||
|
ConstantType::I64 => PrimitiveType::I64,
|
||||||
|
ConstantType::U8 => PrimitiveType::U8,
|
||||||
|
ConstantType::U16 => PrimitiveType::U16,
|
||||||
|
ConstantType::U32 => PrimitiveType::U32,
|
||||||
|
ConstantType::U64 => PrimitiveType::U64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for PrimitiveType {
|
||||||
|
type Err = PrimOpError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"i8" => Ok(PrimitiveType::I8),
|
||||||
|
"i16" => Ok(PrimitiveType::I16),
|
||||||
|
"i32" => Ok(PrimitiveType::I32),
|
||||||
|
"i64" => Ok(PrimitiveType::I64),
|
||||||
|
"u8" => Ok(PrimitiveType::U8),
|
||||||
|
"u16" => Ok(PrimitiveType::U16),
|
||||||
|
"u32" => Ok(PrimitiveType::U32),
|
||||||
|
"u64" => Ok(PrimitiveType::U64),
|
||||||
|
_ => Err(PrimOpError::UnknownPrimType(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimitiveType {
|
||||||
|
/// Try to cast the given value to this type, returning the new value.
|
||||||
|
///
|
||||||
|
/// Returns an error if the cast is not safe *in* *general*. This means that
|
||||||
|
/// this function will error even if the number will actually fit in the target
|
||||||
|
/// type, but it would not be generally safe to cast a member of the given
|
||||||
|
/// type to the target type. (So, for example, "1i64" is a number that could
|
||||||
|
/// work as a "u64", but since negative numbers wouldn't work, a cast from
|
||||||
|
/// "1i64" to "u64" will fail.)
|
||||||
|
pub fn safe_cast(&self, source: &Value) -> Result<Value, PrimOpError> {
|
||||||
|
match (self, source) {
|
||||||
|
(PrimitiveType::U8, Value::U8(x)) => Ok(Value::U8(*x)),
|
||||||
|
(PrimitiveType::U16, Value::U8(x)) => Ok(Value::U16(*x as u16)),
|
||||||
|
(PrimitiveType::U16, Value::U16(x)) => Ok(Value::U16(*x)),
|
||||||
|
(PrimitiveType::U32, Value::U8(x)) => Ok(Value::U32(*x as u32)),
|
||||||
|
(PrimitiveType::U32, Value::U16(x)) => Ok(Value::U32(*x as u32)),
|
||||||
|
(PrimitiveType::U32, Value::U32(x)) => Ok(Value::U32(*x)),
|
||||||
|
(PrimitiveType::U64, Value::U8(x)) => Ok(Value::U64(*x as u64)),
|
||||||
|
(PrimitiveType::U64, Value::U16(x)) => Ok(Value::U64(*x as u64)),
|
||||||
|
(PrimitiveType::U64, Value::U32(x)) => Ok(Value::U64(*x as u64)),
|
||||||
|
(PrimitiveType::U64, Value::U64(x)) => Ok(Value::U64(*x)),
|
||||||
|
|
||||||
|
(PrimitiveType::I8, Value::I8(x)) => Ok(Value::I8(*x)),
|
||||||
|
(PrimitiveType::I16, Value::I8(x)) => Ok(Value::I16(*x as i16)),
|
||||||
|
(PrimitiveType::I16, Value::I16(x)) => Ok(Value::I16(*x)),
|
||||||
|
(PrimitiveType::I32, Value::I8(x)) => Ok(Value::I32(*x as i32)),
|
||||||
|
(PrimitiveType::I32, Value::I16(x)) => Ok(Value::I32(*x as i32)),
|
||||||
|
(PrimitiveType::I32, Value::I32(x)) => Ok(Value::I32(*x)),
|
||||||
|
(PrimitiveType::I64, Value::I8(x)) => Ok(Value::I64(*x as i64)),
|
||||||
|
(PrimitiveType::I64, Value::I16(x)) => Ok(Value::I64(*x as i64)),
|
||||||
|
(PrimitiveType::I64, Value::I32(x)) => Ok(Value::I64(*x as i64)),
|
||||||
|
(PrimitiveType::I64, Value::I64(x)) => Ok(Value::I64(*x)),
|
||||||
|
|
||||||
|
_ => Err(PrimOpError::UnsafeCast {
|
||||||
|
from: source.into(),
|
||||||
|
to: *self,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
use crate::syntax::{self, ConstantType, Location};
|
use crate::{
|
||||||
|
eval::PrimitiveType,
|
||||||
|
syntax::{self, ConstantType, Location},
|
||||||
|
};
|
||||||
use internment::ArcIntern;
|
use internment::ArcIntern;
|
||||||
use pretty::{DocAllocator, Pretty};
|
use pretty::{DocAllocator, Pretty};
|
||||||
use proptest::{
|
use proptest::{
|
||||||
prelude::Arbitrary,
|
prelude::Arbitrary,
|
||||||
strategy::{BoxedStrategy, Strategy},
|
strategy::{BoxedStrategy, Strategy},
|
||||||
};
|
};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
/// We're going to represent variables as interned strings.
|
/// We're going to represent variables as interned strings.
|
||||||
///
|
///
|
||||||
@@ -115,6 +119,7 @@ where
|
|||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Value(Location, Value),
|
Value(Location, Value),
|
||||||
Reference(Location, Variable),
|
Reference(Location, Variable),
|
||||||
|
Cast(Location, Type, ValueOrRef),
|
||||||
Primitive(Location, Primitive, Vec<ValueOrRef>),
|
Primitive(Location, Primitive, Vec<ValueOrRef>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +132,11 @@ where
|
|||||||
match self {
|
match self {
|
||||||
Expression::Value(_, val) => val.pretty(allocator),
|
Expression::Value(_, val) => val.pretty(allocator),
|
||||||
Expression::Reference(_, var) => allocator.text(var.as_ref().to_string()),
|
Expression::Reference(_, var) => allocator.text(var.as_ref().to_string()),
|
||||||
|
Expression::Cast(_, t, e) => allocator
|
||||||
|
.text("<")
|
||||||
|
.append(t.pretty(allocator))
|
||||||
|
.append(allocator.text(">"))
|
||||||
|
.append(e.pretty(allocator)),
|
||||||
Expression::Primitive(_, op, exprs) if exprs.len() == 1 => {
|
Expression::Primitive(_, op, exprs) if exprs.len() == 1 => {
|
||||||
op.pretty(allocator).append(exprs[0].pretty(allocator))
|
op.pretty(allocator).append(exprs[0].pretty(allocator))
|
||||||
}
|
}
|
||||||
@@ -283,3 +293,28 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub enum Type {
|
||||||
|
Primitive(PrimitiveType),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Type
|
||||||
|
where
|
||||||
|
A: 'a,
|
||||||
|
D: ?Sized + DocAllocator<'a, A>,
|
||||||
|
{
|
||||||
|
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
|
||||||
|
match self {
|
||||||
|
Type::Primitive(pt) => allocator.text(format!("{}", pt)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Type {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Type::Primitive(pt) => pt.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::eval::{EvalEnvironment, EvalError, Value};
|
use crate::eval::{EvalEnvironment, EvalError, Value};
|
||||||
use crate::ir::{Expression, Program, Statement};
|
use crate::ir::{Expression, Program, Statement};
|
||||||
|
|
||||||
use super::{Primitive, ValueOrRef};
|
use super::{Primitive, Type, ValueOrRef};
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
/// Evaluate the program, returning either an error or a string containing everything
|
/// Evaluate the program, returning either an error or a string containing everything
|
||||||
@@ -47,6 +47,19 @@ impl Expression {
|
|||||||
|
|
||||||
Expression::Reference(_, n) => Ok(env.lookup(n.clone())?),
|
Expression::Reference(_, n) => Ok(env.lookup(n.clone())?),
|
||||||
|
|
||||||
|
Expression::Cast(_, t, valref) => {
|
||||||
|
let value = match valref {
|
||||||
|
ValueOrRef::Ref(_, n) => env.lookup(n.clone())?,
|
||||||
|
ValueOrRef::Value(loc, val) => {
|
||||||
|
Expression::Value(loc.clone(), val.clone()).eval(env)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match t {
|
||||||
|
Type::Primitive(pt) => Ok(pt.safe_cast(&value)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Expression::Primitive(_, op, args) => {
|
Expression::Primitive(_, op, args) => {
|
||||||
let mut arg_values = Vec::with_capacity(args.len());
|
let mut arg_values = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use internment::ArcIntern;
|
use internment::ArcIntern;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
|
||||||
|
use crate::eval::PrimitiveType;
|
||||||
use crate::ir::ast as ir;
|
use crate::ir::ast as ir;
|
||||||
use crate::syntax;
|
use crate::syntax;
|
||||||
|
|
||||||
@@ -104,7 +106,20 @@ impl syntax::Expression {
|
|||||||
(vec![], ValueOrRef::Ref(loc, ArcIntern::new(name)))
|
(vec![], ValueOrRef::Ref(loc, ArcIntern::new(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
syntax::Expression::Cast(_, _, _) => unimplemented!(),
|
syntax::Expression::Cast(loc, t, expr) => {
|
||||||
|
let (mut prereqs, new_expr) = expr.rebind(base_name);
|
||||||
|
let new_name = gensym(base_name);
|
||||||
|
prereqs.push(ir::Statement::Binding(
|
||||||
|
loc.clone(),
|
||||||
|
new_name.clone(),
|
||||||
|
ir::Expression::Cast(
|
||||||
|
loc.clone(),
|
||||||
|
ir::Type::Primitive(PrimitiveType::from_str(&t).unwrap()),
|
||||||
|
new_expr,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
(prereqs, ValueOrRef::Ref(loc, new_name))
|
||||||
|
}
|
||||||
|
|
||||||
// Primitive expressions are where we do the real work.
|
// Primitive expressions are where we do the real work.
|
||||||
syntax::Expression::Primitive(loc, prim, mut expressions) => {
|
syntax::Expression::Primitive(loc, prim, mut expressions) => {
|
||||||
|
|||||||
@@ -124,7 +124,30 @@ impl Arbitrary for Expression {
|
|||||||
Union::new([value_strategy, reference_strategy]).boxed()
|
Union::new([value_strategy, reference_strategy]).boxed()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let cast_strategy = if let Some(bigger_type) = target_type {
|
||||||
|
let mut smaller_types = bigger_type.safe_casts_to();
|
||||||
|
|
||||||
|
if smaller_types.is_empty() {
|
||||||
leaf_strategy
|
leaf_strategy
|
||||||
|
} else {
|
||||||
|
let duplicated_env = defined_variables.clone();
|
||||||
|
let cast_exp = |t, e| Expression::Cast(Location::manufactured(), t, Box::new(e));
|
||||||
|
|
||||||
|
let smaller_strats: Vec<BoxedStrategy<Expression>> = smaller_types
|
||||||
|
.drain(..)
|
||||||
|
.map(|t| {
|
||||||
|
Expression::arbitrary_with((Some(duplicated_env.clone()), Some(t)))
|
||||||
|
.prop_map(move |e| cast_exp(t.name(), e))
|
||||||
|
.boxed()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Union::new(smaller_strats).boxed()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
leaf_strategy
|
||||||
|
};
|
||||||
|
|
||||||
|
cast_strategy
|
||||||
.prop_recursive(3, 64, 2, move |inner| {
|
.prop_recursive(3, 64, 2, move |inner| {
|
||||||
(select(OPERATORS), proptest::collection::vec(inner, 2)).prop_map(
|
(select(OPERATORS), proptest::collection::vec(inner, 2)).prop_map(
|
||||||
move |((operator, arg_count), mut exprs)| {
|
move |((operator, arg_count), mut exprs)| {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use internment::ArcIntern;
|
use internment::ArcIntern;
|
||||||
|
|
||||||
use crate::eval::{EvalEnvironment, EvalError, Value};
|
use crate::eval::{EvalEnvironment, EvalError, PrimitiveType, Value};
|
||||||
use crate::syntax::{ConstantType, Expression, Program, Statement};
|
use crate::syntax::{ConstantType, Expression, Program, Statement};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
/// Evaluate the program, returning either an error or what it prints out when run.
|
/// Evaluate the program, returning either an error or what it prints out when run.
|
||||||
@@ -59,7 +60,11 @@ impl Expression {
|
|||||||
|
|
||||||
Expression::Reference(_, n) => Ok(env.lookup(ArcIntern::new(n.clone()))?),
|
Expression::Reference(_, n) => Ok(env.lookup(ArcIntern::new(n.clone()))?),
|
||||||
|
|
||||||
Expression::Cast(_, _, _) => unimplemented!(),
|
Expression::Cast(_, target, expr) => {
|
||||||
|
let target_type = PrimitiveType::from_str(target)?;
|
||||||
|
let value = expr.eval(env)?;
|
||||||
|
Ok(target_type.safe_cast(&value)?)
|
||||||
|
}
|
||||||
|
|
||||||
Expression::Primitive(_, op, args) => {
|
Expression::Primitive(_, op, args) => {
|
||||||
let mut arg_values = Vec::with_capacity(args.len());
|
let mut arg_values = Vec::with_capacity(args.len());
|
||||||
|
|||||||
@@ -175,6 +175,34 @@ impl ConstantType {
|
|||||||
ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64
|
ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the set of types that can be safely casted into this type.
|
||||||
|
pub fn safe_casts_to(self) -> Vec<ConstantType> {
|
||||||
|
match self {
|
||||||
|
ConstantType::I8 => vec![],
|
||||||
|
ConstantType::I16 => vec![ConstantType::I8],
|
||||||
|
ConstantType::I32 => vec![ConstantType::I16, ConstantType::I8],
|
||||||
|
ConstantType::I64 => vec![ConstantType::I32, ConstantType::I16, ConstantType::I8],
|
||||||
|
ConstantType::U8 => vec![],
|
||||||
|
ConstantType::U16 => vec![ConstantType::U8],
|
||||||
|
ConstantType::U32 => vec![ConstantType::U16, ConstantType::U8],
|
||||||
|
ConstantType::U64 => vec![ConstantType::U32, ConstantType::U16, ConstantType::U8],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the name of the given type, as a string
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
match self {
|
||||||
|
ConstantType::I8 => "i8".to_string(),
|
||||||
|
ConstantType::I16 => "i16".to_string(),
|
||||||
|
ConstantType::I32 => "i32".to_string(),
|
||||||
|
ConstantType::I64 => "i64".to_string(),
|
||||||
|
ConstantType::U8 => "u8".to_string(),
|
||||||
|
ConstantType::U16 => "u16".to_string(),
|
||||||
|
ConstantType::U32 => "u32".to_string(),
|
||||||
|
ConstantType::U64 => "u64".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error, PartialEq)]
|
#[derive(Debug, Error, PartialEq)]
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ impl Expression {
|
|||||||
vec![Error::UnboundVariable(loc.clone(), var.clone())],
|
vec![Error::UnboundVariable(loc.clone(), var.clone())],
|
||||||
vec![],
|
vec![],
|
||||||
),
|
),
|
||||||
Expression::Cast(_, _, _) => unimplemented!(),
|
Expression::Cast(_, _, expr) => expr.validate(variable_map),
|
||||||
Expression::Primitive(_, _, args) => {
|
Expression::Primitive(_, _, args) => {
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
let mut warnings = vec![];
|
let mut warnings = vec![];
|
||||||
|
|||||||
Reference in New Issue
Block a user