mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 13:11:32 +00:00
ffi: new native function format
This commit is contained in:
@ -300,8 +300,8 @@ types:
|
|||||||
values:
|
values:
|
||||||
- name: form
|
- name: form
|
||||||
type: Symbol
|
type: Symbol
|
||||||
- name: definition
|
- name: definitions
|
||||||
type: StringLiteral
|
type: List<StringLiteral>
|
||||||
IndexedBy:
|
IndexedBy:
|
||||||
parent: Expression
|
parent: Expression
|
||||||
values:
|
values:
|
||||||
|
@ -6,23 +6,23 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("native")
|
@SerialName("native")
|
||||||
class Native(val form: Symbol, val definition: StringLiteral) : Node() {
|
class Native(val form: Symbol, val definitions: List<StringLiteral>) : Node() {
|
||||||
override val type: NodeType = NodeType.Native
|
override val type: NodeType = NodeType.Native
|
||||||
|
|
||||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||||
visitor.visitNodes(form, definition)
|
visitor.visitAll(listOf(form), definitions)
|
||||||
|
|
||||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||||
visitor.visitNative(this)
|
visitor.visitNative(this)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is Native) return false
|
if (other !is Native) return false
|
||||||
return other.form == form && other.definition == definition
|
return other.form == form && other.definitions == definitions
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = form.hashCode()
|
var result = form.hashCode()
|
||||||
result = 31 * result + definition.hashCode()
|
result = 31 * result + definitions.hashCode()
|
||||||
result = 31 * result + type.hashCode()
|
result = 31 * result + type.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ class FunctionContext(val compilationUnitContext: CompilationUnitContext, val no
|
|||||||
val native = node.native!!
|
val native = node.native!!
|
||||||
val nativeFunctionProvider =
|
val nativeFunctionProvider =
|
||||||
compilationUnitContext.evaluator.nativeFunctionProvider(native.form.id)
|
compilationUnitContext.evaluator.nativeFunctionProvider(native.form.id)
|
||||||
nativeFunctionProvider.provideNativeFunction(native.definition.text, node.arguments)
|
nativeFunctionProvider.provideNativeFunction(native.definitions.map { it.text }, node.arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val nativeCached by lazy { resolveMaybeNative() }
|
private val nativeCached by lazy { resolveMaybeNative() }
|
||||||
|
@ -10,7 +10,8 @@ class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
|
|||||||
"listInitWith" to CallableFunction(::listInitWith)
|
"listInitWith" to CallableFunction(::listInitWith)
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun provideNativeFunction(definition: String, arguments: List<ArgumentSpec>): CallableFunction {
|
override fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>): CallableFunction {
|
||||||
|
val definition = definitions[0]
|
||||||
return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition")
|
return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,5 +3,5 @@ package gay.pizza.pork.evaluator
|
|||||||
import gay.pizza.pork.ast.ArgumentSpec
|
import gay.pizza.pork.ast.ArgumentSpec
|
||||||
|
|
||||||
interface NativeProvider {
|
interface NativeProvider {
|
||||||
fun provideNativeFunction(definition: String, arguments: List<ArgumentSpec>): CallableFunction
|
fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>): CallableFunction
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
export let SDL_INIT_VIDEO = 32
|
export let SDL_INIT_VIDEO = 32
|
||||||
|
|
||||||
export func SDL_Init(flags)
|
export func SDL_Init(flags)
|
||||||
native ffi "SDL2:SDL_Init:int:unsigned int"
|
native ffi "SDL2" "int SDL_Init(unsigned int)"
|
||||||
|
|
||||||
export func SDL_Quit()
|
export func SDL_Quit()
|
||||||
native ffi "SDL2:SDL_Quit:void"
|
native ffi "SDL2" "void SDL_Quit()"
|
||||||
|
|
||||||
// SDL_video.h
|
// SDL_video.h
|
||||||
|
|
||||||
@ -23,18 +23,18 @@ export func SDL_WINDOWPOS_CENTERED_DISPLAY(x) { SDL_WINDOWPOS_CENTERED_MASK | x
|
|||||||
export let SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED_DISPLAY(0)
|
export let SDL_WINDOWPOS_CENTERED = SDL_WINDOWPOS_CENTERED_DISPLAY(0)
|
||||||
|
|
||||||
export func SDL_CreateWindow(title, x, y, w, h, flags)
|
export func SDL_CreateWindow(title, x, y, w, h, flags)
|
||||||
native ffi "SDL2:SDL_CreateWindow:void*:char*,int,int,int,int,unsigned int"
|
native ffi "SDL2" "void* SDL_CreateWindow(char*, int, int, int, int, unsigned int)"
|
||||||
|
|
||||||
export func SDL_DestroyWindow(window)
|
export func SDL_DestroyWindow(window)
|
||||||
native ffi "SDL2:SDL_DestroyWindow:void:void*"
|
native ffi "SDL2" "void SDL_DestroyWindow(void*)"
|
||||||
|
|
||||||
// SDL_event.h
|
// SDL_event.h
|
||||||
|
|
||||||
export func SDL_PumpEvents()
|
export func SDL_PumpEvents()
|
||||||
native ffi "SDL2:SDL_PumpEvents:void"
|
native ffi "SDL2" "void SDL_PumpEvents()"
|
||||||
|
|
||||||
export func SDL_WaitEvent(event)
|
export func SDL_WaitEvent(event)
|
||||||
native ffi "SDL2:SDL_WaitEvent:int:void*"
|
native ffi "SDL2" "int SDL_WaitEvent(void*)"
|
||||||
|
|
||||||
|
|
||||||
// SDL_keyboard.h
|
// SDL_keyboard.h
|
||||||
@ -54,36 +54,36 @@ export let KMOD_CAPS = 8192
|
|||||||
export let KMOD_SCROLL = 32768
|
export let KMOD_SCROLL = 32768
|
||||||
|
|
||||||
export func SDL_GetModState()
|
export func SDL_GetModState()
|
||||||
native ffi "SDL2:SDL_GetModState:int"
|
native ffi "SDL2" "int SDL_GetModState()"
|
||||||
|
|
||||||
// SDL_renderer.h
|
// SDL_renderer.h
|
||||||
|
|
||||||
export let SDL_RENDERER_PRESENTVSYNC = 4
|
export let SDL_RENDERER_PRESENTVSYNC = 4
|
||||||
|
|
||||||
export func SDL_CreateRenderer(window, index, flags)
|
export func SDL_CreateRenderer(window, index, flags)
|
||||||
native ffi "SDL2:SDL_CreateRenderer:void*:void*,int,unsigned int"
|
native ffi "SDL2" "void* SDL_CreateRenderer(void*, int, unsigned int)"
|
||||||
|
|
||||||
export func SDL_DestroyRenderer(renderer)
|
export func SDL_DestroyRenderer(renderer)
|
||||||
native ffi "SDL2:SDL_DestroyRenderer:void:void*"
|
native ffi "SDL2" "void SDL_DestroyRenderer(void*)"
|
||||||
|
|
||||||
export func SDL_RenderSetLogicalSize(renderer, w, h)
|
export func SDL_RenderSetLogicalSize(renderer, w, h)
|
||||||
native ffi "SDL2:SDL_RenderSetLogicalSize:int:void*,int,int"
|
native ffi "SDL2" "int SDL_RenderSetLogicalSize(void*,int,int)"
|
||||||
|
|
||||||
export func SDL_RenderPresent(renderer)
|
export func SDL_RenderPresent(renderer)
|
||||||
native ffi "SDL2:SDL_RenderPresent:void:void*"
|
native ffi "SDL2" "void SDL_RenderPresent(void*)"
|
||||||
|
|
||||||
export func SDL_SetRenderDrawColor(renderer, r, g, b, a)
|
export func SDL_SetRenderDrawColor(renderer, r, g, b, a)
|
||||||
native ffi "SDL2:SDL_SetRenderDrawColor:int:void*,unsigned int,unsigned int,unsigned int,unsigned int"
|
native ffi "SDL2" "int SDL_SetRenderDrawColor(void*, unsigned int, unsigned int, unsigned int, unsigned int)"
|
||||||
|
|
||||||
export func SDL_RenderClear(renderer)
|
export func SDL_RenderClear(renderer)
|
||||||
native ffi "SDL2:SDL_RenderClear:int:void*"
|
native ffi "SDL2" "int SDL_RenderClear(void*)"
|
||||||
|
|
||||||
export func SDL_RenderDrawLine(renderer, x1, y1, x2, y2)
|
export func SDL_RenderDrawLine(renderer, x1, y1, x2, y2)
|
||||||
native ffi "SDL2:SDL_RenderDrawLine:int:void*,int,int,int,int"
|
native ffi "SDL2" "int SDL_RenderDrawLine(void*,int,int,int,int)"
|
||||||
|
|
||||||
// SDL_hints.h
|
// SDL_hints.h
|
||||||
|
|
||||||
export let SDL_HINT_RENDER_LOGICAL_SIZE_MODE = "SDL_RENDER_LOGICAL_SIZE_MODE"
|
export let SDL_HINT_RENDER_LOGICAL_SIZE_MODE = "SDL_RENDER_LOGICAL_SIZE_MODE"
|
||||||
|
|
||||||
export func SDL_SetHint(name, value)
|
export func SDL_SetHint(name, value)
|
||||||
native ffi "SDL2:SDL_SetHint:int:char*,char*"
|
native ffi "SDL2" "int SDL_SetHint(char*,char*)"
|
||||||
|
@ -7,21 +7,30 @@ class FfiFunctionDefinition(
|
|||||||
val parameters: List<String>
|
val parameters: List<String>
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun parse(def: String): FfiFunctionDefinition {
|
fun parse(library: String, def: String): FfiFunctionDefinition {
|
||||||
val parts = def.split(":", limit = 4)
|
fun invalid(): Nothing {
|
||||||
if (parts.size !in arrayOf(3, 4) || parts.any { it.trim().isEmpty() }) {
|
|
||||||
throw RuntimeException(
|
throw RuntimeException(
|
||||||
"FFI function definition is invalid, " +
|
"FFI function definition is invalid, " +
|
||||||
"accepted format is 'library:function:return-type:(optional)parameters' " +
|
"accepted format is 'return-type function-name(parameter, parameter...)' " +
|
||||||
"but '${def}' was specified")
|
"but '${def}' was specified")
|
||||||
}
|
}
|
||||||
val (library, function, returnType) = parts
|
|
||||||
val parametersString = if (parts.size == 4) parts[3] else ""
|
val parts = def.split(" ", limit = 2)
|
||||||
|
if (parts.size != 2) {
|
||||||
|
invalid()
|
||||||
|
}
|
||||||
|
val (returnType, functionNameAndParameters) = parts
|
||||||
|
var (functionName, parametersAndClosingParentheses) = functionNameAndParameters.split("(", limit = 2)
|
||||||
|
parametersAndClosingParentheses = parametersAndClosingParentheses.trim()
|
||||||
|
if (!parametersAndClosingParentheses.endsWith(")")) {
|
||||||
|
invalid()
|
||||||
|
}
|
||||||
|
val parameterString = parametersAndClosingParentheses.substring(0, parametersAndClosingParentheses.length - 1)
|
||||||
return FfiFunctionDefinition(
|
return FfiFunctionDefinition(
|
||||||
library,
|
library,
|
||||||
function,
|
functionName,
|
||||||
returnType,
|
returnType,
|
||||||
parametersString.split(",")
|
parameterString.split(",").map { it.trim() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ class JavaAutogen(val javaClass: Class<*>) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
private fun asNative(functionDefinition: JavaFunctionDefinition): Native =
|
private fun asNative(functionDefinition: JavaFunctionDefinition): Native =
|
||||||
Native(Symbol("java"), StringLiteral(functionDefinition.encode()))
|
Native(Symbol("java"), functionDefinition.encode().map { StringLiteral(it) })
|
||||||
|
|
||||||
private fun discriminate(parameter: Parameter): String =
|
private fun discriminate(parameter: Parameter): String =
|
||||||
parameter.type.simpleName.lowercase().replace("[]", "_array")
|
parameter.type.simpleName.lowercase().replace("[]", "_array")
|
||||||
|
@ -8,25 +8,25 @@ class JavaFunctionDefinition(
|
|||||||
val parameters: List<String>
|
val parameters: List<String>
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun parse(def: String): JavaFunctionDefinition {
|
fun parse(defs: List<String>): JavaFunctionDefinition {
|
||||||
val parts = def.split(":", limit = 5)
|
if (defs.size != 4 && defs.size != 5) {
|
||||||
if (!(parts.size == 4 || parts.size == 5) || parts.any { it.trim().isEmpty() }) {
|
|
||||||
throw RuntimeException(
|
throw RuntimeException(
|
||||||
"Java function definition is invalid, " +
|
"Java function definition is invalid, " +
|
||||||
"accepted format is 'type:kind:symbol:return-type:(optional)parameters' " +
|
"accepted format is 'type:kind:symbol:return-type:(optional)parameters' " +
|
||||||
"but '${def}' was specified")
|
"but ${defs.joinToString(" ", prefix = "\"", postfix = "\"")} was specified")
|
||||||
}
|
}
|
||||||
val (type, kind, symbol, returnType) = parts
|
val (type, kind, symbol, returnType) = defs
|
||||||
val parameters = if (parts.size > 4) parts[4].split(",") else emptyList()
|
val parameterString = if (defs.size == 5) defs[4] else ""
|
||||||
|
val parameters = if (parameterString.isNotEmpty()) parameterString.split(",") else emptyList()
|
||||||
return JavaFunctionDefinition(type, kind, symbol, returnType, parameters)
|
return JavaFunctionDefinition(type, kind, symbol, returnType, parameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encode(): String = buildString {
|
fun encode(): List<String> = listOf(
|
||||||
append("${type}:${kind}:${symbol}:${returnType}")
|
type,
|
||||||
if (parameters.isNotEmpty()) {
|
kind,
|
||||||
append(":")
|
symbol,
|
||||||
append(parameters.joinToString(","))
|
returnType,
|
||||||
}
|
parameters.joinToString(",")
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import java.lang.invoke.MethodType
|
|||||||
class JavaNativeProvider : NativeProvider {
|
class JavaNativeProvider : NativeProvider {
|
||||||
private val lookup = MethodHandles.lookup()
|
private val lookup = MethodHandles.lookup()
|
||||||
|
|
||||||
override fun provideNativeFunction(definition: String, arguments: List<ArgumentSpec>): CallableFunction {
|
override fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>): CallableFunction {
|
||||||
val functionDefinition = JavaFunctionDefinition.parse(definition)
|
val functionDefinition = JavaFunctionDefinition.parse(definitions)
|
||||||
val javaClass = lookupClass(functionDefinition.type)
|
val javaClass = lookupClass(functionDefinition.type)
|
||||||
val returnTypeClass = lookupClass(functionDefinition.returnType)
|
val returnTypeClass = lookupClass(functionDefinition.returnType)
|
||||||
val parameterClasses = functionDefinition.parameters.map { lookupClass(it) }
|
val parameterClasses = functionDefinition.parameters.map { lookupClass(it) }
|
||||||
|
@ -8,8 +8,8 @@ import gay.pizza.pork.evaluator.NativeProvider
|
|||||||
import gay.pizza.pork.evaluator.None
|
import gay.pizza.pork.evaluator.None
|
||||||
|
|
||||||
class JnaNativeProvider : NativeProvider {
|
class JnaNativeProvider : NativeProvider {
|
||||||
override fun provideNativeFunction(definition: String, arguments: List<ArgumentSpec>): CallableFunction {
|
override fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>): CallableFunction {
|
||||||
val functionDefinition = FfiFunctionDefinition.parse(definition)
|
val functionDefinition = FfiFunctionDefinition.parse(definitions[0], definitions[1])
|
||||||
val library = NativeLibrary.getInstance(functionDefinition.library)
|
val library = NativeLibrary.getInstance(functionDefinition.library)
|
||||||
val function = library.getFunction(functionDefinition.function)
|
val function = library.getFunction(functionDefinition.function)
|
||||||
?: throw RuntimeException("Failed to find function ${functionDefinition.function} in library ${functionDefinition.library}")
|
?: throw RuntimeException("Failed to find function ${functionDefinition.function} in library ${functionDefinition.library}")
|
||||||
|
@ -308,8 +308,11 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
|
|||||||
override fun parseNative(): Native = guarded(NodeType.Native) {
|
override fun parseNative(): Native = guarded(NodeType.Native) {
|
||||||
expect(TokenType.Native)
|
expect(TokenType.Native)
|
||||||
val form = parseSymbol()
|
val form = parseSymbol()
|
||||||
val definition = parseStringLiteral()
|
val definitions = mutableListOf<StringLiteral>()
|
||||||
Native(form, definition)
|
while (peek(TokenType.StringLiteral)) {
|
||||||
|
definitions.add(parseStringLiteral())
|
||||||
|
}
|
||||||
|
Native(form, definitions)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun parseNoneLiteral(): NoneLiteral = guarded(NodeType.NoneLiteral) {
|
override fun parseNoneLiteral(): NoneLiteral = guarded(NodeType.NoneLiteral) {
|
||||||
|
@ -82,7 +82,12 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
|||||||
append("native ")
|
append("native ")
|
||||||
visit(node.form)
|
visit(node.form)
|
||||||
append(" ")
|
append(" ")
|
||||||
visit(node.definition)
|
for ((index, argument) in node.definitions.withIndex()) {
|
||||||
|
visit(argument)
|
||||||
|
if (index + 1 != node.definitions.size) {
|
||||||
|
append(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitNoneLiteral(node: NoneLiteral) {
|
override fun visitNoneLiteral(node: NoneLiteral) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
export func malloc(size)
|
export func malloc(size)
|
||||||
native ffi "c:malloc:void*:size_t"
|
native ffi "c" "void* malloc(size_t)"
|
||||||
|
|
||||||
export func calloc(size, count)
|
export func calloc(size, count)
|
||||||
native ffi "c:calloc:void*:size_t,size_t"
|
native ffi "c" "void* calloc(size_t, size_t)"
|
||||||
|
|
||||||
export func free(pointer)
|
export func free(pointer)
|
||||||
native ffi "c:free:void:void*"
|
native ffi "c" "void free(void*)"
|
||||||
|
Reference in New Issue
Block a user