0
# REPL Support
1
2
Legacy REPL API compatibility layer for interactive script evaluation and compilation with incremental execution capabilities.
3
4
## Capabilities
5
6
### JvmReplCompiler
7
8
REPL compilation wrapper that provides legacy REPL API compatibility for incremental script compilation.
9
10
```kotlin { .api }
11
/**
12
* REPL compiler implementation for legacy REPL APIs
13
* @param scriptCompilationConfiguration Compilation configuration for REPL scripts
14
* @param hostConfiguration Host configuration with JVM-specific settings
15
*/
16
class JvmReplCompiler(
17
val scriptCompilationConfiguration: ScriptCompilationConfiguration,
18
val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration
19
) : ReplCompilerWithoutCheck {
20
21
/**
22
* Creates new REPL compilation state
23
* @param lock Read-write lock for thread synchronization
24
* @returns New REPL stage state for compilation
25
*/
26
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*>
27
28
/**
29
* Compiles a single REPL code line
30
* @param state Current REPL compilation state
31
* @param codeLine REPL code line to compile
32
* @returns Compilation result with compiled classes or errors
33
*/
34
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult
35
}
36
```
37
38
### JvmReplEvaluator
39
40
REPL evaluation wrapper that executes compiled REPL scripts with state management and history tracking.
41
42
```kotlin { .api }
43
/**
44
* REPL evaluator implementation for legacy REPL APIs
45
* @param baseScriptEvaluationConfiguration Base evaluation configuration
46
* @param scriptEvaluator Script evaluator for executing compiled scripts
47
*/
48
class JvmReplEvaluator(
49
val baseScriptEvaluationConfiguration: ScriptEvaluationConfiguration,
50
val scriptEvaluator: ScriptEvaluator = BasicJvmScriptEvaluator()
51
) : ReplEvaluator {
52
53
/**
54
* Creates new REPL evaluation state
55
* @param lock Read-write lock for thread synchronization
56
* @returns New REPL stage state for evaluation
57
*/
58
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*>
59
60
/**
61
* Evaluates compiled REPL script classes
62
* @param state Current REPL evaluation state
63
* @param compileResult Compiled classes from REPL compiler
64
* @param scriptArgs Optional script arguments with types
65
* @param invokeWrapper Optional wrapper for function invocation
66
* @returns Evaluation result with value or error
67
*/
68
override fun eval(
69
state: IReplStageState<*>,
70
compileResult: ReplCompileResult.CompiledClasses,
71
scriptArgs: ScriptArgsWithTypes? = null,
72
invokeWrapper: InvokeWrapper? = null
73
): ReplEvalResult
74
}
75
```
76
77
**Usage Examples:**
78
79
```kotlin
80
import kotlin.script.experimental.jvmhost.repl.*
81
import kotlin.script.experimental.api.*
82
import kotlin.script.experimental.jvm.util.*
83
import java.util.concurrent.locks.ReentrantReadWriteLock
84
85
// Create REPL compiler and evaluator
86
val replConfig = ScriptCompilationConfiguration {
87
dependencies(JvmDependency(kotlinStdlib))
88
compilerOptions.append("-language-version", "2.0")
89
}
90
91
val evalConfig = ScriptEvaluationConfiguration {
92
// REPL-specific evaluation configuration
93
enableScriptsInstancesSharing()
94
}
95
96
val replCompiler = JvmReplCompiler(
97
scriptCompilationConfiguration = replConfig,
98
hostConfiguration = defaultJvmScriptingHostConfiguration
99
)
100
101
val replEvaluator = JvmReplEvaluator(
102
baseScriptEvaluationConfiguration = evalConfig
103
)
104
105
// Initialize REPL states
106
val lock = ReentrantReadWriteLock()
107
val compilerState = replCompiler.createState(lock)
108
val evaluatorState = replEvaluator.createState(lock)
109
110
// Interactive REPL session
111
val replLines = listOf(
112
"val x = 42",
113
"val y = x * 2",
114
"data class Person(val name: String, val age: Int)",
115
"val person = Person(\"Alice\", 30)",
116
"println(\"Person: \$person, y = \$y\")",
117
"person.age + y"
118
)
119
120
replLines.forEachIndexed { index, code ->
121
println("[$index] $code")
122
123
// Compile line
124
val codeLine = ReplCodeLine(index, 0, code)
125
val compileResult = replCompiler.compile(compilerState, codeLine)
126
127
when (compileResult) {
128
is ReplCompileResult.CompiledClasses -> {
129
println(" Compiled successfully")
130
131
// Evaluate compiled code
132
val evalResult = replEvaluator.eval(evaluatorState, compileResult)
133
134
when (evalResult) {
135
is ReplEvalResult.ValueResult -> {
136
println(" Result: ${evalResult.value} : ${evalResult.type}")
137
}
138
is ReplEvalResult.UnitResult -> {
139
println(" Executed successfully")
140
}
141
is ReplEvalResult.Error -> {
142
println(" Evaluation error: ${evalResult.error.message}")
143
}
144
is ReplEvalResult.Incomplete -> {
145
println(" Incomplete input")
146
}
147
}
148
}
149
is ReplCompileResult.Error -> {
150
println(" Compilation error: ${compileResult.message}")
151
}
152
is ReplCompileResult.Incomplete -> {
153
println(" Incomplete input - expecting more")
154
}
155
}
156
println()
157
}
158
```
159
160
### JvmReplEvaluatorState
161
162
REPL evaluator state implementation that manages execution history and supports script instance sharing.
163
164
```kotlin { .api }
165
/**
166
* REPL evaluator state with history management
167
* @param scriptEvaluationConfiguration Evaluation configuration for REPL scripts
168
* @param lock Read-write lock for thread synchronization
169
*/
170
open class JvmReplEvaluatorState(
171
val scriptEvaluationConfiguration: ScriptEvaluationConfiguration,
172
lock: ReentrantReadWriteLock
173
) : IReplStageState<Any> {
174
175
/** History of evaluated script instances and their results */
176
val history: IReplStageHistory<Pair<KClass<*>?, Any?>>
177
178
/** Current generation/version of the REPL state */
179
val currentGeneration: Int
180
}
181
```
182
183
### ReplStageHistoryWithReplace
184
185
Enhanced REPL history implementation that supports replacing and updating previous evaluations.
186
187
```kotlin { .api }
188
/**
189
* REPL stage history with replacement capabilities
190
* @param lock Read-write lock for thread synchronization
191
*/
192
open class ReplStageHistoryWithReplace<T>(lock: ReentrantReadWriteLock) : IReplStageHistory<T> {
193
194
/**
195
* Replaces an existing history entry
196
* @param id Line identifier to replace
197
* @param item New item to replace with
198
* @returns True if replacement succeeded, false if not found
199
*/
200
fun replace(id: ILineId, item: T): Boolean
201
202
/**
203
* Replaces existing entry or pushes new one if not found
204
* @param id Line identifier
205
* @param item Item to replace or add
206
*/
207
fun replaceOrPush(id: ILineId, item: T)
208
209
/**
210
* Gets sequence of items that were evaluated before the given line
211
* @param id Line identifier to get previous items for
212
* @returns Sequence of previous items in order
213
*/
214
fun previousItems(id: ILineId): Sequence<T>
215
}
216
```
217
218
**Advanced REPL Example with History Management:**
219
220
```kotlin
221
import kotlin.script.experimental.jvmhost.repl.*
222
223
// Create REPL with custom history management
224
class InteractiveReplSession {
225
private val lock = ReentrantReadWriteLock()
226
private val replCompiler = JvmReplCompiler(
227
ScriptCompilationConfiguration {
228
dependencies(JvmDependency(kotlinStdlib))
229
// Enable script instance sharing for REPL
230
refineConfiguration {
231
onAnnotations { context ->
232
ScriptCompilationConfiguration(context.compilationConfiguration) {
233
implicitReceivers(String::class) // Example: implicit string receiver
234
}.asSuccess()
235
}
236
}
237
}
238
)
239
240
private val replEvaluator = JvmReplEvaluator(
241
ScriptEvaluationConfiguration {
242
enableScriptsInstancesSharing()
243
// Provide previous results as context
244
providedProperties("results" to List::class)
245
}
246
)
247
248
private val compilerState = replCompiler.createState(lock)
249
private val evaluatorState = replEvaluator.createState(lock)
250
private val results = mutableListOf<Any?>()
251
252
fun evalLine(lineNumber: Int, code: String): ReplEvalResult {
253
val codeLine = ReplCodeLine(lineNumber, 0, code)
254
255
// Compile
256
val compileResult = replCompiler.compile(compilerState, codeLine)
257
258
return when (compileResult) {
259
is ReplCompileResult.CompiledClasses -> {
260
// Evaluate with context
261
val evalResult = replEvaluator.eval(evaluatorState, compileResult)
262
263
// Store result for future reference
264
when (evalResult) {
265
is ReplEvalResult.ValueResult -> {
266
results.add(evalResult.value)
267
}
268
is ReplEvalResult.UnitResult -> {
269
results.add(Unit)
270
}
271
else -> {
272
results.add(null)
273
}
274
}
275
276
evalResult
277
}
278
is ReplCompileResult.Error -> {
279
ReplEvalResult.Error.CompileTime(
280
"Compilation failed: ${compileResult.message}"
281
)
282
}
283
is ReplCompileResult.Incomplete -> {
284
ReplEvalResult.Incomplete()
285
}
286
}
287
}
288
289
fun replaceLineAndReevaluate(lineNumber: Int, newCode: String): List<ReplEvalResult> {
290
// This would require more sophisticated state management
291
// to re-evaluate dependent lines after replacement
292
val results = mutableListOf<ReplEvalResult>()
293
294
// Replace the specific line
295
val newResult = evalLine(lineNumber, newCode)
296
results.add(newResult)
297
298
// Re-evaluate subsequent lines that might depend on this change
299
// This is a simplified example - real implementation would need
300
// dependency tracking and selective re-evaluation
301
302
return results
303
}
304
305
fun getHistory(): List<Any?> = results.toList()
306
307
fun getCurrentGeneration(): Int {
308
return (evaluatorState as? JvmReplEvaluatorState)?.currentGeneration ?: 0
309
}
310
}
311
312
// Usage example
313
val replSession = InteractiveReplSession()
314
315
// Interactive session
316
val inputs = listOf(
317
"val numbers = listOf(1, 2, 3, 4, 5)",
318
"val doubled = numbers.map { it * 2 }",
319
"println(\"Original: \$numbers\")",
320
"println(\"Doubled: \$doubled\")",
321
"doubled.sum()"
322
)
323
324
inputs.forEachIndexed { index, input ->
325
println("> $input")
326
327
val result = replSession.evalLine(index, input)
328
when (result) {
329
is ReplEvalResult.ValueResult -> {
330
println(" = ${result.value} : ${result.type}")
331
}
332
is ReplEvalResult.UnitResult -> {
333
println(" (executed)")
334
}
335
is ReplEvalResult.Error -> {
336
println(" Error: ${result.message}")
337
}
338
is ReplEvalResult.Incomplete -> {
339
println(" (incomplete)")
340
}
341
}
342
}
343
344
println("\nSession history: ${replSession.getHistory()}")
345
println("Generation: ${replSession.getCurrentGeneration()}")
346
```
347
348
## Source Code Integration
349
350
Utility classes for converting between REPL code lines and standard source code representations.
351
352
### SourceCodeFromReplCodeLine
353
354
```kotlin { .api }
355
/**
356
* Internal source code implementation from REPL code line
357
* @param codeLine REPL code line to wrap
358
* @param compilationConfiguration Compilation configuration for context
359
*/
360
internal class SourceCodeFromReplCodeLine(
361
val codeLine: ReplCodeLine,
362
compilationConfiguration: ScriptCompilationConfiguration
363
) : SourceCode {
364
365
/** Script text content from REPL line */
366
override val text: String
367
368
/** Script name derived from REPL line identifier */
369
override val name: String
370
371
/** Location identifier for error reporting */
372
override val locationId: String?
373
}
374
```
375
376
### REPL Extension Functions
377
378
```kotlin { .api }
379
/**
380
* Converts REPL code line to source code for compilation
381
* @param compilationConfiguration Compilation configuration for context
382
* @returns SourceCode representation of the REPL line
383
*/
384
internal fun ReplCodeLine.toSourceCode(
385
compilationConfiguration: ScriptCompilationConfiguration
386
): SourceCode
387
```
388
389
## Internal Utility Classes
390
391
The REPL support includes several internal utility classes for bridging between REPL-specific types and standard scripting API types.
392
393
### SourceCodeFromReplCodeLine
394
395
```kotlin { .api }
396
/**
397
* Internal source code implementation that wraps REPL code lines
398
* @param codeLine The REPL code line to wrap
399
* @param compilationConfiguration Compilation configuration for context
400
*/
401
internal class SourceCodeFromReplCodeLine(
402
val codeLine: ReplCodeLine,
403
compilationConfiguration: ScriptCompilationConfiguration
404
) : SourceCode {
405
406
/** Script text content from the REPL line */
407
override val text: String
408
409
/** Script name derived from REPL line identifier and sequence number */
410
override val name: String
411
412
/** Location identifier for error reporting and source tracking */
413
override val locationId: String?
414
}
415
```
416
417
**Note**: This class is internal to the REPL implementation and is used to bridge between REPL-specific `ReplCodeLine` objects and the standard `SourceCode` interface used by the compilation system.
418
419
**Source Code Integration Example:**
420
421
```kotlin
422
import kotlin.script.experimental.jvmhost.repl.*
423
424
// Custom REPL implementation with source code tracking
425
class SourceTrackingRepl {
426
private val sourceHistory = mutableMapOf<Int, SourceCode>()
427
428
fun compileWithSourceTracking(
429
compiler: JvmReplCompiler,
430
state: IReplStageState<*>,
431
lineNumber: Int,
432
code: String
433
): ReplCompileResult {
434
val codeLine = ReplCodeLine(lineNumber, 0, code)
435
436
// Convert to source code for tracking
437
val sourceCode = codeLine.toSourceCode(compiler.scriptCompilationConfiguration)
438
sourceHistory[lineNumber] = sourceCode
439
440
// Compile normally
441
val result = compiler.compile(state, codeLine)
442
443
// Log compilation with source information
444
when (result) {
445
is ReplCompileResult.CompiledClasses -> {
446
println("Compiled line $lineNumber: ${sourceCode.name}")
447
println(" Source location: ${sourceCode.locationId}")
448
println(" Text length: ${sourceCode.text.length} characters")
449
}
450
is ReplCompileResult.Error -> {
451
println("Compilation failed for line $lineNumber")
452
println(" Source: ${sourceCode.name}")
453
println(" Error: ${result.message}")
454
}
455
}
456
457
return result
458
}
459
460
fun getSourceForLine(lineNumber: Int): SourceCode? {
461
return sourceHistory[lineNumber]
462
}
463
464
fun getAllSources(): Map<Int, SourceCode> {
465
return sourceHistory.toMap()
466
}
467
}
468
469
// Usage
470
val sourceTracker = SourceTrackingRepl()
471
val compiler = JvmReplCompiler(ScriptCompilationConfiguration.Default)
472
val state = compiler.createState(ReentrantReadWriteLock())
473
474
// Compile with source tracking
475
val result = sourceTracker.compileWithSourceTracking(
476
compiler, state, 1, "val greeting = \"Hello, REPL!\""
477
)
478
479
// Access source information
480
val source = sourceTracker.getSourceForLine(1)
481
println("Source name: ${source?.name}")
482
println("Source location: ${source?.locationId}")
483
```
484
485
## Integration with Modern Scripting API
486
487
The REPL support provides bridge functionality between legacy REPL APIs and modern Kotlin scripting infrastructure.
488
489
### Migration from Legacy REPL
490
491
```kotlin
492
// Legacy REPL usage (deprecated)
493
// val legacyRepl = KotlinJsr223JvmLocalScriptEngine()
494
495
// Modern REPL usage through compatibility layer
496
val modernReplCompiler = JvmReplCompiler(
497
ScriptCompilationConfiguration {
498
dependencies(JvmDependency(kotlinStdlib))
499
baseClass(KotlinType(Any::class))
500
}
501
)
502
503
val modernReplEvaluator = JvmReplEvaluator(
504
ScriptEvaluationConfiguration {
505
enableScriptsInstancesSharing()
506
jvm {
507
baseClassLoader(Thread.currentThread().contextClassLoader)
508
}
509
}
510
)
511
512
// Bridge to JSR-223 if needed
513
val jsr223Engine = KotlinJsr223ScriptEngineImpl(
514
factory = KotlinJsr223DefaultScriptEngineFactory(),
515
baseCompilationConfiguration = modernReplCompiler.scriptCompilationConfiguration,
516
baseEvaluationConfiguration = modernReplEvaluator.baseScriptEvaluationConfiguration
517
) { context ->
518
// Extract arguments from JSR-223 context
519
val args = context.getBindings(ScriptContext.ENGINE_SCOPE)["args"] as? Array<String>
520
args?.let { ScriptArgsWithTypes(it, arrayOf(Array<String>::class)) }
521
}
522
```
523
524
### Performance Considerations
525
526
REPL support is optimized for interactive use but has specific performance characteristics:
527
528
- **Compilation Overhead**: Each line requires separate compilation
529
- **State Management**: History tracking increases memory usage
530
- **Thread Safety**: Synchronization adds latency to operations
531
- **Dependency Resolution**: Repeated dependency loading for each line
532
533
**Performance Optimization Example:**
534
535
```kotlin
536
class OptimizedReplSession {
537
// Reuse configurations to avoid repeated setup
538
private val sharedCompilationConfig = ScriptCompilationConfiguration {
539
dependencies(JvmDependency(kotlinStdlib))
540
// Cache dependencies
541
hostConfiguration(ScriptingHostConfiguration {
542
jvm {
543
compilationCache(FileBasedScriptCache(File("repl-cache")))
544
}
545
})
546
}
547
548
// Batch compilation for related lines
549
fun compileMultipleLines(lines: List<String>): List<ReplCompileResult> {
550
val compiler = JvmReplCompiler(sharedCompilationConfig)
551
val state = compiler.createState(ReentrantReadWriteLock())
552
553
return lines.mapIndexed { index, code ->
554
val codeLine = ReplCodeLine(index, 0, code)
555
compiler.compile(state, codeLine)
556
}
557
}
558
559
// Lazy evaluation for expensive operations
560
fun evaluateWithLazyResults(
561
compileResults: List<ReplCompileResult.CompiledClasses>
562
): Sequence<ReplEvalResult> {
563
val evaluator = JvmReplEvaluator(ScriptEvaluationConfiguration.Default)
564
val state = evaluator.createState(ReentrantReadWriteLock())
565
566
return compileResults.asSequence().map { compiled ->
567
evaluator.eval(state, compiled)
568
}
569
}
570
}
571
```
572
573
## Types
574
575
### REPL Evaluation Result Types
576
577
```kotlin { .api }
578
// Imported from kotlin.script.experimental.api and related packages
579
sealed class ReplEvalResult : Serializable {
580
581
/** Successful evaluation with a returned value */
582
class ValueResult(
583
val name: String,
584
val value: Any?,
585
val type: String?,
586
val snippetInstance: Any? = null
587
) : ReplEvalResult()
588
589
/** Successful evaluation with no return value */
590
class UnitResult : ReplEvalResult()
591
592
/** Incomplete input requiring more code */
593
class Incomplete(val message: String) : ReplEvalResult()
594
595
/** History state mismatch error */
596
class HistoryMismatch(val lineNo: Int) : ReplEvalResult()
597
598
/** Evaluation errors */
599
sealed class Error(val message: String) : ReplEvalResult() {
600
/** Runtime execution error */
601
class Runtime(message: String, val cause: Throwable? = null) : Error(message)
602
603
/** Compile-time error */
604
class CompileTime(message: String, val location: CompilerMessageLocation? = null) : Error(message)
605
}
606
}
607
608
/** Compilation result for REPL code lines */
609
sealed class ReplCompileResult {
610
class CompiledClasses(
611
val lineId: ReplCodeLine.Id,
612
val data: Any,
613
val hasResult: Boolean,
614
val classpathAddendum: List<File> = emptyList()
615
) : ReplCompileResult()
616
617
class Incomplete : ReplCompileResult()
618
619
class Error(
620
val message: String,
621
val location: CompilerMessageLocation? = null
622
) : ReplCompileResult()
623
}
624
```