Compare commits
No commits in common. "master" and "0.6.7" have entirely different histories.
@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
|||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
version = "0.6.8-SNAPSHOT"
|
version = "0.6.7-SNAPSHOT"
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
package net.sergeych.lyng
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special version of the [Context] used to `apply` new this object to
|
|
||||||
* _parent context property.
|
|
||||||
*
|
|
||||||
* @param _parent context to apply to
|
|
||||||
* @param args arguments for the new context
|
|
||||||
* @param appliedContext the new context to apply, it will have lower priority except for `this` which
|
|
||||||
* will be reset by appliedContext's `this`.
|
|
||||||
*/
|
|
||||||
class AppliedContext(_parent: Context, args: Arguments, val appliedContext: Context)
|
|
||||||
: Context(_parent, args, appliedContext.pos, appliedContext.thisObj) {
|
|
||||||
override fun get(name: String): ObjRecord? =
|
|
||||||
if (name == "this") thisObj.asReadonly
|
|
||||||
else super.get(name) ?: appliedContext[name]
|
|
||||||
}
|
|
@ -110,7 +110,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
*/
|
*/
|
||||||
data class Item(
|
data class Item(
|
||||||
val name: String,
|
val name: String,
|
||||||
val type: TypeDecl = TypeDecl.TypeAny,
|
val type: TypeDecl = TypeDecl.Obj,
|
||||||
val pos: Pos = Pos.builtIn,
|
val pos: Pos = Pos.builtIn,
|
||||||
val isEllipsis: Boolean = false,
|
val isEllipsis: Boolean = false,
|
||||||
/**
|
/**
|
||||||
|
@ -387,14 +387,14 @@ class Compiler(
|
|||||||
val argsDeclaration = parseArgsDeclaration(cc)
|
val argsDeclaration = parseArgsDeclaration(cc)
|
||||||
if (argsDeclaration != null && argsDeclaration.endTokenType != Token.Type.ARROW)
|
if (argsDeclaration != null && argsDeclaration.endTokenType != Token.Type.ARROW)
|
||||||
throw ScriptError(startPos, "lambda must have either valid arguments declaration with '->' or no arguments")
|
throw ScriptError(startPos, "lambda must have either valid arguments declaration with '->' or no arguments")
|
||||||
|
val pos = cc.currentPos()
|
||||||
val body = parseBlock(cc, skipLeadingBrace = true)
|
val body = parseBlock(cc, skipLeadingBrace = true)
|
||||||
|
|
||||||
var closure: Context? = null
|
var closure: Context? = null
|
||||||
|
|
||||||
val callStatement = statement {
|
val callStatement = statement {
|
||||||
// and the source closure of the lambda which might have other thisObj.
|
// and the source closure of the lambda which might have other thisObj.
|
||||||
val context = AppliedContext(closure!!, args, this)
|
val context = closure!!.copy(pos, args).applyContext(this)
|
||||||
if (argsDeclaration == null) {
|
if (argsDeclaration == null) {
|
||||||
// no args: automatic var 'it'
|
// no args: automatic var 'it'
|
||||||
val l = args.list
|
val l = args.list
|
||||||
@ -560,11 +560,11 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseTypeDeclaration(cc: CompilerContext): TypeDecl {
|
private fun parseTypeDeclaration(cc: CompilerContext): TypeDecl {
|
||||||
return if (cc.skipTokenOfType(Token.Type.COLON, isOptional = true)) {
|
val result = TypeDecl.Obj
|
||||||
val tt = cc.requireToken(Token.Type.ID, "type name or type expression required")
|
cc.ifNextIs(Token.Type.COLON) {
|
||||||
val isNullable = cc.skipTokenOfType(Token.Type.QUESTION, isOptional = true)
|
TODO("parse type declaration here")
|
||||||
TypeDecl.Simple(tt.value, isNullable)
|
}
|
||||||
} else TypeDecl.TypeAny
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -590,8 +590,6 @@ class Compiler(
|
|||||||
cc.previous()
|
cc.previous()
|
||||||
parseExpression(cc)?.let { args += ParsedArgument(it, t.pos) }
|
parseExpression(cc)?.let { args += ParsedArgument(it, t.pos) }
|
||||||
?: throw ScriptError(t.pos, "Expecting arguments list")
|
?: throw ScriptError(t.pos, "Expecting arguments list")
|
||||||
if (cc.current().type == Token.Type.COLON)
|
|
||||||
parseTypeDeclaration(cc)
|
|
||||||
// Here should be a valid termination:
|
// Here should be a valid termination:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -726,7 +724,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse keyword-starting statement.
|
* Parse keyword-starting statenment.
|
||||||
* @return parsed statement or null if, for example. [id] is not among keywords
|
* @return parsed statement or null if, for example. [id] is not among keywords
|
||||||
*/
|
*/
|
||||||
private fun parseKeywordStatement(id: Token, cc: CompilerContext): Statement? = when (id.value) {
|
private fun parseKeywordStatement(id: Token, cc: CompilerContext): Statement? = when (id.value) {
|
||||||
@ -737,6 +735,7 @@ class Compiler(
|
|||||||
"for" -> parseForStatement(cc)
|
"for" -> parseForStatement(cc)
|
||||||
"break" -> parseBreakStatement(id.pos, cc)
|
"break" -> parseBreakStatement(id.pos, cc)
|
||||||
"continue" -> parseContinueStatement(id.pos, cc)
|
"continue" -> parseContinueStatement(id.pos, cc)
|
||||||
|
"fn", "fun" -> parseFunctionDeclaration(cc)
|
||||||
"if" -> parseIfStatement(cc)
|
"if" -> parseIfStatement(cc)
|
||||||
"class" -> parseClassDeclaration(cc, false)
|
"class" -> parseClassDeclaration(cc, false)
|
||||||
"try" -> parseTryStatement(cc)
|
"try" -> parseTryStatement(cc)
|
||||||
@ -745,16 +744,11 @@ class Compiler(
|
|||||||
else -> {
|
else -> {
|
||||||
// triples
|
// triples
|
||||||
cc.previous()
|
cc.previous()
|
||||||
val isExtern = cc.skipId("extern")
|
|
||||||
when {
|
when {
|
||||||
cc.matchQualifiers("fun", "private") -> parseFunctionDeclaration(cc, Visibility.Private, isExtern)
|
cc.matchQualifiers("fun", "private") -> parseFunctionDeclaration(cc, Visibility.Private)
|
||||||
cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration(cc, Visibility.Private, isExtern)
|
cc.matchQualifiers("fn", "private") -> parseFunctionDeclaration(cc, Visibility.Private)
|
||||||
cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(cc, isOpen = true, isExtern = isExtern)
|
cc.matchQualifiers("fun", "open") -> parseFunctionDeclaration(cc, isOpen = true)
|
||||||
cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(cc, isOpen = true, isExtern = isExtern)
|
cc.matchQualifiers("fn", "open") -> parseFunctionDeclaration(cc, isOpen = true)
|
||||||
|
|
||||||
cc.matchQualifiers("fun") -> parseFunctionDeclaration(cc, isOpen = false, isExtern = isExtern)
|
|
||||||
cc.matchQualifiers("fn") -> parseFunctionDeclaration(cc, isOpen = false, isExtern = isExtern)
|
|
||||||
|
|
||||||
cc.matchQualifiers("val", "private") -> parseVarDeclaration(false, Visibility.Private, cc)
|
cc.matchQualifiers("val", "private") -> parseVarDeclaration(false, Visibility.Private, cc)
|
||||||
cc.matchQualifiers("var", "private") -> parseVarDeclaration(true, Visibility.Private, cc)
|
cc.matchQualifiers("var", "private") -> parseVarDeclaration(true, Visibility.Private, cc)
|
||||||
cc.matchQualifiers("val", "open") -> parseVarDeclaration(false, Visibility.Private, cc, true)
|
cc.matchQualifiers("val", "open") -> parseVarDeclaration(false, Visibility.Private, cc, true)
|
||||||
@ -1425,35 +1419,29 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseFunctionDeclaration(
|
private fun parseFunctionDeclaration(
|
||||||
cc: CompilerContext,
|
tokens: CompilerContext,
|
||||||
visibility: Visibility = Visibility.Public,
|
visibility: Visibility = Visibility.Public,
|
||||||
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false,
|
@Suppress("UNUSED_PARAMETER") isOpen: Boolean = false
|
||||||
isExtern: Boolean = false
|
|
||||||
): Statement {
|
): Statement {
|
||||||
var t = cc.next()
|
var t = tokens.next()
|
||||||
val start = t.pos
|
val start = t.pos
|
||||||
val name = if (t.type != Token.Type.ID)
|
val name = if (t.type != Token.Type.ID)
|
||||||
throw ScriptError(t.pos, "Expected identifier after 'fn'")
|
throw ScriptError(t.pos, "Expected identifier after 'fn'")
|
||||||
else t.value
|
else t.value
|
||||||
|
|
||||||
t = cc.next()
|
t = tokens.next()
|
||||||
if (t.type != Token.Type.LPAREN)
|
if (t.type != Token.Type.LPAREN)
|
||||||
throw ScriptError(t.pos, "Bad function definition: expected '(' after 'fn ${name}'")
|
throw ScriptError(t.pos, "Bad function definition: expected '(' after 'fn ${name}'")
|
||||||
|
|
||||||
val argsDeclaration = parseArgsDeclaration(cc)
|
val argsDeclaration = parseArgsDeclaration(tokens)
|
||||||
if (argsDeclaration == null || argsDeclaration.endTokenType != Token.Type.RPAREN)
|
if (argsDeclaration == null || argsDeclaration.endTokenType != Token.Type.RPAREN)
|
||||||
throw ScriptError(
|
throw ScriptError(
|
||||||
t.pos,
|
t.pos,
|
||||||
"Bad function definition: expected valid argument declaration or () after 'fn ${name}'"
|
"Bad function definition: expected valid argument declaration or () after 'fn ${name}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (cc.current().type == Token.Type.COLON) parseTypeDeclaration(cc)
|
|
||||||
|
|
||||||
// Here we should be at open body
|
// Here we should be at open body
|
||||||
val fnStatements = if (isExtern)
|
val fnStatements = parseBlock(tokens)
|
||||||
statement { raiseError("extern function not provided: $name") }
|
|
||||||
else
|
|
||||||
parseBlock(cc)
|
|
||||||
|
|
||||||
var closure: Context? = null
|
var closure: Context? = null
|
||||||
|
|
||||||
|
@ -53,20 +53,6 @@ internal class CompilerContext(val tokens: List<Token>) {
|
|||||||
|
|
||||||
fun currentPos(): Pos = tokens[currentIndex].pos
|
fun currentPos(): Pos = tokens[currentIndex].pos
|
||||||
|
|
||||||
/**
|
|
||||||
* If the next token is identifier `name`, skip it and return `true`.
|
|
||||||
* else leave where it is and return `false`
|
|
||||||
*/
|
|
||||||
fun skipId(name: String): Boolean {
|
|
||||||
current().let { t ->
|
|
||||||
if( t.type == Token.Type.ID && t.value == name ) {
|
|
||||||
next()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skips next token if its type is `tokenType`, returns `true` if so.
|
* Skips next token if its type is `tokenType`, returns `true` if so.
|
||||||
* @param errorMessage message to throw if next token is not `tokenType`
|
* @param errorMessage message to throw if next token is not `tokenType`
|
||||||
@ -110,6 +96,7 @@ internal class CompilerContext(val tokens: List<Token>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun addBreak() {
|
inline fun addBreak() {
|
||||||
breakFound = true
|
breakFound = true
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
open class Context(
|
class Context(
|
||||||
val parent: Context?,
|
val parent: Context?,
|
||||||
val args: Arguments = Arguments.EMPTY,
|
val args: Arguments = Arguments.EMPTY,
|
||||||
var pos: Pos = Pos.builtIn,
|
var pos: Pos = Pos.builtIn,
|
||||||
@ -13,6 +13,15 @@ open class Context(
|
|||||||
)
|
)
|
||||||
: this(Script.defaultContext, args, pos)
|
: this(Script.defaultContext, args, pos)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Making this context priority one
|
||||||
|
*/
|
||||||
|
fun applyContext(other: Context): Context {
|
||||||
|
if (other.thisObj != ObjVoid) thisObj = other.thisObj
|
||||||
|
appliedContext = other
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
fun raiseNotImplemented(what: String = "operation"): Nothing = raiseError("$what is not implemented")
|
fun raiseNotImplemented(what: String = "operation"): Nothing = raiseError("$what is not implemented")
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@ -65,13 +74,15 @@ open class Context(
|
|||||||
inline fun <reified T : Obj> thisAs(): T = (thisObj as? T)
|
inline fun <reified T : Obj> thisAs(): T = (thisObj as? T)
|
||||||
?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}")
|
?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}")
|
||||||
|
|
||||||
|
internal var appliedContext: Context? = null
|
||||||
internal val objects = mutableMapOf<String, ObjRecord>()
|
internal val objects = mutableMapOf<String, ObjRecord>()
|
||||||
|
|
||||||
open operator fun get(name: String): ObjRecord? =
|
operator fun get(name: String): ObjRecord? =
|
||||||
if (name == "this") thisObj.asReadonly
|
if (name == "this") thisObj.asReadonly
|
||||||
else {
|
else {
|
||||||
objects[name]
|
objects[name]
|
||||||
?: parent?.get(name)
|
?: parent?.get(name)
|
||||||
|
?: appliedContext?.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context =
|
fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Context =
|
||||||
@ -126,4 +137,5 @@ open class Context(
|
|||||||
|
|
||||||
fun containsLocal(name: String): Boolean = name in objects
|
fun containsLocal(name: String): Boolean = name in objects
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
@file:Suppress("unused")
|
|
||||||
|
|
||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
// this is highly experimental and subject to complete redesign
|
// this is highly experimental and subject to complete redesign
|
||||||
// very soon
|
// very soon
|
||||||
sealed class TypeDecl(val isNullable:Boolean = false) {
|
sealed class TypeDecl {
|
||||||
// ??
|
// ??
|
||||||
// data class Fn(val argTypes: List<ArgsDeclaration.Item>, val retType: TypeDecl) : TypeDecl()
|
// data class Fn(val argTypes: List<ArgsDeclaration.Item>, val retType: TypeDecl) : TypeDecl()
|
||||||
object TypeAny : TypeDecl()
|
object Obj : TypeDecl()
|
||||||
object TypeNullableAny : TypeDecl(true)
|
|
||||||
|
|
||||||
class Simple(val name: String,isNullable: Boolean) : TypeDecl(isNullable)
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
package io.github.kotlin.fibonacci
|
||||||
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import net.sergeych.lyng.*
|
import net.sergeych.lyng.*
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import net.sergeych.lyng.eval
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
class TypesTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testTypeCollection1() = runTest {
|
|
||||||
eval("""
|
|
||||||
class Point(x: Real, y: Real)
|
|
||||||
assert(Point(1,2).x == 1)
|
|
||||||
assert(Point(1,2).y == 2)
|
|
||||||
assert(Point(1,2) is Point)
|
|
||||||
""".trimIndent())
|
|
||||||
|
|
||||||
}
|
|
||||||
@Test
|
|
||||||
fun testTypeCollection2() = runTest {
|
|
||||||
eval("""
|
|
||||||
fun fn1(x: Real, y: Real): Real { x + y }
|
|
||||||
""".trimIndent())
|
|
||||||
|
|
||||||
}
|
|
||||||
@Test
|
|
||||||
fun testTypeCollection3() = runTest {
|
|
||||||
eval("""
|
|
||||||
class Test(a: Int) {
|
|
||||||
fun fn1(x: Real, y: Real): Real { x + y }
|
|
||||||
}
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testExternDeclarations() = runTest {
|
|
||||||
eval("""
|
|
||||||
extern fun foo1(a: String): Void
|
|
||||||
assertThrows { foo1("1") }
|
|
||||||
class Test(a: Int) {
|
|
||||||
extern fun fn1(x: Real, y: Real): Real
|
|
||||||
// extern val b: Int
|
|
||||||
}
|
|
||||||
// println("1")
|
|
||||||
val t = Test(0)
|
|
||||||
// println(t.b)
|
|
||||||
// println("2")
|
|
||||||
assertThrows {
|
|
||||||
t.fn1(1,2)
|
|
||||||
}
|
|
||||||
// println("4")
|
|
||||||
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user