mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Field access
This commit is contained in:
parent
255d4c620f
commit
7a2cc3e7d2
@ -215,6 +215,7 @@ impl Eval for Expr {
|
||||
Self::Array(v) => v.eval(ctx, scp).map(Value::Array),
|
||||
Self::Dict(v) => v.eval(ctx, scp).map(Value::Dict),
|
||||
Self::Group(v) => v.eval(ctx, scp),
|
||||
Self::FieldAccess(v) => v.eval(ctx, scp),
|
||||
Self::FuncCall(v) => v.eval(ctx, scp),
|
||||
Self::MethodCall(v) => v.eval(ctx, scp),
|
||||
Self::Closure(v) => v.eval(ctx, scp),
|
||||
@ -434,6 +435,23 @@ impl BinaryExpr {
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for FieldAccess {
|
||||
type Output = Value;
|
||||
|
||||
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
|
||||
let object = self.object().eval(ctx, scp)?;
|
||||
Ok(match object {
|
||||
Value::Dict(dict) => dict.get(self.field().take()).at(self.span())?.clone(),
|
||||
|
||||
v => bail!(
|
||||
self.object().span(),
|
||||
"cannot access field on {}",
|
||||
v.type_name()
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for FuncCall {
|
||||
type Output = Value;
|
||||
|
||||
@ -442,14 +460,8 @@ impl Eval for FuncCall {
|
||||
let args = self.args().eval(ctx, scp)?;
|
||||
|
||||
Ok(match callee {
|
||||
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).at(self.span())?
|
||||
}
|
||||
|
||||
Value::Array(array) => array.get(args.into_index()?).at(self.span())?.clone(),
|
||||
Value::Dict(dict) => dict.get(args.into_key()?).at(self.span())?.clone(),
|
||||
Value::Func(func) => {
|
||||
let point = || Tracepoint::Call(func.name().map(ToString::to_string));
|
||||
func.call(ctx, args).trace(point, self.span())?
|
||||
|
@ -368,10 +368,9 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
|
||||
};
|
||||
|
||||
loop {
|
||||
// Exclamation mark, parenthesis or bracket means this is a function
|
||||
// call.
|
||||
// Parenthesis or bracket means this is a function call.
|
||||
if let Some(NodeKind::LeftParen | NodeKind::LeftBracket) = p.peek_direct() {
|
||||
func_call(p, marker)?;
|
||||
marker.perform(p, NodeKind::FuncCall, |p| args(p, true, true))?;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -379,8 +378,14 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
|
||||
break;
|
||||
}
|
||||
|
||||
if p.at(&NodeKind::Dot) {
|
||||
method_call(p, marker)?;
|
||||
// Method call or field access.
|
||||
if p.eat_if(&NodeKind::Dot) {
|
||||
ident(p)?;
|
||||
if let Some(NodeKind::LeftParen | NodeKind::LeftBracket) = p.peek_direct() {
|
||||
marker.perform(p, NodeKind::MethodCall, |p| args(p, true, true))?;
|
||||
} else {
|
||||
marker.end(p, NodeKind::FieldAccess);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -715,20 +720,6 @@ fn content_block(p: &mut Parser) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Parse a function call.
|
||||
fn func_call(p: &mut Parser, callee: Marker) -> ParseResult {
|
||||
callee.perform(p, NodeKind::FuncCall, |p| args(p, true, true))
|
||||
}
|
||||
|
||||
/// Parse a method call.
|
||||
fn method_call(p: &mut Parser, marker: Marker) -> ParseResult {
|
||||
marker.perform(p, NodeKind::MethodCall, |p| {
|
||||
p.eat_assert(&NodeKind::Dot);
|
||||
ident(p)?;
|
||||
args(p, true, true)
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse the arguments to a function call.
|
||||
fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult {
|
||||
match if direct { p.peek_direct() } else { p.peek() } {
|
||||
|
@ -237,6 +237,8 @@ pub enum Expr {
|
||||
Unary(UnaryExpr),
|
||||
/// A binary operation: `a + b`.
|
||||
Binary(BinaryExpr),
|
||||
/// A field access: `properties.age`.
|
||||
FieldAccess(FieldAccess),
|
||||
/// An invocation of a function: `f(x, y)`.
|
||||
FuncCall(FuncCall),
|
||||
/// An invocation of a method: `array.push(v)`.
|
||||
@ -280,6 +282,7 @@ impl TypedNode for Expr {
|
||||
NodeKind::DictExpr => node.cast().map(Self::Dict),
|
||||
NodeKind::UnaryExpr => node.cast().map(Self::Unary),
|
||||
NodeKind::BinaryExpr => node.cast().map(Self::Binary),
|
||||
NodeKind::FieldAccess => node.cast().map(Self::FieldAccess),
|
||||
NodeKind::FuncCall => node.cast().map(Self::FuncCall),
|
||||
NodeKind::MethodCall => node.cast().map(Self::MethodCall),
|
||||
NodeKind::ClosureExpr => node.cast().map(Self::Closure),
|
||||
@ -310,6 +313,7 @@ impl TypedNode for Expr {
|
||||
Self::Group(v) => v.as_red(),
|
||||
Self::Unary(v) => v.as_red(),
|
||||
Self::Binary(v) => v.as_red(),
|
||||
Self::FieldAccess(v) => v.as_red(),
|
||||
Self::FuncCall(v) => v.as_red(),
|
||||
Self::MethodCall(v) => v.as_red(),
|
||||
Self::Closure(v) => v.as_red(),
|
||||
@ -789,6 +793,23 @@ pub enum Associativity {
|
||||
Right,
|
||||
}
|
||||
|
||||
node! {
|
||||
/// A field access: `properties.age`.
|
||||
FieldAccess: FieldAccess
|
||||
}
|
||||
|
||||
impl FieldAccess {
|
||||
/// The object with the field.
|
||||
pub fn object(&self) -> Expr {
|
||||
self.0.cast_first_child().expect("field access is missing object")
|
||||
}
|
||||
|
||||
/// The name of the field.
|
||||
pub fn field(&self) -> Ident {
|
||||
self.0.cast_last_child().expect("field access call is missing name")
|
||||
}
|
||||
}
|
||||
|
||||
node! {
|
||||
/// An invocation of a function: `f(x, y)`.
|
||||
FuncCall: FuncCall
|
||||
|
@ -209,6 +209,7 @@ impl Category {
|
||||
NodeKind::Named => None,
|
||||
NodeKind::UnaryExpr => None,
|
||||
NodeKind::BinaryExpr => None,
|
||||
NodeKind::FieldAccess => None,
|
||||
NodeKind::FuncCall => None,
|
||||
NodeKind::MethodCall => None,
|
||||
NodeKind::CallArgs => None,
|
||||
|
@ -653,6 +653,8 @@ pub enum NodeKind {
|
||||
UnaryExpr,
|
||||
/// A binary operation: `a + b`.
|
||||
BinaryExpr,
|
||||
/// A field access: `properties.age`.
|
||||
FieldAccess,
|
||||
/// An invocation of a function: `f(x, y)`.
|
||||
FuncCall,
|
||||
/// An invocation of a method: `array.push(v)`.
|
||||
@ -898,6 +900,7 @@ impl NodeKind {
|
||||
Self::Named => "named argument",
|
||||
Self::UnaryExpr => "unary expression",
|
||||
Self::BinaryExpr => "binary expression",
|
||||
Self::FieldAccess => "field access",
|
||||
Self::FuncCall => "function call",
|
||||
Self::MethodCall => "method call",
|
||||
Self::CallArgs => "call arguments",
|
||||
@ -1021,6 +1024,7 @@ impl Hash for NodeKind {
|
||||
Self::Named => {}
|
||||
Self::UnaryExpr => {}
|
||||
Self::BinaryExpr => {}
|
||||
Self::FieldAccess => {}
|
||||
Self::FuncCall => {}
|
||||
Self::MethodCall => {}
|
||||
Self::CallArgs => {}
|
||||
|
24
tests/typ/code/field.typ
Normal file
24
tests/typ/code/field.typ
Normal file
@ -0,0 +1,24 @@
|
||||
// Test field access.
|
||||
// Ref: false
|
||||
|
||||
---
|
||||
#let dict = (nothing: "ness", hello: "world")
|
||||
#test(dict.nothing, "ness")
|
||||
{
|
||||
let world = dict
|
||||
.hello
|
||||
|
||||
test(world, "world")
|
||||
}
|
||||
|
||||
---
|
||||
// Error: 2-13 dictionary does not contain key: "invalid"
|
||||
{(:).invalid}
|
||||
|
||||
---
|
||||
// Error: 2-7 cannot access field on boolean
|
||||
{false.ok}
|
||||
|
||||
---
|
||||
// Error: 8-12 expected identifier, found boolean
|
||||
{false.true}
|
Loading…
x
Reference in New Issue
Block a user