Skip to content

Commit

Permalink
Fix index access when using an identifier
Browse files Browse the repository at this point in the history
This was a large change rewriting how index access works. Another PR will likely remove the OP_GET_INDEX, OP_SET_INDEX ops in favor of using just OP_GET_PROPERTY, OP_SET_PROPERTY
  • Loading branch information
kyleect committed Jan 11, 2024
1 parent 7c82e74 commit f7fa3e9
Show file tree
Hide file tree
Showing 22 changed files with 282 additions and 52 deletions.
12 changes: 12 additions & 0 deletions res/examples/index_access/instance_as_index.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class A {

}

class B {

}

let a = A();
let b = B();

println(a[b]); // out: TypeError: expected an index type "string" but got "instance"
5 changes: 5 additions & 0 deletions res/examples/list/get_index_with_bool_false.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let list = [1, 2];

class Test {}

println(list[false]); // out: SyntaxError: unexpected "false"
5 changes: 5 additions & 0 deletions res/examples/list/get_index_with_bool_true.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let list = [1, 2];

class Test {}

println(list[true]); // out: SyntaxError: unexpected "true"
5 changes: 5 additions & 0 deletions res/examples/list/get_index_with_class.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let list = [1, 2];

class Test {}

println(list[Test]); // out: TypeError: expected an index type "number" but got "class"
4 changes: 4 additions & 0 deletions res/examples/list/get_index_with_identifier.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let list = [1, 2, 3];
let index = 1;

println(list[index]); // out: 2
7 changes: 7 additions & 0 deletions res/examples/list/get_index_with_instance.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
let list = [1, 2];

class Test {}

let test = Test();

println(list[test]); // out: TypeError: expected an index type "number" but got "instance"
5 changes: 5 additions & 0 deletions res/examples/list/get_index_with_nil.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let list = [1, 2];

class Test {}

println(list[nil]); // out: SyntaxError: unexpected "nil"
3 changes: 0 additions & 3 deletions res/examples/list/get_index_with_non_number_index.locks

This file was deleted.

3 changes: 3 additions & 0 deletions res/examples/list/get_index_with_string.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let list = [1, 2];

println(list["b"]); // out: TypeError: expected an index type "number" but got "string"
3 changes: 3 additions & 0 deletions res/examples/list/set_index_with_bool_false.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let list = [1, 2];

list[false] = 100; // out: SyntaxError: unexpected "false"
3 changes: 3 additions & 0 deletions res/examples/list/set_index_with_bool_true.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let list = [1, 2];

list[true] = 100; // out: SyntaxError: unexpected "true"
5 changes: 5 additions & 0 deletions res/examples/list/set_index_with_class.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let list = [1, 2];

class Test {}

list[Test] = 100; // out: TypeError: expected an index type "number" but got "class"
6 changes: 6 additions & 0 deletions res/examples/list/set_index_with_identifier.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let list = [1, 2, 3];
let index = 1;

list[index] = 100;

println(list[1]); // out: 100
7 changes: 7 additions & 0 deletions res/examples/list/set_index_with_instance.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
let list = [1, 2];

class Test {}

let test = Test();

list[test] = 100; // out: TypeError: expected an index type "number" but got "instance"
5 changes: 5 additions & 0 deletions res/examples/list/set_index_with_nil.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let list = [1, 2];

class Test {}

list[nil] = 100; // out: SyntaxError: unexpected "nil"
3 changes: 3 additions & 0 deletions res/examples/list/set_index_with_string.locks
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let list = [1, 2];

