MISC: even more flatten!

This commit is contained in:
imkiva
2021-05-08 19:04:01 +08:00
committed by Kiva Oyama
parent 56167cce5d
commit 236072395c
46 changed files with 565 additions and 728 deletions
@@ -1,6 +0,0 @@
package io.neolang.ast
/**
* @author kiva
*/
class NeoLangEOFToken : NeoLangToken(NeoLangTokenType.EOF, NeoLangTokenValue.EOF)
@@ -1,13 +0,0 @@
package io.neolang.ast
/**
* @author kiva
*/
open class NeoLangToken(val tokenType: NeoLangTokenType, val tokenValue: NeoLangTokenValue) {
var lineNumber = 0
override fun toString(): String {
return "Token { tokenType: $tokenType, tokenValue: $tokenValue };"
}
}
@@ -1,19 +0,0 @@
package io.neolang.ast
/**
* @author kiva
*/
enum class NeoLangTokenType {
NUMBER,
ID,
STRING,
BRACKET_START,
BRACKET_END,
ARRAY_START,
ARRAY_END,
COLON,
COMMA,
EOL,
EOF,
}
@@ -1,12 +0,0 @@
package io.neolang.ast.base
import io.neolang.ast.visitor.VisitorFactory
/**
* @author kiva
*/
open class NeoLangAst {
fun visit(): VisitorFactory {
return VisitorFactory(this)
}
}
@@ -1,3 +0,0 @@
package io.neolang.ast.base
open class NeoLangBaseNode : NeoLangAst()
@@ -1,12 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.base.NeoLangBaseNode
/**
* @author kiva
*/
class NeoLangArrayNode(val arrayNameNode: NeoLangStringNode, val elements: Array<ArrayElement>) : NeoLangBaseNode() {
companion object {
class ArrayElement(val index: Int, val block: NeoLangBlockNode)
}
}
@@ -1,12 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.base.NeoLangBaseNode
/**
* @author kiva
*/
open class NeoLangAstBasedNode(val ast: NeoLangBaseNode) : NeoLangBaseNode() {
override fun toString(): String {
return "${javaClass.simpleName} { ast: $ast }"
}
}
@@ -1,13 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.base.NeoLangBaseNode
/**
* @author kiva
*/
class NeoLangAttributeNode(val stringNode: NeoLangStringNode, val blockNode: NeoLangBlockNode) : NeoLangBaseNode() {
override fun toString(): String {
return "NeoLangAttributeNode { stringNode: $stringNode, block: $blockNode }"
}
}
@@ -1,14 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.base.NeoLangBaseNode
/**
* @author kiva
*/
class NeoLangBlockNode(blockElement: NeoLangBaseNode) : NeoLangAstBasedNode(blockElement) {
companion object {
fun emptyNode(): NeoLangBlockNode {
return NeoLangBlockNode(NeoLangDummyNode())
}
}
}
@@ -1,8 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.base.NeoLangBaseNode
/**
* @author kiva
*/
class NeoLangDummyNode : NeoLangBaseNode()
@@ -1,19 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.base.NeoLangBaseNode
/**
* @author kiva
*/
class NeoLangGroupNode(val attributes: Array<NeoLangAttributeNode>) : NeoLangBaseNode() {
override fun toString(): String {
return "NeoLangGroupNode { attrs: $attributes }"
}
companion object {
fun emptyNode(): NeoLangGroupNode {
return NeoLangGroupNode(arrayOf())
}
}
}
@@ -1,8 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.NeoLangToken
/**
* @author kiva
*/
class NeoLangNumberNode(token: NeoLangToken) : NeoLangTokenBasedNode(token)
@@ -1,21 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.base.NeoLangBaseNode
/**
* @author kiva
*/
class NeoLangProgramNode(val groups: List<NeoLangGroupNode>) : NeoLangBaseNode() {
override fun toString(): String {
return "NeoLangProgramNode { groups: $groups }"
}
companion object {
fun emptyNode(): NeoLangProgramNode {
return NeoLangProgramNode(listOf())
}
}
}
@@ -1,8 +0,0 @@
package io.neolang.ast.node
import io.neolang.ast.NeoLangToken
/**
* @author kiva
*/
class NeoLangStringNode(token: NeoLangToken) : NeoLangTokenBasedNode(token)
@@ -1,18 +0,0 @@
package io.neolang.ast.node
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) : NeoLangBaseNode() {
override fun toString(): String {
return "${javaClass.simpleName} { token: $token }"
}
fun eval(): NeoLangValue {
return token.tokenValue.value
}
}
@@ -1,17 +0,0 @@
package io.neolang.ast.visitor
import io.neolang.ast.base.NeoLangAst
/**
* @author kiva
*/
class AstVisitor(private val ast: NeoLangAst, private val visitorCallback: IVisitorCallback) {
fun start() {
AstVisitorImpl.visitStartAst(ast, visitorCallback)
}
@Suppress("UNCHECKED_CAST")
fun <T : IVisitorCallback> getCallback(): T {
return visitorCallback as T
}
}
@@ -1,18 +0,0 @@
package io.neolang.ast.visitor
import io.neolang.runtime.context.NeoLangContext
/**
* @author kiva
*/
interface IVisitorCallback {
fun onStart()
fun onFinish()
fun onEnterContext(contextName: String)
fun onExitContext()
fun getCurrentContext(): NeoLangContext
}
@@ -1,24 +0,0 @@
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!")
}
}
@@ -1,18 +0,0 @@
package io.neolang.ast.visitor
import io.neolang.ast.base.NeoLangAst
/**
* @author kiva
*/
class VisitorFactory(private val ast: NeoLangAst) {
fun getVisitor(callbackInterface: Class<out IVisitorCallback>): AstVisitor? {
try {
return AstVisitor(ast, callbackInterface.newInstance())
} catch (e: Exception) {
return null
}
}
}
@@ -1,18 +1,21 @@
package io.neolang.ast.visitor package io.neolang.frontend
import io.neolang.ast.base.NeoLangAst
import io.neolang.ast.node.*
import io.neolang.runtime.type.NeoLangValue
import io.neolang.runtime.NeoLangContext
import io.neolang.runtime.NeoLangValue
/** /**
* grammar: [ * @author kiva
* program: group (group)*
* group: attribute (attribute)*
* attribute: ID COLON block
* block: STRING | NUMBER | (BRACKET_START [group|] BRACKET_END) | (ARRAY_START [block(<,block>)+|] ARRAY_END)
* ]
*/ */
class AstVisitor(private val ast: NeoLangAst, private val visitorCallback: IVisitorCallback) {
fun start() {
AstVisitorImpl.visitStartAst(ast, visitorCallback)
}
@Suppress("UNCHECKED_CAST")
fun <T : IVisitorCallback> getCallback(): T {
return visitorCallback as T
}
}
/** /**
* @author kiva * @author kiva
@@ -102,3 +105,54 @@ internal object AstVisitorImpl {
} }
} }
} }
/**
* @author kiva
*/
interface IVisitorCallback {
fun onStart()
fun onFinish()
fun onEnterContext(contextName: String)
fun onExitContext()
fun getCurrentContext(): 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!")
}
}
/**
* @author kiva
*/
class VisitorFactory(private val ast: NeoLangAst) {
fun getVisitor(callbackInterface: Class<out IVisitorCallback>): AstVisitor? {
try {
return AstVisitor(ast, callbackInterface.newInstance())
} catch (e: Exception) {
return null
}
}
}
@@ -1,18 +1,4 @@
package io.neolang.parser package io.neolang.frontend
import io.neolang.ast.NeoLangEOFToken
import io.neolang.ast.NeoLangToken
import io.neolang.ast.NeoLangTokenType
import io.neolang.ast.NeoLangTokenValue
/**
* grammar: [
* program: group (group)*
* group: attribute (attribute)*
* attribute: ID COLON block
* block: STRING | NUMBER | (BRACKET_START [group|] BRACKET_END) | (ARRAY_START [block(<,block>)+|] ARRAY_END)
* ]
*/
/** /**
* @author kiva * @author kiva
@@ -201,9 +187,7 @@ class NeoLangLexer {
var value = numberValue var value = numberValue
var loop = moveToNextChar() // skip 'x' or 'X' var loop = moveToNextChar() // skip 'x' or 'X'
while (loop && (currentChar.isHexNumber())) { while (loop && (currentChar.isHexNumber())) {
value *= 16 value *= 16 + (currentChar.toInt().and(15)) + if (currentChar >= 'A') 9 else 0
+(currentChar.toInt().and(15))
+if (currentChar >= 'A') 9 else 0
loop = moveToNextChar() loop = moveToNextChar()
} }
return value return value
@@ -270,3 +254,208 @@ class NeoLangLexer {
|| this in ('A'..'F') || this in ('A'..'F')
} }
} }
/**
* @author kiva
*/
class NeoLangParser {
private val lexer = NeoLangLexer()
private var tokens = mutableListOf<NeoLangToken>()
private var currentPosition: Int = 0
private var currentToken: NeoLangToken? = null
fun setInputSource(programCode: String?) {
lexer.setInputSource(programCode)
}
fun parse(): NeoLangAst {
return updateParserStatus(lexer.lex()) ?: throw ParseException("AST is null")
}
private fun updateParserStatus(tokens: List<NeoLangToken>): NeoLangAst? {
if (tokens.isEmpty()) {
// Allow empty program
return NeoLangProgramNode.emptyNode()
}
this.tokens.clear()
this.tokens.addAll(tokens)
currentPosition = 0
currentToken = tokens[currentPosition]
return program()
}
private fun match(tokenType: NeoLangTokenType, errorThrow: Boolean = false): Boolean {
val currentToken = this.currentToken ?: throw InvalidTokenException("Unexpected token: null")
if (currentToken.tokenType === tokenType) {
currentPosition++
if (currentPosition >= tokens.size) {
this.currentToken = NeoLangToken(NeoLangTokenType.EOF, NeoLangTokenValue.EOF)
} else {
this.currentToken = tokens[currentPosition]
}
return true
} else if (errorThrow) {
throw InvalidTokenException(
"Unexpected token `${currentToken.tokenValue}' typed " +
"`${currentToken.tokenType}' near line ${currentToken.lineNumber}, " +
"expected $tokenType",
)
}
return false
}
private fun program(): NeoLangProgramNode {
val token = currentToken
var group = group()
if (group != null) {
val groups = mutableListOf(group)
while (token?.tokenType !== NeoLangTokenType.EOF) {
group = group()
if (group == null) {
break
}
groups.add(group)
}
return NeoLangProgramNode(groups)
}
return NeoLangProgramNode.emptyNode()
}
/**
* @param attrName Only available when group is a attribute value
*/
private fun group(): NeoLangGroupNode? {
val token = currentToken ?: throw InvalidTokenException("Unexpected token: null")
var attr = attribute()
if (attr != null) {
val attributes = mutableListOf(attr)
while (token.tokenType !== NeoLangTokenType.EOF
&& token.tokenType !== NeoLangTokenType.BRACKET_END
&& token.tokenType !== NeoLangTokenType.ARRAY_END
) {
attr = attribute()
if (attr == null) {
break
}
attributes.add(attr)
}
return NeoLangGroupNode(attributes.toTypedArray())
}
return null
}
private fun attribute(): NeoLangAttributeNode? {
val token = currentToken ?: throw InvalidTokenException("Unexpected token: null")
if (match(NeoLangTokenType.ID)) {
match(NeoLangTokenType.COLON, errorThrow = true)
val attrName = NeoLangStringNode(token)
val block = block(attrName) ?: NeoLangBlockNode.emptyNode()
return NeoLangAttributeNode(attrName, block)
}
return null
}
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(NeoLangArrayNode.Companion.ArrayElement(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(NeoLangArrayNode.Companion.ArrayElement(index++, block))
// Meet the last element
if (!match(NeoLangTokenType.COMMA)) {
break
}
}
}
return NeoLangArrayNode(arrayName, elements.toTypedArray())
}
return null
}
/**
* @param 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")
}
}
/**
* @param attrName Only available when group is a attribute value
*/
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))
}
NeoLangTokenType.ID -> {
match(NeoLangTokenType.ID, errorThrow = true)
return NeoLangBlockNode(NeoLangStringNode(token))
}
NeoLangTokenType.STRING -> {
match(NeoLangTokenType.STRING, errorThrow = true)
return NeoLangBlockNode(NeoLangStringNode(token))
}
NeoLangTokenType.BRACKET_START -> {
match(NeoLangTokenType.BRACKET_START, errorThrow = true)
val group = group()
match(NeoLangTokenType.BRACKET_END, errorThrow = true)
// Allow empty blocks
return if (group != null) NeoLangBlockNode(group) else NeoLangBlockNode.emptyNode()
}
else -> null
}
}
}
open class InvalidTokenException(message: String) : ParseException(message)
open class ParseException(message: String) : RuntimeException(message)
@@ -0,0 +1,114 @@
package io.neolang.frontend
import io.neolang.runtime.NeoLangValue
/**
* @author kiva
*/
open class NeoLangAst {
fun visit(): VisitorFactory {
return VisitorFactory(this)
}
}
open class NeoLangBaseNode : NeoLangAst()
/**
* @author kiva
*/
class NeoLangArrayNode(val arrayNameNode: NeoLangStringNode, val elements: Array<ArrayElement>) : NeoLangBaseNode() {
companion object {
class ArrayElement(val index: Int, val block: NeoLangBlockNode)
}
}
/**
* @author kiva
*/
open class NeoLangAstBasedNode(val ast: NeoLangBaseNode) : NeoLangBaseNode() {
override fun toString(): String {
return "${javaClass.simpleName} { ast: $ast }"
}
}
/**
* @author kiva
*/
class NeoLangAttributeNode(val stringNode: NeoLangStringNode, val blockNode: NeoLangBlockNode) : NeoLangBaseNode() {
override fun toString(): String {
return "NeoLangAttributeNode { stringNode: $stringNode, block: $blockNode }"
}
}
/**
* @author kiva
*/
class NeoLangBlockNode(blockElement: NeoLangBaseNode) : NeoLangAstBasedNode(blockElement) {
companion object {
fun emptyNode(): NeoLangBlockNode {
return NeoLangBlockNode(NeoLangDummyNode())
}
}
}
/**
* @author kiva
*/
class NeoLangDummyNode : NeoLangBaseNode()
/**
* @author kiva
*/
class NeoLangGroupNode(val attributes: Array<NeoLangAttributeNode>) : NeoLangBaseNode() {
override fun toString(): String {
return "NeoLangGroupNode { attrs: $attributes }"
}
companion object {
fun emptyNode(): NeoLangGroupNode {
return NeoLangGroupNode(arrayOf())
}
}
}
/**
* @author kiva
*/
class NeoLangNumberNode(token: NeoLangToken) : NeoLangTokenBasedNode(token)
/**
* @author kiva
*/
class NeoLangProgramNode(val groups: List<NeoLangGroupNode>) : NeoLangBaseNode() {
override fun toString(): String {
return "NeoLangProgramNode { groups: $groups }"
}
companion object {
fun emptyNode(): NeoLangProgramNode {
return NeoLangProgramNode(listOf())
}
}
}
/**
* @author kiva
*/
class NeoLangStringNode(token: NeoLangToken) : NeoLangTokenBasedNode(token)
/**
* @author kiva
*/
open class NeoLangTokenBasedNode(val token: NeoLangToken) : NeoLangBaseNode() {
override fun toString(): String {
return "${javaClass.simpleName} { token: $token }"
}
fun eval(): NeoLangValue {
return token.tokenValue.value
}
}
@@ -1,6 +1,41 @@
package io.neolang.ast package io.neolang.frontend
import io.neolang.runtime.type.NeoLangValue import io.neolang.runtime.NeoLangValue
/**
* @author kiva
*/
class NeoLangEOFToken : NeoLangToken(NeoLangTokenType.EOF, NeoLangTokenValue.EOF)
/**
* @author kiva
*/
open class NeoLangToken(val tokenType: NeoLangTokenType, val tokenValue: NeoLangTokenValue) {
var lineNumber = 0
override fun toString(): String {
return "Token { tokenType: $tokenType, tokenValue: $tokenValue };"
}
}
/**
* @author kiva
*/
enum class NeoLangTokenType {
NUMBER,
ID,
STRING,
BRACKET_START,
BRACKET_END,
ARRAY_START,
ARRAY_END,
COLON,
COMMA,
EOL,
EOF,
}
/** /**
* @author kiva * @author kiva
@@ -1,9 +1,9 @@
package io.neolang.visitor package io.neolang.frontend
import io.neolang.ast.visitor.IVisitorCallback import io.neolang.runtime.NeoLangArray
import io.neolang.runtime.context.NeoLangContext import io.neolang.runtime.NeoLangContext
import io.neolang.runtime.type.NeoLangArray import io.neolang.runtime.NeoLangValue
import io.neolang.runtime.type.NeoLangValue import java.util.*
class ConfigVisitor : IVisitorCallback { class ConfigVisitor : IVisitorCallback {
private var rootContext: NeoLangContext? = null private var rootContext: NeoLangContext? = null
@@ -70,4 +70,41 @@ class ConfigVisitor : IVisitorCallback {
override fun getCurrentContext(): NeoLangContext { override fun getCurrentContext(): NeoLangContext {
return currentContext!! return currentContext!!
} }
} }
/**
* @author kiva
*/
class DisplayProcessVisitor : IVisitorCallbackAdapter() {
private val contextStack = Stack<NeoLangContext>()
override fun onStart() {
println(">>> Start")
onEnterContext("global")
}
override fun onFinish() {
while (contextStack.isNotEmpty()) {
onExitContext()
}
println(">>> Finish")
}
override fun onEnterContext(contextName: String) {
val context = NeoLangContext(contextName)
contextStack.push(context)
println(">>> Entering Context `$contextName'")
}
override fun onExitContext() {
val context = contextStack.pop()
println(">>> Exiting & Dumping Context ${context.contextName}")
context.getAttributes().entries.forEach {
println(" > [${it.key}]: ${it.value.asString()}")
}
}
override fun getCurrentContext(): NeoLangContext {
return contextStack.peek()
}
}
@@ -1,40 +0,0 @@
package io.neolang.main
import io.neolang.parser.NeoLangParser
import io.neolang.visitor.DisplayProcessVisitor
import java.io.FileInputStream
/**
* @author kiva
*/
class Main {
companion object {
@JvmStatic
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("Usage: NeoLang <program.nl>")
return
}
val parser = NeoLangParser()
args.forEach {
val programCode = readFully(it)
parser.setInputSource(programCode)
val ast = parser.parse()
println("Compile `$it'")
ast.visit()
.getVisitor(DisplayProcessVisitor::class.java)
?.start()
}
return
}
private fun readFully(file: String): String {
FileInputStream(file).use {
val bytes = ByteArray(it.available())
it.read(bytes)
return String(bytes)
}
}
}
}
@@ -1,6 +0,0 @@
package io.neolang.parser
/**
* @author kiva
*/
open class InvalidTokenException(message: String) : ParseException(message)
@@ -1,209 +0,0 @@
package io.neolang.parser
import io.neolang.ast.NeoLangToken
import io.neolang.ast.NeoLangTokenType
import io.neolang.ast.NeoLangTokenValue
import io.neolang.ast.base.NeoLangAst
import io.neolang.ast.node.*
/**
* @author kiva
*/
class NeoLangParser {
private val lexer = NeoLangLexer()
private var tokens = mutableListOf<NeoLangToken>()
private var currentPosition: Int = 0
private var currentToken: NeoLangToken? = null
fun setInputSource(programCode: String?) {
lexer.setInputSource(programCode)
}
fun parse(): NeoLangAst {
return updateParserStatus(lexer.lex()) ?: throw ParseException("AST is null")
}
private fun updateParserStatus(tokens: List<NeoLangToken>): NeoLangAst? {
if (tokens.isEmpty()) {
// Allow empty program
return NeoLangProgramNode.emptyNode()
}
this.tokens.clear()
this.tokens.addAll(tokens)
currentPosition = 0
currentToken = tokens[currentPosition]
return program()
}
private fun match(tokenType: NeoLangTokenType, errorThrow: Boolean = false): Boolean {
val currentToken = this.currentToken ?: throw InvalidTokenException("Unexpected token: null")
if (currentToken.tokenType === tokenType) {
currentPosition++
if (currentPosition >= tokens.size) {
this.currentToken = NeoLangToken(NeoLangTokenType.EOF, NeoLangTokenValue.EOF)
} else {
this.currentToken = tokens[currentPosition]
}
return true
} else if (errorThrow) {
throw InvalidTokenException(
"Unexpected token `${currentToken.tokenValue}' typed " +
"`${currentToken.tokenType}' near line ${currentToken.lineNumber}, " +
"expected $tokenType"
)
}
return false
}
private fun program(): NeoLangProgramNode {
val token = currentToken
var group = group()
if (group != null) {
val groups = mutableListOf(group)
while (token?.tokenType !== NeoLangTokenType.EOF) {
group = group()
if (group == null) {
break
}
groups.add(group)
}
return NeoLangProgramNode(groups)
}
return NeoLangProgramNode.emptyNode()
}
/**
* @param attrName Only available when group is a attribute value
*/
private fun group(): NeoLangGroupNode? {
val token = currentToken ?: throw InvalidTokenException("Unexpected token: null")
var attr = attribute()
if (attr != null) {
val attributes = mutableListOf(attr)
while (token.tokenType !== NeoLangTokenType.EOF
&& token.tokenType !== NeoLangTokenType.BRACKET_END
&& token.tokenType !== NeoLangTokenType.ARRAY_END
) {
attr = attribute()
if (attr == null) {
break
}
attributes.add(attr)
}
return NeoLangGroupNode(attributes.toTypedArray())
}
return null
}
private fun attribute(): NeoLangAttributeNode? {
val token = currentToken ?: throw InvalidTokenException("Unexpected token: null")
if (match(NeoLangTokenType.ID)) {
match(NeoLangTokenType.COLON, errorThrow = true)
val attrName = NeoLangStringNode(token)
val block = block(attrName) ?: NeoLangBlockNode.emptyNode()
return NeoLangAttributeNode(attrName, block)
}
return null
}
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(NeoLangArrayNode.Companion.ArrayElement(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(NeoLangArrayNode.Companion.ArrayElement(index++, block))
// Meet the last element
if (!match(NeoLangTokenType.COMMA)) {
break
}
}
}
return NeoLangArrayNode(arrayName, elements.toTypedArray())
}
return null
}
/**
* @param 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")
}
}
/**
* @param attrName Only available when group is a attribute value
*/
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))
}
NeoLangTokenType.ID -> {
match(NeoLangTokenType.ID, errorThrow = true)
return NeoLangBlockNode(NeoLangStringNode(token))
}
NeoLangTokenType.STRING -> {
match(NeoLangTokenType.STRING, errorThrow = true)
return NeoLangBlockNode(NeoLangStringNode(token))
}
NeoLangTokenType.BRACKET_START -> {
match(NeoLangTokenType.BRACKET_START, errorThrow = true)
val group = group()
match(NeoLangTokenType.BRACKET_END, errorThrow = true)
// Allow empty blocks
return if (group != null) NeoLangBlockNode(group) else NeoLangBlockNode.emptyNode()
}
else -> null
}
}
}
@@ -1,6 +0,0 @@
package io.neolang.parser
/**
* @author kiva
*/
open class ParseException(message: String) : RuntimeException(message)
@@ -1,6 +1,4 @@
package io.neolang.runtime.context package io.neolang.runtime
import io.neolang.runtime.type.NeoLangValue
/** /**
* @author kiva * @author kiva
@@ -1,18 +0,0 @@
package io.neolang.runtime.type
/**
* @author kiva
*/
open class NeoLangArrayElement {
open fun eval(): NeoLangValue {
return NeoLangValue.UNDEFINED
}
open fun eval(key: String): NeoLangValue {
return NeoLangValue.UNDEFINED
}
open fun isBlock(): Boolean {
return false
}
}
@@ -1,30 +0,0 @@
package io.neolang.runtime.type
/**
* @author kiva
*/
class NeoLangValue(private val rawValue: Any) {
fun asString(): String {
return rawValue.toString()
}
fun asNumber(): Double {
if (rawValue is Array<*>) {
return 0.0
}
try {
return rawValue.toString().toDouble()
} catch (e: Throwable) {
return 0.0
}
}
fun isValid(): Boolean {
return this != UNDEFINED
}
companion object {
val UNDEFINED = NeoLangValue("<undefined>")
}
}
@@ -1,6 +1,4 @@
package io.neolang.runtime.type package io.neolang.runtime
import io.neolang.runtime.context.NeoLangContext
/** /**
* @author kiva * @author kiva
@@ -59,4 +57,50 @@ class NeoLangArray private constructor(
override fun iterator(): Iterator<NeoLangArrayElement> { override fun iterator(): Iterator<NeoLangArrayElement> {
return elements.iterator() return elements.iterator()
} }
} }
/**
* @author kiva
*/
open class NeoLangArrayElement {
open fun eval(): NeoLangValue {
return NeoLangValue.UNDEFINED
}
open fun eval(key: String): NeoLangValue {
return NeoLangValue.UNDEFINED
}
open fun isBlock(): Boolean {
return false
}
}
/**
* @author kiva
*/
class NeoLangValue(private val rawValue: Any) {
fun asString(): String {
return rawValue.toString()
}
fun asNumber(): Double {
if (rawValue is Array<*>) {
return 0.0
}
try {
return rawValue.toString().toDouble()
} catch (e: Throwable) {
return 0.0
}
}
fun isValid(): Boolean {
return this != UNDEFINED
}
companion object {
val UNDEFINED = NeoLangValue("<undefined>")
}
}
@@ -1,42 +0,0 @@
package io.neolang.visitor
import io.neolang.ast.visitor.IVisitorCallbackAdapter
import io.neolang.runtime.context.NeoLangContext
import java.util.*
/**
* @author kiva
*/
class DisplayProcessVisitor : IVisitorCallbackAdapter() {
private val contextStack = Stack<NeoLangContext>()
override fun onStart() {
println(">>> Start")
onEnterContext("global")
}
override fun onFinish() {
while (contextStack.isNotEmpty()) {
onExitContext()
}
println(">>> Finish")
}
override fun onEnterContext(contextName: String) {
val context = NeoLangContext(contextName)
contextStack.push(context)
println(">>> Entering Context `$contextName'")
}
override fun onExitContext() {
val context = contextStack.pop()
println(">>> Exiting & Dumping Context ${context.contextName}")
context.getAttributes().entries.forEach {
println(" > [${it.key}]: ${it.value.asString()}")
}
}
override fun getCurrentContext(): NeoLangContext {
return contextStack.peek()
}
}
@@ -1,17 +0,0 @@
package io.neolang
import io.neolang.main.Main
import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see [Testing documentation](http://d.android.com/tools/testing)
*/
class NeoLangTest {
@Test
fun arrayTest() {
Main.main(arrayOf("NeoLang/example/extra-key.nl"))
}
}
@@ -1,41 +0,0 @@
package io.neoterm.component
import android.content.Context
import io.neoterm.component.codegen.CodeGenComponent
import io.neoterm.component.colorscheme.ColorSchemeComponent
import io.neoterm.component.completion.CompletionComponent
import io.neoterm.component.config.ConfigureComponent
import io.neoterm.component.extrakey.ExtraKeyComponent
import io.neoterm.component.font.FontComponent
import io.neoterm.component.pm.PackageComponent
import io.neoterm.component.profile.ProfileComponent
import io.neoterm.component.session.SessionComponent
import io.neoterm.component.session.ShellProfile
import io.neoterm.component.userscript.UserScriptComponent
import io.neoterm.utils.NLog
/**
* @author kiva
*/
object NeoInitializer {
fun init(context: Context) {
NLog.init(context)
initComponents()
}
fun initComponents() {
ComponentManager.registerComponent(ConfigureComponent::class.java)
ComponentManager.registerComponent(CodeGenComponent::class.java)
ComponentManager.registerComponent(ColorSchemeComponent::class.java)
ComponentManager.registerComponent(FontComponent::class.java)
ComponentManager.registerComponent(UserScriptComponent::class.java)
ComponentManager.registerComponent(ExtraKeyComponent::class.java)
ComponentManager.registerComponent(CompletionComponent::class.java)
ComponentManager.registerComponent(PackageComponent::class.java)
ComponentManager.registerComponent(SessionComponent::class.java)
ComponentManager.registerComponent(ProfileComponent::class.java)
val profileComp = ComponentManager.getComponent<ProfileComponent>()
profileComp.registerProfile(ShellProfile.PROFILE_META_NAME, ShellProfile::class.java)
}
}
@@ -1,7 +1,7 @@
package io.neoterm.component.colorscheme package io.neoterm.component.colorscheme
import android.content.Context import android.content.Context
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.ConfigVisitor
import io.neoterm.App import io.neoterm.App
import io.neoterm.R import io.neoterm.R
import io.neoterm.component.ComponentManager import io.neoterm.component.ComponentManager
@@ -1,6 +1,6 @@
package io.neoterm.component.colorscheme package io.neoterm.component.colorscheme
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.ConfigVisitor
import io.neoterm.backend.TerminalColorScheme import io.neoterm.backend.TerminalColorScheme
import io.neoterm.backend.TerminalColors import io.neoterm.backend.TerminalColors
import io.neoterm.component.ConfigFileBasedObject import io.neoterm.component.ConfigFileBasedObject
@@ -1,5 +1,18 @@
package io.neoterm.component package io.neoterm.component
import android.content.Context
import io.neoterm.component.codegen.CodeGenComponent
import io.neoterm.component.colorscheme.ColorSchemeComponent
import io.neoterm.component.completion.CompletionComponent
import io.neoterm.component.config.ConfigureComponent
import io.neoterm.component.extrakey.ExtraKeyComponent
import io.neoterm.component.font.FontComponent
import io.neoterm.component.pm.PackageComponent
import io.neoterm.component.profile.ProfileComponent
import io.neoterm.component.session.SessionComponent
import io.neoterm.component.session.ShellProfile
import io.neoterm.component.userscript.UserScriptComponent
import io.neoterm.utils.NLog
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
interface NeoComponent { interface NeoComponent {
@@ -50,3 +63,28 @@ object ComponentManager {
class ComponentDuplicateException(serviceName: String) : RuntimeException("Service $serviceName duplicate") class ComponentDuplicateException(serviceName: String) : RuntimeException("Service $serviceName duplicate")
class ComponentNotFoundException(serviceName: String) : RuntimeException("Component `$serviceName' not found") class ComponentNotFoundException(serviceName: String) : RuntimeException("Component `$serviceName' not found")
/**
* @author kiva
*/
object NeoInitializer {
fun init(context: Context) {
NLog.init(context)
initComponents()
}
fun initComponents() {
ComponentManager.registerComponent(ConfigureComponent::class.java)
ComponentManager.registerComponent(CodeGenComponent::class.java)
ComponentManager.registerComponent(ColorSchemeComponent::class.java)
ComponentManager.registerComponent(FontComponent::class.java)
ComponentManager.registerComponent(UserScriptComponent::class.java)
ComponentManager.registerComponent(ExtraKeyComponent::class.java)
ComponentManager.registerComponent(CompletionComponent::class.java)
ComponentManager.registerComponent(PackageComponent::class.java)
ComponentManager.registerComponent(SessionComponent::class.java)
ComponentManager.registerComponent(ProfileComponent::class.java)
val profileComp = ComponentManager.getComponent<ProfileComponent>()
profileComp.registerProfile(ShellProfile.PROFILE_META_NAME, ShellProfile::class.java)
}
}
@@ -6,8 +6,8 @@ import android.preference.PreferenceManager
import android.system.ErrnoException import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.util.TypedValue import android.util.TypedValue
import io.neolang.parser.NeoLangParser import io.neolang.frontend.ConfigVisitor
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.NeoLangParser
import io.neoterm.App import io.neoterm.App
import io.neoterm.R import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
@@ -1,7 +1,7 @@
package io.neoterm.component.config package io.neoterm.component.config
import io.neolang.runtime.type.NeoLangValue import io.neolang.frontend.ConfigVisitor
import io.neolang.visitor.ConfigVisitor import io.neolang.runtime.NeoLangValue
import io.neoterm.component.colorscheme.NeoColorScheme import io.neoterm.component.colorscheme.NeoColorScheme
import io.neoterm.component.extrakey.NeoExtraKey import io.neoterm.component.extrakey.NeoExtraKey
import io.neoterm.frontend.session.view.extrakey.TextButton import io.neoterm.frontend.session.view.extrakey.TextButton
@@ -1,6 +1,6 @@
package io.neoterm.component package io.neoterm.component
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.ConfigVisitor
import io.neoterm.component.config.ConfigureComponent import io.neoterm.component.config.ConfigureComponent
import io.neoterm.utils.NLog import io.neoterm.utils.NLog
import java.io.File import java.io.File
@@ -1,7 +1,7 @@
package io.neoterm.component.extrakey package io.neoterm.component.extrakey
import android.content.Context import android.content.Context
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.ConfigVisitor
import io.neoterm.App import io.neoterm.App
import io.neoterm.component.ConfigFileBasedComponent import io.neoterm.component.ConfigFileBasedComponent
import io.neoterm.component.config.NeoTermPath import io.neoterm.component.config.NeoTermPath
@@ -1,6 +1,6 @@
package io.neoterm.component.extrakey package io.neoterm.component.extrakey
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.ConfigVisitor
import io.neoterm.component.ConfigFileBasedObject import io.neoterm.component.ConfigFileBasedObject
import io.neoterm.frontend.session.view.extrakey.ExtraKeysView import io.neoterm.frontend.session.view.extrakey.ExtraKeysView
import io.neoterm.frontend.session.view.extrakey.IExtraButton import io.neoterm.frontend.session.view.extrakey.IExtraButton
@@ -1,6 +1,6 @@
package io.neoterm.component.profile package io.neoterm.component.profile
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.ConfigVisitor
import io.neoterm.component.ConfigFileBasedComponent import io.neoterm.component.ConfigFileBasedComponent
import io.neoterm.component.config.NeoTermPath import io.neoterm.component.config.NeoTermPath
import io.neoterm.utils.NLog import io.neoterm.utils.NLog
@@ -1,6 +1,6 @@
package io.neoterm.component.profile package io.neoterm.component.profile
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.ConfigVisitor
import io.neoterm.component.ComponentManager import io.neoterm.component.ComponentManager
import io.neoterm.component.ConfigFileBasedObject import io.neoterm.component.ConfigFileBasedObject
import io.neoterm.component.codegen.CodeGenObject import io.neoterm.component.codegen.CodeGenObject
@@ -1,7 +1,7 @@
package io.neoterm.component.session package io.neoterm.component.session
import android.content.Context import android.content.Context
import io.neolang.visitor.ConfigVisitor import io.neolang.frontend.ConfigVisitor
import io.neoterm.App import io.neoterm.App
import io.neoterm.R import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession