🤔 Add a type inference engine, along with typed literals. (#4)
The typed literal formatting mirrors that of Rust. If no type can be inferred for an untagged literal, the type inference engine will warn the user and then assume that they meant an unsigned 64-bit number. (This is slightly inconvenient, because there can be cases in which our Arbitrary instance may generate a unary negation, in which we should assume that it's a signed 64-bit number; we may want to revisit this later.) The type inference engine is a standard two phase one, in which we first generate a series of type constraints, and then we solve those constraints. In this particular implementation, we actually use a third phase to generate a final AST. Finally, to increase the amount of testing performed, I've removed the overflow checking in the evaluator. The only thing we now check for is division by zero. This does make things a trace slower in testing, but hopefully we get more coverage this way.
This commit was merged in pull request #4.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
use crate::syntax::Location;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
|
||||
/// The set of valid binary operators.
|
||||
pub static BINARY_OPERATORS: &[&str] = &["+", "-", "*", "/"];
|
||||
use internment::ArcIntern;
|
||||
|
||||
pub use crate::syntax::tokens::ConstantType;
|
||||
use crate::syntax::Location;
|
||||
|
||||
/// A structure represented a parsed program.
|
||||
///
|
||||
@@ -16,6 +19,56 @@ pub struct Program {
|
||||
pub statements: Vec<Statement>,
|
||||
}
|
||||
|
||||
/// A Name.
|
||||
///
|
||||
/// This is basically a string, but annotated with the place the string
|
||||
/// is in the source file.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Name {
|
||||
pub name: String,
|
||||
pub location: Location,
|
||||
}
|
||||
|
||||
impl Name {
|
||||
pub fn new<S: ToString>(n: S, location: Location) -> Name {
|
||||
Name {
|
||||
name: n.to_string(),
|
||||
location,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn manufactured<S: ToString>(n: S) -> Name {
|
||||
Name {
|
||||
name: n.to_string(),
|
||||
location: Location::manufactured(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intern(self) -> ArcIntern<String> {
|
||||
ArcIntern::new(self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Name {}
|
||||
|
||||
impl Hash for Name {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.name.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A parsed statement.
|
||||
///
|
||||
/// Statements are guaranteed to be syntactically valid, but may be
|
||||
@@ -29,8 +82,8 @@ pub struct Program {
|
||||
/// thing, not if they are the exact same statement.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Statement {
|
||||
Binding(Location, String, Expression),
|
||||
Print(Location, String),
|
||||
Binding(Location, Name, Expression),
|
||||
Print(Location, Name),
|
||||
}
|
||||
|
||||
impl PartialEq for Statement {
|
||||
@@ -58,6 +111,7 @@ impl PartialEq for Statement {
|
||||
pub enum Expression {
|
||||
Value(Location, Value),
|
||||
Reference(Location, String),
|
||||
Cast(Location, String, Box<Expression>),
|
||||
Primitive(Location, String, Vec<Expression>),
|
||||
}
|
||||
|
||||
@@ -72,6 +126,10 @@ impl PartialEq for Expression {
|
||||
Expression::Reference(_, var2) => var1 == var2,
|
||||
_ => false,
|
||||
},
|
||||
Expression::Cast(_, t1, e1) => match other {
|
||||
Expression::Cast(_, t2, e2) => t1 == t2 && e1 == e2,
|
||||
_ => false,
|
||||
},
|
||||
Expression::Primitive(_, prim1, args1) => match other {
|
||||
Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2,
|
||||
_ => false,
|
||||
@@ -83,6 +141,12 @@ impl PartialEq for Expression {
|
||||
/// A value from the source syntax
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Value {
|
||||
/// The value of the number, and an optional base that it was written in
|
||||
Number(Option<u8>, i64),
|
||||
/// The value of the number, an optional base that it was written in, and any
|
||||
/// type information provided.
|
||||
///
|
||||
/// u64 is chosen because it should be big enough to carry the amount of
|
||||
/// information we need, and technically we interpret -4 as the primitive unary
|
||||
/// operation "-" on the number 4. We'll translate this into a type-specific
|
||||
/// number at a later time.
|
||||
Number(Option<u8>, Option<ConstantType>, u64),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user