mirror of
https://github.com/typst/typst
synced 2025-06-28 00:03:17 +08:00
Keyed pairs
This commit is contained in:
parent
e674fd7e90
commit
2a45650dcc
@ -5,6 +5,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use super::{Args, Array, Func, Value};
|
use super::{Args, Array, Func, Value};
|
||||||
use crate::diag::{StrResult, TypResult};
|
use crate::diag::{StrResult, TypResult};
|
||||||
|
use crate::parse::is_ident;
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Spanned;
|
||||||
use crate::util::{ArcExt, EcoString};
|
use crate::util::{ArcExt, EcoString};
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
@ -127,7 +128,11 @@ impl Debug for Dict {
|
|||||||
f.write_char(':')?;
|
f.write_char(':')?;
|
||||||
}
|
}
|
||||||
for (i, (key, value)) in self.iter().enumerate() {
|
for (i, (key, value)) in self.iter().enumerate() {
|
||||||
|
if is_ident(key) {
|
||||||
f.write_str(key)?;
|
f.write_str(key)?;
|
||||||
|
} else {
|
||||||
|
write!(f, "{key:?}")?;
|
||||||
|
}
|
||||||
f.write_str(": ")?;
|
f.write_str(": ")?;
|
||||||
value.fmt(f)?;
|
value.fmt(f)?;
|
||||||
if i + 1 < self.0.len() {
|
if i + 1 < self.0.len() {
|
||||||
|
@ -335,6 +335,9 @@ impl Eval for DictExpr {
|
|||||||
DictItem::Named(named) => {
|
DictItem::Named(named) => {
|
||||||
map.insert(named.name().take(), named.expr().eval(ctx, scp)?);
|
map.insert(named.name().take(), named.expr().eval(ctx, scp)?);
|
||||||
}
|
}
|
||||||
|
DictItem::Keyed(keyed) => {
|
||||||
|
map.insert(keyed.key(), keyed.expr().eval(ctx, scp)?);
|
||||||
|
}
|
||||||
DictItem::Spread(expr) => match expr.eval(ctx, scp)? {
|
DictItem::Spread(expr) => match expr.eval(ctx, scp)? {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Dict(dict) => map.extend(dict.into_iter()),
|
Value::Dict(dict) => map.extend(dict.into_iter()),
|
||||||
|
@ -526,7 +526,7 @@ fn parenthesized(p: &mut Parser, atomic: bool) -> ParseResult {
|
|||||||
|
|
||||||
p.start_group(Group::Paren);
|
p.start_group(Group::Paren);
|
||||||
let colon = p.eat_if(NodeKind::Colon);
|
let colon = p.eat_if(NodeKind::Colon);
|
||||||
let kind = collection(p).0;
|
let kind = collection(p, true).0;
|
||||||
p.end_group();
|
p.end_group();
|
||||||
|
|
||||||
// Leading colon makes this a dictionary.
|
// Leading colon makes this a dictionary.
|
||||||
@ -568,14 +568,14 @@ enum CollectionKind {
|
|||||||
///
|
///
|
||||||
/// Returns the length of the collection and whether the literal contained any
|
/// Returns the length of the collection and whether the literal contained any
|
||||||
/// commas.
|
/// commas.
|
||||||
fn collection(p: &mut Parser) -> (CollectionKind, usize) {
|
fn collection(p: &mut Parser, keyed: bool) -> (CollectionKind, usize) {
|
||||||
let mut kind = None;
|
let mut kind = None;
|
||||||
let mut items = 0;
|
let mut items = 0;
|
||||||
let mut can_group = true;
|
let mut can_group = true;
|
||||||
let mut missing_coma: Option<Marker> = None;
|
let mut missing_coma: Option<Marker> = None;
|
||||||
|
|
||||||
while !p.eof() {
|
while !p.eof() {
|
||||||
if let Ok(item_kind) = item(p) {
|
if let Ok(item_kind) = item(p, keyed) {
|
||||||
match item_kind {
|
match item_kind {
|
||||||
NodeKind::Spread => can_group = false,
|
NodeKind::Spread => can_group = false,
|
||||||
NodeKind::Named if kind.is_none() => {
|
NodeKind::Named if kind.is_none() => {
|
||||||
@ -619,7 +619,7 @@ fn collection(p: &mut Parser) -> (CollectionKind, usize) {
|
|||||||
|
|
||||||
/// Parse an expression or a named pair, returning whether it's a spread or a
|
/// Parse an expression or a named pair, returning whether it's a spread or a
|
||||||
/// named pair.
|
/// named pair.
|
||||||
fn item(p: &mut Parser) -> ParseResult<NodeKind> {
|
fn item(p: &mut Parser, keyed: bool) -> ParseResult<NodeKind> {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
if p.eat_if(NodeKind::Dots) {
|
if p.eat_if(NodeKind::Dots) {
|
||||||
marker.perform(p, NodeKind::Spread, expr)?;
|
marker.perform(p, NodeKind::Spread, expr)?;
|
||||||
@ -629,18 +629,27 @@ fn item(p: &mut Parser) -> ParseResult<NodeKind> {
|
|||||||
expr(p)?;
|
expr(p)?;
|
||||||
|
|
||||||
if p.at(NodeKind::Colon) {
|
if p.at(NodeKind::Colon) {
|
||||||
marker.perform(p, NodeKind::Named, |p| {
|
match marker.after(p).map(|c| c.kind()) {
|
||||||
if let Some(NodeKind::Ident(_)) = marker.after(p).map(|c| c.kind()) {
|
Some(NodeKind::Ident(_)) => {
|
||||||
p.eat();
|
p.eat();
|
||||||
expr(p)
|
marker.perform(p, NodeKind::Named, expr)?;
|
||||||
} else {
|
}
|
||||||
let error = NodeKind::Error(ErrorPos::Full, "expected identifier".into());
|
Some(NodeKind::Str(_)) if keyed => {
|
||||||
|
p.eat();
|
||||||
|
marker.perform(p, NodeKind::Keyed, expr)?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mut msg = EcoString::from("expected identifier");
|
||||||
|
if keyed {
|
||||||
|
msg.push_str(" or string");
|
||||||
|
}
|
||||||
|
let error = NodeKind::Error(ErrorPos::Full, msg);
|
||||||
marker.end(p, error);
|
marker.end(p, error);
|
||||||
p.eat();
|
p.eat();
|
||||||
expr(p).ok();
|
marker.perform(p, NodeKind::Named, expr).ok();
|
||||||
Err(ParseError)
|
return Err(ParseError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(NodeKind::Named)
|
Ok(NodeKind::Named)
|
||||||
} else {
|
} else {
|
||||||
@ -653,6 +662,7 @@ fn item(p: &mut Parser) -> ParseResult<NodeKind> {
|
|||||||
fn array(p: &mut Parser, marker: Marker) {
|
fn array(p: &mut Parser, marker: Marker) {
|
||||||
marker.filter_children(p, |x| match x.kind() {
|
marker.filter_children(p, |x| match x.kind() {
|
||||||
NodeKind::Named => Err("expected expression, found named pair"),
|
NodeKind::Named => Err("expected expression, found named pair"),
|
||||||
|
NodeKind::Keyed => Err("expected expression, found keyed pair"),
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
});
|
});
|
||||||
marker.end(p, NodeKind::ArrayExpr);
|
marker.end(p, NodeKind::ArrayExpr);
|
||||||
@ -664,18 +674,18 @@ fn dict(p: &mut Parser, marker: Marker) {
|
|||||||
let mut used = HashSet::new();
|
let mut used = HashSet::new();
|
||||||
marker.filter_children(p, |x| match x.kind() {
|
marker.filter_children(p, |x| match x.kind() {
|
||||||
kind if kind.is_paren() => Ok(()),
|
kind if kind.is_paren() => Ok(()),
|
||||||
NodeKind::Named => {
|
NodeKind::Named | NodeKind::Keyed => {
|
||||||
if let Some(NodeKind::Ident(ident)) =
|
if let Some(NodeKind::Ident(key) | NodeKind::Str(key)) =
|
||||||
x.children().first().map(|child| child.kind())
|
x.children().first().map(|child| child.kind())
|
||||||
{
|
{
|
||||||
if !used.insert(ident.clone()) {
|
if !used.insert(key.clone()) {
|
||||||
return Err("pair has duplicate key");
|
return Err("pair has duplicate key");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
NodeKind::Comma | NodeKind::Colon | NodeKind::Spread => Ok(()),
|
NodeKind::Spread | NodeKind::Comma | NodeKind::Colon => Ok(()),
|
||||||
_ => Err("expected named pair, found expression"),
|
_ => Err("expected named or keyed pair, found expression"),
|
||||||
});
|
});
|
||||||
marker.end(p, NodeKind::DictExpr);
|
marker.end(p, NodeKind::DictExpr);
|
||||||
}
|
}
|
||||||
@ -685,7 +695,7 @@ fn dict(p: &mut Parser, marker: Marker) {
|
|||||||
fn params(p: &mut Parser, marker: Marker) {
|
fn params(p: &mut Parser, marker: Marker) {
|
||||||
marker.filter_children(p, |x| match x.kind() {
|
marker.filter_children(p, |x| match x.kind() {
|
||||||
kind if kind.is_paren() => Ok(()),
|
kind if kind.is_paren() => Ok(()),
|
||||||
NodeKind::Named | NodeKind::Comma | NodeKind::Ident(_) => Ok(()),
|
NodeKind::Named | NodeKind::Ident(_) | NodeKind::Comma => Ok(()),
|
||||||
NodeKind::Spread
|
NodeKind::Spread
|
||||||
if matches!(
|
if matches!(
|
||||||
x.children().last().map(|child| child.kind()),
|
x.children().last().map(|child| child.kind()),
|
||||||
@ -694,7 +704,7 @@ fn params(p: &mut Parser, marker: Marker) {
|
|||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err("expected identifier"),
|
_ => Err("expected identifier, named pair or argument sink"),
|
||||||
});
|
});
|
||||||
marker.end(p, NodeKind::ClosureParams);
|
marker.end(p, NodeKind::ClosureParams);
|
||||||
}
|
}
|
||||||
@ -746,12 +756,12 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult {
|
|||||||
if p.at(NodeKind::LeftParen) {
|
if p.at(NodeKind::LeftParen) {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
p.start_group(Group::Paren);
|
p.start_group(Group::Paren);
|
||||||
collection(p);
|
collection(p, false);
|
||||||
p.end_group();
|
p.end_group();
|
||||||
|
|
||||||
let mut used = HashSet::new();
|
let mut used = HashSet::new();
|
||||||
marker.filter_children(p, |x| {
|
marker.filter_children(p, |x| match x.kind() {
|
||||||
if x.kind() == &NodeKind::Named {
|
NodeKind::Named => {
|
||||||
if let Some(NodeKind::Ident(ident)) =
|
if let Some(NodeKind::Ident(ident)) =
|
||||||
x.children().first().map(|child| child.kind())
|
x.children().first().map(|child| child.kind())
|
||||||
{
|
{
|
||||||
@ -759,8 +769,9 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult {
|
|||||||
return Err("duplicate argument");
|
return Err("duplicate argument");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,7 +796,7 @@ fn let_expr(p: &mut Parser) -> ParseResult {
|
|||||||
if has_params {
|
if has_params {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
p.start_group(Group::Paren);
|
p.start_group(Group::Paren);
|
||||||
collection(p);
|
collection(p, false);
|
||||||
p.end_group();
|
p.end_group();
|
||||||
params(p, marker);
|
params(p, marker);
|
||||||
}
|
}
|
||||||
@ -904,7 +915,7 @@ fn import_expr(p: &mut Parser) -> ParseResult {
|
|||||||
p.perform(NodeKind::ImportItems, |p| {
|
p.perform(NodeKind::ImportItems, |p| {
|
||||||
p.start_group(Group::Imports);
|
p.start_group(Group::Imports);
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
let items = collection(p).1;
|
let items = collection(p, false).1;
|
||||||
if items == 0 {
|
if items == 0 {
|
||||||
p.expected("import items");
|
p.expected("import items");
|
||||||
}
|
}
|
||||||
|
@ -472,7 +472,7 @@ impl ArrayExpr {
|
|||||||
/// An item in an array expresssion.
|
/// An item in an array expresssion.
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
pub enum ArrayItem {
|
pub enum ArrayItem {
|
||||||
/// A simple value: `12`.
|
/// A bare value: `12`.
|
||||||
Pos(Expr),
|
Pos(Expr),
|
||||||
/// A spreaded value: `..things`.
|
/// A spreaded value: `..things`.
|
||||||
Spread(Expr),
|
Spread(Expr),
|
||||||
@ -509,8 +509,10 @@ impl DictExpr {
|
|||||||
/// An item in an dictionary expresssion.
|
/// An item in an dictionary expresssion.
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
pub enum DictItem {
|
pub enum DictItem {
|
||||||
/// A simple named pair: `12`.
|
/// A named pair: `thickness: 3pt`.
|
||||||
Named(Named),
|
Named(Named),
|
||||||
|
/// A keyed pair: `"spaced key": true`.
|
||||||
|
Keyed(Keyed),
|
||||||
/// A spreaded value: `..things`.
|
/// A spreaded value: `..things`.
|
||||||
Spread(Expr),
|
Spread(Expr),
|
||||||
}
|
}
|
||||||
@ -519,6 +521,7 @@ impl TypedNode for DictItem {
|
|||||||
fn from_red(node: RedRef) -> Option<Self> {
|
fn from_red(node: RedRef) -> Option<Self> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
NodeKind::Named => node.cast().map(Self::Named),
|
NodeKind::Named => node.cast().map(Self::Named),
|
||||||
|
NodeKind::Keyed => node.cast().map(Self::Keyed),
|
||||||
NodeKind::Spread => node.cast_first_child().map(Self::Spread),
|
NodeKind::Spread => node.cast_first_child().map(Self::Spread),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -527,28 +530,52 @@ impl TypedNode for DictItem {
|
|||||||
fn as_red(&self) -> RedRef<'_> {
|
fn as_red(&self) -> RedRef<'_> {
|
||||||
match self {
|
match self {
|
||||||
Self::Named(v) => v.as_red(),
|
Self::Named(v) => v.as_red(),
|
||||||
|
Self::Keyed(v) => v.as_red(),
|
||||||
Self::Spread(v) => v.as_red(),
|
Self::Spread(v) => v.as_red(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A pair of a name and an expression: `pattern: dashed`.
|
/// A pair of a name and an expression: `thickness: 3pt`.
|
||||||
Named
|
Named
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Named {
|
impl Named {
|
||||||
/// The name: `pattern`.
|
/// The name: `thickness`.
|
||||||
pub fn name(&self) -> Ident {
|
pub fn name(&self) -> Ident {
|
||||||
self.0.cast_first_child().expect("named pair is missing name")
|
self.0.cast_first_child().expect("named pair is missing name")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The right-hand side of the pair: `dashed`.
|
/// The right-hand side of the pair: `3pt`.
|
||||||
pub fn expr(&self) -> Expr {
|
pub fn expr(&self) -> Expr {
|
||||||
self.0.cast_last_child().expect("named pair is missing expression")
|
self.0.cast_last_child().expect("named pair is missing expression")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node! {
|
||||||
|
/// A pair of a string key and an expression: `"spaced key": true`.
|
||||||
|
Keyed
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keyed {
|
||||||
|
/// The key: `"spaced key"`.
|
||||||
|
pub fn key(&self) -> EcoString {
|
||||||
|
self.0
|
||||||
|
.children()
|
||||||
|
.find_map(|node| match node.kind() {
|
||||||
|
NodeKind::Str(key) => Some(key.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.expect("keyed pair is missing key")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The right-hand side of the pair: `true`.
|
||||||
|
pub fn expr(&self) -> Expr {
|
||||||
|
self.0.cast_last_child().expect("keyed pair is missing expression")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
/// A unary operation: `-x`.
|
/// A unary operation: `-x`.
|
||||||
UnaryExpr: UnaryExpr
|
UnaryExpr: UnaryExpr
|
||||||
|
@ -215,6 +215,7 @@ impl Category {
|
|||||||
NodeKind::ArrayExpr => None,
|
NodeKind::ArrayExpr => None,
|
||||||
NodeKind::DictExpr => None,
|
NodeKind::DictExpr => None,
|
||||||
NodeKind::Named => None,
|
NodeKind::Named => None,
|
||||||
|
NodeKind::Keyed => None,
|
||||||
NodeKind::UnaryExpr => None,
|
NodeKind::UnaryExpr => None,
|
||||||
NodeKind::BinaryExpr => None,
|
NodeKind::BinaryExpr => None,
|
||||||
NodeKind::FieldAccess => None,
|
NodeKind::FieldAccess => None,
|
||||||
|
@ -648,6 +648,8 @@ pub enum NodeKind {
|
|||||||
DictExpr,
|
DictExpr,
|
||||||
/// A named pair: `thickness: 3pt`.
|
/// A named pair: `thickness: 3pt`.
|
||||||
Named,
|
Named,
|
||||||
|
/// A keyed pair: `"spaced key": true`.
|
||||||
|
Keyed,
|
||||||
/// A unary operation: `-x`.
|
/// A unary operation: `-x`.
|
||||||
UnaryExpr,
|
UnaryExpr,
|
||||||
/// A binary operation: `a + b`.
|
/// A binary operation: `a + b`.
|
||||||
@ -896,7 +898,8 @@ impl NodeKind {
|
|||||||
Self::GroupExpr => "group",
|
Self::GroupExpr => "group",
|
||||||
Self::ArrayExpr => "array",
|
Self::ArrayExpr => "array",
|
||||||
Self::DictExpr => "dictionary",
|
Self::DictExpr => "dictionary",
|
||||||
Self::Named => "named argument",
|
Self::Named => "named pair",
|
||||||
|
Self::Keyed => "keyed pair",
|
||||||
Self::UnaryExpr => "unary expression",
|
Self::UnaryExpr => "unary expression",
|
||||||
Self::BinaryExpr => "binary expression",
|
Self::BinaryExpr => "binary expression",
|
||||||
Self::FieldAccess => "field access",
|
Self::FieldAccess => "field access",
|
||||||
@ -1021,6 +1024,7 @@ impl Hash for NodeKind {
|
|||||||
Self::ArrayExpr => {}
|
Self::ArrayExpr => {}
|
||||||
Self::DictExpr => {}
|
Self::DictExpr => {}
|
||||||
Self::Named => {}
|
Self::Named => {}
|
||||||
|
Self::Keyed => {}
|
||||||
Self::UnaryExpr => {}
|
Self::UnaryExpr => {}
|
||||||
Self::BinaryExpr => {}
|
Self::BinaryExpr => {}
|
||||||
Self::FieldAccess => {}
|
Self::FieldAccess => {}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 4.7 KiB |
@ -92,3 +92,7 @@
|
|||||||
// Named pair after this is already identified as an array.
|
// Named pair after this is already identified as an array.
|
||||||
// Error: 6-10 expected expression, found named pair
|
// Error: 6-10 expected expression, found named pair
|
||||||
{(1, b: 2)}
|
{(1, b: 2)}
|
||||||
|
|
||||||
|
// Keyed pair after this is already identified as an array.
|
||||||
|
// Error: 6-14 expected expression, found keyed pair
|
||||||
|
{(1, "key": 2)}
|
||||||
|
@ -86,6 +86,9 @@
|
|||||||
// Error: 7-8 expected identifier
|
// Error: 7-8 expected identifier
|
||||||
#func(1:2)
|
#func(1:2)
|
||||||
|
|
||||||
|
// Error: 7-12 expected identifier
|
||||||
|
#func("abc":2)
|
||||||
|
|
||||||
// Error: 7-10 expected identifier
|
// Error: 7-10 expected identifier
|
||||||
{func((x):1)}
|
{func((x):1)}
|
||||||
|
|
||||||
|
@ -143,3 +143,15 @@
|
|||||||
// Error: 23-35 unexpected argument
|
// Error: 23-35 unexpected argument
|
||||||
test(greet("Typst", whatever: 10))
|
test(greet("Typst", whatever: 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 6-16 expected identifier, named pair or argument sink
|
||||||
|
{(a, "named": b) => none}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 10-15 expected identifier
|
||||||
|
#let foo("key": b) = key
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 10-14 expected identifier
|
||||||
|
#let foo(none: b) = key
|
||||||
|
@ -7,8 +7,12 @@
|
|||||||
// Empty
|
// Empty
|
||||||
{(:)}
|
{(:)}
|
||||||
|
|
||||||
// Two pairs.
|
// Two pairs and string key.
|
||||||
{(a1: 1, a2: 2)}
|
#let dict = (normal: 1, "spaced key": 2)
|
||||||
|
#dict
|
||||||
|
|
||||||
|
#test(dict.normal, 1)
|
||||||
|
#test(dict("spaced key"), 2)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test lvalue and rvalue access.
|
// Test lvalue and rvalue access.
|
||||||
@ -39,14 +43,18 @@
|
|||||||
// Error: 24-32 pair has duplicate key
|
// Error: 24-32 pair has duplicate key
|
||||||
{(first: 1, second: 2, first: 3)}
|
{(first: 1, second: 2, first: 3)}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 17-23 pair has duplicate key
|
||||||
|
{(a: 1, "b": 2, "a": 3)}
|
||||||
|
|
||||||
---
|
---
|
||||||
// Simple expression after already being identified as a dictionary.
|
// Simple expression after already being identified as a dictionary.
|
||||||
// Error: 9-10 expected named pair, found expression
|
// Error: 9-10 expected named or keyed pair, found expression
|
||||||
{(a: 1, b)}
|
{(a: 1, b)}
|
||||||
|
|
||||||
// Identified as dictionary due to initial colon.
|
// Identified as dictionary due to initial colon.
|
||||||
// Error: 4-5 expected named pair, found expression
|
// Error: 4-5 expected named or keyed pair, found expression
|
||||||
// Error: 5 expected comma
|
// Error: 5 expected comma
|
||||||
// Error: 12-16 expected identifier
|
// Error: 12-16 expected identifier or string
|
||||||
// Error: 17-18 expected expression, found colon
|
// Error: 17-18 expected expression, found colon
|
||||||
{(:1 b:"", true::)}
|
{(:1 b:"", true::)}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Ref: false
|
// Ref: false
|
||||||
|
|
||||||
---
|
---
|
||||||
|
// Test field on dictionary.
|
||||||
#let dict = (nothing: "ness", hello: "world")
|
#let dict = (nothing: "ness", hello: "world")
|
||||||
#test(dict.nothing, "ness")
|
#test(dict.nothing, "ness")
|
||||||
{
|
{
|
||||||
@ -11,6 +12,16 @@
|
|||||||
test(world, "world")
|
test(world, "world")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test field on node.
|
||||||
|
#show node: list as {
|
||||||
|
test(node.items.len(), 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
- A
|
||||||
|
- B
|
||||||
|
- C
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 6-13 dictionary does not contain key "invalid"
|
// Error: 6-13 dictionary does not contain key "invalid"
|
||||||
{(:).invalid}
|
{(:).invalid}
|
||||||
@ -19,6 +30,11 @@
|
|||||||
// Error: 2-7 cannot access field on boolean
|
// Error: 2-7 cannot access field on boolean
|
||||||
{false.ok}
|
{false.ok}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 29-32 unknown field "fun"
|
||||||
|
#show node: heading as node.fun
|
||||||
|
= A
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 8-12 expected identifier, found boolean
|
// Error: 8-12 expected identifier, found boolean
|
||||||
{false.true}
|
{false.true}
|
||||||
|
@ -115,3 +115,7 @@ This is never reached.
|
|||||||
// Should output `, a from "target.typ"`.
|
// Should output `, a from "target.typ"`.
|
||||||
// Error: 10 expected keyword `from`
|
// Error: 10 expected keyword `from`
|
||||||
#import *, a from "target.typ"
|
#import *, a from "target.typ"
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 9-13 expected identifier
|
||||||
|
#import a: 1 from ""
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
#min(.."nope")
|
#min(.."nope")
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 8-14 expected identifier
|
// Error: 8-14 expected identifier, named pair or argument sink
|
||||||
#let f(..true) = none
|
#let f(..true) = none
|
||||||
|
|
||||||
---
|
---
|
||||||
|
Loading…
x
Reference in New Issue
Block a user