mirror of
https://github.com/typst/typst
synced 2025-06-28 16:22:53 +08:00
Make compute functions possible 💻
Ships with the amazing new `rgb` function!
This commit is contained in:
parent
6d7e7d945b
commit
77dac270a8
@ -8,16 +8,14 @@ use super::value::FuncValue;
|
|||||||
/// A map from identifiers to functions.
|
/// A map from identifiers to functions.
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
functions: HashMap<String, FuncValue>,
|
functions: HashMap<String, FuncValue>,
|
||||||
fallback: FuncValue,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
// Create a new empty scope with a fallback function that is invoked when no
|
// Create a new empty scope with a fallback function that is invoked when no
|
||||||
// match is found.
|
// match is found.
|
||||||
pub fn new(fallback: FuncValue) -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
functions: HashMap::new(),
|
functions: HashMap::new(),
|
||||||
fallback,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,11 +28,6 @@ impl Scope {
|
|||||||
pub fn func(&self, name: &str) -> Option<&FuncValue> {
|
pub fn func(&self, name: &str) -> Option<&FuncValue> {
|
||||||
self.functions.get(name)
|
self.functions.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the fallback function.
|
|
||||||
pub fn fallback(&self) -> &FuncValue {
|
|
||||||
&self.fallback
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Scope {
|
impl Debug for Scope {
|
||||||
|
@ -115,6 +115,17 @@ impl<V> Table<V> {
|
|||||||
self.lowest_free += 1;
|
self.lowest_free += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterator over all borrowed keys and values.
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (BorrowedKey, &V)> {
|
||||||
|
self.nums().map(|(&k, v)| (BorrowedKey::Num(k), v))
|
||||||
|
.chain(self.strs().map(|(k, v)| (BorrowedKey::Str(k), v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over all values in the table.
|
||||||
|
pub fn values(&self) -> impl Iterator<Item = &V> {
|
||||||
|
self.nums().map(|(_, v)| v).chain(self.strs().map(|(_, v)| v))
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterate over the number key-value pairs.
|
/// Iterate over the number key-value pairs.
|
||||||
pub fn nums(&self) -> std::collections::btree_map::Iter<u64, V> {
|
pub fn nums(&self) -> std::collections::btree_map::Iter<u64, V> {
|
||||||
self.nums.iter()
|
self.nums.iter()
|
||||||
@ -125,9 +136,16 @@ impl<V> Table<V> {
|
|||||||
self.strs.iter()
|
self.strs.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all values in the table.
|
/// Move into an owned iterator over owned keys and values.
|
||||||
pub fn values(&self) -> impl Iterator<Item = &V> {
|
pub fn into_iter(self) -> impl Iterator<Item = (OwnedKey, V)> {
|
||||||
self.nums().map(|(_, v)| v).chain(self.strs().map(|(_, v)| v))
|
self.nums.into_iter().map(|(k, v)| (OwnedKey::Num(k), v))
|
||||||
|
.chain(self.strs.into_iter().map(|(k, v)| (OwnedKey::Str(k), v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move into an owned iterator over all values in the table.
|
||||||
|
pub fn into_values(self) -> impl Iterator<Item = V> {
|
||||||
|
self.nums.into_iter().map(|(_, v)| v)
|
||||||
|
.chain(self.strs.into_iter().map(|(_, v)| v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over the number key-value pairs.
|
/// Iterate over the number key-value pairs.
|
||||||
@ -139,12 +157,6 @@ impl<V> Table<V> {
|
|||||||
pub fn into_strs(self) -> std::collections::btree_map::IntoIter<String, V> {
|
pub fn into_strs(self) -> std::collections::btree_map::IntoIter<String, V> {
|
||||||
self.strs.into_iter()
|
self.strs.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move into an owned iterator over all values in the table.
|
|
||||||
pub fn into_values(self) -> impl Iterator<Item = V> {
|
|
||||||
self.nums.into_iter().map(|(_, v)| v)
|
|
||||||
.chain(self.strs.into_iter().map(|(_, v)| v))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K, V> Index<K> for Table<V>
|
impl<'a, K, V> Index<K> for Table<V>
|
||||||
@ -168,7 +180,7 @@ impl<V: Eq> Eq for Table<V> {}
|
|||||||
|
|
||||||
impl<V: PartialEq> PartialEq for Table<V> {
|
impl<V: PartialEq> PartialEq for Table<V> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.nums().eq(other.nums()) && self.strs().eq(other.strs())
|
self.iter().eq(other.iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +230,15 @@ pub enum OwnedKey {
|
|||||||
Str(String),
|
Str(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<BorrowedKey<'_>> for OwnedKey {
|
||||||
|
fn from(key: BorrowedKey<'_>) -> Self {
|
||||||
|
match key {
|
||||||
|
BorrowedKey::Num(num) => Self::Num(num),
|
||||||
|
BorrowedKey::Str(string) => Self::Str(string.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u64> for OwnedKey {
|
impl From<u64> for OwnedKey {
|
||||||
fn from(num: u64) -> Self {
|
fn from(num: u64) -> Self {
|
||||||
Self::Num(num)
|
Self::Num(num)
|
||||||
|
@ -7,11 +7,11 @@ use std::rc::Rc;
|
|||||||
use fontdock::{FontStyle, FontWeight, FontWidth};
|
use fontdock::{FontStyle, FontWeight, FontWidth};
|
||||||
|
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::layout::{Commands, Dir, LayoutContext, SpecAlign};
|
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
|
||||||
use crate::length::{Length, ScaleLength};
|
use crate::length::{Length, ScaleLength};
|
||||||
use crate::paper::Paper;
|
use crate::paper::Paper;
|
||||||
use crate::syntax::span::{Span, Spanned};
|
use crate::syntax::span::{Span, Spanned};
|
||||||
use crate::syntax::tree::SyntaxTree;
|
use crate::syntax::tree::{SyntaxTree, SyntaxNode};
|
||||||
use crate::syntax::Ident;
|
use crate::syntax::Ident;
|
||||||
use crate::{DynFuture, Feedback, Pass};
|
use crate::{DynFuture, Feedback, Pass};
|
||||||
use super::table::{SpannedEntry, Table};
|
use super::table::{SpannedEntry, Table};
|
||||||
@ -61,6 +61,47 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Spanned<Value> {
|
||||||
|
/// Transform this value into something layoutable.
|
||||||
|
///
|
||||||
|
/// If this is already a command-value, it is simply unwrapped, otherwise
|
||||||
|
/// the value is represented as layoutable content in a reasonable way.
|
||||||
|
pub fn into_commands(self) -> Commands {
|
||||||
|
match self.v {
|
||||||
|
Value::Commands(commands) => commands,
|
||||||
|
Value::Tree(tree) => vec![Command::LayoutSyntaxTree(tree)],
|
||||||
|
|
||||||
|
// Forward to each entry, separated with spaces.
|
||||||
|
Value::Table(table) => {
|
||||||
|
let mut commands = vec![];
|
||||||
|
let mut end = None;
|
||||||
|
for entry in table.into_values() {
|
||||||
|
if let Some(last_end) = end {
|
||||||
|
let span = Span::new(last_end, entry.key.start);
|
||||||
|
commands.push(Command::LayoutSyntaxTree(vec![
|
||||||
|
Spanned::new(SyntaxNode::Spacing, span)
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
end = Some(entry.val.span.end);
|
||||||
|
commands.extend(entry.val.into_commands());
|
||||||
|
}
|
||||||
|
commands
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format with debug.
|
||||||
|
val => vec![
|
||||||
|
Command::LayoutSyntaxTree(vec![
|
||||||
|
Spanned::new(
|
||||||
|
SyntaxNode::Text(format!("{:?}", val)),
|
||||||
|
self.span,
|
||||||
|
)
|
||||||
|
])
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Value {
|
impl Debug for Value {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
|
@ -45,7 +45,7 @@ pub struct BoxLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The context for layouting.
|
/// The context for layouting.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LayoutContext<'a> {
|
pub struct LayoutContext<'a> {
|
||||||
/// The font loader to query fonts from when typesetting text.
|
/// The font loader to query fonts from when typesetting text.
|
||||||
pub loader: &'a SharedFontLoader,
|
pub loader: &'a SharedFontLoader,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
//! Layouting of syntax trees.
|
//! Layouting of syntax trees.
|
||||||
|
|
||||||
use crate::compute::value::Value;
|
|
||||||
use crate::style::LayoutStyle;
|
use crate::style::LayoutStyle;
|
||||||
use crate::syntax::decoration::Decoration;
|
use crate::syntax::decoration::Decoration;
|
||||||
use crate::syntax::span::{Span, Spanned};
|
use crate::syntax::span::{Span, Spanned};
|
||||||
@ -62,12 +61,7 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match &node.v {
|
match &node.v {
|
||||||
SyntaxNode::Spacing => {
|
SyntaxNode::Spacing => self.layout_space(),
|
||||||
self.layouter.add_primary_spacing(
|
|
||||||
self.style.text.word_spacing(),
|
|
||||||
SpacingKind::WORD,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
SyntaxNode::Linebreak => self.layouter.finish_line(),
|
SyntaxNode::Linebreak => self.layouter.finish_line(),
|
||||||
|
|
||||||
SyntaxNode::ToggleItalic => {
|
SyntaxNode::ToggleItalic => {
|
||||||
@ -93,45 +87,11 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn layout_par(&mut self, par: &SyntaxTree) {
|
fn layout_space(&mut self) {
|
||||||
self.layouter.add_secondary_spacing(
|
self.layouter.add_primary_spacing(
|
||||||
self.style.text.paragraph_spacing(),
|
self.style.text.word_spacing(),
|
||||||
SpacingKind::PARAGRAPH,
|
SpacingKind::WORD,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.layout_tree(par).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn layout_call(&mut self, call: Spanned<&CallExpr>) {
|
|
||||||
let name = call.v.name.v.as_str();
|
|
||||||
let span = call.v.name.span;
|
|
||||||
|
|
||||||
let (func, deco) = if let Some(func) = self.ctx.scope.func(name) {
|
|
||||||
(func, Decoration::Resolved)
|
|
||||||
} else {
|
|
||||||
error!(@self.feedback, span, "unknown function");
|
|
||||||
(self.ctx.scope.fallback(), Decoration::Unresolved)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.feedback.decorations.push(Spanned::new(deco, span));
|
|
||||||
|
|
||||||
let args = call.v.args.eval();
|
|
||||||
let pass = func(span, args, LayoutContext {
|
|
||||||
style: &self.style,
|
|
||||||
spaces: self.layouter.remaining(),
|
|
||||||
root: true,
|
|
||||||
..self.ctx
|
|
||||||
}).await;
|
|
||||||
|
|
||||||
self.feedback.extend(pass.feedback);
|
|
||||||
|
|
||||||
if let Value::Commands(commands) = pass.output {
|
|
||||||
for command in commands {
|
|
||||||
self.execute_command(command, call.span).await;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.layout_raw(&[format!("{:?}", pass.output)]).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn layout_text(&mut self, text: &str) {
|
async fn layout_text(&mut self, text: &str) {
|
||||||
@ -154,24 +114,44 @@ impl<'a> TreeLayouter<'a> {
|
|||||||
self.style.text.fallback
|
self.style.text.fallback
|
||||||
.list_mut()
|
.list_mut()
|
||||||
.insert(0, "monospace".to_string());
|
.insert(0, "monospace".to_string());
|
||||||
|
|
||||||
self.style.text.fallback.flatten();
|
self.style.text.fallback.flatten();
|
||||||
|
|
||||||
// Layout the first line.
|
let mut first = true;
|
||||||
let mut iter = lines.iter();
|
for line in lines {
|
||||||
if let Some(line) = iter.next() {
|
if !first {
|
||||||
self.layout_text(line).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put a newline before each following line.
|
|
||||||
for line in iter {
|
|
||||||
self.layouter.finish_line();
|
self.layouter.finish_line();
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
self.layout_text(line).await;
|
self.layout_text(line).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.style.text.fallback = fallback;
|
self.style.text.fallback = fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn layout_par(&mut self, par: &SyntaxTree) {
|
||||||
|
self.layout_tree(par).await;
|
||||||
|
self.layouter.add_secondary_spacing(
|
||||||
|
self.style.text.paragraph_spacing(),
|
||||||
|
SpacingKind::PARAGRAPH,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn layout_call(&mut self, call: Spanned<&CallExpr>) {
|
||||||
|
let ctx = LayoutContext {
|
||||||
|
style: &self.style,
|
||||||
|
spaces: self.layouter.remaining(),
|
||||||
|
root: false,
|
||||||
|
..self.ctx
|
||||||
|
};
|
||||||
|
|
||||||
|
let val = call.v.eval(&ctx, &mut self.feedback).await;
|
||||||
|
let commands = Spanned::new(val, call.span).into_commands();
|
||||||
|
|
||||||
|
for command in commands {
|
||||||
|
self.execute_command(command, call.span).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn execute_command(&mut self, command: Command, span: Span) {
|
async fn execute_command(&mut self, command: Command, span: Span) {
|
||||||
use Command::*;
|
use Command::*;
|
||||||
|
|
||||||
|
26
src/library/color.rs
Normal file
26
src/library/color.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use crate::color::RgbaColor;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// `rgb`: Create an RGB(A) color.
|
||||||
|
pub async fn rgb(span: Span, mut args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
|
||||||
|
let mut f = Feedback::new();
|
||||||
|
|
||||||
|
let color = RgbaColor::new(
|
||||||
|
clamp(args.expect::<Spanned<f64>>("red value", span, &mut f), &mut f),
|
||||||
|
clamp(args.expect::<Spanned<f64>>("green value", span, &mut f), &mut f),
|
||||||
|
clamp(args.expect::<Spanned<f64>>("blue value", span, &mut f), &mut f),
|
||||||
|
clamp(args.take::<Spanned<f64>>(), &mut f),
|
||||||
|
);
|
||||||
|
|
||||||
|
args.unexpected(&mut f);
|
||||||
|
Pass::new(Value::Color(color), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp(component: Option<Spanned<f64>>, f: &mut Feedback) -> u8 {
|
||||||
|
component.map(|c| {
|
||||||
|
if c.v < 0.0 || c.v > 255.0 {
|
||||||
|
error!(@f, c.span, "should be between 0 and 255")
|
||||||
|
}
|
||||||
|
c.v.min(255.0).max(0.0).round() as u8
|
||||||
|
}).unwrap_or_default()
|
||||||
|
}
|
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
mod align;
|
mod align;
|
||||||
mod boxed;
|
mod boxed;
|
||||||
|
mod color;
|
||||||
mod font;
|
mod font;
|
||||||
mod page;
|
mod page;
|
||||||
mod spacing;
|
mod spacing;
|
||||||
|
|
||||||
pub use align::*;
|
pub use align::*;
|
||||||
pub use boxed::*;
|
pub use boxed::*;
|
||||||
|
pub use color::*;
|
||||||
pub use font::*;
|
pub use font::*;
|
||||||
pub use page::*;
|
pub use page::*;
|
||||||
pub use spacing::*;
|
pub use spacing::*;
|
||||||
@ -18,10 +20,10 @@ use crate::compute::scope::Scope;
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
macro_rules! std {
|
macro_rules! std {
|
||||||
(fallback: $fallback:expr $(, $name:literal => $func:expr)* $(,)?) => {
|
($($name:literal => $func:expr),* $(,)?) => {
|
||||||
/// Create a scope with all standard library functions.
|
/// Create a scope with all standard library functions.
|
||||||
pub fn _std() -> Scope {
|
pub fn _std() -> Scope {
|
||||||
let mut std = Scope::new(wrap!(val));
|
let mut std = Scope::new();
|
||||||
$(std.insert($name, wrap!($func));)*
|
$(std.insert($name, wrap!($func));)*
|
||||||
std
|
std
|
||||||
}
|
}
|
||||||
@ -35,32 +37,12 @@ macro_rules! wrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std! {
|
std! {
|
||||||
fallback: val,
|
|
||||||
"align" => align,
|
"align" => align,
|
||||||
"box" => boxed,
|
"box" => boxed,
|
||||||
"dump" => dump,
|
|
||||||
"font" => font,
|
"font" => font,
|
||||||
"h" => h,
|
"h" => h,
|
||||||
"page" => page,
|
"page" => page,
|
||||||
"pagebreak" => pagebreak,
|
"pagebreak" => pagebreak,
|
||||||
|
"rgb" => rgb,
|
||||||
"v" => v,
|
"v" => v,
|
||||||
"val" => val,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `val`: Layouts its body flatly, ignoring other arguments.
|
|
||||||
///
|
|
||||||
/// This is also the fallback function, which is used when a function name
|
|
||||||
/// cannot be resolved.
|
|
||||||
pub async fn val(_: Span, mut args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
|
|
||||||
let commands = match args.take::<SyntaxTree>() {
|
|
||||||
Some(tree) => vec![LayoutSyntaxTree(tree)],
|
|
||||||
None => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
Pass::commands(commands, Feedback::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `dump`: Dumps its arguments into the document.
|
|
||||||
pub async fn dump(_: Span, args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
|
|
||||||
Pass::okay(Value::Table(args))
|
|
||||||
}
|
}
|
||||||
|
@ -252,10 +252,9 @@ impl<'s> Iterator for Tokens<'s> {
|
|||||||
let text = self.read_string_until(|n| {
|
let text = self.read_string_until(|n| {
|
||||||
let val = match n {
|
let val = match n {
|
||||||
c if c.is_whitespace() => true,
|
c if c.is_whitespace() => true,
|
||||||
'[' | ']' | '/' | '*' => true,
|
'[' | ']' | '{' | '}' | '/' | '*' => true,
|
||||||
'\\' | '_' | '`' if body => true,
|
'\\' | '_' | '`' if body => true,
|
||||||
':' | '=' | ',' | '"' |
|
':' | '=' | ',' | '"' | '(' | ')' if !body => true,
|
||||||
'(' | ')' | '{' | '}' if !body => true,
|
|
||||||
'+' | '-' if !body && !last_was_e => true,
|
'+' | '-' if !body && !last_was_e => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,10 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::compute::table::{SpannedEntry, Table};
|
use crate::compute::table::{SpannedEntry, Table};
|
||||||
use crate::compute::value::{TableValue, Value};
|
use crate::compute::value::{TableValue, Value};
|
||||||
|
use crate::layout::LayoutContext;
|
||||||
use crate::length::Length;
|
use crate::length::Length;
|
||||||
|
use crate::{DynFuture, Feedback};
|
||||||
|
use super::decoration::Decoration;
|
||||||
use super::span::{Spanned, SpanVec};
|
use super::span::{Spanned, SpanVec};
|
||||||
use super::Ident;
|
use super::Ident;
|
||||||
|
|
||||||
@ -91,7 +94,11 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate the expression to a value.
|
/// Evaluate the expression to a value.
|
||||||
pub fn eval(&self) -> Value {
|
pub async fn eval(
|
||||||
|
&self,
|
||||||
|
ctx: &LayoutContext<'_>,
|
||||||
|
f: &mut Feedback,
|
||||||
|
) -> Value {
|
||||||
use Expr::*;
|
use Expr::*;
|
||||||
match self {
|
match self {
|
||||||
Ident(i) => Value::Ident(i.clone()),
|
Ident(i) => Value::Ident(i.clone()),
|
||||||
@ -100,9 +107,9 @@ impl Expr {
|
|||||||
&Number(n) => Value::Number(n),
|
&Number(n) => Value::Number(n),
|
||||||
&Length(s) => Value::Length(s),
|
&Length(s) => Value::Length(s),
|
||||||
&Color(c) => Value::Color(c),
|
&Color(c) => Value::Color(c),
|
||||||
Table(t) => Value::Table(t.eval()),
|
Table(t) => Value::Table(t.eval(ctx, f).await),
|
||||||
Tree(t) => Value::Tree(t.clone()),
|
Tree(t) => Value::Tree(t.clone()),
|
||||||
Call(_) => todo!("eval call"),
|
Call(call) => call.eval(ctx, f).await,
|
||||||
Neg(_) => todo!("eval neg"),
|
Neg(_) => todo!("eval neg"),
|
||||||
Add(_, _) => todo!("eval add"),
|
Add(_, _) => todo!("eval add"),
|
||||||
Sub(_, _) => todo!("eval sub"),
|
Sub(_, _) => todo!("eval sub"),
|
||||||
@ -144,18 +151,23 @@ pub type TableExpr = Table<SpannedEntry<Expr>>;
|
|||||||
|
|
||||||
impl TableExpr {
|
impl TableExpr {
|
||||||
/// Evaluate the table expression to a table value.
|
/// Evaluate the table expression to a table value.
|
||||||
pub fn eval(&self) -> TableValue {
|
pub fn eval<'a>(
|
||||||
|
&'a self,
|
||||||
|
ctx: &'a LayoutContext<'a>,
|
||||||
|
f: &'a mut Feedback,
|
||||||
|
) -> DynFuture<'a, TableValue> {
|
||||||
|
Box::pin(async move {
|
||||||
let mut table = TableValue::new();
|
let mut table = TableValue::new();
|
||||||
|
|
||||||
for (&key, entry) in self.nums() {
|
for (key, entry) in self.iter() {
|
||||||
table.insert(key, entry.as_ref().map(|val| val.eval()));
|
let val = entry.val.v.eval(ctx, f).await;
|
||||||
}
|
let spanned = Spanned::new(val, entry.val.span);
|
||||||
|
let entry = SpannedEntry::new(entry.key, spanned);
|
||||||
for (key, entry) in self.strs() {
|
table.insert(key, entry);
|
||||||
table.insert(key.clone(), entry.as_ref().map(|val| val.eval()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table
|
table
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,3 +177,23 @@ pub struct CallExpr {
|
|||||||
pub name: Spanned<Ident>,
|
pub name: Spanned<Ident>,
|
||||||
pub args: TableExpr,
|
pub args: TableExpr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CallExpr {
|
||||||
|
/// Evaluate the call expression to a value.
|
||||||
|
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
|
||||||
|
let name = self.name.v.as_str();
|
||||||
|
let span = self.name.span;
|
||||||
|
let args = self.args.eval(ctx, f).await;
|
||||||
|
|
||||||
|
if let Some(func) = ctx.scope.func(name) {
|
||||||
|
let pass = func(span, args, ctx.clone()).await;
|
||||||
|
f.extend(pass.feedback);
|
||||||
|
f.decorations.push(Spanned::new(Decoration::Resolved, span));
|
||||||
|
pass.output
|
||||||
|
} else {
|
||||||
|
error!(@f, span, "unknown function");
|
||||||
|
f.decorations.push(Spanned::new(Decoration::Unresolved, span));
|
||||||
|
Value::Table(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user