mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 21:00:56 +00:00
while loop support, and native functions (including ffi!)
This commit is contained in:
parent
ddff6cb365
commit
236f812caf
@ -95,16 +95,18 @@ types:
|
||||
- name: arguments
|
||||
type: List<Symbol>
|
||||
- name: block
|
||||
type: Block
|
||||
type: Block?
|
||||
- name: native
|
||||
type: Native?
|
||||
If:
|
||||
parent: Expression
|
||||
values:
|
||||
- name: condition
|
||||
type: Expression
|
||||
- name: thenExpression
|
||||
type: Expression
|
||||
- name: elseExpression
|
||||
type: Expression?
|
||||
- name: thenBlock
|
||||
type: Block
|
||||
- name: elseBlock
|
||||
type: Block?
|
||||
ImportDeclaration:
|
||||
parent: Declaration
|
||||
values:
|
||||
@ -150,3 +152,23 @@ types:
|
||||
values:
|
||||
- name: symbol
|
||||
type: Symbol
|
||||
While:
|
||||
parent: Expression
|
||||
values:
|
||||
- name: condition
|
||||
type: Expression
|
||||
- name: block
|
||||
type: Block
|
||||
Break:
|
||||
parent: Expression
|
||||
values: []
|
||||
Continue:
|
||||
parent: Expression
|
||||
values: []
|
||||
Native:
|
||||
parent: Node
|
||||
values:
|
||||
- name: form
|
||||
type: Symbol
|
||||
- name: definition
|
||||
type: StringLiteral
|
||||
|
22
ast/src/main/kotlin/gay/pizza/pork/ast/Break.kt
Normal file
22
ast/src/main/kotlin/gay/pizza/pork/ast/Break.kt
Normal file
@ -0,0 +1,22 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("break")
|
||||
class Break : Expression() {
|
||||
override val type: NodeType = NodeType.Break
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitBreak(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Break) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int =
|
||||
31 * type.hashCode()
|
||||
}
|
22
ast/src/main/kotlin/gay/pizza/pork/ast/Continue.kt
Normal file
22
ast/src/main/kotlin/gay/pizza/pork/ast/Continue.kt
Normal file
@ -0,0 +1,22 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("continue")
|
||||
class Continue : Expression() {
|
||||
override val type: NodeType = NodeType.Continue
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitContinue(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Continue) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int =
|
||||
31 * type.hashCode()
|
||||
}
|
@ -6,18 +6,18 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("functionDefinition")
|
||||
class FunctionDefinition(override val modifiers: DefinitionModifiers, override val symbol: Symbol, val arguments: List<Symbol>, val block: Block) : Definition() {
|
||||
class FunctionDefinition(override val modifiers: DefinitionModifiers, override val symbol: Symbol, val arguments: List<Symbol>, val block: Block?, val native: Native?) : Definition() {
|
||||
override val type: NodeType = NodeType.FunctionDefinition
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitAll(listOf(symbol), arguments, listOf(block))
|
||||
visitor.visitAll(listOf(symbol), arguments, listOf(block), listOf(native))
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitFunctionDefinition(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is FunctionDefinition) return false
|
||||
return other.modifiers == modifiers && other.symbol == symbol && other.arguments == arguments && other.block == block
|
||||
return other.modifiers == modifiers && other.symbol == symbol && other.arguments == arguments && other.block == block && other.native == native
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@ -25,6 +25,7 @@ class FunctionDefinition(override val modifiers: DefinitionModifiers, override v
|
||||
result = 31 * result + symbol.hashCode()
|
||||
result = 31 * result + arguments.hashCode()
|
||||
result = 31 * result + block.hashCode()
|
||||
result = 31 * result + native.hashCode()
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
|
@ -6,24 +6,24 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("if")
|
||||
class If(val condition: Expression, val thenExpression: Expression, val elseExpression: Expression?) : Expression() {
|
||||
class If(val condition: Expression, val thenBlock: Block, val elseBlock: Block?) : Expression() {
|
||||
override val type: NodeType = NodeType.If
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(condition, thenExpression, elseExpression)
|
||||
visitor.visitNodes(condition, thenBlock, elseBlock)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitIf(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is If) return false
|
||||
return other.condition == condition && other.thenExpression == thenExpression && other.elseExpression == elseExpression
|
||||
return other.condition == condition && other.thenBlock == thenBlock && other.elseBlock == elseBlock
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = condition.hashCode()
|
||||
result = 31 * result + thenExpression.hashCode()
|
||||
result = 31 * result + elseExpression.hashCode()
|
||||
result = 31 * result + thenBlock.hashCode()
|
||||
result = 31 * result + elseBlock.hashCode()
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
|
29
ast/src/main/kotlin/gay/pizza/pork/ast/Native.kt
Normal file
29
ast/src/main/kotlin/gay/pizza/pork/ast/Native.kt
Normal file
@ -0,0 +1,29 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("native")
|
||||
class Native(val form: Symbol, val definition: StringLiteral) : Node() {
|
||||
override val type: NodeType = NodeType.Native
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(form, definition)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitNative(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Native) return false
|
||||
return other.form == form && other.definition == definition
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = form.hashCode()
|
||||
result = 31 * result + definition.hashCode()
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
@ -8,9 +8,15 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||
override fun visitBooleanLiteral(node: BooleanLiteral): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitBreak(node: Break): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitCompilationUnit(node: CompilationUnit): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitContinue(node: Continue): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitFunctionCall(node: FunctionCall): Unit =
|
||||
handle(node)
|
||||
|
||||
@ -35,6 +41,9 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||
override fun visitListLiteral(node: ListLiteral): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitNative(node: Native): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitParentheses(node: Parentheses): Unit =
|
||||
handle(node)
|
||||
|
||||
@ -50,6 +59,9 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||
override fun visitSymbolReference(node: SymbolReference): Unit =
|
||||
handle(node)
|
||||
|
||||
override fun visitWhile(node: While): Unit =
|
||||
handle(node)
|
||||
|
||||
fun handle(node: Node) {
|
||||
handler(node)
|
||||
node.visitChildren(this)
|
||||
|
@ -6,7 +6,9 @@ enum class NodeType(val parent: NodeType? = null) {
|
||||
Block(Node),
|
||||
Expression(Node),
|
||||
BooleanLiteral(Expression),
|
||||
Break(Expression),
|
||||
CompilationUnit(Node),
|
||||
Continue(Expression),
|
||||
Declaration(Node),
|
||||
Definition(Node),
|
||||
FunctionCall(Expression),
|
||||
@ -17,9 +19,11 @@ enum class NodeType(val parent: NodeType? = null) {
|
||||
IntLiteral(Expression),
|
||||
LetAssignment(Expression),
|
||||
ListLiteral(Expression),
|
||||
Native(Node),
|
||||
Parentheses(Expression),
|
||||
PrefixOperation(Expression),
|
||||
StringLiteral(Expression),
|
||||
Symbol(Node),
|
||||
SymbolReference(Expression)
|
||||
SymbolReference(Expression),
|
||||
While(Expression)
|
||||
}
|
||||
|
@ -6,8 +6,12 @@ interface NodeVisitor<T> {
|
||||
|
||||
fun visitBooleanLiteral(node: BooleanLiteral): T
|
||||
|
||||
fun visitBreak(node: Break): T
|
||||
|
||||
fun visitCompilationUnit(node: CompilationUnit): T
|
||||
|
||||
fun visitContinue(node: Continue): T
|
||||
|
||||
fun visitFunctionCall(node: FunctionCall): T
|
||||
|
||||
fun visitFunctionDefinition(node: FunctionDefinition): T
|
||||
@ -24,6 +28,8 @@ interface NodeVisitor<T> {
|
||||
|
||||
fun visitListLiteral(node: ListLiteral): T
|
||||
|
||||
fun visitNative(node: Native): T
|
||||
|
||||
fun visitParentheses(node: Parentheses): T
|
||||
|
||||
fun visitPrefixOperation(node: PrefixOperation): T
|
||||
@ -33,4 +39,6 @@ interface NodeVisitor<T> {
|
||||
fun visitSymbol(node: Symbol): T
|
||||
|
||||
fun visitSymbolReference(node: SymbolReference): T
|
||||
|
||||
fun visitWhile(node: While): T
|
||||
}
|
||||
|
@ -19,10 +19,14 @@ fun <T> NodeVisitor<T>.visit(node: Node): T =
|
||||
is PrefixOperation -> visitPrefixOperation(node)
|
||||
is StringLiteral -> visitStringLiteral(node)
|
||||
is SymbolReference -> visitSymbolReference(node)
|
||||
is While -> visitWhile(node)
|
||||
is Break -> visitBreak(node)
|
||||
is Continue -> visitContinue(node)
|
||||
is Native -> visitNative(node)
|
||||
}
|
||||
|
||||
fun <T> NodeVisitor<T>.visitNodes(vararg nodes: Node?): List<T> =
|
||||
nodes.asSequence().filterNotNull().map { visit(it) }.toList()
|
||||
|
||||
fun <T> NodeVisitor<T>.visitAll(vararg nodeLists: List<Node>): List<T> =
|
||||
nodeLists.asSequence().flatten().map { visit(it) }.toList()
|
||||
fun <T> NodeVisitor<T>.visitAll(vararg nodeLists: List<Node?>): List<T> =
|
||||
nodeLists.asSequence().flatten().filterNotNull().map { visit(it) }.toList()
|
||||
|
29
ast/src/main/kotlin/gay/pizza/pork/ast/While.kt
Normal file
29
ast/src/main/kotlin/gay/pizza/pork/ast/While.kt
Normal file
@ -0,0 +1,29 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("while")
|
||||
class While(val condition: Expression, val block: Block) : Expression() {
|
||||
override val type: NodeType = NodeType.While
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(condition, block)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitWhile(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is While) return false
|
||||
return other.condition == condition && other.block == block
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = condition.hashCode()
|
||||
result = 31 * result + block.hashCode()
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
@ -120,11 +120,12 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
extensionOf = "NodeVisitor<T>",
|
||||
returnType = "List<T>",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("nodeLists", type = "List<Node>", vararg = true)
|
||||
KotlinParameter("nodeLists", type = "List<Node?>", vararg = true)
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitAllFunction.body.add("nodeLists.asSequence().flatten().map { visit(it) }.toList()")
|
||||
visitAllFunction.body.add(
|
||||
"nodeLists.asSequence().flatten().filterNotNull().map { visit(it) }.toList()")
|
||||
visitorExtensionSet.functions.add(visitAllFunction)
|
||||
|
||||
write("NodeVisitorExtensions.kt", KotlinWriter(visitorExtensionSet))
|
||||
@ -239,7 +240,8 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
kotlinClassLike.inherits.add("$parentName()")
|
||||
}
|
||||
|
||||
for (value in type.values) {
|
||||
if (type.values != null) {
|
||||
for (value in type.values!!) {
|
||||
val member = KotlinMember(value.name, toKotlinType(value.typeRef))
|
||||
member.abstract = value.abstract
|
||||
if (type.isParentAbstract(value)) {
|
||||
@ -250,6 +252,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
}
|
||||
kotlinClassLike.members.add(member)
|
||||
}
|
||||
}
|
||||
|
||||
if (role == AstTypeRole.Enum) {
|
||||
val kotlinEnum = kotlinClassLike as KotlinEnum
|
||||
@ -279,25 +282,26 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
val anyListMembers = type.values.any { it.typeRef.form == AstTypeRefForm.List }
|
||||
val anyListMembers = type.values?.any { it.typeRef.form == AstTypeRefForm.List } ?: false
|
||||
val elideVisitChildren: Boolean
|
||||
if (anyListMembers) {
|
||||
val visitParameters = type.values.mapNotNull {
|
||||
val visitParameters = (type.values?.mapNotNull {
|
||||
if (it.typeRef.primitive != null) {
|
||||
null
|
||||
} else if (it.typeRef.type != null &&
|
||||
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
null
|
||||
} else if (it.typeRef.form == AstTypeRefForm.Single) {
|
||||
} else if (it.typeRef.form == AstTypeRefForm.Single ||
|
||||
it.typeRef.form == AstTypeRefForm.Nullable) {
|
||||
"listOf(${it.name})"
|
||||
} else {
|
||||
it.name
|
||||
}
|
||||
}.joinToString(", ")
|
||||
} ?: emptyList()).joinToString(", ")
|
||||
elideVisitChildren = visitParameters.isEmpty()
|
||||
visitChildrenFunction.body.add("visitor.visitAll(${visitParameters})")
|
||||
} else {
|
||||
val visitParameters = type.values.mapNotNull {
|
||||
val visitParameters = (type.values?.mapNotNull {
|
||||
if (it.typeRef.primitive != null) {
|
||||
null
|
||||
} else if (it.typeRef.type != null &&
|
||||
@ -306,7 +310,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
} else {
|
||||
it.name
|
||||
}
|
||||
}.joinToString(", ")
|
||||
} ?: emptyList()).joinToString(", ")
|
||||
elideVisitChildren = visitParameters.isEmpty()
|
||||
visitChildrenFunction.body.add("visitor.visitNodes(${visitParameters})")
|
||||
}
|
||||
@ -341,9 +345,12 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
"Any?"
|
||||
))
|
||||
equalsFunction.body.add("if (other !is ${type.name}) return false")
|
||||
val predicate = equalsAndHashCodeMembers.mapNotNull {
|
||||
var predicate = equalsAndHashCodeMembers.mapNotNull {
|
||||
if (it == "type") null else "other.${it} == $it"
|
||||
}.joinToString(" && ")
|
||||
if (predicate.isEmpty()) {
|
||||
predicate = "true"
|
||||
}
|
||||
equalsFunction.body.add("return $predicate")
|
||||
kotlinClassLike.functions.add(equalsFunction)
|
||||
|
||||
|
@ -1,17 +1,24 @@
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
class AstType(val name: String, var parent: AstType? = null) {
|
||||
private val internalValues = mutableListOf<AstValue>()
|
||||
private var internalValues: MutableList<AstValue>? = null
|
||||
private val internalEnums = mutableListOf<AstEnum>()
|
||||
|
||||
val values: List<AstValue>
|
||||
val values: List<AstValue>?
|
||||
get() = internalValues
|
||||
|
||||
val enums: List<AstEnum>
|
||||
get() = internalEnums
|
||||
|
||||
internal fun markHasValues() {
|
||||
if (internalValues == null) {
|
||||
internalValues = mutableListOf()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun addValue(value: AstValue) {
|
||||
internalValues.add(value)
|
||||
markHasValues()
|
||||
internalValues!!.add(value)
|
||||
}
|
||||
|
||||
internal fun addEnum(enum: AstEnum) {
|
||||
@ -25,7 +32,7 @@ class AstType(val name: String, var parent: AstType? = null) {
|
||||
|
||||
var current = parent
|
||||
while (current != null) {
|
||||
val abstract = current.values.firstOrNull {
|
||||
val abstract = current.values?.firstOrNull {
|
||||
it.name == value.name && it.abstract
|
||||
}
|
||||
if (abstract != null) {
|
||||
|
@ -2,6 +2,6 @@ package gay.pizza.pork.buildext.ast
|
||||
|
||||
data class AstTypeDescription(
|
||||
val parent: String? = null,
|
||||
val values: List<AstValueDescription> = emptyList(),
|
||||
val values: List<AstValueDescription>? = null,
|
||||
val enums: List<AstEnumDescription> = emptyList()
|
||||
)
|
||||
|
@ -21,13 +21,14 @@ class AstTypeRegistry {
|
||||
when {
|
||||
type.enums.isNotEmpty() ->
|
||||
AstTypeRole.Enum
|
||||
type.parent == null && type.values.isEmpty() ->
|
||||
type.parent == null && type.values == null ->
|
||||
AstTypeRole.RootNode
|
||||
type.parent != null && type.values.all { it.abstract } ->
|
||||
type.parent != null && (type.values == null ||
|
||||
(type.values!!.isNotEmpty() && type.values!!.all { it.abstract })) ->
|
||||
AstTypeRole.HierarchyNode
|
||||
type.parent != null && type.values.none { it.abstract } ->
|
||||
type.parent != null && (type.values != null && type.values!!.none { it.abstract }) ->
|
||||
AstTypeRole.AstNode
|
||||
type.parent == null && type.values.isNotEmpty() ->
|
||||
type.parent == null && (type.values != null && type.values!!.isNotEmpty()) ->
|
||||
AstTypeRole.ValueHolder
|
||||
else -> throw RuntimeException("Unable to determine role of type ${type.name}")
|
||||
}
|
||||
|
@ -39,11 +39,14 @@ class AstWorld {
|
||||
type.parent = world.typeRegistry.lookup(typeDescription.parent)
|
||||
}
|
||||
|
||||
if (typeDescription.values != null) {
|
||||
type.markHasValues()
|
||||
for (value in typeDescription.values) {
|
||||
val typeRef = AstTypeRef.parse(value.type, world.typeRegistry)
|
||||
val typeValue = AstValue(value.name, typeRef, abstract = value.required)
|
||||
type.addValue(typeValue)
|
||||
}
|
||||
}
|
||||
|
||||
for (enum in typeDescription.enums) {
|
||||
val astEnum = AstEnum(enum.name, enum.values)
|
||||
|
@ -39,7 +39,7 @@ class CompilationUnitContext(
|
||||
}
|
||||
|
||||
private fun definitionValue(definition: Definition): Any = when (definition) {
|
||||
is FunctionDefinition -> FunctionContext(definition, internalScope)
|
||||
is FunctionDefinition -> FunctionContext(this, definition)
|
||||
}
|
||||
|
||||
private fun processAllImports() {
|
||||
|
@ -8,6 +8,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
|
||||
override fun visitIntLiteral(node: IntLiteral): Any = node.value
|
||||
override fun visitStringLiteral(node: StringLiteral): Any = node.text
|
||||
override fun visitBooleanLiteral(node: BooleanLiteral): Any = node.value
|
||||
|
||||
override fun visitBreak(node: Break): Any = throw BreakMarker
|
||||
|
||||
override fun visitListLiteral(node: ListLiteral): Any =
|
||||
node.items.map { it.visit(this) }
|
||||
|
||||
@ -15,7 +18,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
|
||||
|
||||
override fun visitFunctionCall(node: FunctionCall): Any {
|
||||
val arguments = node.arguments.map { it.visit(this) }
|
||||
return currentScope.call(node.symbol.id, Arguments(arguments))
|
||||
val functionValue = currentScope.value(node.symbol.id) as CallableFunction
|
||||
return functionValue.call(Arguments(arguments))
|
||||
}
|
||||
|
||||
override fun visitLetAssignment(node: LetAssignment): Any {
|
||||
@ -27,6 +31,26 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
|
||||
override fun visitSymbolReference(node: SymbolReference): Any =
|
||||
currentScope.value(node.symbol.id)
|
||||
|
||||
override fun visitWhile(node: While): Any {
|
||||
val blockFunction = node.block.visit(this) as BlockFunction
|
||||
var result: Any? = null
|
||||
while (true) {
|
||||
val value = node.condition.visit(this)
|
||||
if (value !is Boolean) {
|
||||
throw RuntimeException("While loop attempted on non-boolean value: $value")
|
||||
}
|
||||
if (!value) break
|
||||
try {
|
||||
scoped { result = blockFunction.call() }
|
||||
} catch (_: BreakMarker) {
|
||||
break
|
||||
} catch (_: ContinueMarker) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return result ?: None
|
||||
}
|
||||
|
||||
override fun visitParentheses(node: Parentheses): Any =
|
||||
node.expression.visit(this)
|
||||
|
||||
@ -45,11 +69,12 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
|
||||
override fun visitIf(node: If): Any {
|
||||
val condition = node.condition.visit(this)
|
||||
return if (condition == true) {
|
||||
node.thenExpression.visit(this)
|
||||
} else {
|
||||
val elseExpression = node.elseExpression
|
||||
elseExpression?.visit(this) ?: None
|
||||
}
|
||||
val blockFunction = node.thenBlock.visit(this) as BlockFunction
|
||||
scoped { blockFunction.call() }
|
||||
} else if (node.elseBlock != null) {
|
||||
val blockFunction = node.elseBlock!!.visit(this) as BlockFunction
|
||||
scoped { blockFunction.call() }
|
||||
} else None
|
||||
}
|
||||
|
||||
override fun visitInfixOperation(node: InfixOperation): Any {
|
||||
@ -100,14 +125,35 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
|
||||
override fun visitImportDeclaration(node: ImportDeclaration): Any {
|
||||
throw RuntimeException(
|
||||
"Import declarations cannot be visited in an EvaluationVisitor. " +
|
||||
"Utilize an EvaluationContext."
|
||||
"Utilize an CompilationUnitContext."
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitCompilationUnit(node: CompilationUnit): Any {
|
||||
throw RuntimeException(
|
||||
"Compilation units cannot be visited in an EvaluationVisitor. " +
|
||||
"Utilize an EvaluationContext."
|
||||
"Utilize an CompilationUnitContext."
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitNative(node: Native): Any {
|
||||
throw RuntimeException(
|
||||
"Native definition cannot be visited in an EvaluationVisitor. " +
|
||||
"Utilize an FunctionContext."
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitContinue(node: Continue): Any = ContinueMarker
|
||||
|
||||
private inline fun <T> scoped(block: () -> T): T {
|
||||
currentScope = currentScope.fork()
|
||||
try {
|
||||
return block()
|
||||
} finally {
|
||||
currentScope = currentScope.leave()
|
||||
}
|
||||
}
|
||||
|
||||
private object BreakMarker : RuntimeException("Break Marker")
|
||||
private object ContinueMarker: RuntimeException("Continue Marker")
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import gay.pizza.pork.frontend.World
|
||||
|
||||
class Evaluator(val world: World, val scope: Scope) {
|
||||
private val contexts = mutableMapOf<String, CompilationUnitContext>()
|
||||
private val nativeFunctionProviders = mutableMapOf<String, NativeFunctionProvider>()
|
||||
|
||||
fun evaluate(path: String): Scope =
|
||||
context(path).externalScope
|
||||
@ -17,4 +18,13 @@ class Evaluator(val world: World, val scope: Scope) {
|
||||
context.initIfNeeded()
|
||||
return context
|
||||
}
|
||||
|
||||
fun nativeFunctionProvider(form: String): NativeFunctionProvider {
|
||||
return nativeFunctionProviders[form] ?:
|
||||
throw RuntimeException("Unknown native function form: $form")
|
||||
}
|
||||
|
||||
fun addNativeFunctionProvider(form: String, nativeFunctionProvider: NativeFunctionProvider) {
|
||||
nativeFunctionProviders[form] = nativeFunctionProvider
|
||||
}
|
||||
}
|
||||
|
@ -2,14 +2,27 @@ package gay.pizza.pork.evaluator
|
||||
|
||||
import gay.pizza.pork.ast.FunctionDefinition
|
||||
|
||||
class FunctionContext(val node: FunctionDefinition, val internalScope: Scope) : CallableFunction {
|
||||
class FunctionContext(val compilationUnitContext: CompilationUnitContext, val node: FunctionDefinition) : CallableFunction {
|
||||
override fun call(arguments: Arguments): Any {
|
||||
val scope = internalScope.fork()
|
||||
val scope = compilationUnitContext.internalScope.fork()
|
||||
for ((index, argumentSymbol) in node.arguments.withIndex()) {
|
||||
scope.define(argumentSymbol.id, arguments.values[index])
|
||||
}
|
||||
|
||||
if (node.native != null) {
|
||||
val native = node.native!!
|
||||
val nativeFunctionProvider =
|
||||
compilationUnitContext.evaluator.nativeFunctionProvider(native.form.id)
|
||||
val nativeFunction = nativeFunctionProvider.provideNativeFunction(native.definition.text)
|
||||
return nativeFunction.call(arguments)
|
||||
}
|
||||
|
||||
if (node.block == null) {
|
||||
throw RuntimeException("Native or Block is required for FunctionDefinition")
|
||||
}
|
||||
|
||||
val visitor = EvaluationVisitor(scope)
|
||||
val blockFunction = visitor.visitBlock(node.block)
|
||||
val blockFunction = visitor.visitBlock(node.block!!)
|
||||
return blockFunction.call()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
interface NativeFunctionProvider {
|
||||
fun provideNativeFunction(definition: String): CallableFunction
|
||||
}
|
@ -41,27 +41,19 @@ class Scope(val parent: Scope? = null, inherits: List<Scope> = emptyList()) {
|
||||
return value
|
||||
}
|
||||
|
||||
fun call(name: String, arguments: Arguments): Any {
|
||||
val value = value(name)
|
||||
if (value !is CallableFunction) {
|
||||
throw RuntimeException("$value is not callable")
|
||||
}
|
||||
return value.call(arguments)
|
||||
}
|
||||
|
||||
fun fork(): Scope =
|
||||
Scope(this)
|
||||
|
||||
fun leave(): Scope {
|
||||
if (parent == null) {
|
||||
throw RuntimeException("Parent context not found")
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
internal fun inherit(scope: Scope) {
|
||||
inherited.add(scope)
|
||||
}
|
||||
|
||||
fun leave(): Scope {
|
||||
if (parent == null) {
|
||||
throw RuntimeException("Attempted to leave the root scope!")
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
private object NotFound
|
||||
}
|
||||
|
13
examples/ffi.pork
Normal file
13
examples/ffi.pork
Normal file
@ -0,0 +1,13 @@
|
||||
func malloc(size)
|
||||
native ffi "libc.dylib:malloc:void*"
|
||||
|
||||
func free(pointer)
|
||||
native ffi "libc.dylib:free:void"
|
||||
|
||||
export func main() {
|
||||
while true {
|
||||
let pointer = malloc(8192)
|
||||
println(pointer)
|
||||
free(pointer)
|
||||
}
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
/* fibonacci sequence */
|
||||
func fib(n) {
|
||||
if n == 0
|
||||
then 0
|
||||
else if n == 1
|
||||
then 1
|
||||
else fib(n - 1) + fib(n - 2)
|
||||
if n == 0 {
|
||||
0
|
||||
} else {
|
||||
if n == 1 {
|
||||
1
|
||||
} else {
|
||||
fib(n - 1) + fib(n - 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export func main() {
|
||||
|
6
examples/loop.pork
Normal file
6
examples/loop.pork
Normal file
@ -0,0 +1,6 @@
|
||||
export func main() {
|
||||
while true {
|
||||
println("Hello World")
|
||||
break
|
||||
}
|
||||
}
|
10
ffi/build.gradle.kts
Normal file
10
ffi/build.gradle.kts
Normal file
@ -0,0 +1,10 @@
|
||||
plugins {
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":evaluator"))
|
||||
|
||||
implementation(project(":common"))
|
||||
implementation("net.java.dev.jna:jna:5.13.0")
|
||||
}
|
26
ffi/src/main/kotlin/gay/pizza/pork/ffi/JnaNativeProvider.kt
Normal file
26
ffi/src/main/kotlin/gay/pizza/pork/ffi/JnaNativeProvider.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package gay.pizza.pork.ffi
|
||||
|
||||
import com.sun.jna.Function
|
||||
import gay.pizza.pork.evaluator.CallableFunction
|
||||
import gay.pizza.pork.evaluator.NativeFunctionProvider
|
||||
|
||||
class JnaNativeProvider : NativeFunctionProvider {
|
||||
override fun provideNativeFunction(definition: String): CallableFunction {
|
||||
val (libraryName, functionSymbol, returnType, _) =
|
||||
definition.split(":", limit = 3)
|
||||
val function = Function.getFunction(libraryName, functionSymbol)
|
||||
return CallableFunction {
|
||||
return@CallableFunction invoke(function, it.values.toTypedArray(), returnType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun invoke(function: Function, values: Array<Any>, type: String): Any = when (type) {
|
||||
"void*" -> function.invokePointer(values)
|
||||
"int" -> function.invokeInt(values)
|
||||
"long" -> function.invokeLong(values)
|
||||
"float" -> function.invokeFloat(values)
|
||||
"double" -> function.invokeDouble(values)
|
||||
"void" -> function.invokeVoid(values)
|
||||
else -> throw RuntimeException("Unsupported ffi return type: $type")
|
||||
}
|
||||
}
|
@ -72,13 +72,26 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
||||
private fun readIf(): If = within {
|
||||
expect(TokenType.If)
|
||||
val condition = readExpression()
|
||||
expect(TokenType.Then)
|
||||
val thenExpression = readExpression()
|
||||
var elseExpression: Expression? = null
|
||||
val thenBlock = readBlock()
|
||||
var elseBlock: Block? = null
|
||||
if (next(TokenType.Else)) {
|
||||
elseExpression = readExpression()
|
||||
elseBlock = readBlock()
|
||||
}
|
||||
If(condition, thenExpression, elseExpression)
|
||||
If(condition, thenBlock, elseBlock)
|
||||
}
|
||||
|
||||
private fun readWhile(): While = within {
|
||||
expect(TokenType.While)
|
||||
val condition = readExpression()
|
||||
val block = readBlock()
|
||||
While(condition, block)
|
||||
}
|
||||
|
||||
private fun readNative(): Native = within {
|
||||
expect(TokenType.Native)
|
||||
val form = readSymbolRaw()
|
||||
val definition = readStringLiteral()
|
||||
Native(form, definition)
|
||||
}
|
||||
|
||||
fun readExpression(): Expression {
|
||||
@ -120,6 +133,20 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
||||
readIf()
|
||||
}
|
||||
|
||||
TokenType.While -> {
|
||||
readWhile()
|
||||
}
|
||||
|
||||
TokenType.Break -> {
|
||||
expect(TokenType.Break)
|
||||
Break()
|
||||
}
|
||||
|
||||
TokenType.Continue -> {
|
||||
expect(TokenType.Continue)
|
||||
Continue()
|
||||
}
|
||||
|
||||
else -> {
|
||||
throw RuntimeException(
|
||||
"Failed to parse token: ${token.type} '${token.text}' as" +
|
||||
@ -178,7 +205,15 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
||||
readSymbolRaw()
|
||||
}
|
||||
expect(TokenType.RightParentheses)
|
||||
FunctionDefinition(modifiers, name, arguments, readBlock())
|
||||
|
||||
var native: Native? = null
|
||||
var block: Block? = null
|
||||
if (peek(TokenType.Native)) {
|
||||
native = readNative()
|
||||
} else {
|
||||
block = readBlock()
|
||||
}
|
||||
FunctionDefinition(modifiers, name, arguments, block, native)
|
||||
}
|
||||
|
||||
private fun maybeReadDefinition(): Definition? {
|
||||
|
@ -38,6 +38,8 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBreak(node: Break): Unit = append("break")
|
||||
|
||||
override fun visitListLiteral(node: ListLiteral) {
|
||||
append("[")
|
||||
if (node.items.isNotEmpty()) {
|
||||
@ -55,6 +57,13 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
append("]")
|
||||
}
|
||||
|
||||
override fun visitNative(node: Native) {
|
||||
append("native ")
|
||||
visit(node.form)
|
||||
append(" ")
|
||||
visit(node.definition)
|
||||
}
|
||||
|
||||
override fun visitSymbol(node: Symbol) {
|
||||
append(node.id)
|
||||
}
|
||||
@ -82,6 +91,13 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
visit(node.symbol)
|
||||
}
|
||||
|
||||
override fun visitWhile(node: While) {
|
||||
append("while ")
|
||||
visit(node.condition)
|
||||
append(" ")
|
||||
visit(node.block)
|
||||
}
|
||||
|
||||
override fun visitParentheses(node: Parentheses) {
|
||||
append("(")
|
||||
visit(node.expression)
|
||||
@ -96,18 +112,13 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
override fun visitIf(node: If) {
|
||||
append("if ")
|
||||
visit(node.condition)
|
||||
append(" then")
|
||||
out.increaseIndent()
|
||||
appendLine()
|
||||
visit(node.thenExpression)
|
||||
out.decreaseIndent()
|
||||
if (node.elseExpression != null) {
|
||||
appendLine()
|
||||
append(" ")
|
||||
visit(node.thenBlock)
|
||||
if (node.elseBlock != null) {
|
||||
append(" ")
|
||||
append("else")
|
||||
out.increaseIndent()
|
||||
appendLine()
|
||||
visit(node.elseExpression!!)
|
||||
out.decreaseIndent()
|
||||
append(" ")
|
||||
visit(node.elseBlock!!)
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +131,7 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
}
|
||||
|
||||
override fun visitFunctionDefinition(node: FunctionDefinition) {
|
||||
append("fn ")
|
||||
append("func ")
|
||||
visit(node.symbol)
|
||||
append("(")
|
||||
for ((index, argument) in node.arguments.withIndex()) {
|
||||
@ -130,7 +141,13 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
}
|
||||
}
|
||||
append(") ")
|
||||
visit(node.block)
|
||||
if (node.block != null) {
|
||||
visit(node.block!!)
|
||||
}
|
||||
|
||||
if (node.native != null) {
|
||||
visit(node.native!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBlock(node: Block) {
|
||||
@ -167,4 +184,6 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitContinue(node: Continue): Unit = append("continue")
|
||||
}
|
||||
|
@ -25,11 +25,14 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
|
||||
False(Keyword("false"), KeywordFamily),
|
||||
True(Keyword("true"), KeywordFamily),
|
||||
If(Keyword("if"), KeywordFamily),
|
||||
Then(Keyword("then"), KeywordFamily),
|
||||
Else(Keyword("else"), KeywordFamily),
|
||||
While(Keyword("while"), KeywordFamily),
|
||||
Continue(Keyword("continue"), KeywordFamily),
|
||||
Break(Keyword("break"), KeywordFamily),
|
||||
Import(Keyword("import"), KeywordFamily),
|
||||
Export(Keyword("export"), KeywordFamily),
|
||||
Func(Keyword("func"), KeywordFamily),
|
||||
Native(Keyword("native"), KeywordFamily),
|
||||
Let(Keyword("let"), KeywordFamily),
|
||||
Whitespace(CharConsumer { it == ' ' || it == '\r' || it == '\n' || it == '\t' }),
|
||||
BlockComment(CommentFamily),
|
||||
|
@ -8,16 +8,6 @@ include(
|
||||
":parser",
|
||||
":frontend",
|
||||
":evaluator",
|
||||
":ffi",
|
||||
":tool"
|
||||
)
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
version("clikt", "4.2.0")
|
||||
|
||||
library("clikt", "com.github.ajalt.clikt", "clikt")
|
||||
.versionRef("clikt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ dependencies {
|
||||
api(project(":parser"))
|
||||
api(project(":frontend"))
|
||||
api(project(":evaluator"))
|
||||
implementation(libs.clikt)
|
||||
api(project(":ffi"))
|
||||
api("com.github.ajalt.clikt:clikt:4.2.0")
|
||||
implementation(project(":common"))
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,11 @@ import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
import gay.pizza.pork.evaluator.Arguments
|
||||
import gay.pizza.pork.evaluator.CallableFunction
|
||||
import gay.pizza.pork.evaluator.None
|
||||
import gay.pizza.pork.evaluator.Scope
|
||||
import gay.pizza.pork.ffi.JnaNativeProvider
|
||||
|
||||
class RunCommand : CliktCommand(help = "Run Program", name = "run") {
|
||||
val loop by option("--loop", help = "Loop Program").flag()
|
||||
@ -28,8 +30,12 @@ class RunCommand : CliktCommand(help = "Run Program", name = "run") {
|
||||
None
|
||||
})
|
||||
|
||||
val main = tool.loadMainFunction(scope, setupEvaluator = {
|
||||
addNativeFunctionProvider("ffi", JnaNativeProvider())
|
||||
})
|
||||
|
||||
maybeLoopAndMeasure(loop, measure) {
|
||||
tool.evaluate(scope)
|
||||
main.call(Arguments(emptyList()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import gay.pizza.pork.parser.Printer
|
||||
import gay.pizza.pork.ast.CompilationUnit
|
||||
import gay.pizza.pork.ast.visit
|
||||
import gay.pizza.pork.evaluator.Arguments
|
||||
import gay.pizza.pork.evaluator.CallableFunction
|
||||
import gay.pizza.pork.evaluator.Evaluator
|
||||
import gay.pizza.pork.evaluator.Scope
|
||||
import gay.pizza.pork.frontend.ContentSource
|
||||
@ -29,11 +30,12 @@ abstract class Tool {
|
||||
|
||||
fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse())
|
||||
|
||||
fun evaluate(scope: Scope) {
|
||||
fun loadMainFunction(scope: Scope, setupEvaluator: Evaluator.() -> Unit = {}): CallableFunction {
|
||||
val contentSource = createContentSource()
|
||||
val world = World(contentSource)
|
||||
val evaluator = Evaluator(world, scope)
|
||||
setupEvaluator(evaluator)
|
||||
val resultingScope = evaluator.evaluate(rootFilePath())
|
||||
resultingScope.call("main", Arguments(emptyList()))
|
||||
return resultingScope.value("main") as CallableFunction
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user