summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-19 16:37:16 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-19 16:37:16 +0200
commit7a2cc3e7d29d16c5cf9b5a93a688e14da93c8662 (patch)
tree8ca787c56b84b83f5d34ee7b4701c0e8f4778753 /src
parent255d4c620f39133b40a9132843781f2a620a6008 (diff)
Field access
Diffstat (limited to 'src')
-rw-r--r--src/eval/mod.rs28
-rw-r--r--src/parse/mod.rs29
-rw-r--r--src/syntax/ast.rs21
-rw-r--r--src/syntax/highlight.rs1
-rw-r--r--src/syntax/mod.rs4
5 files changed, 56 insertions, 27 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 4a616b58..d9651cce 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -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())?
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index be947170..c387de0c 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -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() } {
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 60856691..8af359bf 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -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(),
@@ -790,6 +794,23 @@ pub enum Associativity {
}
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
}
diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs
index 004ff957..10dfce69 100644
--- a/src/syntax/highlight.rs
+++ b/src/syntax/highlight.rs
@@ -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,
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index d18b6a3d..71646cb2 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -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 => {}