summaryrefslogtreecommitdiff
path: root/src/syntax/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/syntax/expr.rs')
-rw-r--r--src/syntax/expr.rs397
1 files changed, 265 insertions, 132 deletions
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs
index 3160e0e4..f431ba8d 100644
--- a/src/syntax/expr.rs
+++ b/src/syntax/expr.rs
@@ -1,3 +1,5 @@
+use std::rc::Rc;
+
use super::*;
use crate::color::RgbaColor;
use crate::geom::{AngularUnit, LengthUnit};
@@ -5,29 +7,10 @@ use crate::geom::{AngularUnit, LengthUnit};
/// An expression.
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
- /// The none literal: `none`.
- None,
- /// A identifier literal: `left`.
+ /// A literal.
+ Lit(Lit),
+ /// An identifier: `left`.
Ident(Ident),
- /// A boolean literal: `true`, `false`.
- Bool(bool),
- /// An integer literal: `120`.
- Int(i64),
- /// A floating-point literal: `1.2`, `10e-4`.
- Float(f64),
- /// A length literal: `12pt`, `3cm`.
- Length(f64, LengthUnit),
- /// An angle literal: `1.5rad`, `90deg`.
- Angle(f64, AngularUnit),
- /// A percent literal: `50%`.
- ///
- /// _Note_: `50%` is stored as `50.0` here, but as `0.5` in the
- /// corresponding [value](crate::geom::Relative).
- Percent(f64),
- /// A color literal: `#ffccee`.
- Color(RgbaColor),
- /// A string literal: `"hello!"`.
- Str(String),
/// An array expression: `(1, "hi", 12cm)`.
Array(ExprArray),
/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
@@ -52,11 +35,92 @@ pub enum Expr {
For(ExprFor),
}
+impl Expr {
+ /// The source code location.
+ pub fn span(&self) -> Span {
+ match self {
+ Self::Lit(v) => v.span,
+ Self::Ident(v) => v.span,
+ Self::Array(v) => v.span,
+ Self::Dict(v) => v.span,
+ Self::Template(v) => v.span,
+ Self::Group(v) => v.span,
+ Self::Block(v) => v.span,
+ Self::Unary(v) => v.span,
+ Self::Binary(v) => v.span,
+ Self::Call(v) => v.span,
+ Self::Let(v) => v.span,
+ Self::If(v) => v.span,
+ Self::For(v) => v.span,
+ }
+ }
+}
+
impl Pretty for Expr {
fn pretty(&self, p: &mut Printer) {
match self {
- Self::None => p.push_str("none"),
+ Self::Lit(v) => v.pretty(p),
Self::Ident(v) => v.pretty(p),
+ Self::Array(v) => v.pretty(p),
+ Self::Dict(v) => v.pretty(p),
+ Self::Template(v) => v.pretty(p),
+ Self::Group(v) => v.pretty(p),
+ Self::Block(v) => v.pretty(p),
+ Self::Unary(v) => v.pretty(p),
+ Self::Binary(v) => v.pretty(p),
+ Self::Call(v) => v.pretty(p),
+ Self::Let(v) => v.pretty(p),
+ Self::If(v) => v.pretty(p),
+ Self::For(v) => v.pretty(p),
+ }
+ }
+}
+
+/// A literal.
+#[derive(Debug, Clone, PartialEq)]
+pub struct Lit {
+ /// The source code location.
+ pub span: Span,
+ /// The kind of literal.
+ pub kind: LitKind,
+}
+
+impl Pretty for Lit {
+ fn pretty(&self, p: &mut Printer) {
+ self.kind.pretty(p);
+ }
+}
+
+/// A kind of literal.
+#[derive(Debug, Clone, PartialEq)]
+pub enum LitKind {
+ /// The none literal: `none`.
+ None,
+ /// A boolean literal: `true`, `false`.
+ Bool(bool),
+ /// An integer literal: `120`.
+ Int(i64),
+ /// A floating-point literal: `1.2`, `10e-4`.
+ Float(f64),
+ /// A length literal: `12pt`, `3cm`.
+ Length(f64, LengthUnit),
+ /// An angle literal: `1.5rad`, `90deg`.
+ Angle(f64, AngularUnit),
+ /// A percent literal: `50%`.
+ ///
+ /// _Note_: `50%` is stored as `50.0` here, but as `0.5` in the
+ /// corresponding [value](crate::geom::Relative).
+ Percent(f64),
+ /// A color literal: `#ffccee`.
+ Color(RgbaColor),
+ /// A string literal: `"hello!"`.
+ Str(String),
+}
+
+impl Pretty for LitKind {
+ fn pretty(&self, p: &mut Printer) {
+ match self {
+ Self::None => p.push_str("none"),
Self::Bool(v) => v.pretty(p),
Self::Int(v) => v.pretty(p),
Self::Float(v) => v.pretty(p),
@@ -71,33 +135,24 @@ impl Pretty for Expr {
}
Self::Color(v) => v.pretty(p),
Self::Str(v) => v.pretty(p),
- Self::Array(v) => v.pretty(p),
- Self::Dict(v) => v.pretty(p),
- Self::Template(v) => pretty_template(v, p),
- Self::Group(v) => {
- p.push('(');
- v.v.pretty(p);
- p.push(')');
- }
- Self::Block(v) => v.pretty(p),
- Self::Unary(v) => v.pretty(p),
- Self::Binary(v) => v.pretty(p),
- Self::Call(v) => v.pretty(p),
- Self::Let(v) => v.pretty(p),
- Self::If(v) => v.pretty(p),
- Self::For(v) => v.pretty(p),
}
}
}
/// An array expression: `(1, "hi", 12cm)`.
-pub type ExprArray = SpanVec<Expr>;
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExprArray {
+ /// The source code location.
+ pub span: Span,
+ /// The entries of the array.
+ pub items: Vec<Expr>,
+}
impl Pretty for ExprArray {
fn pretty(&self, p: &mut Printer) {
p.push('(');
- p.join(self, ", ", |item, p| item.v.pretty(p));
- if self.len() == 1 {
+ p.join(&self.items, ", ", |item, p| item.pretty(p));
+ if self.items.len() == 1 {
p.push(',');
}
p.push(')');
@@ -105,15 +160,21 @@ impl Pretty for ExprArray {
}
/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
-pub type ExprDict = Vec<Named>;
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExprDict {
+ /// The source code location.
+ pub span: Span,
+ /// The named dictionary entries.
+ pub items: Vec<Named>,
+}
impl Pretty for ExprDict {
fn pretty(&self, p: &mut Printer) {
p.push('(');
- if self.is_empty() {
+ if self.items.is_empty() {
p.push(':');
} else {
- p.join(self, ", ", |named, p| named.pretty(p));
+ p.join(&self.items, ", ", |named, p| named.pretty(p));
}
p.push(')');
}
@@ -123,43 +184,73 @@ impl Pretty for ExprDict {
#[derive(Debug, Clone, PartialEq)]
pub struct Named {
/// The name: `pattern`.
- pub name: Spanned<Ident>,
+ pub name: Ident,
/// The right-hand side of the pair: `dashed`.
- pub expr: Spanned<Expr>,
+ pub expr: Expr,
+}
+
+impl Named {
+ /// The source code location.
+ pub fn span(&self) -> Span {
+ self.name.span.join(self.expr.span())
+ }
}
impl Pretty for Named {
fn pretty(&self, p: &mut Printer) {
- self.name.v.pretty(p);
+ self.name.pretty(p);
p.push_str(": ");
- self.expr.v.pretty(p);
+ self.expr.pretty(p);
}
}
/// A template expression: `[*Hi* there!]`.
-pub type ExprTemplate = Tree;
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExprTemplate {
+ /// The source code location.
+ pub span: Span,
+ /// The contents of the template.
+ pub tree: Rc<Tree>,
+}
-/// Pretty print a template.
-pub fn pretty_template(template: &ExprTemplate, p: &mut Printer) {
- if let [Spanned { v: Node::Expr(Expr::Call(call)), .. }] = template.as_slice() {
- pretty_func_template(call, p, false)
- } else {
- p.push('[');
- template.pretty(p);
- p.push(']');
+impl Pretty for ExprTemplate {
+ fn pretty(&self, p: &mut Printer) {
+ if let [Node::Expr(Expr::Call(call))] = self.tree.as_slice() {
+ call.pretty_bracketed(p, false);
+ } else {
+ p.push('[');
+ self.tree.pretty(p);
+ p.push(']');
+ }
}
}
/// A grouped expression: `(1 + 2)`.
-pub type ExprGroup = SpanBox<Expr>;
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExprGroup {
+ /// The source code location.
+ pub span: Span,
+ /// The wrapped expression.
+ pub expr: Box<Expr>,
+}
+
+impl Pretty for ExprGroup {
+ fn pretty(&self, p: &mut Printer) {
+ p.push('(');
+ self.expr.pretty(p);
+ p.push(')');
+ }
+}
/// A block expression: `{ #let x = 1; x + 2 }`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprBlock {
+ /// The source code location.
+ pub span: Span,
/// The list of expressions contained in the block.
- pub exprs: SpanVec<Expr>,
+ pub exprs: Vec<Expr>,
/// Whether the block should create a scope.
- pub scopes: bool,
+ pub scoping: bool,
}
impl Pretty for ExprBlock {
@@ -168,7 +259,7 @@ impl Pretty for ExprBlock {
if self.exprs.len() > 1 {
p.push(' ');
}
- p.join(&self.exprs, "; ", |expr, p| expr.v.pretty(p));
+ p.join(&self.exprs, "; ", |expr, p| expr.pretty(p));
if self.exprs.len() > 1 {
p.push(' ');
}
@@ -179,19 +270,21 @@ impl Pretty for ExprBlock {
/// A unary operation: `-x`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprUnary {
+ /// The source code location.
+ pub span: Span,
/// The operator: `-`.
- pub op: Spanned<UnOp>,
+ pub op: UnOp,
/// The expression to operator on: `x`.
- pub expr: SpanBox<Expr>,
+ pub expr: Box<Expr>,
}
impl Pretty for ExprUnary {
fn pretty(&self, p: &mut Printer) {
- self.op.v.pretty(p);
- if self.op.v == UnOp::Not {
+ self.op.pretty(p);
+ if self.op == UnOp::Not {
p.push(' ');
}
- self.expr.v.pretty(p);
+ self.expr.pretty(p);
}
}
@@ -244,21 +337,23 @@ impl Pretty for UnOp {
/// A binary operation: `a + b`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprBinary {
+ /// The source code location.
+ pub span: Span,
/// The left-hand side of the operation: `a`.
- pub lhs: SpanBox<Expr>,
+ pub lhs: Box<Expr>,
/// The operator: `+`.
- pub op: Spanned<BinOp>,
+ pub op: BinOp,
/// The right-hand side of the operation: `b`.
- pub rhs: SpanBox<Expr>,
+ pub rhs: Box<Expr>,
}
impl Pretty for ExprBinary {
fn pretty(&self, p: &mut Printer) {
- self.lhs.v.pretty(p);
+ self.lhs.pretty(p);
p.push(' ');
- self.op.v.pretty(p);
+ self.op.pretty(p);
p.push(' ');
- self.rhs.v.pretty(p);
+ self.rhs.pretty(p);
}
}
@@ -407,71 +502,83 @@ pub enum Associativity {
/// An invocation of a function: `foo(...)`, `#[foo ...]`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprCall {
+ /// The source code location.
+ pub span: Span,
/// The callee of the function.
- pub callee: SpanBox<Expr>,
+ pub callee: Box<Expr>,
/// The arguments to the function.
- pub args: Spanned<ExprArgs>,
+ pub args: ExprArgs,
}
impl Pretty for ExprCall {
fn pretty(&self, p: &mut Printer) {
- self.callee.v.pretty(p);
+ self.callee.pretty(p);
p.push('(');
- self.args.v.pretty(p);
+ self.args.pretty(p);
p.push(')');
}
}
-/// Pretty print a function template, with body or chaining when possible.
-pub fn pretty_func_template(call: &ExprCall, p: &mut Printer, chained: bool) {
- if chained {
- p.push_str(" | ");
- } else {
- p.push_str("#[");
- }
+impl ExprCall {
+ /// Pretty print a function template, with body or chaining when possible.
+ pub fn pretty_bracketed(&self, p: &mut Printer, chained: bool) {
+ if chained {
+ p.push_str(" | ");
+ } else {
+ p.push_str("#[");
+ }
- // Function name.
- call.callee.v.pretty(p);
+ // Function name.
+ self.callee.pretty(p);
- // Find out whether this can be written with a body or as a chain.
- //
- // Example: Transforms "#[v [Hi]]" => "#[v][Hi]".
- if let [head @ .., Argument::Pos(Spanned { v: Expr::Template(template), .. })] =
- call.args.v.as_slice()
- {
- // Previous arguments.
- if !head.is_empty() {
- p.push(' ');
- p.join(head, ", ", |item, p| item.pretty(p));
- }
+ let mut write_args = |items: &[Argument]| {
+ if !items.is_empty() {
+ p.push(' ');
+ p.join(items, ", ", |item, p| item.pretty(p));
+ }
+ };
+
+ match self.args.items.as_slice() {
+ // This can written as a chain.
+ //
+ // Example: Transforms "#[v][[f]]" => "#[v | f]".
+ [head @ .., Argument::Pos(Expr::Call(call))] => {
+ write_args(head);
+ call.pretty_bracketed(p, true);
+ }
- // Find out whether this can written as a chain.
- //
- // Example: Transforms "#[v][[f]]" => "#[v | f]".
- if let [Spanned { v: Node::Expr(Expr::Call(call)), .. }] = template.as_slice() {
- return pretty_func_template(call, p, true);
- } else {
- p.push_str("][");
- template.pretty(p);
+ // This can be written with a body.
+ //
+ // Example: Transforms "#[v [Hi]]" => "#[v][Hi]".
+ [head @ .., Argument::Pos(Expr::Template(template))] => {
+ write_args(head);
+ p.push(']');
+ template.pretty(p);
+ }
+
+ items => {
+ write_args(items);
+ p.push(']');
+ }
}
- } else if !call.args.v.is_empty() {
- p.push(' ');
- call.args.v.pretty(p);
}
-
- // Either end of header or end of body.
- p.push(']');
}
/// The arguments to a function: `12, draw: false`.
///
/// In case of a bracketed invocation with a body, the body is _not_
/// included in the span for the sake of clearer error messages.
-pub type ExprArgs = Vec<Argument>;
+#[derive(Debug, Clone, PartialEq)]
+pub struct ExprArgs {
+ /// The source code location.
+ pub span: Span,
+ /// The positional and named arguments.
+ pub items: Vec<Argument>,
+}
-impl Pretty for Vec<Argument> {
+impl Pretty for ExprArgs {
fn pretty(&self, p: &mut Printer) {
- p.join(self, ", ", |item, p| item.pretty(p));
+ p.join(&self.items, ", ", |item, p| item.pretty(p));
}
}
@@ -479,15 +586,25 @@ impl Pretty for Vec<Argument> {
#[derive(Debug, Clone, PartialEq)]
pub enum Argument {
/// A positional arguments.
- Pos(Spanned<Expr>),
+ Pos(Expr),
/// A named argument.
Named(Named),
}
+impl Argument {
+ /// The source code location.
+ pub fn span(&self) -> Span {
+ match self {
+ Self::Pos(expr) => expr.span(),
+ Self::Named(named) => named.span(),
+ }
+ }
+}
+
impl Pretty for Argument {
fn pretty(&self, p: &mut Printer) {
match self {
- Self::Pos(expr) => expr.v.pretty(p),
+ Self::Pos(expr) => expr.pretty(p),
Self::Named(named) => named.pretty(p),
}
}
@@ -496,19 +613,21 @@ impl Pretty for Argument {
/// A let expression: `#let x = 1`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprLet {
- /// The pattern to assign to.
- pub pat: Spanned<Ident>,
+ /// The source code location.
+ pub span: Span,
+ /// The binding to assign to.
+ pub binding: Ident,
/// The expression the pattern is initialized with.
- pub init: Option<SpanBox<Expr>>,
+ pub init: Option<Box<Expr>>,
}
impl Pretty for ExprLet {
fn pretty(&self, p: &mut Printer) {
p.push_str("#let ");
- self.pat.v.pretty(p);
+ self.binding.pretty(p);
if let Some(init) = &self.init {
p.push_str(" = ");
- init.v.pretty(p);
+ init.pretty(p);
}
}
}
@@ -516,23 +635,25 @@ impl Pretty for ExprLet {
/// An if expression: `#if x { y } #else { z }`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprIf {
+ /// The source code location.
+ pub span: Span,
/// The condition which selects the body to evaluate.
- pub condition: SpanBox<Expr>,
+ pub condition: Box<Expr>,
/// The expression to evaluate if the condition is true.
- pub if_body: SpanBox<Expr>,
+ pub if_body: Box<Expr>,
/// The expression to evaluate if the condition is false.
- pub else_body: Option<SpanBox<Expr>>,
+ pub else_body: Option<Box<Expr>>,
}
impl Pretty for ExprIf {
fn pretty(&self, p: &mut Printer) {
p.push_str("#if ");
- self.condition.v.pretty(p);
+ self.condition.pretty(p);
p.push(' ');
- self.if_body.v.pretty(p);
+ self.if_body.pretty(p);
if let Some(expr) = &self.else_body {
p.push_str(" #else ");
- expr.v.pretty(p);
+ expr.pretty(p);
}
}
}
@@ -540,22 +661,24 @@ impl Pretty for ExprIf {
/// A for expression: `#for x #in y { z }`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprFor {
+ /// The source code location.
+ pub span: Span,
/// The pattern to assign to.
- pub pat: Spanned<ForPattern>,
+ pub pattern: ForPattern,
/// The expression to iterate over.
- pub iter: SpanBox<Expr>,
+ pub iter: Box<Expr>,
/// The expression to evaluate for each iteration.
- pub body: SpanBox<Expr>,
+ pub body: Box<Expr>,
}
impl Pretty for ExprFor {
fn pretty(&self, p: &mut Printer) {
p.push_str("#for ");
- self.pat.v.pretty(p);
+ self.pattern.pretty(p);
p.push_str(" #in ");
- self.iter.v.pretty(p);
+ self.iter.pretty(p);
p.push(' ');
- self.body.v.pretty(p);
+ self.body.pretty(p);
}
}
@@ -568,6 +691,16 @@ pub enum ForPattern {
KeyValue(Ident, Ident),
}
+impl ForPattern {
+ /// The source code location.
+ pub fn span(&self) -> Span {
+ match self {
+ Self::Value(v) => v.span,
+ Self::KeyValue(k, v) => k.span.join(v.span),
+ }
+ }
+}
+
impl Pretty for ForPattern {
fn pretty(&self, p: &mut Printer) {
match self {