diff --git a/playground/src/pages/docs.tsx b/playground/src/pages/docs.tsx
index 31510fb..11d75d3 100644
--- a/playground/src/pages/docs.tsx
+++ b/playground/src/pages/docs.tsx
@@ -134,8 +134,13 @@ const Docs: React.FC = () => (
(
height="400px"
/>
+
+
Errors{' '}
diff --git a/res/examples/field/index_access_get_method_on_instance.locks b/res/examples/field/index_access_get_method_on_instance.locks
new file mode 100644
index 0000000..06378d5
--- /dev/null
+++ b/res/examples/field/index_access_get_method_on_instance.locks
@@ -0,0 +1,15 @@
+class Box {
+ let value;
+
+ fn init(value) {
+ this.value = value;
+ }
+
+ fn get() {
+ return this.value;
+ }
+}
+
+let box = Box(123);
+
+print box["get"](); // out: 123
diff --git a/res/examples/field/index_access_get_nested.locks b/res/examples/field/index_access_get_nested.locks
new file mode 100644
index 0000000..aa1fcc1
--- /dev/null
+++ b/res/examples/field/index_access_get_nested.locks
@@ -0,0 +1,23 @@
+class Object {
+ let box;
+
+ fn init(value) {
+ this.box = Box(value);
+ }
+}
+
+class Box {
+ let value;
+
+ fn init(value) {
+ this.value = value;
+ }
+
+ fn get() {
+ return this.value;
+ }
+}
+
+let obj = Object(123);
+
+print obj["box"]["get"](); // out: 123
diff --git a/res/examples/field/index_access_get_set_on_super.locks b/res/examples/field/index_access_get_set_on_super.locks
new file mode 100644
index 0000000..c9e3c71
--- /dev/null
+++ b/res/examples/field/index_access_get_set_on_super.locks
@@ -0,0 +1,25 @@
+class Parent {
+ let value;
+
+ fn init(value) {
+ this["value"] = value;
+ }
+
+ fn get() {
+ return this["value"];
+ }
+}
+
+class Child extends Parent {
+ fn init(value) {
+ super["init"](value);
+ }
+
+ fn get() {
+ return this.value;
+ }
+}
+
+let child = Child(123);
+
+print child.get(); // out: 123
diff --git a/res/examples/field/index_access_get_set_on_this.locks b/res/examples/field/index_access_get_set_on_this.locks
new file mode 100644
index 0000000..d20fa46
--- /dev/null
+++ b/res/examples/field/index_access_get_set_on_this.locks
@@ -0,0 +1,15 @@
+class Box {
+ let value;
+
+ fn init(value) {
+ this["value"] = value;
+ }
+
+ fn get() {
+ return this["value"];
+ }
+}
+
+let box = Box(123);
+
+print box.get(); // out: 123
diff --git a/res/examples/field/index_access_get_undefined.locks b/res/examples/field/index_access_get_undefined.locks
new file mode 100644
index 0000000..63fe391
--- /dev/null
+++ b/res/examples/field/index_access_get_undefined.locks
@@ -0,0 +1,6 @@
+class Object {
+}
+
+let obj = Object();
+
+print obj["key"]; // out: AttributeError: "Object" object has no attribute "key"
diff --git a/res/examples/field/index_access_set_method_on_instance.locks b/res/examples/field/index_access_set_method_on_instance.locks
new file mode 100644
index 0000000..76a8bdf
--- /dev/null
+++ b/res/examples/field/index_access_set_method_on_instance.locks
@@ -0,0 +1,15 @@
+class Box {
+ let value;
+
+ fn init(value) {
+ this.value = value;
+ }
+
+ fn get() {
+ return this.value;
+ }
+}
+
+let box = Box(123);
+
+box["get"] = 123; // out: TypeError: methods on instances can not be reassigned (e.g. instance.get = "...")
diff --git a/res/examples/field/index_access_set_undefined.locks b/res/examples/field/index_access_set_undefined.locks
new file mode 100644
index 0000000..2241c7f
--- /dev/null
+++ b/res/examples/field/index_access_set_undefined.locks
@@ -0,0 +1,6 @@
+class Object {
+}
+
+let obj = Object();
+
+obj["key"] = "new value"; // out: AttributeError: "Object" object has no attribute "key"
diff --git a/res/examples/field/index_access_with_expr.locks b/res/examples/field/index_access_with_expr.locks
new file mode 100644
index 0000000..c84f293
--- /dev/null
+++ b/res/examples/field/index_access_with_expr.locks
@@ -0,0 +1,12 @@
+class Record {
+ let key = "value";
+}
+
+let key = "key";
+let record = Record();
+
+print record[key]; // out: value
+
+record[key] = "new value";
+
+print record[key]; // out: new value
diff --git a/res/examples/field/index_access_with_string.locks b/res/examples/field/index_access_with_string.locks
new file mode 100644
index 0000000..af20249
--- /dev/null
+++ b/res/examples/field/index_access_with_string.locks
@@ -0,0 +1,11 @@
+class Record {
+ let key = "value";
+}
+
+let record = Record();
+
+print record["key"]; // out: value
+
+record["key"] = "new value";
+
+print record["key"]; // out: new value
diff --git a/res/grammar.lalrpop b/res/grammar.lalrpop
index 6415e4a..f5ea2e6 100644
--- a/res/grammar.lalrpop
+++ b/res/grammar.lalrpop
@@ -121,6 +121,10 @@ ExprAssign = {
})),
> "." "=" =>
ast::Expr::Set(Box::new(ast::ExprSet { <> })),
+ > "[" "]" "=" =>
+ ast::Expr::Set(Box::new(ast::ExprSet { <> })),
+ > "[" "]" "=" =>
+ ast::Expr::Set(Box::new(ast::ExprSet { <> })),
ExprLogicOr,
}
@@ -172,6 +176,10 @@ ExprCall: ast::Expr = {
ast::Expr::Call(Box::new(ast::ExprCall { callee, args })),
> "." =>
ast::Expr::Get(Box::new(ast::ExprGet { <> })),
+ > "[" "]" =>
+ ast::Expr::Get(Box::new(ast::ExprGet { <> })),
+ > "[" "]" =>
+ ast::Expr::Get(Box::new(ast::ExprGet { <> })),
"super" "." =>
ast::Expr::Super(ast::ExprSuper {
super_: ast::Identifier {
@@ -180,6 +188,22 @@ ExprCall: ast::Expr = {
},
name,
}),
+ "super" "[" "]" =>
+ ast::Expr::Super(ast::ExprSuper {
+ super_: ast::Identifier {
+ name: "super".to_string(),
+ depth: None,
+ },
+ name,
+ }),
+ "super" "[" "]" =>
+ ast::Expr::Super(ast::ExprSuper {
+ super_: ast::Identifier {
+ name: "super".to_string(),
+ depth: None,
+ },
+ name,
+ }),
ExprPrimary,
}
@@ -253,6 +277,8 @@ extern {
")" => lexer::Token::RtParen,
"{" => lexer::Token::LtBrace,
"}" => lexer::Token::RtBrace,
+ "[" => lexer::Token::LtBracket,
+ "]" => lexer::Token::RtBracket,
"," => lexer::Token::Comma,
"." => lexer::Token::Dot,
"-" => lexer::Token::Minus,
diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs
index 53bca35..e26137c 100644
--- a/src/syntax/lexer.rs
+++ b/src/syntax/lexer.rs
@@ -70,6 +70,10 @@ pub enum Token {
LtBrace,
#[token("}")]
RtBrace,
+ #[token("[")]
+ LtBracket,
+ #[token("]")]
+ RtBracket,
#[token(",")]
Comma,
#[token(".")]
diff --git a/src/vm/mod.rs b/src/vm/mod.rs
index 2aeeb41..407026b 100644
--- a/src/vm/mod.rs
+++ b/src/vm/mod.rs
@@ -788,10 +788,6 @@ impl VM {
/// Create a new [`ObjectInstance`] of an [`ObjectClass`].
///
/// Calls the `init` method if it exists on the [`ObjectClass`].
- ///
- /// ```
- /// let instance = Class();
- /// ```
fn call_class(&mut self, class: *mut ObjectClass, arg_count: usize) -> Result<()> {
let instance = self.alloc(ObjectInstance::new(class));