Skip to content

Commit

Permalink
language: prelude and internal functions, and varargs support
Browse files Browse the repository at this point in the history
  • Loading branch information
azenla committed Sep 10, 2023
1 parent 1cfb197 commit e8c984f
Show file tree
Hide file tree
Showing 24 changed files with 166 additions and 104 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ A work-in-progress programming language.
```pork
/* 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 < 2 {
n
} else {
fib(n - 1) + fib(n - 2)
}
}
func main() {
Expand Down
8 changes: 7 additions & 1 deletion ast/src/main/ast/pork.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ types:
type: Symbol
- name: arguments
type: List<Expression>
ArgumentSpec:
values:
- name: symbol
type: Symbol
- name: multiple
type: Boolean
FunctionDefinition:
parent: Definition
values:
Expand All @@ -125,7 +131,7 @@ types:
- name: symbol
type: Symbol
- name: arguments
type: List<Symbol>
type: List<ArgumentSpec>
- name: block
type: Block?
- name: native
Expand Down
3 changes: 3 additions & 0 deletions ast/src/main/graph/types.dot
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ digraph A {
type_InfixOperation [shape=box,label="InfixOperation"]
type_BooleanLiteral [shape=box,label="BooleanLiteral"]
type_FunctionCall [shape=box,label="FunctionCall"]
type_ArgumentSpec [shape=box,label="ArgumentSpec"]
type_FunctionDefinition [shape=box,label="FunctionDefinition"]
type_If [shape=box,label="If"]
type_ImportDeclaration [shape=box,label="ImportDeclaration"]
Expand Down Expand Up @@ -70,8 +71,10 @@ digraph A {
type_InfixOperation -> type_InfixOperator [style=dotted]
type_FunctionCall -> type_Symbol [style=dotted]
type_FunctionCall -> type_Expression [style=dotted]
type_ArgumentSpec -> type_Symbol [style=dotted]
type_FunctionDefinition -> type_DefinitionModifiers [style=dotted]
type_FunctionDefinition -> type_Symbol [style=dotted]
type_FunctionDefinition -> type_ArgumentSpec [style=dotted]
type_FunctionDefinition -> type_Block [style=dotted]
type_FunctionDefinition -> type_Native [style=dotted]
type_If -> type_Expression [style=dotted]
Expand Down
9 changes: 9 additions & 0 deletions ast/src/main/kotlin/gay/pizza/pork/ast/ArgumentSpec.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// GENERATED CODE FROM PORK AST CODEGEN
package gay.pizza.pork.ast

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
@SerialName("argumentSpec")
class ArgumentSpec(var symbol: Symbol, var multiple: Boolean)
4 changes: 2 additions & 2 deletions ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import kotlinx.serialization.Serializable

@Serializable
@SerialName("functionDefinition")
class FunctionDefinition(override val modifiers: DefinitionModifiers, override val symbol: Symbol, val arguments: List<Symbol>, val block: Block?, val native: Native?) : Definition() {
class FunctionDefinition(override val modifiers: DefinitionModifiers, override val symbol: Symbol, val arguments: List<ArgumentSpec>, 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), listOf(native))
visitor.visitAll(listOf(symbol), listOf(block), listOf(native))

override fun <T> visit(visitor: NodeVisitor<T>): T =
visitor.visitFunctionDefinition(this)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package gay.pizza.pork.evaluator

import gay.pizza.pork.ast.CompilationUnit
import gay.pizza.pork.ast.Definition
import gay.pizza.pork.ast.FunctionDefinition
import gay.pizza.pork.ast.ImportDeclaration
import gay.pizza.pork.ast.*
import gay.pizza.pork.frontend.ImportLocator

class CompilationUnitContext(
Expand Down Expand Up @@ -45,6 +42,7 @@ class CompilationUnitContext(
}

private fun processAllImports() {
processPreludeImport()
val imports = compilationUnit.declarations.filterIsInstance<ImportDeclaration>()
for (import in imports) {
processImport(import)
Expand All @@ -57,4 +55,18 @@ class CompilationUnitContext(
val evaluationContext = evaluator.context(importLocator)
internalScope.inherit(evaluationContext.externalScope)
}

private fun processPreludeImport() {
processImport(preludeImport)
}

companion object {
private val preludeImport = ImportDeclaration(
Symbol("std"),
listOf(
Symbol("lang"),
Symbol("prelude")
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
subtract = { a, b -> a - b },
multiply = { a, b -> a * b },
divide = { a, b -> a / b },
euclideanModulo = { _, _ -> throw RuntimeException("Can't perform integer modulo between floating point types") },
remainder = { _, _ -> throw RuntimeException("Can't perform integer remainder between floating point types") },
euclideanModulo = { _, _ -> floatingPointTypeError("integer modulo") },
remainder = { _, _ -> floatingPointTypeError("integer remainder") },
lesser = { a, b -> a < b },
greater = { a, b -> a > b },
lesserEqual = { a, b -> a <= b },
Expand All @@ -138,8 +138,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
subtract = { a, b -> a - b },
multiply = { a, b -> a * b },
divide = { a, b -> a / b },
euclideanModulo = { _, _ -> throw RuntimeException("Can't perform integer modulo between floating point types") },
remainder = { _, _ -> throw RuntimeException("Can't perform integer remainder between floating point types") },
euclideanModulo = { _, _ -> floatingPointTypeError("integer modulo") },
remainder = { _, _ -> floatingPointTypeError("integer remainder") },
lesser = { a, b -> a < b },
greater = { a, b -> a > b },
lesserEqual = { a, b -> a <= b },
Expand Down Expand Up @@ -228,31 +228,19 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
}

override fun visitFunctionDefinition(node: FunctionDefinition): Any {
throw RuntimeException(
"Function declarations cannot be visited in an EvaluationVisitor. " +
"Utilize a FunctionContext."
)
topLevelUsedError("FunctionDefinition", "FunctionContext")
}

override fun visitImportDeclaration(node: ImportDeclaration): Any {
throw RuntimeException(
"Import declarations cannot be visited in an EvaluationVisitor. " +
"Utilize an CompilationUnitContext."
)
topLevelUsedError("ImportDeclaration", "CompilationUnitContext")
}

override fun visitCompilationUnit(node: CompilationUnit): Any {
throw RuntimeException(
"Compilation units cannot be visited in an EvaluationVisitor. " +
"Utilize an CompilationUnitContext."
)
topLevelUsedError("CompilationUnit", "CompilationUnitContext")
}

override fun visitNative(node: Native): Any {
throw RuntimeException(
"Native definition cannot be visited in an EvaluationVisitor. " +
"Utilize an FunctionContext."
)
topLevelUsedError("Native", "FunctionContext")
}

override fun visitContinue(node: Continue): Any = ContinueMarker
Expand All @@ -266,6 +254,17 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
}
}

private fun floatingPointTypeError(operation: String): Nothing {
throw RuntimeException("Can't perform $operation between floating point types")
}

private fun topLevelUsedError(name: String, alternative: String): Nothing {
throw RuntimeException(
"$name cannot be visited in an EvaluationVisitor. " +
"Utilize an $alternative instead."
)
}

private object BreakMarker : RuntimeException("Break Marker")
private object ContinueMarker: RuntimeException("Continue Marker")
}
10 changes: 5 additions & 5 deletions evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Evaluator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,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>()
private val nativeProviders = mutableMapOf<String, NativeProvider>()

fun evaluate(locator: ImportLocator): Scope =
context(locator).externalScope
Expand All @@ -20,12 +20,12 @@ class Evaluator(val world: World, val scope: Scope) {
return context
}

fun nativeFunctionProvider(form: String): NativeFunctionProvider {
return nativeFunctionProviders[form] ?:
fun nativeFunctionProvider(form: String): NativeProvider {
return nativeProviders[form] ?:
throw RuntimeException("Unknown native function form: $form")
}

fun addNativeFunctionProvider(form: String, nativeFunctionProvider: NativeFunctionProvider) {
nativeFunctionProviders[form] = nativeFunctionProvider
fun addNativeProvider(form: String, nativeProvider: NativeProvider) {
nativeProviders[form] = nativeProvider
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@ class FunctionContext(val compilationUnitContext: CompilationUnitContext, val no
}

val scope = compilationUnitContext.internalScope.fork()
for ((index, argumentSymbol) in node.arguments.withIndex()) {
scope.define(argumentSymbol.id, arguments.values[index])
for ((index, spec) in node.arguments.withIndex()) {
if (spec.multiple) {
val list = arguments.values.subList(index, arguments.values.size - 1)
scope.define(spec.symbol.id, list)
break
} else {
scope.define(spec.symbol.id, arguments.values[index])
}
}

if (node.block == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package gay.pizza.pork.evaluator

class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
private val functions = mutableMapOf(
"println" to CallableFunction(::printLine)
)

override fun provideNativeFunction(definition: String): CallableFunction {
return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition")
}

private fun printLine(arguments: Arguments): Any {
if (quiet) {
return None
}
when (arguments.values.count()) {
0 -> println()
1 -> println(arguments.values[0])
else -> println(arguments.values.joinToString(" "))
}
return None
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package gay.pizza.pork.evaluator

interface NativeFunctionProvider {
interface NativeProvider {
fun provideNativeFunction(definition: String): CallableFunction
}
12 changes: 4 additions & 8 deletions examples/fib.pork
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
/* fibonacci sequence */
func fib(n) {
if n == 0 {
0
if n < 2 {
n
} else {
if n == 1 {
1
} else {
fib(n - 1) + fib(n - 2)
}
fib(n - 1) + fib(n - 2)
}
}

export func main() {
let result = fib(20)
let result = fib(30)
println(result)
}
14 changes: 7 additions & 7 deletions ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaAutogen.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
package gay.pizza.pork.ffi

import gay.pizza.pork.ast.CompilationUnit
import gay.pizza.pork.ast.DefinitionModifiers
import gay.pizza.pork.ast.FunctionDefinition
import gay.pizza.pork.ast.Native
import gay.pizza.pork.ast.StringLiteral
import gay.pizza.pork.ast.Symbol
import gay.pizza.pork.ast.*
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.lang.reflect.Parameter
Expand Down Expand Up @@ -149,7 +144,12 @@ class JavaAutogen(val javaClass: Class<*>) {
FunctionDefinition(
modifiers = DefinitionModifiers(true),
symbol = Symbol("${prefix}_${name}"),
arguments = parameterNames.map { Symbol(it) },
arguments = parameterNames.map {
ArgumentSpec(
symbol = Symbol(it),
multiple = false
)
},
native = asNative(functionDefinition),
block = null
)
Expand Down
4 changes: 2 additions & 2 deletions ffi/src/main/kotlin/gay/pizza/pork/ffi/JavaNativeProvider.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package gay.pizza.pork.ffi

import gay.pizza.pork.evaluator.CallableFunction
import gay.pizza.pork.evaluator.NativeFunctionProvider
import gay.pizza.pork.evaluator.NativeProvider
import gay.pizza.pork.evaluator.None
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType

class JavaNativeProvider : NativeFunctionProvider {
class JavaNativeProvider : NativeProvider {
private val lookup = MethodHandles.lookup()

override fun provideNativeFunction(definition: String): CallableFunction {
Expand Down
4 changes: 2 additions & 2 deletions ffi/src/main/kotlin/gay/pizza/pork/ffi/JnaNativeProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package gay.pizza.pork.ffi

import com.sun.jna.Function
import gay.pizza.pork.evaluator.CallableFunction
import gay.pizza.pork.evaluator.NativeFunctionProvider
import gay.pizza.pork.evaluator.NativeProvider

class JnaNativeProvider : NativeFunctionProvider {
class JnaNativeProvider : NativeProvider {
override fun provideNativeFunction(definition: String): CallableFunction {
val functionDefinition = FfiFunctionDefinition.parse(definition)
val function = Function.getFunction(functionDefinition.library, functionDefinition.function)
Expand Down
9 changes: 7 additions & 2 deletions parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
private fun readImportDeclaration(): ImportDeclaration = within {
expect(TokenType.Import)
val form = readSymbolRaw()
val components = oneAndContinuedBy(TokenType.Period) { readSymbolRaw() }
val components = oneAndContinuedBy(TokenType.Dot) { readSymbolRaw() }
ImportDeclaration(form, components)
}

Expand All @@ -237,7 +237,12 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
val name = readSymbolRaw()
expect(TokenType.LeftParentheses)
val arguments = collect(TokenType.RightParentheses, TokenType.Comma) {
readSymbolRaw()
val symbol = readSymbolRaw()
var multiple: Boolean = false
if (next(TokenType.DotDotDot)) {
multiple = true
}
ArgumentSpec(symbol, multiple)
}
expect(TokenType.RightParentheses)

Expand Down
5 changes: 4 additions & 1 deletion parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
visit(node.symbol)
append("(")
for ((index, argument) in node.arguments.withIndex()) {
visit(argument)
visit(argument.symbol)
if (argument.multiple) {
append("...")
}
if (index + 1 != node.arguments.size) {
append(", ")
}
Expand Down
Loading

0 comments on commit e8c984f

Please sign in to comment.