summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-08-13 12:21:14 +0200
committerLaurenz <laurmaedje@gmail.com>2021-08-13 14:33:58 +0200
commit144f20882136ef81b79d77bd8a68f42b76c66676 (patch)
tree7a452ab2a092f674d93cd994d80b88cc6808e540 /src
parentd002cdf451e1c6efbf7cd7f2303264526b6f8a92 (diff)
Add file information to spans
Diffstat (limited to 'src')
-rw-r--r--src/diag.rs104
-rw-r--r--src/eval/function.rs21
-rw-r--r--src/eval/mod.rs209
-rw-r--r--src/library/elements.rs2
-rw-r--r--src/library/layout.rs2
-rw-r--r--src/library/text.rs2
-rw-r--r--src/library/utility.rs7
-rw-r--r--src/main.rs16
-rw-r--r--src/parse/mod.rs2
-rw-r--r--src/parse/parser.rs97
-rw-r--r--src/parse/resolve.rs10
-rw-r--r--src/source.rs111
-rw-r--r--src/syntax/span.rs104
13 files changed, 342 insertions, 345 deletions
diff --git a/src/diag.rs b/src/diag.rs
index 1905d480..154de24b 100644
--- a/src/diag.rs
+++ b/src/diag.rs
@@ -2,8 +2,18 @@
use serde::{Deserialize, Serialize};
-use crate::source::SourceId;
-use crate::syntax::Span;
+use crate::syntax::{Span, Spanned};
+
+/// Early-return with a vec-boxed [`Error`].
+macro_rules! bail {
+ ($span:expr, $message:expr $(,)?) => {
+ return Err($crate::diag::Error::boxed($span, $message,));
+ };
+
+ ($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
+ bail!($span, format!($fmt, $($arg),+));
+ };
+}
/// The result type for typesetting and all its subpasses.
pub type TypResult<T> = Result<T, Box<Vec<Error>>>;
@@ -14,35 +24,19 @@ pub type StrResult<T> = Result<T, String>;
/// An error in a source file.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Error {
- /// The id of the source file that contains the error.
- pub source: SourceId,
/// The erroneous location in the source code.
pub span: Span,
/// A diagnostic message describing the problem.
pub message: String,
/// The trace of function calls leading to the error.
- pub trace: Vec<(SourceId, Span, Tracepoint)>,
-}
-
-/// A part of an error's [trace](Error::trace).
-#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
-pub enum Tracepoint {
- /// A function call.
- Call(Option<String>),
- /// A module import.
- Import,
+ pub trace: Vec<Spanned<Tracepoint>>,
}
impl Error {
/// Create a new, bare error.
- pub fn new(
- source: SourceId,
- span: impl Into<Span>,
- message: impl Into<String>,
- ) -> Self {
+ pub fn new(span: Span, message: impl Into<String>) -> Self {
Self {
- source,
- span: span.into(),
+ span,
trace: vec![],
message: message.into(),
}
@@ -50,36 +44,52 @@ impl Error {
/// Create a boxed vector containing one error. The return value is suitable
/// as the `Err` variant of a [`TypResult`].
- pub fn boxed(
- source: SourceId,
- span: impl Into<Span>,
- message: impl Into<String>,
- ) -> Box<Vec<Self>> {
- Box::new(vec![Self::new(source, span, message)])
+ pub fn boxed(span: Span, message: impl Into<String>) -> Box<Vec<Self>> {
+ Box::new(vec![Self::new(span, message)])
}
+}
- /// Create a closure that contains the positional information for an error
- /// and just needs the message to yield a vec-boxed error.
- ///
- /// This is useful in to convert from [`StrResult`] to a [`TypResult`] using
- /// [`map_err`](Result::map_err).
- pub fn at<S: Into<String>>(
- source: SourceId,
- span: impl Into<Span>,
- ) -> impl FnOnce(S) -> Box<Vec<Self>> {
- move |message| Self::boxed(source, span, message)
+/// A part of an error's [trace](Error::trace).
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
+pub enum Tracepoint {
+ /// A function call.
+ Call(Option<String>),
+ /// A module import.
+ Import,
+}
+
+/// Convert a [`StrResult`] to a [`TypResult`] by adding span information.
+pub trait At<T> {
+ /// Add the span information.
+ fn at(self, span: Span) -> TypResult<T>;
+}
+
+impl<T> At<T> for StrResult<T> {
+ fn at(self, span: Span) -> TypResult<T> {
+ self.map_err(|message| Error::boxed(span, message))
}
}
-/// Early-return with a vec-boxed [`Error`].
-macro_rules! bail {
- ($source:expr, $span:expr, $message:expr $(,)?) => {
- return Err(Box::new(vec![$crate::diag::Error::new(
- $source, $span, $message,
- )]));
- };
+/// Enrich a [`TypResult`] with a tracepoint.
+pub trait Trace<T> {
+ /// Add the tracepoint to all errors that lie outside the `span`.
+ fn trace<F>(self, make_point: F, span: Span) -> Self
+ where
+ F: Fn() -> Tracepoint;
+}
- ($source:expr, $span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => {
- bail!($source, $span, format!($fmt, $($arg),+));
- };
+impl<T> Trace<T> for TypResult<T> {
+ fn trace<F>(self, make_point: F, span: Span) -> Self
+ where
+ F: Fn() -> Tracepoint,
+ {
+ self.map_err(|mut errors| {
+ for error in errors.iter_mut() {
+ if !span.contains(error.span) {
+ error.trace.push(Spanned::new(make_point(), span));
+ }
+ }
+ errors
+ })
+ }
}
diff --git a/src/eval/function.rs b/src/eval/function.rs
index be7894f4..d2ec1bb9 100644
--- a/src/eval/function.rs
+++ b/src/eval/function.rs
@@ -3,8 +3,7 @@ use std::ops::Deref;
use std::rc::Rc;
use super::{Cast, EvalContext, Value};
-use crate::diag::{Error, TypResult};
-use crate::source::SourceId;
+use crate::diag::{At, TypResult};
use crate::syntax::{Span, Spanned};
use crate::util::EcoString;
@@ -59,8 +58,6 @@ impl PartialEq for Function {
/// Evaluated arguments to a function.
#[derive(Debug, Clone, PartialEq)]
pub struct FuncArgs {
- /// The id of the source file in which the function was called.
- pub source: SourceId,
/// The span of the whole argument list.
pub span: Span,
/// The positional arguments.
@@ -103,7 +100,7 @@ impl FuncArgs {
{
match self.eat() {
Some(found) => Ok(found),
- None => bail!(self.source, self.span, "missing argument: {}", what),
+ None => bail!(self.span, "missing argument: {}", what),
}
}
@@ -134,14 +131,14 @@ impl FuncArgs {
let value = self.items.remove(index).value;
let span = value.span;
- T::cast(value).map(Some).map_err(Error::at(self.source, span))
+ T::cast(value).map(Some).at(span)
}
/// Return an "unexpected argument" error if there is any remaining
/// argument.
pub fn finish(self) -> TypResult<()> {
if let Some(arg) = self.items.first() {
- bail!(self.source, arg.span, "unexpected argument");
+ bail!(arg.span, "unexpected argument");
}
Ok(())
}
@@ -165,19 +162,17 @@ impl FuncArgs {
{
let mut iter = self.items.into_iter();
let value = match iter.next() {
+ Some(FuncArg { name: None, value, .. }) => value.v.cast().at(value.span)?,
None => {
- bail!(self.source, self.span, "missing {}", what);
+ bail!(self.span, "missing {}", what);
}
Some(FuncArg { name: Some(_), span, .. }) => {
- bail!(self.source, span, "named pair is not allowed here");
- }
- Some(FuncArg { name: None, value, .. }) => {
- value.v.cast().map_err(Error::at(self.source, value.span))?
+ bail!(span, "named pair is not allowed here");
}
};
if let Some(arg) = iter.next() {
- bail!(self.source, arg.span, "only one {} is allowed", what);
+ bail!(arg.span, "only one {} is allowed", what);
}
Ok(value)
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 1b851a9b..0932dc71 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -27,7 +27,7 @@ use std::mem;
use std::path::PathBuf;
use std::rc::Rc;
-use crate::diag::{Error, StrResult, Tracepoint, TypResult};
+use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult};
use crate::geom::{Angle, Fractional, Length, Relative};
use crate::image::ImageStore;
use crate::loading::Loader;
@@ -71,8 +71,6 @@ pub struct EvalContext<'a> {
pub images: &'a mut ImageStore,
/// Caches evaluated modules.
pub modules: &'a mut ModuleCache,
- /// The id of the currently evaluated source file.
- pub source: SourceId,
/// The stack of imported files that led to evaluation of the current file.
pub route: Vec<SourceId>,
/// The active scopes.
@@ -89,8 +87,7 @@ impl<'a> EvalContext<'a> {
sources: &mut ctx.sources,
images: &mut ctx.images,
modules: &mut ctx.modules,
- source,
- route: vec![],
+ route: vec![source],
scopes: Scopes::new(Some(&ctx.std)),
map: ExprMap::new(),
}
@@ -101,15 +98,15 @@ impl<'a> EvalContext<'a> {
// Load the source file.
let full = self.make_path(path);
let id = self.sources.load(&full).map_err(|err| {
- Error::boxed(self.source, span, match err.kind() {
+ Error::boxed(span, match err.kind() {
io::ErrorKind::NotFound => "file not found".into(),
_ => format!("failed to load source file ({})", err),
})
})?;
// Prevent cyclic importing.
- if self.source == id || self.route.contains(&id) {
- bail!(self.source, span, "cyclic import");
+ if self.route.contains(&id) {
+ bail!(span, "cyclic import");
}
// Check whether the module was already loaded.
@@ -124,23 +121,14 @@ impl<'a> EvalContext<'a> {
// Prepare the new context.
let new_scopes = Scopes::new(self.scopes.base);
let old_scopes = mem::replace(&mut self.scopes, new_scopes);
- self.route.push(self.source);
- self.source = id;
+ self.route.push(id);
// Evaluate the module.
- let result = Rc::new(ast).eval(self);
+ let template = Rc::new(ast).eval(self).trace(|| Tracepoint::Import, span)?;
// Restore the old context.
let new_scopes = mem::replace(&mut self.scopes, old_scopes);
- self.source = self.route.pop().unwrap();
-
- // Add a tracepoint to the errors.
- let template = result.map_err(|mut errors| {
- for error in errors.iter_mut() {
- error.trace.push((self.source, span, Tracepoint::Import));
- }
- errors
- })?;
+ self.route.pop().unwrap();
// Save the evaluated module.
let module = Module { scope: new_scopes.top, template };
@@ -152,11 +140,13 @@ impl<'a> EvalContext<'a> {
/// Complete a user-entered path (relative to the source file) to be
/// relative to the compilation environment's root.
pub fn make_path(&self, path: &str) -> PathBuf {
- if let Some(dir) = self.sources.get(self.source).path().parent() {
- dir.join(path)
- } else {
- path.into()
+ if let Some(&id) = self.route.last() {
+ if let Some(dir) = self.sources.get(id).path().parent() {
+ return dir.join(path);
+ }
}
+
+ path.into()
}
}
@@ -198,10 +188,7 @@ impl Eval for Expr {
Self::Percent(_, v) => Value::Relative(Relative::new(v / 100.0)),
Self::Fractional(_, v) => Value::Fractional(Fractional::new(v)),
Self::Str(_, ref v) => Value::Str(v.clone()),
- Self::Ident(ref v) => match ctx.scopes.get(&v) {
- Some(slot) => slot.borrow().clone(),
- None => bail!(ctx.source, v.span, "unknown variable"),
- },
+ Self::Ident(ref v) => v.eval(ctx)?,
Self::Array(ref v) => Value::Array(v.eval(ctx)?),
Self::Dict(ref v) => Value::Dict(v.eval(ctx)?),
Self::Template(ref v) => Value::Template(v.eval(ctx)?),
@@ -222,6 +209,17 @@ impl Eval for Expr {
}
}
+impl Eval for Ident {
+ type Output = Value;
+
+ fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
+ match ctx.scopes.get(self) {
+ Some(slot) => Ok(slot.borrow().clone()),
+ None => bail!(self.span, "unknown variable"),
+ }
+ }
+}
+
impl Eval for ArrayExpr {
type Output = Array;
@@ -268,8 +266,7 @@ impl Eval for BlockExpr {
let mut output = Value::None;
for expr in &self.exprs {
let value = expr.eval(ctx)?;
- output =
- ops::join(output, value).map_err(Error::at(ctx.source, expr.span()))?;
+ output = ops::join(output, value).at(expr.span())?;
}
if self.scoping {
@@ -290,7 +287,7 @@ impl Eval for UnaryExpr {
UnOp::Neg => ops::neg(value),
UnOp::Not => ops::not(value),
};
- result.map_err(Error::at(ctx.source, self.span))
+ result.at(self.span)
}
}
@@ -337,7 +334,7 @@ impl BinaryExpr {
}
let rhs = self.rhs.eval(ctx)?;
- op(lhs, rhs).map_err(Error::at(ctx.source, self.span))
+ op(lhs, rhs).at(self.span)
}
/// Apply an assignment operation.
@@ -345,11 +342,10 @@ impl BinaryExpr {
where
F: FnOnce(Value, Value) -> StrResult<Value>,
{
- let source = ctx.source;
let rhs = self.rhs.eval(ctx)?;
let mut target = self.lhs.access(ctx)?;
let lhs = mem::take(&mut *target);
- *target = op(lhs, rhs).map_err(Error::at(source, self.span))?;
+ *target = op(lhs, rhs).at(self.span)?;
Ok(Value::None)
}
}
@@ -362,36 +358,22 @@ impl Eval for CallExpr {
let mut args = self.args.eval(ctx)?;
match callee {
- Value::Array(array) => array
- .get(args.into_index()?)
- .map(Value::clone)
- .map_err(Error::at(ctx.source, self.span)),
+ Value::Array(array) => {
+ array.get(args.into_index()?).map(Value::clone).at(self.span)
+ }
- Value::Dict(dict) => dict
- .get(&args.into_key()?)
- .map(Value::clone)
- .map_err(Error::at(ctx.source, self.span)),
+ Value::Dict(dict) => {
+ dict.get(&args.into_key()?).map(Value::clone).at(self.span)
+ }
Value::Func(func) => {
- let returned = func(ctx, &mut args).map_err(|mut errors| {
- for error in errors.iter_mut() {
- // Skip errors directly related to arguments.
- if error.source != ctx.source || !self.span.contains(error.span) {
- error.trace.push((
- ctx.source,
- self.span,
- Tracepoint::Call(func.name().map(Into::into)),
- ));
- }
- }
- errors
- })?;
+ let point = || Tracepoint::Call(func.name().map(Into::into));
+ let value = func(ctx, &mut args).trace(point, self.span)?;
args.finish()?;
- Ok(returned)
+ Ok(value)
}
v => bail!(
- ctx.source,
self.callee.span(),
"expected function or collection, found {}",
v.type_name(),
@@ -405,7 +387,6 @@ impl Eval for CallArgs {
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
Ok(FuncArgs {
- source: ctx.source,
span: self.span,
items: self
.items
@@ -444,9 +425,6 @@ impl Eval for ClosureExpr {
default: Option<Value>,
}
- let file = ctx.source;
- let name = self.name.as_ref().map(|name| name.string.clone());
-
// Evaluate default values for named parameters.
let params: Vec<_> = self
.params
@@ -472,36 +450,31 @@ impl Eval for ClosureExpr {
// Clone the body expression so that we don't have a lifetime
// dependence on the AST.
let body = Rc::clone(&self.body);
+ let name = self.name.as_ref().map(|name| name.string.clone());
// Define the actual function.
let func = Function::new(name, move |ctx, args| {
- let prev_file = mem::replace(&mut ctx.source, file);
-
// Don't leak the scopes from the call site. Instead, we use the
// scope of captured variables we collected earlier.
let prev_scopes = mem::take(&mut ctx.scopes);
ctx.scopes.top = captured.clone();
- let mut try_eval = || {
- // Parse the arguments according to the parameter list.
- for param in &params {
- let value = match &param.default {
- None => args.expect::<Value>(&param.name)?,
- Some(default) => args
- .named::<Value>(&param.name)?
- .unwrap_or_else(|| default.clone()),
- };
-
- ctx.scopes.def_mut(&param.name, value);
- }
+ // Parse the arguments according to the parameter list.
+ for param in &params {
+ let value = match &param.default {
+ None => args.expect::<Value>(&param.name)?,
+ Some(default) => args
+ .named::<Value>(&param.name)?
+ .unwrap_or_else(|| default.clone()),
+ };
+
+ ctx.scopes.def_mut(&param.name, value);
+ }
- body.eval(ctx)
- };
+ let value = body.eval(ctx)?;
- let result = try_eval();
ctx.scopes = prev_scopes;
- ctx.source = prev_file;
- result
+ Ok(value)
});
Ok(Value::Func(func))
@@ -512,11 +485,7 @@ impl Eval for WithExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
- let callee = self
- .callee
- .eval(ctx)?
- .cast::<Function>()
- .map_err(Error::at(ctx.source, self.callee.span()))?;
+ let callee = self.callee.eval(ctx)?.cast::<Function>().at(self.callee.span())?;
let name = callee.name().cloned();
let applied = self.args.eval(ctx)?;
@@ -562,11 +531,8 @@ impl Eval for IfExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
- let condition = self
- .condition
- .eval(ctx)?
- .cast::<bool>()
- .map_err(Error::at(ctx.source, self.condition.span()))?;
+ let condition =
+ self.condition.eval(ctx)?.cast::<bool>().at(self.condition.span())?;
if condition {
self.if_body.eval(ctx)
@@ -584,15 +550,9 @@ impl Eval for WhileExpr {
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
let mut output = Value::None;
- while self
- .condition
- .eval(ctx)?
- .cast::<bool>()
- .map_err(Error::at(ctx.source, self.condition.span()))?
- {
+ while self.condition.eval(ctx)?.cast::<bool>().at(self.condition.span())? {
let value = self.body.eval(ctx)?;
- output = ops::join(output, value)
- .map_err(Error::at(ctx.source, self.body.span()))?;
+ output = ops::join(output, value).at(self.body.span())?;
}
Ok(output)
@@ -614,7 +574,7 @@ impl Eval for ForExpr {
let value = self.body.eval(ctx)?;
output = ops::join(output, value)
- .map_err(Error::at(ctx.source, self.body.span()))?;
+ .at(self.body.span())?;
}
ctx.scopes.exit();
@@ -640,14 +600,11 @@ impl Eval for ForExpr {
iter!(for (k => key, v => value) in dict.into_iter())
}
(ForPattern::KeyValue(_, _), Value::Str(_)) => {
- bail!(ctx.source, self.pattern.span(), "mismatched pattern");
+ bail!(self.pattern.span(), "mismatched pattern");
+ }
+ (_, iter) => {
+ bail!(self.iter.span(), "cannot loop over {}", iter.type_name());
}
- (_, iter) => bail!(
- ctx.source,
- self.iter.span(),
- "cannot loop over {}",
- iter.type_name(),
- ),
}
}
}
@@ -656,11 +613,7 @@ impl Eval for ImportExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
- let path = self
- .path
- .eval(ctx)?
- .cast::<EcoString>()
- .map_err(Error::at(ctx.source, self.path.span()))?;
+ let path = self.path.eval(ctx)?.cast::<EcoString>().at(self.path.span())?;
let file = ctx.import(&path, self.path.span())?;
let module = &ctx.modules[&file];
@@ -676,7 +629,7 @@ impl Eval for ImportExpr {
if let Some(slot) = module.scope.get(&ident) {
ctx.scopes.def_mut(ident.as_str(), slot.borrow().clone());
} else {
- bail!(ctx.source, ident.span, "unresolved import");
+ bail!(ident.span, "unresolved import");
}
}
}
@@ -690,11 +643,7 @@ impl Eval for IncludeExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> TypResult<Self::Output> {
- let path = self
- .path
- .eval(ctx)?
- .cast::<EcoString>()
- .map_err(Error::at(ctx.source, self.path.span()))?;
+ let path = self.path.eval(ctx)?.cast::<EcoString>().at(self.path.span())?;
let file = ctx.import(&path, self.path.span())?;
let module = &ctx.modules[&file];
@@ -753,42 +702,34 @@ impl Access for Expr {
match self {
Expr::Ident(ident) => ident.access(ctx),
Expr::Call(call) => call.access(ctx),
- _ => bail!(
- ctx.source,
- self.span(),
- "cannot access this expression mutably",
- ),
+ _ => bail!(self.span(), "cannot access this expression mutably"),
}
}
}
impl Access for Ident {
fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>> {
- ctx.scopes
- .get(self)
- .ok_or("unknown variable")
- .and_then(|slot| {
- slot.try_borrow_mut().map_err(|_| "cannot mutate a constant")
- })
- .map_err(Error::at(ctx.source, self.span))
+ match ctx.scopes.get(self) {
+ Some(slot) => match slot.try_borrow_mut() {
+ Ok(guard) => Ok(guard),
+ Err(_) => bail!(self.span, "cannot mutate a constant"),
+ },
+ None => bail!(self.span, "unknown variable"),
+ }
}
}
impl Access for CallExpr {
fn access<'a>(&self, ctx: &'a mut EvalContext) -> TypResult<RefMut<'a, Value>> {
- let source = ctx.source;
let args = self.args.eval(ctx)?;
let guard = self.callee.access(ctx)?;
RefMut::try_map(guard, |value| match value {
- Value::Array(array) => array
- .get_mut(args.into_index()?)
- .map_err(Error::at(source, self.span)),
+ Value::Array(array) => array.get_mut(args.into_index()?).at(self.span),
Value::Dict(dict) => Ok(dict.get_mut(args.into_key()?)),
v => bail!(
- source,
self.callee.span(),
"expected collection, found {}",
v.type_name(),
diff --git a/src/library/elements.rs b/src/library/elements.rs
index 98165204..f0e36994 100644
--- a/src/library/elements.rs
+++ b/src/library/elements.rs
@@ -17,7 +17,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
let full = ctx.make_path(&path.v);
let id = ctx.images.load(&full).map_err(|err| {
- Error::boxed(args.source, path.span, match err.kind() {
+ Error::boxed(path.span, match err.kind() {
io::ErrorKind::NotFound => "file not found".into(),
_ => format!("failed to load image ({})", err),
})
diff --git a/src/library/layout.rs b/src/library/layout.rs
index 0d778206..53e3a450 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -6,7 +6,7 @@ use crate::paper::{Paper, PaperClass};
pub fn page(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
let paper = match args.eat::<Spanned<EcoString>>() {
Some(name) => match Paper::from_name(&name.v) {
- None => bail!(args.source, name.span, "invalid paper name"),
+ None => bail!(name.span, "invalid paper name"),
paper => paper,
},
None => None,
diff --git a/src/library/text.rs b/src/library/text.rs
index cd97691c..55cabd15 100644
--- a/src/library/text.rs
+++ b/src/library/text.rs
@@ -132,7 +132,7 @@ pub fn lang(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
if dir.v.axis() == SpecAxis::Horizontal {
Some(dir.v)
} else {
- bail!(args.source, dir.span, "must be horizontal");
+ bail!(dir.span, "must be horizontal");
}
} else {
iso.as_deref().map(lang_dir)
diff --git a/src/library/utility.rs b/src/library/utility.rs
index 22bde3a1..20d10830 100644
--- a/src/library/utility.rs
+++ b/src/library/utility.rs
@@ -25,7 +25,7 @@ pub fn len(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
Value::Str(v) => Value::Int(v.len() as i64),
Value::Array(v) => Value::Int(v.len()),
Value::Dict(v) => Value::Int(v.len()),
- _ => bail!(args.source, span, "expected string, array or dictionary"),
+ _ => bail!(span, "expected string, array or dictionary"),
})
}
@@ -35,7 +35,7 @@ pub fn rgb(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
if let Some(string) = args.eat::<Spanned<EcoString>>() {
match RgbaColor::from_str(&string.v) {
Ok(color) => color,
- Err(_) => bail!(args.source, string.span, "invalid color"),
+ Err(_) => bail!(string.span, "invalid color"),
}
} else {
let r = args.expect("red component")?;
@@ -60,7 +60,7 @@ pub fn max(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
/// Find the minimum or maximum of a sequence of values.
fn minmax(args: &mut FuncArgs, goal: Ordering) -> TypResult<Value> {
- let &mut FuncArgs { source, span, .. } = args;
+ let span = args.span;
let mut extremum = args.expect::<Value>("value")?;
for value in args.all::<Value>() {
@@ -71,7 +71,6 @@ fn minmax(args: &mut FuncArgs, goal: Ordering) -> TypResult<Value> {
}
}
None => bail!(
- source,
span,
"cannot compare {} with {}",
extremum.type_name(),
diff --git a/src/main.rs b/src/main.rs
index f3a97d51..9fa89e42 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -113,15 +113,15 @@ fn print_diagnostics(
for error in errors {
// The main diagnostic.
- let main = Diagnostic::error()
- .with_message(error.message)
- .with_labels(vec![Label::primary(error.source, error.span.to_range())]);
+ let main = Diagnostic::error().with_message(error.message).with_labels(vec![
+ Label::primary(error.span.source, error.span.to_range()),
+ ]);
term::emit(&mut writer, &config, sources, &main)?;
// Stacktrace-like helper diagnostics.
- for (file, span, point) in error.trace {
- let message = match point {
+ for point in error.trace {
+ let message = match point.v {
Tracepoint::Call(Some(name)) => {
format!("error occured in this call of function `{}`", name)
}
@@ -129,9 +129,9 @@ fn print_diagnostics(
Tracepoint::Import => "error occured while importing this module".into(),
};
- let help = Diagnostic::help()
- .with_message(message)
- .with_labels(vec![Label::primary(file, span.to_range())]);
+ let help = Diagnostic::help().with_message(message).with_labels(vec![
+ Label::primary(point.span.source, point.span.to_range()),
+ ]);
term::emit(&mut writer, &config, sources, &help)?;
}
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 9678b129..03026df4 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -549,7 +549,7 @@ fn call(p: &mut Parser, callee: Expr) -> Option<Expr> {
let mut args = match p.peek_direct() {
Some(Token::LeftParen) => args(p),
Some(Token::LeftBracket) => CallArgs {
- span: Span::at(callee.span().end),
+ span: Span::at(p.id(), callee.span().end),
items: vec![],
},
_ => {
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index 4f2e59c6..83e77a6a 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -1,13 +1,14 @@
use std::fmt::{self, Debug, Formatter};
+use std::ops::Range;
use super::{TokenMode, Tokens};
use crate::diag::Error;
-use crate::source::SourceFile;
-use crate::syntax::{Pos, Span, Token};
+use crate::source::{SourceFile, SourceId};
+use crate::syntax::{IntoSpan, Pos, Span, Token};
/// A convenient token-based parser.
pub struct Parser<'s> {
- /// The id of the parsed file.
+ /// The parsed file.
source: &'s SourceFile,
/// Parsing errors.
errors: Vec<Error>,
@@ -20,18 +21,18 @@ pub struct Parser<'s> {
/// The peeked token.
/// (Same as `next` except if we are at the end of group, then `None`).
peeked: Option<Token<'s>>,
- /// The end position of the last (non-whitespace if in code mode) token.
- prev_end: Pos,
- /// The start position of the peeked token.
- next_start: Pos,
+ /// The end index of the last (non-whitespace if in code mode) token.
+ prev_end: usize,
+ /// The start index of the peeked token.
+ next_start: usize,
}
/// A logical group of tokens, e.g. `[...]`.
#[derive(Debug, Copy, Clone)]
struct GroupEntry {
- /// The start position of the group. Used by `Parser::end_group` to return
- /// The group's full span.
- pub start: Pos,
+ /// The start index of the group. Used by `Parser::end_group` to return the
+ /// group's full span.
+ pub start: usize,
/// The kind of group this is. This decides which tokens will end the group.
/// For example, a [`Group::Paren`] will be ended by
/// [`Token::RightParen`].
@@ -69,8 +70,8 @@ impl<'s> Parser<'s> {
groups: vec![],
next,
peeked: next,
- prev_end: Pos::ZERO,
- next_start: Pos::ZERO,
+ prev_end: 0,
+ next_start: 0,
}
}
@@ -79,6 +80,11 @@ impl<'s> Parser<'s> {
self.errors
}
+ /// The id of the parsed source file.
+ pub fn id(&self) -> SourceId {
+ self.source.id()
+ }
+
/// Whether the end of the source string or group is reached.
pub fn eof(&self) -> bool {
self.peek().is_none()
@@ -95,7 +101,7 @@ impl<'s> Parser<'s> {
pub fn eat_span(&mut self) -> Span {
let start = self.next_start();
self.eat();
- Span::new(start, self.prev_end())
+ Span::new(self.id(), start, self.prev_end())
}
/// Consume the next token if it is the given one.
@@ -166,12 +172,12 @@ impl<'s> Parser<'s> {
///
/// Has length zero if `peek()` returns `None`.
pub fn peek_span(&self) -> Span {
- Span::new(self.next_start(), self.next_end())
+ Span::new(self.id(), self.next_start(), self.next_end())
}
/// Peek at the source of the next token.
pub fn peek_src(&self) -> &'s str {
- self.get(self.peek_span())
+ self.get(self.next_start() .. self.next_end())
}
/// Checks whether the next token fulfills a condition.
@@ -184,39 +190,39 @@ impl<'s> Parser<'s> {
self.peek().map_or(false, f)
}
- /// The byte position at which the last token ended.
+ /// The byte index at which the last token ended.
///
/// Refers to the end of the last _non-whitespace_ token in code mode.
- pub fn prev_end(&self) -> Pos {
- self.prev_end.into()
+ pub fn prev_end(&self) -> usize {
+ self.prev_end
}
- /// The byte position at which the next token starts.
- pub fn next_start(&self) -> Pos {
- self.next_start.into()
+ /// The byte index at which the next token starts.
+ pub fn next_start(&self) -> usize {
+ self.next_start
}
- /// The byte position at which the next token will end.
+ /// The byte index at which the next token will end.
///
/// Is the same as [`next_start()`][Self::next_start] if `peek()` returns
/// `None`.
- pub fn next_end(&self) -> Pos {
- self.tokens.index().into()
+ pub fn next_end(&self) -> usize {
+ self.tokens.index()
}
- /// The span from `start` to [`self.prev_end()`](Self::prev_end).
- pub fn span_from(&self, start: Pos) -> Span {
- Span::new(start, self.prev_end())
+ /// Determine the column index for the given byte index.
+ pub fn column(&self, index: usize) -> usize {
+ self.source.byte_to_column(index).unwrap()
}
- /// Determine the column index for the given byte position.
- pub fn column(&self, pos: Pos) -> usize {
- self.source.pos_to_column(pos).unwrap()
+ /// Slice out part of the source string.
+ pub fn get(&self, range: Range<usize>) -> &'s str {
+ self.source.get(range).unwrap()
}
- /// Slice out part of the source string.
- pub fn get(&self, span: impl Into<Span>) -> &'s str {
- self.tokens.scanner().get(span.into().to_range())
+ /// The span from `start` to [`self.prev_end()`](Self::prev_end).
+ pub fn span_from(&self, start: impl Into<Pos>) -> Span {
+ Span::new(self.id(), start, self.prev_end())
}
/// Continue parsing in a group.
@@ -271,17 +277,20 @@ impl<'s> Parser<'s> {
self.bump();
rescan = false;
} else if required {
- self.error(self.next_start(), format!("expected {}", end.name()));
+ self.error(
+ self.next_start() .. self.next_start(),
+ format!("expected {}", end.name()),
+ );
}
}
// Rescan the peeked token if the mode changed.
if rescan {
- self.tokens.jump(self.prev_end().to_usize());
+ self.tokens.jump(self.prev_end());
self.bump();
}
- Span::new(group.start, self.prev_end())
+ Span::new(self.id(), group.start, self.prev_end())
}
/// The tokenization mode outside of the current group.
@@ -296,8 +305,13 @@ impl<'s> Parser<'s> {
}
/// Add an error with location and message.
- pub fn error(&mut self, span: impl Into<Span>, message: impl Into<String>) {
- self.errors.push(Error::new(self.source.id(), span, message));
+ pub fn error(&mut self, span: impl IntoSpan, message: impl Into<String>) {
+ self.errors.push(Error::new(span.into_span(self.id()), message));
+ }
+
+ /// Add an error that `what` was expected at the given span.
+ pub fn expected_at(&mut self, span: impl IntoSpan, what: &str) {
+ self.error(span, format!("expected {}", what));
}
/// Eat the next token and add an error that it is not the expected `thing`.
@@ -314,11 +328,6 @@ impl<'s> Parser<'s> {
}
}
- /// Add an error that `what` was expected at the given position.
- pub fn expected_at(&mut self, pos: Pos, what: &str) {
- self.error(pos, format!("expected {}", what));
- }
-
/// Eat the next token and add an error that it is unexpected.
pub fn unexpected(&mut self) {
let before = self.next_start();
@@ -386,7 +395,7 @@ impl<'s> Parser<'s> {
impl Debug for Parser<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut s = self.tokens.scanner();
- s.jump(self.next_start().to_usize());
+ s.jump(self.next_start());
write!(f, "Parser({}|{})", s.eaten(), s.rest())
}
}
diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs
index 7bd160f9..7ceac128 100644
--- a/src/parse/resolve.rs
+++ b/src/parse/resolve.rs
@@ -52,8 +52,12 @@ pub fn resolve_raw(span: Span, text: &str, backticks: usize) -> RawNode {
if backticks > 1 {
let (tag, inner) = split_at_lang_tag(text);
let (text, block) = trim_and_split_raw(inner);
- let lang = Ident::new(tag, span.start .. span.start + tag.len());
- RawNode { span, lang, text: text.into(), block }
+ RawNode {
+ span,
+ lang: Ident::new(tag, span.with_end(span.start + tag.len())),
+ text: text.into(),
+ block,
+ }
} else {
RawNode {
span,
@@ -176,7 +180,7 @@ mod tests {
text: &str,
block: bool,
) {
- let node = resolve_raw(Span::ZERO, raw, backticks);
+ let node = resolve_raw(Span::detached(), raw, backticks);
assert_eq!(node.lang.as_deref(), lang);
assert_eq!(node.text, text);
assert_eq!(node.block, block);
diff --git a/src/source.rs b/src/source.rs
index 0a5a2860..0f1637a9 100644
--- a/src/source.rs
+++ b/src/source.rs
@@ -2,6 +2,7 @@
use std::collections::HashMap;
use std::io;
+use std::ops::Range;
use std::path::{Path, PathBuf};
use std::rc::Rc;
@@ -11,7 +12,6 @@ use serde::{Deserialize, Serialize};
use crate::loading::{FileHash, Loader};
use crate::parse::{is_newline, Scanner};
-use crate::syntax::{Pos, Span};
use crate::util::PathExt;
/// A unique identifier for a loaded source file.
@@ -119,13 +119,13 @@ pub struct SourceFile {
id: SourceId,
path: PathBuf,
src: String,
- line_starts: Vec<Pos>,
+ line_starts: Vec<usize>,
}
impl SourceFile {
/// Create a new source file.
pub fn new(id: SourceId, path: &Path, src: String) -> Self {
- let mut line_starts = vec![Pos::ZERO];
+ let mut line_starts = vec![0];
let mut s = Scanner::new(&src);
while let Some(c) = s.eat() {
@@ -133,7 +133,7 @@ impl SourceFile {
if c == '\r' {
s.eat_if('\n');
}
- line_starts.push(s.index().into());
+ line_starts.push(s.index());
}
}
@@ -166,8 +166,8 @@ impl SourceFile {
}
/// Slice out the part of the source code enclosed by the span.
- pub fn get(&self, span: impl Into<Span>) -> Option<&str> {
- self.src.get(span.into().to_range())
+ pub fn get(&self, range: Range<usize>) -> Option<&str> {
+ self.src.get(range)
}
/// Get the length of the file in bytes.
@@ -180,10 +180,10 @@ impl SourceFile {
self.line_starts.len()
}
- /// Return the index of the line that contains the given byte position.
- pub fn pos_to_line(&self, byte_pos: Pos) -> Option<usize> {
- (byte_pos.to_usize() <= self.src.len()).then(|| {
- match self.line_starts.binary_search(&byte_pos) {
+ /// Return the index of the line that contains the given byte index.
+ pub fn byte_to_line(&self, byte_idx: usize) -> Option<usize> {
+ (byte_idx <= self.src.len()).then(|| {
+ match self.line_starts.binary_search(&byte_idx) {
Ok(i) => i,
Err(i) => i - 1,
}
@@ -193,38 +193,42 @@ impl SourceFile {
/// Return the index of the column at the byte index.
///
/// The column is defined as the number of characters in the line before the
- /// byte position.
- pub fn pos_to_column(&self, byte_pos: Pos) -> Option<usize> {
- let line = self.pos_to_line(byte_pos)?;
- let start = self.line_to_pos(line)?;
- let head = self.get(Span::new(start, byte_pos))?;
+ /// byte index.
+ pub fn byte_to_column(&self, byte_idx: usize) -> Option<usize> {
+ let line = self.byte_to_line(byte_idx)?;
+ let start = self.line_to_byte(line)?;
+ let head = self.get(start .. byte_idx)?;
Some(head.chars().count())
}
/// Return the byte position at which the given line starts.
- pub fn line_to_pos(&self, line_idx: usize) -> Option<Pos> {
+ pub fn line_to_byte(&self, line_idx: usize) -> Option<usize> {
self.line_starts.get(line_idx).copied()
}
- /// Return the span which encloses the given line.
- pub fn line_to_span(&self, line_idx: usize) -> Option<Span> {
- let start = self.line_to_pos(line_idx)?;
- let end = self.line_to_pos(line_idx + 1).unwrap_or(self.src.len().into());
- Some(Span::new(start, end))
+ /// Return the range which encloses the given line.
+ pub fn line_to_range(&self, line_idx: usize) -> Option<Range<usize>> {
+ let start = self.line_to_byte(line_idx)?;
+ let end = self.line_to_byte(line_idx + 1).unwrap_or(self.src.len());
+ Some(start .. end)
}
- /// Return the byte position of the given (line, column) pair.
+ /// Return the byte index of the given (line, column) pair.
///
/// The column defines the number of characters to go beyond the start of
/// the line.
- pub fn line_column_to_pos(&self, line_idx: usize, column_idx: usize) -> Option<Pos> {
- let span = self.line_to_span(line_idx)?;
- let line = self.get(span)?;
+ pub fn line_column_to_byte(
+ &self,
+ line_idx: usize,
+ column_idx: usize,
+ ) -> Option<usize> {
+ let range = self.line_to_range(line_idx)?;
+ let line = self.get(range.clone())?;
let mut chars = line.chars();
for _ in 0 .. column_idx {
chars.next();
}
- Some(span.start + (line.len() - chars.as_str().len()))
+ Some(range.start + (line.len() - chars.as_str().len()))
}
}
@@ -251,7 +255,7 @@ impl<'a> Files<'a> for SourceStore {
fn line_index(&'a self, id: SourceId, given: usize) -> Result<usize, files::Error> {
let source = self.get(id);
source
- .pos_to_line(given.into())
+ .byte_to_line(given)
.ok_or_else(|| files::Error::IndexTooLarge { given, max: source.len_bytes() })
}
@@ -262,8 +266,7 @@ impl<'a> Files<'a> for SourceStore {
) -> Result<std::ops::Range<usize>, files::Error> {
let source = self.get(id);
source
- .line_to_span(given)
- .map(Span::to_range)
+ .line_to_range(given)
.ok_or_else(|| files::Error::LineTooLarge { given, max: source.len_lines() })
}
@@ -274,7 +277,7 @@ impl<'a> Files<'a> for SourceStore {
given: usize,
) -> Result<usize, files::Error> {
let source = self.get(id);
- source.pos_to_column(given.into()).ok_or_else(|| {
+ source.byte_to_column(given).ok_or_else(|| {
let max = source.len_bytes();
if given <= max {
files::Error::InvalidCharBoundary { given }
@@ -294,47 +297,47 @@ mod tests {
#[test]
fn test_source_file_new() {
let source = SourceFile::detached(TEST);
- assert_eq!(source.line_starts, vec![Pos(0), Pos(7), Pos(15), Pos(18)]);
+ assert_eq!(source.line_starts, vec![0, 7, 15, 18]);
}
#[test]
fn test_source_file_pos_to_line() {
let source = SourceFile::detached(TEST);
- assert_eq!(source.pos_to_line(Pos(0)), Some(0));
- assert_eq!(source.pos_to_line(Pos(2)), Some(0));
- assert_eq!(source.pos_to_line(Pos(6)), Some(0));
- assert_eq!(source.pos_to_line(Pos(7)), Some(1));
- assert_eq!(source.pos_to_line(Pos(8)), Some(1));
- assert_eq!(source.pos_to_line(Pos(12)), Some(1));
- assert_eq!(source.pos_to_line(Pos(21)), Some(3));
- assert_eq!(source.pos_to_line(Pos(22)), None);
+ assert_eq!(source.byte_to_line(0), Some(0));
+ assert_eq!(source.byte_to_line(2), Some(0));
+ assert_eq!(source.byte_to_line(6), Some(0));
+ assert_eq!(source.byte_to_line(7), Some(1));
+ assert_eq!(source.byte_to_line(8), Some(1));
+ assert_eq!(source.byte_to_line(12), Some(1));
+ assert_eq!(source.byte_to_line(21), Some(3));
+ assert_eq!(source.byte_to_line(22), None);
}
#[test]
fn test_source_file_pos_to_column() {
let source = SourceFile::detached(TEST);
- assert_eq!(source.pos_to_column(Pos(0)), Some(0));
- assert_eq!(source.pos_to_column(Pos(2)), Some(1));
- assert_eq!(source.pos_to_column(Pos(6)), Some(5));
- assert_eq!(source.pos_to_column(Pos(7)), Some(0));
- assert_eq!(source.pos_to_column(Pos(8)), Some(1));
- assert_eq!(source.pos_to_column(Pos(12)), Some(2));
+ assert_eq!(source.byte_to_column(0), Some(0));
+ assert_eq!(source.byte_to_column(2), Some(1));
+ assert_eq!(source.byte_to_column(6), Some(5));
+ assert_eq!(source.byte_to_column(7), Some(0));
+ assert_eq!(source.byte_to_column(8), Some(1));
+ assert_eq!(source.byte_to_column(12), Some(2));
}
#[test]
fn test_source_file_roundtrip() {
#[track_caller]
- fn roundtrip(source: &SourceFile, byte_pos: Pos) {
- let line = source.pos_to_line(byte_pos).unwrap();
- let column = source.pos_to_column(byte_pos).unwrap();
- let result = source.line_column_to_pos(line, column).unwrap();
- assert_eq!(result, byte_pos);
+ fn roundtrip(source: &SourceFile, byte_idx: usize) {
+ let line = source.byte_to_line(byte_idx).unwrap();
+ let column = source.byte_to_column(byte_idx).unwrap();
+ let result = source.line_column_to_byte(line, column).unwrap();
+ assert_eq!(result, byte_idx);
}
let source = SourceFile::detached(TEST);
- roundtrip(&source, Pos(0));
- roundtrip(&source, Pos(7));
- roundtrip(&source, Pos(12));
- roundtrip(&source, Pos(21));
+ roundtrip(&source, 0);
+ roundtrip(&source, 7);
+ roundtrip(&source, 12);
+ roundtrip(&source, 21);
}
}
diff --git a/src/syntax/span.rs b/src/syntax/span.rs
index 800cca19..cad9c5d5 100644
--- a/src/syntax/span.rs
+++ b/src/syntax/span.rs
@@ -3,8 +3,10 @@ use std::ops::{Add, Range};
use serde::{Deserialize, Serialize};
+use crate::source::SourceId;
+
/// A value with the span it corresponds to in the source code.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Serialize, Deserialize)]
pub struct Spanned<T> {
/// The spanned value.
@@ -19,11 +21,6 @@ impl<T> Spanned<T> {
Self { v, span: span.into() }
}
- /// Create a new instance from a value with the zero span.
- pub fn zero(v: T) -> Self {
- Self { v, span: Span::ZERO }
- }
-
/// Convert from `&Spanned<T>` to `Spanned<&T>`
pub fn as_ref(&self) -> Spanned<&T> {
Spanned { v: &self.v, span: self.span }
@@ -51,9 +48,11 @@ impl<T: Debug> Debug for Spanned<T> {
}
/// Bounds of a slice of source code.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Serialize, Deserialize)]
pub struct Span {
+ /// The id of the source file.
+ pub source: SourceId,
/// The inclusive start position.
pub start: Pos,
/// The inclusive end position.
@@ -61,22 +60,46 @@ pub struct Span {
}
impl Span {
- /// The zero span.
- pub const ZERO: Self = Self { start: Pos::ZERO, end: Pos::ZERO };
-
/// Create a new span from start and end positions.
- pub fn new(start: impl Into<Pos>, end: impl Into<Pos>) -> Self {
- Self { start: start.into(), end: end.into() }
+ pub fn new(source: SourceId, start: impl Into<Pos>, end: impl Into<Pos>) -> Self {
+ Self {
+ source,
+ start: start.into(),
+ end: end.into(),
+ }
}
/// Create a span including just a single position.
- pub fn at(pos: impl Into<Pos> + Copy) -> Self {
- Self::new(pos, pos)
+ pub fn at(source: SourceId, pos: impl Into<Pos> + Copy) -> Self {
+ Self::new(source, pos, pos)
+ }
+
+ /// Create a span without real location information, usually for testing.
+ pub fn detached() -> Self {
+ Self {
+ source: SourceId::from_raw(0),
+ start: Pos::ZERO,
+ end: Pos::ZERO,
+ }
+ }
+
+ /// Create a span with a different start position.
+ pub fn with_start(self, start: impl Into<Pos>) -> Self {
+ Self { start: start.into(), ..self }
+ }
+
+ /// Create a span with a different end position.
+ pub fn with_end(self, end: impl Into<Pos>) -> Self {
+ Self { end: end.into(), ..self }
}
/// Create a new span with the earlier start and later end position.
+ ///
+ /// This panics if the spans come from different files.
pub fn join(self, other: Self) -> Self {
+ debug_assert_eq!(self.source, other.source);
Self {
+ source: self.source,
start: self.start.min(other.start),
end: self.end.max(other.end),
}
@@ -89,33 +112,15 @@ impl Span {
/// Test whether one span complete contains the other span.
pub fn contains(self, other: Self) -> bool {
- self.start <= other.start && self.end >= other.end
+ self.source == other.source && self.start <= other.start && self.end >= other.end
}
- /// Convert to a `Range<usize>` for indexing.
+ /// Convert to a `Range<Pos>` for indexing.
pub fn to_range(self) -> Range<usize> {
self.start.to_usize() .. self.end.to_usize()
}
}
-impl<T> From<T> for Span
-where
- T: Into<Pos> + Copy,
-{
- fn from(pos: T) -> Self {
- Self::at(pos)
- }
-}
-
-impl<T> From<Range<T>> for Span
-where
- T: Into<Pos>,
-{
- fn from(range: Range<T>) -> Self {
- Self::new(range.start, range.end)
- }
-}
-
impl Debug for Span {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{:?}-{:?}", self.start, self.end)
@@ -165,3 +170,34 @@ where
Pos(self.0 + rhs.into().0)
}
}
+
+/// Convert a position or range into a span.
+pub trait IntoSpan {
+ /// Convert into a span by providing the source id.
+ fn into_span(self, source: SourceId) -> Span;
+}
+
+impl IntoSpan for Span {
+ fn into_span(self, source: SourceId) -> Span {
+ debug_assert_eq!(self.source, source);
+ self
+ }
+}
+
+impl IntoSpan for Pos {
+ fn into_span(self, source: SourceId) -> Span {
+ Span::new(source, self, self)
+ }
+}
+
+impl IntoSpan for usize {
+ fn into_span(self, source: SourceId) -> Span {
+ Span::new(source, self, self)
+ }
+}
+
+impl IntoSpan for Range<usize> {
+ fn into_span(self, source: SourceId) -> Span {
+ Span::new(source, self.start, self.end)
+ }
+}