🤔 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,13 +1,15 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
|
||||
/// A source location, for use in pointing users towards warnings and errors.
|
||||
///
|
||||
/// Internally, locations are very tied to the `codespan_reporting` library,
|
||||
/// and the primary use of them is to serve as anchors within that library.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Location {
|
||||
file_idx: usize,
|
||||
offset: usize,
|
||||
location: Range<usize>,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
@@ -17,8 +19,8 @@ impl Location {
|
||||
/// The file index is based on the file database being used. See the
|
||||
/// `codespan_reporting::files::SimpleFiles::add` function, which is
|
||||
/// normally where we get this index.
|
||||
pub fn new(file_idx: usize, offset: usize) -> Self {
|
||||
Location { file_idx, offset }
|
||||
pub fn new(file_idx: usize, location: Range<usize>) -> Self {
|
||||
Location { file_idx, location }
|
||||
}
|
||||
|
||||
/// Generate a `Location` for a completely manufactured bit of code.
|
||||
@@ -30,7 +32,7 @@ impl Location {
|
||||
pub fn manufactured() -> Self {
|
||||
Location {
|
||||
file_idx: 0,
|
||||
offset: 0,
|
||||
location: 0..0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +49,7 @@ impl Location {
|
||||
/// actually happened), but you'd probably want to make the first location
|
||||
/// the secondary label to help users find it.
|
||||
pub fn primary_label(&self) -> Label<usize> {
|
||||
Label::primary(self.file_idx, self.offset..self.offset)
|
||||
Label::primary(self.file_idx, self.location.clone())
|
||||
}
|
||||
|
||||
/// Generate a secondary label for a [`Diagnostic`], based on this source
|
||||
@@ -64,35 +66,7 @@ impl Location {
|
||||
/// probably want to make the first location the secondary label to help
|
||||
/// users find it.
|
||||
pub fn secondary_label(&self) -> Label<usize> {
|
||||
Label::secondary(self.file_idx, self.offset..self.offset)
|
||||
}
|
||||
|
||||
/// Given this location and another, generate a primary label that
|
||||
/// specifies the area between those two locations.
|
||||
///
|
||||
/// See [`Self::primary_label`] for some discussion of primary versus
|
||||
/// secondary labels. If the two locations are the same, this method does
|
||||
/// the exact same thing as [`Self::primary_label`]. If this item was
|
||||
/// generated by [`Self::manufactured`], it will act as if you'd called
|
||||
/// `primary_label` on the argument. Otherwise, it will generate the obvious
|
||||
/// span.
|
||||
///
|
||||
/// This function will return `None` only in the case that you provide
|
||||
/// labels from two different files, which it cannot sensibly handle.
|
||||
pub fn range_label(&self, end: &Location) -> Option<Label<usize>> {
|
||||
if self.file_idx == 0 {
|
||||
return Some(end.primary_label());
|
||||
}
|
||||
|
||||
if self.file_idx != end.file_idx {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.offset > end.offset {
|
||||
Some(Label::primary(self.file_idx, end.offset..self.offset))
|
||||
} else {
|
||||
Some(Label::primary(self.file_idx, self.offset..end.offset))
|
||||
}
|
||||
Label::secondary(self.file_idx, self.location.clone())
|
||||
}
|
||||
|
||||
/// Return an error diagnostic centered at this location.
|
||||
@@ -102,10 +76,7 @@ impl Location {
|
||||
/// this particular location. You'll need to extend it with actually useful
|
||||
/// information, like what kind of error it is.
|
||||
pub fn error(&self) -> Diagnostic<usize> {
|
||||
Diagnostic::error().with_labels(vec![Label::primary(
|
||||
self.file_idx,
|
||||
self.offset..self.offset,
|
||||
)])
|
||||
Diagnostic::error().with_labels(vec![Label::primary(self.file_idx, self.location.clone())])
|
||||
}
|
||||
|
||||
/// Return an error diagnostic centered at this location, with the given message.
|
||||
@@ -115,10 +86,34 @@ impl Location {
|
||||
/// even more information to ut, using [`Diagnostic::with_labels`],
|
||||
/// [`Diagnostic::with_notes`], or [`Diagnostic::with_code`].
|
||||
pub fn labelled_error(&self, msg: &str) -> Diagnostic<usize> {
|
||||
Diagnostic::error().with_labels(vec![Label::primary(
|
||||
self.file_idx,
|
||||
self.offset..self.offset,
|
||||
)
|
||||
.with_message(msg)])
|
||||
Diagnostic::error().with_labels(vec![
|
||||
Label::primary(self.file_idx, self.location.clone()).with_message(msg)
|
||||
])
|
||||
}
|
||||
|
||||
/// Merge two locations into a single location spanning the whole range between
|
||||
/// them.
|
||||
///
|
||||
/// This function returns None if the locations are from different files; this
|
||||
/// can happen if one of the locations is manufactured, for example.
|
||||
pub fn merge(&self, other: &Self) -> Option<Self> {
|
||||
if self.file_idx != other.file_idx {
|
||||
None
|
||||
} else {
|
||||
let start = if self.location.start <= other.location.start {
|
||||
self.location.start
|
||||
} else {
|
||||
other.location.start
|
||||
};
|
||||
let end = if self.location.end >= other.location.end {
|
||||
self.location.end
|
||||
} else {
|
||||
other.location.end
|
||||
};
|
||||
Some(Location {
|
||||
file_idx: self.file_idx,
|
||||
location: start..end,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user