Skip to content

Commit 2c73b42

Browse files
committed
Rework the 'map' built-in
Make it a method of the 'Iterable' interface. Also add a special implementation for mapping result on lazy collections in order to preserve the laziness.
1 parent a625ac3 commit 2c73b42

File tree

12 files changed

+147
-62
lines changed

12 files changed

+147
-62
lines changed

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/cli/BaseLKQLChecker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public abstract class BaseLKQLChecker extends AbstractLanguageLauncher {
3232
public static final String checkerSource =
3333
"""
3434
val analysis_units = specified_units()
35-
map(analysis_units, (unit) => node_checker(unit.root))
36-
map(analysis_units, (unit) => unit_checker(unit))
35+
analysis_units.map((unit) => node_checker(unit.root)).to_list
36+
analysis_units.map((unit) => unit_checker(unit)).to_list
3737
""";
3838

3939
// ----- Constructors -----

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/cli/GNATCheckWorker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ public LKQLRuleFileError(String message) {
641641
val analysis_units = specified_units()
642642
val roots = [unit.root for unit in analysis_units]
643643
644-
map(roots, (root) => node_checker(root))
645-
map(analysis_units, (unit) => unit_checker(unit))
644+
roots.map((root) => node_checker(root)).to_list
645+
analysis_units.map((unit) => unit_checker(unit)).to_list
646646
""";
647647
}

lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/BuiltInFunctions.java

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -432,43 +432,6 @@ public boolean executeRepeating(VirtualFrame frame) {
432432
}
433433
}
434434

435-
@BuiltInFunction(name = "map", doc = "Given a collection, a mapping function")
436-
abstract static class MapExpr extends BuiltInBody {
437-
438-
@Specialization(
439-
limit = Constants.SPECIALIZED_LIB_LIMIT,
440-
guards = "function.parameterNames.length == 1"
441-
)
442-
protected LKQLList onValidArgs(
443-
Iterable iterable,
444-
LKQLFunction function,
445-
@CachedLibrary("function") InteropLibrary functionLibrary
446-
) {
447-
Object[] res = new Object[(int) iterable.size()];
448-
int i = 0;
449-
Iterator iterator = iterable.iterator();
450-
451-
while (iterator.hasNext()) {
452-
try {
453-
res[i] = functionLibrary.execute(
454-
function,
455-
function.closure.getContent(),
456-
iterator.next()
457-
);
458-
} catch (
459-
ArityException | UnsupportedTypeException | UnsupportedMessageException e
460-
) {
461-
// TODO: Implement runtime checks in the LKQLFunction class and base computing
462-
// on them (#138)
463-
throw LKQLRuntimeException.fromJavaException(e, this);
464-
}
465-
i++;
466-
}
467-
468-
return new LKQLList(res);
469-
}
470-
}
471-
472435
@BuiltInFunction(
473436
name = "profile",
474437
doc = "Given any object, if it is a callable, return its profile as text"

lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/IterableMethods.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@
88
import com.adacore.lkql_jit.annotations.BuiltInMethod;
99
import com.adacore.lkql_jit.annotations.BuiltinMethodContainer;
1010
import com.adacore.lkql_jit.built_ins.BuiltInBody;
11+
import com.adacore.lkql_jit.runtime.values.LKQLFunction;
1112
import com.adacore.lkql_jit.runtime.values.LKQLTuple;
1213
import com.adacore.lkql_jit.runtime.values.interfaces.Iterable;
1314
import com.adacore.lkql_jit.runtime.values.interfaces.Iterator;
15+
import com.adacore.lkql_jit.runtime.values.lists.BaseLKQLLazyList;
1416
import com.adacore.lkql_jit.runtime.values.lists.LKQLList;
17+
import com.adacore.lkql_jit.runtime.values.lists.LKQLMapResult;
18+
import com.adacore.lkql_jit.utils.Constants;
1519
import com.adacore.lkql_jit.utils.LKQLTypesHelper;
1620
import com.oracle.truffle.api.dsl.Specialization;
17-
import java.util.LinkedList;
18-
import java.util.List;
21+
import com.oracle.truffle.api.interop.InteropLibrary;
22+
import com.oracle.truffle.api.library.CachedLibrary;
1923

2024
/** This class contains all built-in methods for the iterable type in the LKQL language. */
2125
@BuiltinMethodContainer(
@@ -60,14 +64,14 @@ abstract static class ToListExpr extends BuiltInBody {
6064
@Specialization
6165
public LKQLList doGeneric(Iterable self) {
6266
// Create a new list from the iterable
63-
List<Object> resList = new LinkedList<>();
67+
var res = new Object[(int) self.size()];
6468
Iterator iterator = self.iterator();
65-
while (iterator.hasNext()) {
66-
resList.add(iterator.next());
69+
for (int i = 0; i < res.length; i++) {
70+
res[i] = iterator.next();
6771
}
6872

6973
// Return the new list value
70-
return new LKQLList(resList.toArray(new Object[0]));
74+
return new LKQLList(res);
7175
}
7276
}
7377

@@ -79,4 +83,26 @@ public long doGeneric(Iterable self) {
7983
return self.size();
8084
}
8185
}
86+
87+
@BuiltInMethod(
88+
name = "map",
89+
doc = """
90+
Given an iterable and a function that takes one argument and return a value, return \
91+
a new iterable, result of the application of the function on all iterable elements.
92+
The returned iterable value is lazy."""
93+
)
94+
abstract static class MapExpr extends BuiltInBody {
95+
96+
@Specialization(
97+
limit = Constants.SPECIALIZED_LIB_LIMIT,
98+
guards = "function.parameterNames.length == 1"
99+
)
100+
protected BaseLKQLLazyList onIterable(
101+
Iterable iterable,
102+
LKQLFunction function,
103+
@CachedLibrary("function") InteropLibrary functionLibrary
104+
) {
105+
return new LKQLMapResult(iterable, function, functionLibrary);
106+
}
107+
}
82108
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// Copyright (C) 2005-2025, AdaCore
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
//
5+
6+
package com.adacore.lkql_jit.runtime.values.lists;
7+
8+
import com.adacore.lkql_jit.exception.LKQLRuntimeException;
9+
import com.adacore.lkql_jit.runtime.ListStorage;
10+
import com.adacore.lkql_jit.runtime.values.LKQLFunction;
11+
import com.adacore.lkql_jit.runtime.values.interfaces.Iterable;
12+
import com.adacore.lkql_jit.runtime.values.interfaces.Iterator;
13+
import com.oracle.truffle.api.interop.ArityException;
14+
import com.oracle.truffle.api.interop.InteropLibrary;
15+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
16+
import com.oracle.truffle.api.interop.UnsupportedTypeException;
17+
18+
/** This class represents the result of a mapping operation on a lazy list. */
19+
public class LKQLMapResult extends BaseLKQLLazyList {
20+
21+
// ----- Attributes -----
22+
23+
/** Collection that is mapped. */
24+
private final Iterator generatorIterator;
25+
26+
/** Function to perform the mapping. */
27+
private final LKQLFunction mappingFunction;
28+
29+
/** Interop library used to call the mapping function. */
30+
private final InteropLibrary functionLibrary;
31+
32+
// ----- Constructors -----
33+
34+
public LKQLMapResult(
35+
Iterable generator,
36+
LKQLFunction mappingFunction,
37+
InteropLibrary functionLibrary
38+
) {
39+
super(new ListStorage<>(16));
40+
this.generatorIterator = generator.iterator();
41+
this.mappingFunction = mappingFunction;
42+
this.functionLibrary = functionLibrary;
43+
}
44+
45+
// ----- Lazy list required methods -----
46+
47+
@Override
48+
protected void initCacheTo(long n) {
49+
try {
50+
while (this.generatorIterator.hasNext() && (n >= this.cache.size() || n < 0)) {
51+
this.cache.append(
52+
this.functionLibrary.execute(
53+
this.mappingFunction,
54+
this.mappingFunction.closure.getContent(),
55+
this.generatorIterator.next()
56+
)
57+
);
58+
}
59+
} catch (ArityException | UnsupportedTypeException | UnsupportedMessageException e) {
60+
throw LKQLRuntimeException.fromJavaException(e, null);
61+
}
62+
}
63+
}

testsuite/tests/gnatcheck_errors/generic_inst_walking/test.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ Total gnatcheck failures: 1
3232

3333
6. Gnatcheck internal errors
3434

35-
<working-dir>/test.adb:2:04: error: internal issue at checker.lkql:4:22: test.adb:2:4-2:28: dereferencing a null access
35+
<working-dir>/test.adb:2:04: error: internal issue at checker.lkql:4:21: test.adb:2:4-2:28: dereferencing a null access
3636

3737
>>>program returned status code 2
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Perform a simple mapping operation
2+
val lst = [1, 2, 3]
3+
val m_1 = lst.map((x) => x * 2)
4+
print(m_1)
5+
print(m_1.to_list)
6+
7+
# Ensure "map" implementation is lazy
8+
val m_2 = lst.map((x) => { print("Processing " & x.img); x * x })
9+
print(m_2)
10+
print(m_2[2])
11+
print(m_2[3])
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<LazyList>
2+
[2, 4, 6]
3+
<LazyList>
4+
Processing 1
5+
Processing 2
6+
4
7+
Processing 3
8+
9
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
driver: 'interpreter'
2+
project: 'default_project/default.gpr'
3+
lkt_refactor: True

testsuite/tests/rewriting_errors/invalid_rewriting/test.out

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ main.adb:3:4: rule violation: Custom rule violation
33
| ^^^^^^^^^
44

55
error: Error(s) while applying a rewriting context: [Missing ';' at <3:9-3:12>]
6-
checker.lkql:2:31 in <anonymous>
7-
2 | map(analysis_units, (unit) => node_checker(unit.root))
8-
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
checker.lkql:2:30 in <anonymous>
7+
2 | analysis_units.map((unit) => node_checker(unit.root)).to_list
8+
| ^^^^^^^^^^^^^^^^^^^^^^^
99

1010
checker.lkql:2:1 in checker.lkql
11-
2 | map(analysis_units, (unit) => node_checker(unit.root))
12-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11+
2 | analysis_units.map((unit) => node_checker(unit.root)).to_list
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)