Feature: Array Iteration in NeoLang

This commit is contained in:
zt515
2017-08-09 02:38:06 +08:00
parent 44947f05aa
commit 04b73fc50e
6 changed files with 110 additions and 40 deletions

View File

@ -2,6 +2,7 @@ package io.neolang.ast.visitor
import io.neolang.ast.base.NeoLangAst import io.neolang.ast.base.NeoLangAst
import io.neolang.ast.node.* import io.neolang.ast.node.*
import io.neolang.runtime.type.NeoLangValue
/** /**
@ -38,11 +39,31 @@ internal object AstVisitorImpl {
visitorCallback.onEnterContext(arrayName) visitorCallback.onEnterContext(arrayName)
ast.elements.forEach { ast.elements.forEach {
AstVisitorImpl.visitBlock(it.block, it.index.toString(), visitorCallback) AstVisitorImpl.visitArrayElementBlock(it.block, it.index, visitorCallback)
// AstVisitorImpl.visitBlock(it.block, it.index.toString(), visitorCallback)
} }
visitorCallback.onExitContext() visitorCallback.onExitContext()
} }
fun visitArrayElementBlock(ast: NeoLangBlockNode, index: Int, visitorCallback: IVisitorCallback) {
val visitingNode = ast.ast
when (visitingNode) {
is NeoLangGroupNode -> {
// is a sub block, e.g.
// block: { $blockName: {} }
visitorCallback.onEnterContext(index.toString())
AstVisitorImpl.visitGroup(visitingNode, visitorCallback)
visitorCallback.onExitContext()
}
is NeoLangStringNode -> {
definePrimaryData(index.toString(), visitingNode.eval(), visitorCallback)
}
is NeoLangNumberNode -> {
definePrimaryData(index.toString(), visitingNode.eval(), visitorCallback)
}
}
}
fun visitBlock(ast: NeoLangBlockNode, blockName: String, visitorCallback: IVisitorCallback) { fun visitBlock(ast: NeoLangBlockNode, blockName: String, visitorCallback: IVisitorCallback) {
val visitingNode = ast.ast val visitingNode = ast.ast
when (visitingNode) { when (visitingNode) {
@ -50,7 +71,6 @@ internal object AstVisitorImpl {
// is a sub block, e.g. // is a sub block, e.g.
// block: { $blockName: {} } // block: { $blockName: {} }
// FIXME: Block in Array
visitorCallback.onEnterContext(blockName) visitorCallback.onEnterContext(blockName)
AstVisitorImpl.visitGroup(visitingNode, visitorCallback) AstVisitorImpl.visitGroup(visitingNode, visitorCallback)
visitorCallback.onExitContext() visitorCallback.onExitContext()
@ -61,15 +81,19 @@ internal object AstVisitorImpl {
} }
is NeoLangStringNode -> { is NeoLangStringNode -> {
// block: { $blockName: "hello" } // block: { $blockName: "hello" }
visitorCallback.getCurrentContext().defineAttribute(blockName, visitingNode.eval()) definePrimaryData(blockName, visitingNode.eval(), visitorCallback)
} }
is NeoLangNumberNode -> { is NeoLangNumberNode -> {
// block: { $blockName: 123.456 } // block: { $blockName: 123.456 }
visitorCallback.getCurrentContext().defineAttribute(blockName, visitingNode.eval()) definePrimaryData(blockName, visitingNode.eval(), visitorCallback)
} }
} }
} }
private fun definePrimaryData(name: String, value: NeoLangValue, visitorCallback: IVisitorCallback) {
visitorCallback.getCurrentContext().defineAttribute(name, value)
}
fun visitStartAst(ast: NeoLangAst, visitorCallback: IVisitorCallback) { fun visitStartAst(ast: NeoLangAst, visitorCallback: IVisitorCallback) {
when (ast) { when (ast) {
is NeoLangProgramNode -> AstVisitorImpl.visitProgram(ast, visitorCallback) is NeoLangProgramNode -> AstVisitorImpl.visitProgram(ast, visitorCallback)

View File

@ -77,6 +77,9 @@ class NeoLangParser {
return NeoLangProgramNode.emptyNode() return NeoLangProgramNode.emptyNode()
} }
/**
* @param attrName Only available when group is a attribute value
*/
private fun group(): NeoLangGroupNode? { private fun group(): NeoLangGroupNode? {
val token = currentToken ?: throw InvalidTokenException("Unexpected token: null") val token = currentToken ?: throw InvalidTokenException("Unexpected token: null")
@ -115,13 +118,11 @@ class NeoLangParser {
private fun array(arrayName: NeoLangStringNode): NeoLangArrayNode? { private fun array(arrayName: NeoLangStringNode): NeoLangArrayNode? {
val token = currentToken ?: throw InvalidTokenException("Unexpected token: null") val token = currentToken ?: throw InvalidTokenException("Unexpected token: null")
// TODO: Multiple Array // TODO: Multiple Array
var block = blockNonArrayElement(arrayName) var block = blockNonArrayElement(arrayName)
var index = 0 var index = 0
if (block != null) { if (block != null) {
val elements = mutableListOf(NeoLangArrayElement(index++, block)) val elements = mutableListOf(NeoLangArrayElement(index++, block))
if (match(NeoLangTokenType.COMMA)) { if (match(NeoLangTokenType.COMMA)) {
@ -149,7 +150,7 @@ class NeoLangParser {
/** /**
* @attrName The block holder's name * @param attrName The block holder's name
*/ */
private fun block(attrName: NeoLangStringNode): NeoLangBlockNode? { private fun block(attrName: NeoLangStringNode): NeoLangBlockNode? {
val block = blockNonArrayElement(attrName) val block = blockNonArrayElement(attrName)
@ -172,7 +173,10 @@ class NeoLangParser {
} }
} }
private fun blockNonArrayElement(attrName: NeoLangStringNode): NeoLangBlockNode? { /**
* @param attrName Only available when group is a attribute value
*/
private fun blockNonArrayElement(attrName: NeoLangStringNode?): NeoLangBlockNode? {
val token = currentToken ?: throw InvalidTokenException("Unexpected token: null") val token = currentToken ?: throw InvalidTokenException("Unexpected token: null")
return when (token.tokenType) { return when (token.tokenType) {

View File

@ -6,19 +6,32 @@ import io.neolang.runtime.type.NeoLangValue
* @author kiva * @author kiva
*/ */
class NeoLangContext(val contextName: String) { class NeoLangContext(val contextName: String) {
companion object {
private val emptyContext = NeoLangContext("<Context-Empty>")
}
private val attributes = mutableMapOf<String, NeoLangValue>() private val attributes = mutableMapOf<String, NeoLangValue>()
var parent: NeoLangContext? = null
var children = mutableListOf<NeoLangContext>()
fun defineAttribute(attributeName: String, attributeValue: NeoLangValue): NeoLangContext { fun defineAttribute(attributeName: String, attributeValue: NeoLangValue): NeoLangContext {
attributes[attributeName] = attributeValue attributes[attributeName] = attributeValue
return this return this
} }
fun getAttribute(attributeName: String): NeoLangValue { fun getAttribute(attributeName: String): NeoLangValue {
return attributes[attributeName] ?: NeoLangValue.UNDEFINED return attributes[attributeName] ?: parent?.getAttribute(attributeName) ?: NeoLangValue.UNDEFINED
} }
fun defineArray() { fun getChild(contextName: String): NeoLangContext {
var found: NeoLangContext? = null
children.forEach {
if (it.contextName == contextName) {
found = it
}
}
return found ?: emptyContext
} }
fun getAttributes(): Map<String, NeoLangValue> { fun getAttributes(): Map<String, NeoLangValue> {

View File

@ -22,6 +22,9 @@ open class NeoColorScheme {
const val COLOR_META_NAME = "name" const val COLOR_META_NAME = "name"
const val COLOR_META_VERSION = "version" const val COLOR_META_VERSION = "version"
val COLOR_META_PATH = arrayOf(COLOR_META_CONTEXT_NAME)
val COLOR_PATH = arrayOf(COLOR_META_CONTEXT_NAME, COLOR_CONTEXT_NAME)
// const val COLOR_DIM_BLACK = 0 // const val COLOR_DIM_BLACK = 0
// const val COLOR_DIM_RED = 1 // const val COLOR_DIM_RED = 1
// const val COLOR_DIM_GREEN = 2 // const val COLOR_DIM_GREEN = 2
@ -95,7 +98,7 @@ open class NeoColorScheme {
backgroundColor = getColorByVisitor(visitor, "background") backgroundColor = getColorByVisitor(visitor, "background")
foregroundColor = getColorByVisitor(visitor, "foreground") foregroundColor = getColorByVisitor(visitor, "foreground")
cursorColor = getColorByVisitor(visitor, "cursor") cursorColor = getColorByVisitor(visitor, "cursor")
visitor.getContext(COLOR_CONTEXT_NAME).getAttributes().forEach { visitor.getCurrentContext().getChild(COLOR_CONTEXT_NAME).getAttributes().forEach {
val colorIndex = try { val colorIndex = try {
it.key.substringAfter(COLOR_PREFIX).toInt() it.key.substringAfter(COLOR_PREFIX).toInt()
} catch (e: Exception) { } catch (e: Exception) {
@ -114,12 +117,12 @@ open class NeoColorScheme {
} }
private fun getMetaByVisitor(visitor: ConfigVisitor, metaName: String): String? { private fun getMetaByVisitor(visitor: ConfigVisitor, metaName: String): String? {
val value = visitor.getAttribute(COLOR_META_CONTEXT_NAME, metaName) val value = visitor.getAttribute(COLOR_META_PATH, metaName)
return if (value.isValid()) value.asString() else null return if (value.isValid()) value.asString() else null
} }
private fun getColorByVisitor(visitor: ConfigVisitor, colorName: String): String? { private fun getColorByVisitor(visitor: ConfigVisitor, colorName: String): String? {
val value = visitor.getAttribute(COLOR_CONTEXT_NAME, colorName) val value = visitor.getAttribute(COLOR_PATH, colorName)
return if (value.isValid()) value.asString() else null return if (value.isValid()) value.asString() else null
} }
} }

View File

@ -3,47 +3,49 @@ package io.neoterm.frontend.config
import io.neolang.ast.visitor.IVisitorCallback import io.neolang.ast.visitor.IVisitorCallback
import io.neolang.runtime.context.NeoLangContext import io.neolang.runtime.context.NeoLangContext
import io.neolang.runtime.type.NeoLangValue import io.neolang.runtime.type.NeoLangValue
import java.util.*
class ConfigVisitor : IVisitorCallback { class ConfigVisitor : IVisitorCallback {
private val emptyContext = NeoLangContext("<NeoTerm-Empty-Safety>") private var currentContext: NeoLangContext? = null
private val contextStack = Stack<NeoLangContext>()
private val definedContext = mutableListOf<NeoLangContext>()
fun getContext(contextName: String): NeoLangContext { fun getContext(contextPath: Array<String>) : NeoLangContext {
definedContext.forEach { var context = getCurrentContext()
if (it.contextName == contextName) { contextPath.forEach {
return it context = context.getChild(it)
}
} }
return emptyContext return context
} }
fun getAttribute(contextName: String, attrName: String): NeoLangValue { fun getAttribute(contextPath: Array<String>, attrName: String) : NeoLangValue {
return getContext(contextName).getAttribute(attrName) return getContext(contextPath).getAttribute(attrName)
} }
override fun onStart() { override fun onStart() {
onEnterContext("global") currentContext = NeoLangContext("global")
} }
override fun onFinish() { override fun onFinish() {
while (contextStack.isNotEmpty()) { var context = currentContext
onExitContext() while (context != null && context.parent != null) {
context = context.parent
} }
this.currentContext = context
} }
override fun onEnterContext(contextName: String) { override fun onEnterContext(contextName: String) {
val context = NeoLangContext(contextName) val newContext = NeoLangContext(contextName)
contextStack.push(context) newContext.parent = currentContext
currentContext!!.children.add(newContext)
currentContext = newContext
} }
override fun onExitContext() { override fun onExitContext() {
val context = contextStack.pop() val context = currentContext
definedContext.add(context) if (context != null && context.parent != null) {
this.currentContext = context.parent
}
} }
override fun getCurrentContext(): NeoLangContext { override fun getCurrentContext(): NeoLangContext {
return contextStack.peek() return currentContext!!
} }
} }

View File

@ -1,5 +1,6 @@
package io.neoterm package io.neoterm
import io.neoterm.customize.color.NeoColorScheme
import io.neoterm.frontend.config.ConfigVisitor import io.neoterm.frontend.config.ConfigVisitor
import io.neoterm.frontend.config.NeoConfigureFile import io.neoterm.frontend.config.NeoConfigureFile
import org.junit.Test import org.junit.Test
@ -9,21 +10,44 @@ import java.io.File
* @author kiva * @author kiva
*/ */
class ConfigureFileTest { class ConfigureFileTest {
private fun printAttr(visitor: ConfigVisitor, contextName: String, attrName: String) { private fun getMetaByVisitor(visitor: ConfigVisitor, metaName: String): String? {
println("attr [$contextName->$attrName]: ${visitor.getAttribute(contextName, attrName).asString()}") val value = visitor.getAttribute(NeoColorScheme.COLOR_META_PATH, metaName)
return if (value.isValid()) value.asString() else null
} }
private fun parseConfigure(filePath: String, contextName: String, attrName: String) { private fun getColorByVisitor(visitor: ConfigVisitor, colorName: String): String? {
val value = visitor.getAttribute(NeoColorScheme.COLOR_PATH, colorName)
return if (value.isValid()) value.asString() else null
}
private fun parseConfigure(filePath: String): ConfigVisitor? {
val config = NeoConfigureFile(File(filePath)) val config = NeoConfigureFile(File(filePath))
if (config.parseConfigure()) { if (config.parseConfigure()) {
val visitor = config.getVisitor() val visitor = config.getVisitor()
printAttr(visitor, contextName, attrName) return visitor
}
return null
}
@Test
fun colorConfigureTest() {
val visitor = parseConfigure("NeoLang/example/color-scheme.nl")
if (visitor != null) {
println("colorName: ${getMetaByVisitor(visitor, NeoColorScheme.COLOR_META_NAME)}")
println("colorVersion: ${getMetaByVisitor(visitor, NeoColorScheme.COLOR_META_VERSION)}")
println("background: ${getColorByVisitor(visitor, "background")}")
println("foreground: ${getColorByVisitor(visitor, "foreground")}")
} }
} }
@Test
fun extraKeyConfigureTest() {
parseConfigure("NeoLang/example/extra-key.nl")
}
@Test @Test
fun configureFileTest() { fun configureFileTest() {
// parseConfigure("NeoLang/example/color-scheme.nl", "colors", "foreground") colorConfigureTest()
parseConfigure("NeoLang/example/extra-key.nl", "key", "0") extraKeyConfigureTest()
} }
} }