docs on new function metadata API

This commit is contained in:
Sergey Chernov 2026-07-28 00:36:12 +04:00
parent 898ebfa536
commit 2f118a1fef
7 changed files with 138 additions and 6 deletions

View File

@ -1248,7 +1248,7 @@ scopeB()
assertThrows { 42.description } assertThrows { 42.description }
``` ```
This isolation ensures that libraries can use extensions internally without worrying about name collisions with other libraries or the user's code. When a module is imported using `use`, its top-level extensions become available in the importing scope. This isolation ensures that libraries can use extensions internally without worrying about name collisions with other libraries or the user's code. When a package is imported using `import`, its top-level extensions become available in the importing scope. An imported extension property's getter and setter retain the lexical scope of the package that declared them, so they can safely call that package's private helpers and bound `extern` functions and properties.
## dynamic symbols ## dynamic symbols

View File

@ -65,6 +65,8 @@ Primary sources used: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/{Parser,T
- Declaration-side variadic parameters use ellipsis suffix: - Declaration-side variadic parameters use ellipsis suffix:
- functions: `fun f(head, tail...) { ... }` - functions: `fun f(head, tail...) { ... }`
- lambdas: `{ x, rest... -> ... }` - lambdas: `{ x, rest... -> ... }`
- a typed parameter `values: T...` has signature type `T...`, but inside the callable `values`
is a `List<T>` containing the collected arguments.
- Call-side splats use `...expr` and are expanded by argument kind: - Call-side splats use `...expr` and are expanded by argument kind:
- positional splat: `f(...[1,2,3])` - positional splat: `f(...[1,2,3])`
- named splat: `f(...{ a: 1, b: 2 })` (map-style) - named splat: `f(...{ a: 1, b: 2 })` (map-style)

View File

@ -41,6 +41,18 @@ Ellipsis can also appear in **function types** to denote a variadic position:
Ellipsis argument receives what is left from arguments after processing regular one that could be before or after. Ellipsis argument receives what is left from arguments after processing regular one that could be before or after.
For a typed declaration, the ellipsis type describes each argument while the name inside the
function is a list of those arguments. Thus `values: Int...` is `Int...` in the function signature
and `List<Int>` in the function body:
```lyng
fun sum(values: Int...): Int {
var result = 0
for (value in values) result += value
return result
}
```
Ellipsis could be a first argument: Ellipsis could be a first argument:
fun testCountArgs(data...,size) { fun testCountArgs(data...,size) {

View File

@ -107,6 +107,79 @@ val run2 = script.execute(scope)
`Scope.eval("...")` is the low-level shortcut that compiles and executes on the given scope. `Scope.eval("...")` is the low-level shortcut that compiles and executes on the given scope.
For most embedding use cases, prefer `session.eval("...")`. For most embedding use cases, prefer `session.eval("...")`.
### 2.2) Inspect function signatures without executing source
Declaration generators and other host tools can ask the compiler for semantic metadata about
top-level functions without running the script:
```kotlin
val source = Source(
"service.lyng",
"""
@Export
fun greet<T: Object = String>(
first: T,
rest: T...,
punctuation: String = "!",
): String = first.toString() + punctuation
""".trimIndent()
)
val functions = Compiler.resolveFunctionMetadata(
source,
Script.defaultImportManager,
)
val greet = functions.single()
println(greet.name) // greet
println(greet.annotations) // [Export]
println(greet.parameters[1].isEllipsis) // true
println(greet.parameters[2].defaultSource) // "!"
println(greet.returnType) // String
```
`resolveFunctionMetadata(...)` parses the source and resolves its imports and types, but it does
not execute the script, annotation functions, or default expressions. It returns
`ResolvedFunctionMetadata` entries in source order. Each entry provides:
- the function name, visibility, annotations, and source positions;
- generic parameters with their variance, bounds, and default types;
- parameters, variadic markers, and the original text of default expressions when available;
- the complete function type, including receivers, context receivers, and the declared or inferred
return type.
For a variadic declaration such as `values: Int...`, `ResolvedFunctionParameter.type` is the
element type `Int`, `isEllipsis` is `true`, and the parameter in `functionType` is `Int...`.
Inside Lyng function code, the collected `values` variable has type `List<Int>`.
If you already compiled the source, obtain the same information without resolving it again:
```kotlin
val script = Compiler.compile(source, Script.defaultImportManager)
val functions = script.resolvedFunctionMetadata()
```
Only top-level function declarations are returned. Use the Mini-AST APIs when tooling also needs
nested declarations or a syntax-oriented representation.
### 2.3) Prepare imports separately from execution
Low-level hosts that own a scope can install a compiled script's imports without executing its
statements:
```kotlin
val script = Compiler.compile(source, importManager)
val scope = importManager.newModule()
script.importInto(scope)
val result = script.execute(scope)
```
`importInto(...)` is idempotent for a scope and is useful when import preparation and execution
belong to different host lifecycle phases. To create a fresh raw module with the script's imports
already installed, use `script.instantiateModule(seedScope)`. When supplied, `seedScope` provides
the import provider and any seed-bound imports used by the new module.
### 3) Preferred: bind extern globals from Kotlin ### 3) Preferred: bind extern globals from Kotlin
For module-level APIs, the default workflow is: For module-level APIs, the default workflow is:

View File

@ -11802,7 +11802,19 @@ class Compiler(
} }
/** /**
* Compile [source] and return semantic top-level function metadata without evaluating it. * Resolve semantic metadata for top-level functions in [source] without evaluating it.
*
* Functions are returned in source order. Their metadata includes annotations, visibility,
* generic parameters, parameter declarations and defaults, receiver information, and the
* declared or compiler-inferred return type. Annotation names are collected syntactically;
* annotation functions and default expressions are not executed.
*
* This performs parsing, import resolution, and semantic compilation, but does not generate
* executable bytecode. Use [compile] when the resulting script must also be executed.
*
* @param source named Lyng source whose top-level functions should be inspected
* @param importManager provider used to resolve the source's imports and external types
* @return metadata for each top-level function declaration in source order
*/ */
suspend fun resolveFunctionMetadata( suspend fun resolveFunctionMetadata(
source: Source, source: Source,

View File

@ -12,6 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
package net.sergeych.lyng package net.sergeych.lyng
@ -19,9 +20,21 @@ package net.sergeych.lyng
/** /**
* Compiler-resolved metadata for a function parameter. * Compiler-resolved metadata for a function parameter.
* *
* [type] is the declared element type for a variadic parameter, not its collected local type.
* For example, `values: Int...` has a [type] of `Int`, [isEllipsis] is `true`, and the
* corresponding entry in [ResolvedFunctionMetadata.functionType] is `Int...`.
*
* [defaultSource] preserves the source expression for tools that generate declarations. It is * [defaultSource] preserves the source expression for tools that generate declarations. It is
* null for parameters without defaults and may also be null for declarations assembled by a host * null for parameters without defaults and may also be null for declarations assembled by a host
* rather than parsed from source; use [hasDefault] to distinguish those cases. * rather than parsed from source; use [hasDefault] to distinguish those cases.
*
* @property name parameter name as written in the declaration
* @property type compiler-resolved declared type, or the variadic element type when [isEllipsis]
* is `true`
* @property isEllipsis whether this parameter collects repeated arguments
* @property hasDefault whether the declaration supplies a default expression
* @property defaultSource exact source text of the default expression, when available
* @property pos source position of the parameter declaration
*/ */
data class ResolvedFunctionParameter( data class ResolvedFunctionParameter(
val name: String, val name: String,
@ -38,6 +51,18 @@ data class ResolvedFunctionParameter(
* Unlike the editor Mini-AST, this descriptor contains compiler-resolved parameter and return * Unlike the editor Mini-AST, this descriptor contains compiler-resolved parameter and return
* types, including an inferred return type when inference succeeds. Annotation names describe the * types, including an inferred return type when inference succeeds. Annotation names describe the
* declaration syntax; annotation functions are not executed while this metadata is collected. * declaration syntax; annotation functions are not executed while this metadata is collected.
* The descriptor is intended for declaration generators and other embedding tools that need
* semantic signatures without running the script.
*
* @property name declared function name
* @property namePos source position of [name]
* @property declarationPos source position at the start of the function declaration
* @property visibility resolved declaration visibility
* @property annotations annotation names in declaration order, without evaluating annotations
* @property typeParams resolved generic type parameters, including bounds and defaults
* @property parameters resolved parameters in declaration order
* @property functionType complete callable type, including receiver, context receivers, variadic
* markers, and the declared or inferred return type
*/ */
data class ResolvedFunctionMetadata( data class ResolvedFunctionMetadata(
val name: String, val name: String,
@ -49,5 +74,6 @@ data class ResolvedFunctionMetadata(
val parameters: List<ResolvedFunctionParameter>, val parameters: List<ResolvedFunctionParameter>,
val functionType: TypeDecl.Function, val functionType: TypeDecl.Function,
) { ) {
/** Declared or compiler-inferred return type of this function. */
val returnType: TypeDecl get() = functionType.returnType val returnType: TypeDecl get() = functionType.returnType
} }

View File

@ -19,17 +19,16 @@ package net.sergeych.lyng
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.yield import kotlinx.coroutines.yield
import net.sergeych.lyng.Script.Companion.defaultImportManager
import net.sergeych.lyng.bridge.bind import net.sergeych.lyng.bridge.bind
import net.sergeych.lyng.bridge.bindObject import net.sergeych.lyng.bridge.bindObject
import net.sergeych.lyng.bytecode.BytecodeLambdaCallable
import net.sergeych.lyng.bytecode.CmdFunction import net.sergeych.lyng.bytecode.CmdFunction
import net.sergeych.lyng.bytecode.CmdVm import net.sergeych.lyng.bytecode.CmdVm
import net.sergeych.lyng.bytecode.BytecodeLambdaCallable
import net.sergeych.lyng.miniast.* import net.sergeych.lyng.miniast.*
import net.sergeych.lyng.obj.* import net.sergeych.lyng.obj.*
import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.serialization.ObjJsonClass import net.sergeych.lyng.serialization.ObjJsonClass
import net.sergeych.lyng.serialization.bindSerializationFormat import net.sergeych.lyng.serialization.bindSerializationFormat
import net.sergeych.lyng.pacman.ImportManager
import net.sergeych.lyng.stdlib_included.complexLyng import net.sergeych.lyng.stdlib_included.complexLyng
import net.sergeych.lyng.stdlib_included.decimalLyng import net.sergeych.lyng.stdlib_included.decimalLyng
import net.sergeych.lyng.stdlib_included.legacyDigestLyng import net.sergeych.lyng.stdlib_included.legacyDigestLyng
@ -55,7 +54,15 @@ class Script(
) : Statement() { ) : Statement() {
fun statements(): List<Statement> = statements fun statements(): List<Statement> = statements
/** Compiler-resolved metadata for top-level function declarations in source order. */ /**
* Return compiler-resolved metadata for this script's top-level function declarations.
*
* Results retain source order and include declared or inferred signature types. This is a
* read-only view of information produced while compiling the script; calling it does not
* execute the script, its annotations, or parameter default expressions.
*
* @return metadata for every top-level function declaration in source order
*/
fun resolvedFunctionMetadata(): List<ResolvedFunctionMetadata> = fun resolvedFunctionMetadata(): List<ResolvedFunctionMetadata> =
statements.filterIsInstance<FunctionDeclStatement>().map { it.spec.resolvedMetadata } statements.filterIsInstance<FunctionDeclStatement>().map { it.spec.resolvedMetadata }