From 44947f05aaddac01fe1ad187355fadc6d2405493 Mon Sep 17 00:00:00 2001 From: zt515 Date: Tue, 8 Aug 2017 16:04:22 +0800 Subject: [PATCH] Feature: Array in NeoLang --- NeoLang/example/extra-key.nl | 12 ++- .../java/io/neolang/ast/NeoLangTokenType.kt | 3 + .../java/io/neolang/ast/NeoLangTokenValue.kt | 13 ++- .../io/neolang/ast/base/NeoLangAstBaseNode.kt | 3 - .../io/neolang/ast/base/NeoLangBaseNode.kt | 3 + .../io/neolang/ast/node/NeoLangArrayNode.kt | 9 +++ .../neolang/ast/node/NeoLangAstBasedNode.kt | 4 +- .../neolang/ast/node/NeoLangAttributeNode.kt | 4 +- .../io/neolang/ast/node/NeoLangBlockNode.kt | 6 +- .../io/neolang/ast/node/NeoLangDummyNode.kt | 4 +- .../io/neolang/ast/node/NeoLangGroupNode.kt | 4 +- .../io/neolang/ast/node/NeoLangProgramNode.kt | 4 +- .../neolang/ast/node/NeoLangTokenBasedNode.kt | 6 +- .../io/neolang/ast/visitor/AstVisitorImpl.kt | 26 +++++- .../ast/visitor/IVisitorCallbackAdapter.kt | 24 ++++++ .../io/neolang/main/DisplayProcessVisitor.kt | 9 ++- .../java/io/neolang/parser/NeoLangLexer.kt | 16 +++- .../java/io/neolang/parser/NeoLangParser.kt | 81 ++++++++++++++++--- .../neolang/runtime/context/NeoLangContext.kt | 4 + .../runtime/type/NeoLangArrayElement.kt | 8 ++ .../io/neolang/runtime/type/NeoLangValue.kt | 40 ++++++++- .../src/test/java/io/neolang/NeoLangTest.kt | 4 +- app/build.gradle | 8 +- .../test/java/io/neoterm/ConfigureFileTest.kt | 15 ++-- 24 files changed, 254 insertions(+), 56 deletions(-) delete mode 100755 NeoLang/src/main/java/io/neolang/ast/base/NeoLangAstBaseNode.kt create mode 100755 NeoLang/src/main/java/io/neolang/ast/base/NeoLangBaseNode.kt create mode 100644 NeoLang/src/main/java/io/neolang/ast/node/NeoLangArrayNode.kt create mode 100644 NeoLang/src/main/java/io/neolang/ast/visitor/IVisitorCallbackAdapter.kt create mode 100644 NeoLang/src/main/java/io/neolang/runtime/type/NeoLangArrayElement.kt diff --git a/NeoLang/example/extra-key.nl b/NeoLang/example/extra-key.nl index 7350d3a..39a1181 100644 --- a/NeoLang/example/extra-key.nl +++ b/NeoLang/example/extra-key.nl @@ -1,3 +1,13 @@ extra-key: { - program: { "vim", "vi" } + program: [ "vim", "vi" ] + key: [ + { + display: "Ctrl" + code: "" + }, + { + display: "Esc" + code: "" + } + ] } diff --git a/NeoLang/src/main/java/io/neolang/ast/NeoLangTokenType.kt b/NeoLang/src/main/java/io/neolang/ast/NeoLangTokenType.kt index 7e9d414..d61ab9c 100644 --- a/NeoLang/src/main/java/io/neolang/ast/NeoLangTokenType.kt +++ b/NeoLang/src/main/java/io/neolang/ast/NeoLangTokenType.kt @@ -10,7 +10,10 @@ enum class NeoLangTokenType { STRING, BRACKET_START, BRACKET_END, + ARRAY_START, + ARRAY_END, COLON, + COMMA, EOL, EOF, } diff --git a/NeoLang/src/main/java/io/neolang/ast/NeoLangTokenValue.kt b/NeoLang/src/main/java/io/neolang/ast/NeoLangTokenValue.kt index b8e9f42..8d36d6a 100644 --- a/NeoLang/src/main/java/io/neolang/ast/NeoLangTokenValue.kt +++ b/NeoLang/src/main/java/io/neolang/ast/NeoLangTokenValue.kt @@ -13,17 +13,24 @@ class NeoLangTokenValue(val value: NeoLangValue) { companion object { val COLON = NeoLangTokenValue(NeoLangValue(":")) - val BRACKET_START = NeoLangTokenValue(NeoLangValue("{")) - val BRACKET_END = NeoLangTokenValue(NeoLangValue("}")) + val COMMA = NeoLangTokenValue(NeoLangValue(",")) val QUOTE = NeoLangTokenValue(NeoLangValue("\"")) val EOF = NeoLangTokenValue(NeoLangValue("")) + val BRACKET_START = NeoLangTokenValue(NeoLangValue("{")) + val BRACKET_END = NeoLangTokenValue(NeoLangValue("}")) + val ARRAY_START = NeoLangTokenValue(NeoLangValue("[")) + val ARRAY_END = NeoLangTokenValue(NeoLangValue("]")) + fun wrap(tokenText: String): NeoLangTokenValue { return when (tokenText) { COLON.value.asString() -> COLON + COMMA.value.asString() -> COMMA + QUOTE.value.asString() -> QUOTE BRACKET_START.value.asString() -> BRACKET_START BRACKET_END.value.asString() -> BRACKET_END - QUOTE.value.asString() -> QUOTE + ARRAY_START.value.asString() -> ARRAY_START + ARRAY_END.value.asString() -> ARRAY_END else -> NeoLangTokenValue(NeoLangValue(tokenText)) } } diff --git a/NeoLang/src/main/java/io/neolang/ast/base/NeoLangAstBaseNode.kt b/NeoLang/src/main/java/io/neolang/ast/base/NeoLangAstBaseNode.kt deleted file mode 100755 index 4105e4b..0000000 --- a/NeoLang/src/main/java/io/neolang/ast/base/NeoLangAstBaseNode.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.neolang.ast.base - -open class NeoLangAstBaseNode : NeoLangAst() diff --git a/NeoLang/src/main/java/io/neolang/ast/base/NeoLangBaseNode.kt b/NeoLang/src/main/java/io/neolang/ast/base/NeoLangBaseNode.kt new file mode 100755 index 0000000..d8b690f --- /dev/null +++ b/NeoLang/src/main/java/io/neolang/ast/base/NeoLangBaseNode.kt @@ -0,0 +1,3 @@ +package io.neolang.ast.base + +open class NeoLangBaseNode : NeoLangAst() diff --git a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangArrayNode.kt b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangArrayNode.kt new file mode 100644 index 0000000..e66695c --- /dev/null +++ b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangArrayNode.kt @@ -0,0 +1,9 @@ +package io.neolang.ast.node + +import io.neolang.ast.base.NeoLangBaseNode +import io.neolang.runtime.type.NeoLangArrayElement + +/** + * @author kiva + */ +class NeoLangArrayNode(val arrayNameNode: NeoLangStringNode, val elements: Array) : NeoLangBaseNode() \ No newline at end of file diff --git a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangAstBasedNode.kt b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangAstBasedNode.kt index a442d64..f0392b0 100644 --- a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangAstBasedNode.kt +++ b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangAstBasedNode.kt @@ -1,11 +1,11 @@ package io.neolang.ast.node -import io.neolang.ast.base.NeoLangAstBaseNode +import io.neolang.ast.base.NeoLangBaseNode /** * @author kiva */ -open class NeoLangAstBasedNode(val ast: NeoLangAstBaseNode) : NeoLangAstBaseNode() { +open class NeoLangAstBasedNode(val ast: NeoLangBaseNode) : NeoLangBaseNode() { override fun toString(): String { return "${javaClass.simpleName} { ast: $ast }" } diff --git a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangAttributeNode.kt b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangAttributeNode.kt index 5c197fd..06632f5 100644 --- a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangAttributeNode.kt +++ b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangAttributeNode.kt @@ -1,11 +1,11 @@ package io.neolang.ast.node -import io.neolang.ast.base.NeoLangAstBaseNode +import io.neolang.ast.base.NeoLangBaseNode /** * @author kiva */ -class NeoLangAttributeNode(val stringNode: NeoLangStringNode, val blockNode: NeoLangBlockNode) : NeoLangAstBaseNode() { +class NeoLangAttributeNode(val stringNode: NeoLangStringNode, val blockNode: NeoLangBlockNode) : NeoLangBaseNode() { override fun toString(): String { return "NeoLangAttributeNode { stringNode: $stringNode, block: $blockNode }" diff --git a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangBlockNode.kt b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangBlockNode.kt index 96cbaaf..ac33467 100644 --- a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangBlockNode.kt +++ b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangBlockNode.kt @@ -1,13 +1,13 @@ package io.neolang.ast.node -import io.neolang.ast.base.NeoLangAstBaseNode +import io.neolang.ast.base.NeoLangBaseNode /** * @author kiva */ -class NeoLangBlockNode(blockElement: NeoLangAstBaseNode) : NeoLangAstBasedNode(blockElement) { +class NeoLangBlockNode(blockElement: NeoLangBaseNode) : NeoLangAstBasedNode(blockElement) { companion object { - fun emptyNode() : NeoLangBlockNode { + fun emptyNode(): NeoLangBlockNode { return NeoLangBlockNode(NeoLangDummyNode()) } } diff --git a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangDummyNode.kt b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangDummyNode.kt index 798d229..886ef63 100644 --- a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangDummyNode.kt +++ b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangDummyNode.kt @@ -1,8 +1,8 @@ package io.neolang.ast.node -import io.neolang.ast.base.NeoLangAstBaseNode +import io.neolang.ast.base.NeoLangBaseNode /** * @author kiva */ -class NeoLangDummyNode : NeoLangAstBaseNode() \ No newline at end of file +class NeoLangDummyNode : NeoLangBaseNode() \ No newline at end of file diff --git a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangGroupNode.kt b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangGroupNode.kt index 93d7cd6..48e6f12 100644 --- a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangGroupNode.kt +++ b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangGroupNode.kt @@ -1,11 +1,11 @@ package io.neolang.ast.node -import io.neolang.ast.base.NeoLangAstBaseNode +import io.neolang.ast.base.NeoLangBaseNode /** * @author kiva */ -class NeoLangGroupNode(val attributes: List) : NeoLangAstBaseNode() { +class NeoLangGroupNode(val attributes: Array) : NeoLangBaseNode() { override fun toString(): String { return "NeoLangGroupNode { attrs: $attributes }" diff --git a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangProgramNode.kt b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangProgramNode.kt index 1887d81..feb9146 100644 --- a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangProgramNode.kt +++ b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangProgramNode.kt @@ -1,12 +1,12 @@ package io.neolang.ast.node -import io.neolang.ast.base.NeoLangAstBaseNode +import io.neolang.ast.base.NeoLangBaseNode /** * @author kiva */ -class NeoLangProgramNode(val groups: List) : NeoLangAstBaseNode() { +class NeoLangProgramNode(val groups: List) : NeoLangBaseNode() { override fun toString(): String { return "NeoLangProgramNode { groups: $groups }" diff --git a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangTokenBasedNode.kt b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangTokenBasedNode.kt index c6ebf64..9ad1828 100644 --- a/NeoLang/src/main/java/io/neolang/ast/node/NeoLangTokenBasedNode.kt +++ b/NeoLang/src/main/java/io/neolang/ast/node/NeoLangTokenBasedNode.kt @@ -1,18 +1,18 @@ package io.neolang.ast.node -import io.neolang.ast.base.NeoLangAstBaseNode import io.neolang.ast.NeoLangToken +import io.neolang.ast.base.NeoLangBaseNode import io.neolang.runtime.type.NeoLangValue /** * @author kiva */ -open class NeoLangTokenBasedNode(val token: NeoLangToken) : NeoLangAstBaseNode() { +open class NeoLangTokenBasedNode(val token: NeoLangToken) : NeoLangBaseNode() { override fun toString(): String { return "${javaClass.simpleName} { token: $token }" } - fun eval() :NeoLangValue { + fun eval(): NeoLangValue { return token.tokenValue.value } } \ No newline at end of file diff --git a/NeoLang/src/main/java/io/neolang/ast/visitor/AstVisitorImpl.kt b/NeoLang/src/main/java/io/neolang/ast/visitor/AstVisitorImpl.kt index c2c37e1..29b72c8 100644 --- a/NeoLang/src/main/java/io/neolang/ast/visitor/AstVisitorImpl.kt +++ b/NeoLang/src/main/java/io/neolang/ast/visitor/AstVisitorImpl.kt @@ -3,12 +3,13 @@ package io.neolang.ast.visitor import io.neolang.ast.base.NeoLangAst import io.neolang.ast.node.* + /** * grammar: [ * program: group (group)* * group: attribute (attribute)* * attribute: ID COLON block - * block: STRING | NUMBER | (BRACKET_START group BRACKET_END) + * block: STRING | NUMBER | (BRACKET_START [group|] BRACKET_END) | (ARRAY_START [block(<,block>)+|] ARRAY_END) * ] */ @@ -32,22 +33,38 @@ internal object AstVisitorImpl { visitBlock(ast.blockNode, ast.stringNode.eval().asString(), visitorCallback) } + fun visitArray(ast: NeoLangArrayNode, visitorCallback: IVisitorCallback) { + val arrayName = ast.arrayNameNode.eval().asString() + + visitorCallback.onEnterContext(arrayName) + ast.elements.forEach { + AstVisitorImpl.visitBlock(it.block, it.index.toString(), visitorCallback) + } + visitorCallback.onExitContext() + } + fun visitBlock(ast: NeoLangBlockNode, blockName: String, visitorCallback: IVisitorCallback) { val visitingNode = ast.ast when (visitingNode) { is NeoLangGroupNode -> { // is a sub block, e.g. - // block: { sub-block: {} } + // block: { $blockName: {} } + + // FIXME: Block in Array visitorCallback.onEnterContext(blockName) AstVisitorImpl.visitGroup(visitingNode, visitorCallback) visitorCallback.onExitContext() } + is NeoLangArrayNode -> { + // array: [ "a", "b", "c", 1, 2, 3 ] + AstVisitorImpl.visitArray(visitingNode, visitorCallback) + } is NeoLangStringNode -> { - // block: { node: "hello" } + // block: { $blockName: "hello" } visitorCallback.getCurrentContext().defineAttribute(blockName, visitingNode.eval()) } is NeoLangNumberNode -> { - // block: { node: 123.456 } + // block: { $blockName: 123.456 } visitorCallback.getCurrentContext().defineAttribute(blockName, visitingNode.eval()) } } @@ -57,6 +74,7 @@ internal object AstVisitorImpl { when (ast) { is NeoLangProgramNode -> AstVisitorImpl.visitProgram(ast, visitorCallback) is NeoLangGroupNode -> AstVisitorImpl.visitGroup(ast, visitorCallback) + is NeoLangArrayNode -> AstVisitorImpl.visitArray(ast, visitorCallback) } } } diff --git a/NeoLang/src/main/java/io/neolang/ast/visitor/IVisitorCallbackAdapter.kt b/NeoLang/src/main/java/io/neolang/ast/visitor/IVisitorCallbackAdapter.kt new file mode 100644 index 0000000..27a30a8 --- /dev/null +++ b/NeoLang/src/main/java/io/neolang/ast/visitor/IVisitorCallbackAdapter.kt @@ -0,0 +1,24 @@ +package io.neolang.ast.visitor + +import io.neolang.runtime.context.NeoLangContext + +/** + * @author kiva + */ +open class IVisitorCallbackAdapter : IVisitorCallback { + override fun onStart() { + } + + override fun onFinish() { + } + + override fun onEnterContext(contextName: String) { + } + + override fun onExitContext() { + } + + override fun getCurrentContext(): NeoLangContext { + throw RuntimeException("getCurrentContext() not supported in this IVisitorCallback!") + } +} \ No newline at end of file diff --git a/NeoLang/src/main/java/io/neolang/main/DisplayProcessVisitor.kt b/NeoLang/src/main/java/io/neolang/main/DisplayProcessVisitor.kt index ab60722..827c2f0 100644 --- a/NeoLang/src/main/java/io/neolang/main/DisplayProcessVisitor.kt +++ b/NeoLang/src/main/java/io/neolang/main/DisplayProcessVisitor.kt @@ -1,13 +1,13 @@ package io.neolang.main -import io.neolang.ast.visitor.IVisitorCallback +import io.neolang.ast.visitor.IVisitorCallbackAdapter import io.neolang.runtime.context.NeoLangContext import java.util.* /** * @author kiva */ -class DisplayProcessVisitor : IVisitorCallback { +class DisplayProcessVisitor : IVisitorCallbackAdapter() { private val contextStack = Stack() override fun onStart() { @@ -30,7 +30,10 @@ class DisplayProcessVisitor : IVisitorCallback { override fun onExitContext() { val context = contextStack.pop() - println(">>> Exiting Context ${context.contextName}") + println(">>> Exiting & Dumping Context ${context.contextName}") + context.getAttributes().entries.forEach { + println(" > [${it.key}]: ${it.value.asString()}") + } } override fun getCurrentContext(): NeoLangContext { diff --git a/NeoLang/src/main/java/io/neolang/parser/NeoLangLexer.kt b/NeoLang/src/main/java/io/neolang/parser/NeoLangLexer.kt index 4a9dbca..6fe3d3d 100755 --- a/NeoLang/src/main/java/io/neolang/parser/NeoLangLexer.kt +++ b/NeoLang/src/main/java/io/neolang/parser/NeoLangLexer.kt @@ -11,7 +11,7 @@ import java.util.* * program: group (group)* * group: attribute (attribute)* * attribute: ID COLON block - * block: STRING | NUMBER | (BRACKET_START group BRACKET_END) + * block: STRING | NUMBER | (BRACKET_START [group|] BRACKET_END) | (ARRAY_START [block(<,block>)+|] ARRAY_END) * ] */ @@ -97,6 +97,18 @@ class NeoLangLexer { moveToNextChar() NeoLangToken(NeoLangTokenType.BRACKET_END, currentToken) } + NeoLangTokenValue.ARRAY_START -> { + moveToNextChar() + NeoLangToken(NeoLangTokenType.ARRAY_START, currentToken) + } + NeoLangTokenValue.ARRAY_END -> { + moveToNextChar() + NeoLangToken(NeoLangTokenType.ARRAY_END, currentToken) + } + NeoLangTokenValue.COMMA -> { + moveToNextChar(eofThrow = true) + NeoLangToken(NeoLangTokenType.COMMA, currentToken) + } NeoLangTokenValue.QUOTE -> { NeoLangToken(NeoLangTokenType.STRING, NeoLangTokenValue.wrap(getNextTokenAsString())) } @@ -106,7 +118,7 @@ class NeoLangLexer { } else if (isIdentifier(currentChar, true)) { NeoLangToken(NeoLangTokenType.ID, NeoLangTokenValue.wrap(getNextTokenAsId())) } else { - throw InvalidTokenException("Unexpected character: " + currentChar) + throw InvalidTokenException("Unexpected character near line $lineNumber: $currentChar") } } } diff --git a/NeoLang/src/main/java/io/neolang/parser/NeoLangParser.kt b/NeoLang/src/main/java/io/neolang/parser/NeoLangParser.kt index c236707..2fb1e9e 100755 --- a/NeoLang/src/main/java/io/neolang/parser/NeoLangParser.kt +++ b/NeoLang/src/main/java/io/neolang/parser/NeoLangParser.kt @@ -5,6 +5,7 @@ import io.neolang.ast.NeoLangTokenType import io.neolang.ast.NeoLangTokenValue import io.neolang.ast.base.NeoLangAst import io.neolang.ast.node.* +import io.neolang.runtime.type.NeoLangArrayElement /** * @author kiva @@ -84,14 +85,15 @@ class NeoLangParser { val attributes = mutableListOf(attr) while (token.tokenType !== NeoLangTokenType.EOF - && token.tokenType !== NeoLangTokenType.BRACKET_END) { + && token.tokenType !== NeoLangTokenType.BRACKET_END + && token.tokenType !== NeoLangTokenType.ARRAY_END) { attr = attribute() if (attr == null) { break } attributes.add(attr) } - return NeoLangGroupNode(attributes) + return NeoLangGroupNode(attributes.toTypedArray()) } return null @@ -101,15 +103,79 @@ class NeoLangParser { val token = currentToken ?: throw InvalidTokenException("Unexpected token: null") if (match(NeoLangTokenType.ID)) { match(NeoLangTokenType.COLON, errorThrow = true) - val block = block() ?: NeoLangBlockNode.emptyNode() - return NeoLangAttributeNode(NeoLangStringNode(token), block) + + val attrName = NeoLangStringNode(token) + + val block = block(attrName) ?: NeoLangBlockNode.emptyNode() + return NeoLangAttributeNode(attrName, block) } return null } - private fun block(): NeoLangBlockNode? { + private fun array(arrayName: NeoLangStringNode): NeoLangArrayNode? { + val token = currentToken ?: throw InvalidTokenException("Unexpected token: null") + + + // TODO: Multiple Array + var block = blockNonArrayElement(arrayName) + var index = 0 + + if (block != null) { + + val elements = mutableListOf(NeoLangArrayElement(index++, block)) + + if (match(NeoLangTokenType.COMMA)) { + // More than one elements + while (token.tokenType !== NeoLangTokenType.EOF + && token.tokenType !== NeoLangTokenType.ARRAY_END) { + block = blockNonArrayElement(arrayName) + if (block == null) { + break + } + elements.add(NeoLangArrayElement(index++, block)) + + // Meet the last element + if (!match(NeoLangTokenType.COMMA)) { + break + } + } + } + + return NeoLangArrayNode(arrayName, elements.toTypedArray()) + } + + return null + } + + + /** + * @attrName The block holder's name + */ + private fun block(attrName: NeoLangStringNode): NeoLangBlockNode? { + val block = blockNonArrayElement(attrName) + if (block != null) { + return block + } + val token = currentToken ?: throw InvalidTokenException("Unexpected token: null") when (token.tokenType) { + NeoLangTokenType.ARRAY_START -> { + match(NeoLangTokenType.ARRAY_START, errorThrow = true) + val array = array(attrName) + match(NeoLangTokenType.ARRAY_END, errorThrow = true) + + // Allow empty arrays + return if (array != null) NeoLangBlockNode(array) else NeoLangBlockNode.emptyNode() + } + + else -> throw InvalidTokenException("Unexpected token `${token.tokenValue}' typed `${token.tokenType}' for block") + } + } + + private fun blockNonArrayElement(attrName: NeoLangStringNode): NeoLangBlockNode? { + val token = currentToken ?: throw InvalidTokenException("Unexpected token: null") + + return when (token.tokenType) { NeoLangTokenType.NUMBER -> { match(NeoLangTokenType.NUMBER, errorThrow = true) return NeoLangBlockNode(NeoLangNumberNode(token)) @@ -130,10 +196,7 @@ class NeoLangParser { // Allow empty blocks return if (group != null) NeoLangBlockNode(group) else NeoLangBlockNode.emptyNode() } - - else -> throw InvalidTokenException("Unexpected token `$token' for block, " + - "expected `${NeoLangTokenType.NUMBER}', `${NeoLangTokenType.ID}' or `${NeoLangTokenType.BRACKET_START}'") + else -> null } } - } diff --git a/NeoLang/src/main/java/io/neolang/runtime/context/NeoLangContext.kt b/NeoLang/src/main/java/io/neolang/runtime/context/NeoLangContext.kt index 5d0ba38..7e1f2da 100644 --- a/NeoLang/src/main/java/io/neolang/runtime/context/NeoLangContext.kt +++ b/NeoLang/src/main/java/io/neolang/runtime/context/NeoLangContext.kt @@ -17,6 +17,10 @@ class NeoLangContext(val contextName: String) { return attributes[attributeName] ?: NeoLangValue.UNDEFINED } + fun defineArray() { + + } + fun getAttributes(): Map { return attributes } diff --git a/NeoLang/src/main/java/io/neolang/runtime/type/NeoLangArrayElement.kt b/NeoLang/src/main/java/io/neolang/runtime/type/NeoLangArrayElement.kt new file mode 100644 index 0000000..8bd4ba6 --- /dev/null +++ b/NeoLang/src/main/java/io/neolang/runtime/type/NeoLangArrayElement.kt @@ -0,0 +1,8 @@ +package io.neolang.runtime.type + +import io.neolang.ast.node.NeoLangBlockNode + +/** + * @author kiva + */ +class NeoLangArrayElement(val index: Int, val block: NeoLangBlockNode) \ No newline at end of file diff --git a/NeoLang/src/main/java/io/neolang/runtime/type/NeoLangValue.kt b/NeoLang/src/main/java/io/neolang/runtime/type/NeoLangValue.kt index e5c6bee..ebccce5 100644 --- a/NeoLang/src/main/java/io/neolang/runtime/type/NeoLangValue.kt +++ b/NeoLang/src/main/java/io/neolang/runtime/type/NeoLangValue.kt @@ -1,22 +1,54 @@ package io.neolang.runtime.type +import io.neolang.ast.node.NeoLangBlockNode + /** * @author kiva */ class NeoLangValue(private val rawValue: Any) { - fun asString() : String { + fun asString(): String { + if (rawValue is Array<*>) { + val array = asArray() + + return buildString { + append("Array [ ") + array.forEachIndexed { index, value -> + append(value.asString()) + if (index != array.size - 1) { + append(", ") + } + } + append(" ]") + } + } + return rawValue.toString() } - fun asNumber() : Double { + fun asNumber(): Double { + if (rawValue is Array<*>) { + return 0.0 + } + try { - return asString().toDouble() + return rawValue.toString().toDouble() } catch (e: NumberFormatException) { return 0.0 } } - fun isValid() : Boolean { + fun asArray(): Array { + return castArrayOrNull(rawValue) ?: arrayOf() + } + + @Suppress("UNCHECKED_CAST") + private fun castArrayOrNull(rawValue: Any): Array? { + return if (rawValue is Array<*> && rawValue.isNotEmpty() && rawValue[0] is NeoLangValue) + rawValue as Array + else null + } + + fun isValid(): Boolean { return this != UNDEFINED } diff --git a/NeoLang/src/test/java/io/neolang/NeoLangTest.kt b/NeoLang/src/test/java/io/neolang/NeoLangTest.kt index b5adf69..2732603 100644 --- a/NeoLang/src/test/java/io/neolang/NeoLangTest.kt +++ b/NeoLang/src/test/java/io/neolang/NeoLangTest.kt @@ -10,8 +10,8 @@ import org.junit.Test */ class NeoLangTest { @Test - fun testNeoLangParser() { - Main.main(arrayOf("NeoLang/example/color-scheme.nl")) + fun arrayTest() { + Main.main(arrayOf("NeoLang/example/extra-key.nl")) } } diff --git a/app/build.gradle b/app/build.gradle index 736c871..97935b4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,8 +54,8 @@ dependencies { compile 'com.github.wrdlbrnft:modular-adapter:0.2.0.6' compile 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.19' compile 'com.simplecityapps:recyclerview-fastscroll:1.0.16' -// compile 'com.ramotion.cardslider:card-slider:0.1.0' -// compile 'com.github.igalata:Bubble-Picker:v0.2.4' - compile project(path: ':NeoLang') - testCompile project(path: ':NeoLang') + // compile 'com.ramotion.cardslider:card-slider:0.1.0' + // compile 'com.github.igalata:Bubble-Picker:v0.2.4' + implementation project(path: ':NeoLang') + androidTestImplementation project(path: ':NeoLang') } diff --git a/app/src/test/java/io/neoterm/ConfigureFileTest.kt b/app/src/test/java/io/neoterm/ConfigureFileTest.kt index 79c89f8..7fd20b6 100644 --- a/app/src/test/java/io/neoterm/ConfigureFileTest.kt +++ b/app/src/test/java/io/neoterm/ConfigureFileTest.kt @@ -3,6 +3,7 @@ package io.neoterm import io.neoterm.frontend.config.ConfigVisitor import io.neoterm.frontend.config.NeoConfigureFile import org.junit.Test +import java.io.File /** * @author kiva @@ -12,13 +13,17 @@ class ConfigureFileTest { println("attr [$contextName->$attrName]: ${visitor.getAttribute(contextName, attrName).asString()}") } - @Test - fun configureFileTest() { - val config = NeoConfigureFile("NeoLang/example/color-scheme.nl") + private fun parseConfigure(filePath: String, contextName: String, attrName: String) { + val config = NeoConfigureFile(File(filePath)) if (config.parseConfigure()) { - println("Parsed!") val visitor = config.getVisitor() - printAttr(visitor, "colors", "foreground") + printAttr(visitor, contextName, attrName) } } + + @Test + fun configureFileTest() { +// parseConfigure("NeoLang/example/color-scheme.nl", "colors", "foreground") + parseConfigure("NeoLang/example/extra-key.nl", "key", "0") + } } \ No newline at end of file