list["b"] = 100; // out: TypeError: expected an index type "number" but got "string"
75 changes: 63 additions & 12 deletions res/grammar.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,39 @@ ExprAssign = {
})),
<target:Spanned<ExprCall>> "[" <index:number> "]" "=" <value:ExprS> =>
ast::Expr::SetIndex(Box::new(ast::ExprSetIndex {<>})),
<object:Spanned<ExprCall>> "." <name:identifier> "=" <value:ExprS> =>
ast::Expr::Set(Box::new(ast::ExprSet { <> })),
<object:Spanned<ExprCall>> "[" <name:QualifiedIdentifier> "]" "=" <value:ExprS> =>
ast::Expr::Set(Box::new(ast::ExprSet { <> })),
<object:Spanned<ExprCall>> "[" <name:string> "]" "=" <value:ExprS> =>
ast::Expr::Set(Box::new(ast::ExprSet { <> })),
<object:Spanned<ExprCall>> "." <name:Spanned<identifier>> "=" <value:ExprS> =>
ast::Expr::Set(Box::new(ast::ExprSet {
object,
name: (
ast::Expr::Literal(ast::ExprLiteral::String(name.0)),
name.1
),
value
})),
<object:Spanned<ExprCall>> "[" <name:Spanned<QualifiedIdentifier>> "]" "=" <value:ExprS> =>
ast::Expr::Set(Box::new(ast::ExprSet {
object,
name: (
ast::Expr::Identifier(ast::ExprIdentifier {
identifier: ast::Identifier {
name: name.0,
package: None,
depth: None,
}
}),
name.1
),
value
})),
<object:Spanned<ExprCall>> "[" <name:Spanned<string>> "]" "=" <value:ExprS> =>
ast::Expr::Set(Box::new(ast::ExprSet {
object,
name: (
ast::Expr::Literal(ast::ExprLiteral::String(name.0)),
name.1
),
value
})),
ExprLogicOr,
}

Expand Down Expand Up @@ -218,12 +245,36 @@ OpPrefix: ast::OpPrefix = {
ExprCall: ast::Expr = {
<callee:Spanned<ExprCall>> "(" <args:Args> ")" =>
ast::Expr::Call(Box::new(ast::ExprCall { callee, args })),
<object:Spanned<ExprCall>> "." <name:identifier> =>
ast::Expr::Get(Box::new(ast::ExprGet { <> })),
<object:Spanned<ExprCall>> "[" <name:string> "]" =>
ast::Expr::Get(Box::new(ast::ExprGet { <> })),
<object:Spanned<ExprCall>> "[" <name:QualifiedIdentifier> "]" =>
ast::Expr::Get(Box::new(ast::ExprGet { <> })),
<object:Spanned<ExprCall>> "." <name:Spanned<identifier>> =>
ast::Expr::Get(Box::new(ast::ExprGet {
object,
name: (
ast::Expr::Literal(ast::ExprLiteral::String(name.0)),
name.1
)
})),
<object:Spanned<ExprCall>> "[" <name:Spanned<string>> "]" =>
ast::Expr::Get(Box::new(ast::ExprGet {
object,
name: (
ast::Expr::Literal(ast::ExprLiteral::String(name.0)),
name.1
)
})),
<object:Spanned<ExprCall>> "[" <name:Spanned<QualifiedIdentifier>> "]" =>
ast::Expr::Get(Box::new(ast::ExprGet {
object,
name: (
ast::Expr::Identifier(ast::ExprIdentifier {
identifier: ast::Identifier {
name: name.0,
package: None,
depth: None,
}
}),
name.1
)
})),
<target:Spanned<ExprCall>> "[" <index:number> "]" =>
ast::Expr::GetIndex(Box::new(ast::ExprGetIndex {<>})),
"super" "." <name:identifier> =>
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ pub enum TypeError {
NoLength { type_: String },
#[error(r#"expected type "{expected_type}" but got "{actual_type}""#)]
InvalidType { expected_type: String, actual_type: String },
#[error(r#"expected an index type "{expected_type}" but got "{actual_type}""#)]
InvalidIndexType { expected_type: String, actual_type: String },
}

impl AsDiagnostic for TypeError {
Expand Down
4 changes: 2 additions & 2 deletions src/syntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ pub struct ExprCall {
#[derive(Clone, Debug, PartialEq)]
pub struct ExprGet {
pub object: ExprS,
pub name: String,
pub name: ExprS,
}

#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -246,7 +246,7 @@ impl Display for OpPrefix {
#[derive(Clone, Debug, PartialEq)]
pub struct ExprSet {
pub object: ExprS,
pub name: String,
pub name: ExprS,
pub value: ExprS,
}

Expand Down
6 changes: 2 additions & 4 deletions src/vm/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,11 +519,10 @@ impl Compiler {
self.emit_u8(arg_count, span);
}
Expr::Get(get) => {
self.compile_expr(&get.name, gc)?;
self.compile_expr(&get.object, gc)?;

let name = gc.alloc(&get.name).into();
self.emit_u8(op::GET_PROPERTY, span);
self.emit_constant(name, span)?;
}
Expr::GetIndex(get_index) => {
let target = &get_index.target;
Expand Down Expand Up @@ -669,11 +668,10 @@ impl Compiler {
}
Expr::Set(set) => {
self.compile_expr(&set.value, gc)?;
self.compile_expr(&set.name, gc)?;
self.compile_expr(&set.object, gc)?;

let name = gc.alloc(&set.name).into();
self.emit_u8(op::SET_PROPERTY, span);
self.emit_constant(name, span)?;
}
Expr::Super(super_) => {
match self.class_ctx.last() {
Expand Down
8 changes: 4 additions & 4 deletions src/vm/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ impl<'a> Disassembler<'a> {
op::SET_GLOBAL => self.disassemble_op_constant("OP_SET_GLOBAL", op_idx),
op::GET_UPVALUE => self.disassemble_op_byte("OP_GET_UPVALUE", op_idx),
op::SET_UPVALUE => self.disassemble_op_byte("OP_SET_UPVALUE", op_idx),
op::GET_PROPERTY => self.disassemble_op_constant("OP_GET_PROPERTY", op_idx),
op::SET_PROPERTY => self.disassemble_op_constant("OP_SET_PROPERTY", op_idx),
op::GET_PROPERTY => self.disassemble_op_simple("OP_GET_PROPERTY"),
op::SET_PROPERTY => self.disassemble_op_simple("OP_SET_PROPERTY"),
op::GET_SUPER => self.disassemble_op_constant("OP_GET_SUPER", op_idx),
op::EQUAL => self.disassemble_op_simple("OP_EQUAL"),
op::NOT_EQUAL => self.disassemble_op_simple("OP_NOT_EQUAL"),
Expand Down Expand Up @@ -448,7 +448,7 @@ mod tests {
println(greeter.greet(\"World\")); // out: Hello World",
concat!(
"0000 OP_CLASS 0 == 'Greeter'\n0002 OP_DEFINE_GLOBAL 0 == 'Greeter'\n0004 OP_GET_GLOBAL 1 == 'Object'\n0006 OP_GET_GLOBAL 0 == 'Greeter'\n0008 OP_INHERIT\n0009 OP_GET_GLOBAL 0 == 'Greeter'\n0011 OP_NIL\n0012 OP_FIELD 2 == 'greeting'\n0014 OP_POP\n0015 OP_GET_GLOBAL 0 == 'Greeter'\n0017 OP_CLOSURE 3 == '<fn init arity=1>'\n| 0000 OP_GET_LOCAL 1\n| 0002 OP_GET_LOCAL 0\n| 0004 OP_SET_PROPERTY 0 == 'greeting'\n| 0006 OP_POP\n| 0007 OP_GET_LOCAL 0\n| 0009 OP_RETURN\n0019 OP_METHOD 4 == 'init'\n0021 OP_CLOSURE 5 == '<fn greet arity=1>'\n| 0000 OP_GET_LOCAL 0\n| 0002 OP_GET_PROPERTY 0 == 'greeting'\n| 0004 OP_CONSTANT 1 == ' '\n| 0006 OP_ADD\n| 0007 OP_GET_LOCAL 1\n| 0009 OP_ADD\n| 0010 OP_RETURN\n0023 OP_METHOD 6 == 'greet'\n0025 OP_POP\n0026 OP_POP\n0027 OP_GET_GLOBAL 0 == 'Greeter'\n0029 OP_CONSTANT 7 == 'Hello'\n0031 OP_CALL 1\n0033 OP_DEFINE_GLOBAL 8 == 'greeter'\n0035 OP_GET_GLOBAL 9 == 'println'\n0037 OP_GET_GLOBAL 8 == 'greeter'\n0039 OP_GET_PROPERTY 6 == 'greet'\n0041 OP_CONSTANT 10 == 'World'\n0043 OP_CALL 1\n0045 OP_CALL 1\n0047 OP_POP\n0048 OP_NIL\n0049 OP_RETURN\n"
"0000 OP_CLASS 0 == 'Greeter'\n0002 OP_DEFINE_GLOBAL 0 == 'Greeter'\n0004 OP_GET_GLOBAL 1 == 'Object'\n0006 OP_GET_GLOBAL 0 == 'Greeter'\n0008 OP_INHERIT\n0009 OP_GET_GLOBAL 0 == 'Greeter'\n0011 OP_NIL\n0012 OP_FIELD 2 == 'greeting'\n0014 OP_POP\n0015 OP_GET_GLOBAL 0 == 'Greeter'\n0017 OP_CLOSURE 3 == '<fn init arity=1>'\n| 0000 OP_GET_LOCAL 1\n| 0002 OP_CONSTANT 0 == 'greeting'\n| 0004 OP_GET_LOCAL 0\n| 0006 OP_SET_PROPERTY\n| 0007 OP_POP\n| 0008 OP_GET_LOCAL 0\n| 0010 OP_RETURN\n0019 OP_METHOD 4 == 'init'\n0021 OP_CLOSURE 5 == '<fn greet arity=1>'\n| 0000 OP_CONSTANT 0 == 'greeting'\n| 0002 OP_GET_LOCAL 0\n| 0004 OP_GET_PROPERTY\n| 0005 OP_CONSTANT 1 == ' '\n| 0007 OP_ADD\n| 0008 OP_GET_LOCAL 1\n| 0010 OP_ADD\n| 0011 OP_RETURN\n0023 OP_METHOD 6 == 'greet'\n0025 OP_POP\n0026 OP_POP\n0027 OP_GET_GLOBAL 0 == 'Greeter'\n0029 OP_CONSTANT 7 == 'Hello'\n0031 OP_CALL 1\n0033 OP_DEFINE_GLOBAL 8 == 'greeter'\n0035 OP_GET_GLOBAL 9 == 'println'\n0037 OP_CONSTANT 6 == 'greet'\n0039 OP_GET_GLOBAL 8 == 'greeter'\n0041 OP_GET_PROPERTY\n0042 OP_CONSTANT 10 == 'World'\n0044 OP_CALL 1\n0046 OP_CALL 1\n0048 OP_POP\n0049 OP_NIL\n0050 OP_RETURN\n"
)
),
class_static_field: (
Expand All @@ -459,7 +459,7 @@ mod tests {
println(Test.value); // out: 100",
concat!(
"0000 OP_CLASS 0 == 'Test'\n0002 OP_DEFINE_GLOBAL 0 == 'Test'\n0004 OP_GET_GLOBAL 1 == 'Object'\n0006 OP_GET_GLOBAL 0 == 'Test'\n0008 OP_INHERIT\n0009 OP_GET_GLOBAL 0 == 'Test'\n0011 OP_CONSTANT 2 == '100'\n0013 OP_STATIC_FIELD 3 == 'value'\n0015 OP_POP\n0016 OP_POP\n0017 OP_GET_GLOBAL 4 == 'println'\n0019 OP_GET_GLOBAL 0 == 'Test'\n0021 OP_GET_PROPERTY 3 == 'value'\n0023 OP_CALL 1\n0025 OP_POP\n0026 OP_NIL\n0027 OP_RETURN\n"
"0000 OP_CLASS 0 == 'Test'\n0002 OP_DEFINE_GLOBAL 0 == 'Test'\n0004 OP_GET_GLOBAL 1 == 'Object'\n0006 OP_GET_GLOBAL 0 == 'Test'\n0008 OP_INHERIT\n0009 OP_GET_GLOBAL 0 == 'Test'\n0011 OP_CONSTANT 2 == '100'\n0013 OP_STATIC_FIELD 3 == 'value'\n0015 OP_POP\n0016 OP_POP\n0017 OP_GET_GLOBAL 4 == 'println'\n0019 OP_CONSTANT 3 == 'value'\n0021 OP_GET_GLOBAL 0 == 'Test'\n0023 OP_GET_PROPERTY\n0024 OP_CALL 1\n0026 OP_POP\n0027 OP_NIL\n0028 OP_RETURN\n"
)
),
}
Expand Down
Loading

0 comments on commit f7fa3e9

Please sign in to comment.