Skip to content

Commit

Permalink
Lambda function expressions
Browse files Browse the repository at this point in the history
LLVM2: Emit functions and function value wrappers for lambda
expressions. Also, change test functions to be immediately-invoked
lambda function expressions, both to cut down on the noise as well as to
ensure that we don't accidentally call the wrong function (or forget to
call it).
  • Loading branch information
kengorab committed Nov 22, 2023
1 parent f60027d commit 72f62f4
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 60 deletions.
7 changes: 5 additions & 2 deletions abra_cli/abra-files/example.abra
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
//println([])

val arr = [1, 2, 3, 4]
func sum(acc: Int, i: Int): Int = acc + i
println(arr.reduce(0, sum))
//func sum(acc: Int, i: Int): Int = acc + i
//println(arr.reduce(0, (acc, i) => acc + i))

val fn = (x: Int) => x + 1
println(fn(123))
7 changes: 4 additions & 3 deletions abra_core/src/typechecker/typechecker2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ pub enum TypedNode {
Invocation { target: Box<TypedNode>, arguments: Vec<Option<TypedNode>>, type_arg_ids: Vec<TypeId>, type_id: TypeId, resolved_type_id: TypeId },
Accessor { target: Box<TypedNode>, kind: AccessorKind, is_opt_safe: bool, member_idx: usize, member_span: Range, type_id: TypeId, type_arg_ids: Vec<(TypeId, Range)>, resolved_type_id: TypeId },
Indexing { target: Box<TypedNode>, index: IndexingMode<TypedNode>, type_id: TypeId, resolved_type_id: TypeId },
Lambda { span: Range, func_id: FuncId, type_id: TypeId },
Lambda { span: Range, func_id: FuncId, type_id: TypeId, resolved_type_id: TypeId },
Assignment { span: Range, kind: AssignmentKind, type_id: TypeId, expr: Box<TypedNode> },
If { if_token: Token, condition: Box<TypedNode>, condition_binding: Option<BindingPattern>, if_block: Vec<TypedNode>, else_block: Vec<TypedNode>, is_statement: bool, type_id: TypeId, resolved_type_id: TypeId },
Match { match_token: Token, target: Box<TypedNode>, cases: Vec<TypedMatchCase>, is_statement: bool, type_id: TypeId },
Expand Down Expand Up @@ -972,7 +972,7 @@ impl TypedNode {
TypedNode::NoneValue { resolved_type_id, .. } => *resolved_type_id = new_type_id,
TypedNode::Invocation { resolved_type_id, .. } => *resolved_type_id = new_type_id,
TypedNode::Accessor { resolved_type_id, .. } => *resolved_type_id = new_type_id,
TypedNode::Lambda { .. } => todo!(),
TypedNode::Lambda { resolved_type_id, .. } => *resolved_type_id = new_type_id,
TypedNode::Assignment { .. } => {}
TypedNode::Indexing { .. } => {}
TypedNode::If { .. } => {}
Expand Down Expand Up @@ -5384,7 +5384,8 @@ impl<'a, L: LoadModule> Typechecker2<'a, L> {
self.end_child_scope();
self.current_function = prev_func_id;

Ok(TypedNode::Lambda { span, func_id: lambda_func_id, type_id: func_type_id })
let resolved_type_id = type_hint.unwrap_or(func_type_id);
Ok(TypedNode::Lambda { span, func_id: lambda_func_id, type_id: func_type_id, resolved_type_id })
}
AstNode::Try(_, _) => todo!(),
n => unreachable!("Internal error: node is not an expression: {:?}", n),
Expand Down
4 changes: 3 additions & 1 deletion abra_llvm/src/compiler2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1438,7 +1438,9 @@ impl<'a> LLVMCompiler2<'a> {
_ => unreachable!()
}
}
TypedNode::Lambda { .. } => todo!(),
TypedNode::Lambda { func_id, resolved_type_id, .. } => {
Some(self.make_function_value(func_id, resolved_type_id, resolved_generics))
}
TypedNode::Assignment { kind, expr, .. } => {
let expr_val = self.visit_expression(expr, resolved_generics).unwrap();

Expand Down
65 changes: 35 additions & 30 deletions abra_llvm/tests/arrays.abra
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// Test raw construction of array
func testRawConstruction() {
(() => {
val arr = Array<Int>(length: 0, _buffer: Pointer.malloc<Int>(1), _capacity: 1)
arr.push(12)
/// Expect: [12]
println(arr)
}
testRawConstruction()
})()

// Test array literal construction
func testLiteralConstruction() {
(() => {
val emptyArr: Int[] = []
/// Expect: []
println(emptyArr)
Expand All @@ -28,19 +27,17 @@ func testLiteralConstruction() {
val nestedArr = [[1, 2], [3, 4], [5, 6]]
/// Expect: [[1, 2], [3, 4], [5, 6]]
println(nestedArr)
}
testLiteralConstruction()
})()

// Indexing (also Array#get(index: Int))
func testIndexing() {
(() => {
val arr = [1, 2, 3]
/// Expect: None 1 2 3 1 2 3 None
println(arr[-4], arr[-3], arr[-2], arr[-1], arr[0], arr[1], arr[2], arr[3])
}
testIndexing()
})()

// Range indexing (also Array#getRange(startIndex: Int, endIndex: Int))
func testRangeIndexing() {
(() => {
val arr = [1, 2, 3, 4, 5]

/// Expect: [2, 3, 4] [2, 3, 4]
Expand All @@ -60,11 +57,10 @@ func testRangeIndexing() {

/// Expect: [2, 3, 4, 5] [1]
println(arr[x:], arr[:x])
}
testRangeIndexing()
})()

// Array#pop
func testArrayPop() {
(() => {
val arr = [1, 2, 3]
/// Expect: 3
println(arr.pop())
Expand All @@ -85,62 +81,71 @@ func testArrayPop() {
println(arr.pop())
/// Expect: []
println(arr)
}
testArrayPop()
})()

// Array#map
func addOne(i: Int): Int = i + 1
func exclaim(i: Int, _: Int, x = "!"): String = "$i$x"
func testArrayMap() {
(() => {
val arr = [1, 2, 3, 4]

/// Expect: [2, 3, 4, 5]
println(arr.map(addOne))
/// Expect: [2, 3, 4, 5]
println(arr.map(i => i + 1))

/// Expect: [1!, 2!, 3!, 4!]
println(arr.map(exclaim))
}
testArrayMap()
/// Expect: [1!, 2!, 3!, 4!]
println(arr.map((i, _, x = "!") => "$i$x"))
})()

// Array#filter
func isEven(i: Int): Bool = i % 2 == 0
func testArrayFilter() {
(() => {
val arr = [1, 2, 3, 4]

/// Expect: [2, 4]
println(arr.filter(isEven))
}
testArrayFilter()
/// Expect: [2, 4]
println(arr.filter(x => x % 2 == 0))
})()

// Array#reduce
func doSum(acc: Int, i: Int): Int = acc + i
func testArrayReduce() {
(() => {
val arr = [1, 2, 3, 4]

/// Expect: 10
println(arr.reduce(0, doSum))
}
testArrayReduce()
/// Expect: 10
println(arr.reduce(0, (acc, i) => acc + i))
})()

// Array#forEach
func printItem(item: Int) = println(item)
func testArrayForEach() {
(() => {
val arr = [1, 2, 3, 4]

/// Expect: 1
/// Expect: 2
/// Expect: 3
/// Expect: 4
arr.forEach(printItem)
}
testArrayForEach()
/// Expect: 1
/// Expect: 2
/// Expect: 3
/// Expect: 4
arr.forEach(i => {
println(i)
})
})()

// Array#join
func testArrayJoin() {
(() => {
val arr = [123, 456, 789]
/// Expect: 123|456|789
println(arr.join("|"))
/// Expect: 123456789
println(arr.join())
}
testArrayJoin()
})()
41 changes: 17 additions & 24 deletions abra_llvm/tests/strings.abra
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Test raw string construction
func testRawStringConstruction() {
(() => {
val s = String.withLength(3)
s._buffer.offset(0).store(Byte.fromInt(65))
s._buffer.offset(1).store(Byte.fromInt(66))
Expand All @@ -8,17 +8,16 @@ func testRawStringConstruction() {
println(s)
/// Expect: false
println(s.isEmpty())
}
testRawStringConstruction()
})()

func testLiteralConstruction() {
// Test literal construction
(() => {
/// Expect: hello
println("hello")
}
testLiteralConstruction()
})()

// + operator (also String#concat<T>(other: T, *others: Any[]), with no `others`)
func testPlusOperator() {
(() => {
/// Expect: helloworld
println("hello" + "world")
/// Expect: hello12 12hello
Expand All @@ -29,27 +28,24 @@ func testPlusOperator() {
println("hello" + true, true + "hello")
/// Expect: hello[1, 2, 3] [1, 2, 3]hello
println("hello" + [1, 2, 3], [1, 2, 3] + "hello")
}
testPlusOperator()
})()

// Interpolation (also String#concat<T>(other: T, *others: Any[]))
func testInterpolation() {
(() => {
val array = [1, 2, 3]
/// Expect: length of [1, 2, 3] is 3
println("length of $array is ${array.length}")
}
testInterpolation()
})()

// Indexing (also String#get(index: Int))
func testIndexing() {
(() => {
val s = "abc"
/// Expect: | a b c a b c |
println("|", s[-4], s[-3], s[-2], s[-1], s[0], s[1], s[2], s[3], "|")
}
testIndexing()
})()

// Range indexing (also String#getRange(startIndex: Int, endIndex: Int))
func testRangeIndexing() {
(() => {
val s = "hello"

/// Expect: h e l l o
Expand Down Expand Up @@ -78,23 +74,21 @@ func testRangeIndexing() {

/// Expect: ello h
println(s[a:], s[:a])
}
testRangeIndexing()
})()

// String#isEmpty
func testIsEmpty() {
(() => {
val empty = ""
/// Expect: true
println(empty.isEmpty())

val nonEmpty = "hello"
/// Expect: false
println(nonEmpty.isEmpty())
}
testIsEmpty()
})()

// String#concat
func testStringConcat() {
(() => {
/// Expect: helloworld
println("hello".concat("world"))
/// Expect: hello12
Expand All @@ -107,5 +101,4 @@ func testStringConcat() {
println("hello".concat([1, 2, 3]))
/// Expect: hello12.3true[1, 2, 3]
println("hello".concat(1, 2.3, true, [1, 2, 3]))
}
testStringConcat()
})()

0 comments on commit 72f62f4

Please sign in to comment.