feat: add molang package

1. upgrade bukkit chat
2. fix config update error

Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
2022-02-12 16:29:40 +08:00
parent 5ed61829e1
commit 6816e51239
73 changed files with 3103 additions and 129 deletions

View File

@@ -0,0 +1,32 @@
import { Expression, IExpression } from '../expression'
export class ArrayAccessExpression extends Expression {
type = 'ArrayAccessExpression'
constructor(protected name: IExpression, protected lookup: IExpression) {
super()
}
get allExpressions() {
return [this.name, this.lookup]
}
setExpressionAt(index: number, expr: IExpression) {
if (index === 0) this.name = expr
else if (index === 1) this.lookup = expr
}
isStatic() {
return false
}
setPointer(value: unknown) {
;(<any>this.name.eval())[<number>this.lookup.eval()] = value
}
eval() {
return (<any>this.name.eval())[<number>this.lookup.eval()]
}
toString() {
return `${this.name.toString()}[${this.lookup.toString()}]`
}
}

View File

@@ -0,0 +1,21 @@
import { Expression } from '../expression'
export class BooleanExpression extends Expression {
type = 'BooleanExpression'
constructor(protected value: boolean) {
super()
}
get allExpressions() {
return []
}
setExpressionAt() {}
isStatic() {
return true
}
eval() {
return this.value
}
}

View File

@@ -0,0 +1,26 @@
import { Expression } from '../expression'
export class BreakExpression extends Expression {
type = 'BreakExpression'
isBreak = true
constructor() {
super()
}
get allExpressions() {
return []
}
setExpressionAt() {}
isStatic() {
return false
}
eval() {
return 0
}
isString() {
return 'break'
}
}

View File

@@ -0,0 +1,48 @@
import { ExecutionEnvironment } from '../../env/env'
import { Expression, IExpression } from '../expression'
import { NameExpression } from './name'
export class ContextSwitchExpression extends Expression {
type = 'NameExpression'
constructor(
protected leftExpr: NameExpression,
protected rightExpr: NameExpression
) {
super()
}
get allExpressions() {
return [this.leftExpr, this.rightExpr]
}
setExpressionAt(index: number, expr: IExpression) {
if (!(expr instanceof NameExpression))
throw new Error(
`Cannot use context switch operator "->" on ${expr.type}`
)
if (index === 0) this.leftExpr = expr
else if (index === 1) this.rightExpr = expr
}
isStatic() {
return false
}
eval() {
const context = this.leftExpr.eval()
if (typeof context !== 'object') return 0
this.rightExpr.setExecutionEnv(
new ExecutionEnvironment(
context,
this.rightExpr.executionEnv.config
)
)
return this.rightExpr.eval()
}
toString() {
return `${this.leftExpr.toString()}->${this.rightExpr.toString()}`
}
}

View File

@@ -0,0 +1,26 @@
import { Expression } from '../expression'
export class ContinueExpression extends Expression {
type = 'ContinueExpression'
isContinue = true
constructor() {
super()
}
get allExpressions() {
return []
}
setExpressionAt() {}
isStatic() {
return false
}
eval() {
return 0
}
toString() {
return 'continue'
}
}

View File

@@ -0,0 +1,63 @@
import { Expression, IExpression } from '../expression'
export class ForEachExpression extends Expression {
type = 'ForEachExpression'
constructor(
protected variable: IExpression,
protected arrayExpression: IExpression,
protected expression: IExpression
) {
super()
if (!this.variable.setPointer)
throw new Error(
`First for_each() argument must be a variable, received "${typeof this.variable.eval()}"`
)
}
get isReturn() {
return this.expression.isReturn
}
get allExpressions() {
return [this.variable, this.arrayExpression, this.expression]
}
setExpressionAt(index: number, expr: IExpression) {
if (index === 0) this.variable = expr
else if (index === 1) this.arrayExpression = expr
else if (index === 2) this.expression = expr
}
isStatic() {
return (
this.variable.isStatic() &&
this.arrayExpression.isStatic() &&
this.expression.isStatic()
)
}
eval() {
const array = this.arrayExpression.eval()
if (!Array.isArray(array))
throw new Error(
`Second for_each() argument must be an array, received "${typeof array}"`
)
let i = 0
while (i < array.length) {
// Error detection for this.variable is part of the constructor
this.variable.setPointer?.(array[i++])
const res = this.expression.eval()
if (this.expression.isBreak) break
else if (this.expression.isContinue) continue
else if (this.expression.isReturn) return res
}
return 0
}
toString() {
return `loop(${this.variable.toString()},${this.arrayExpression.toString()},${this.expression.toString()})`
}
}

View File

@@ -0,0 +1,46 @@
import { NameExpression } from './name'
import { Expression, IExpression } from '../expression'
export class FunctionExpression extends Expression {
type = 'FunctionExpression'
constructor(protected name: IExpression, protected args: IExpression[]) {
super()
}
get allExpressions() {
return [this.name, ...this.args]
}
setExpressionAt(index: number, expr: Expression) {
if (index === 0) this.name = expr
else if (index > 0) this.args[index - 1] = expr
}
isStatic() {
return false
}
eval() {
const args: unknown[] = []
let i = 0
while (i < this.args.length) args.push(this.args[i++].eval())
const func = <(...args: unknown[]) => unknown>this.name.eval()
if (typeof func !== 'function')
throw new Error(
`${(<NameExpression>this.name).toString()} is not callable!`
)
return func(...args)
}
toString() {
let str = `${this.name.toString()}(`
for (let i = 0; i < this.args.length; i++) {
str += `${this.args[i].toString()}${
i + 1 < this.args.length ? ',' : ''
}`
}
return `${str})`
}
}

View File

@@ -0,0 +1,37 @@
import { Expression, IExpression } from '../expression'
export class GenericOperatorExpression extends Expression {
type = 'GenericOperatorExpression'
constructor(
protected left: IExpression,
protected right: IExpression,
protected operator: string,
protected evalHelper: (
leftExpression: IExpression,
rightExpression: IExpression
) => unknown
) {
super()
}
get allExpressions() {
return [this.left, this.right]
}
setExpressionAt(index: number, expr: IExpression) {
if (index === 0) this.left = expr
else if (index === 1) this.right = expr
}
isStatic() {
return this.left.isStatic() && this.right.isStatic()
}
eval() {
return this.evalHelper(this.left, this.right)
}
toString() {
return `${this.left.toString()}${this.operator}${this.right.toString()}`
}
}

View File

@@ -0,0 +1,38 @@
import { Expression, IExpression } from '../expression'
export class GroupExpression extends Expression {
type = 'GroupExpression'
constructor(protected expression: IExpression, protected brackets: string) {
super()
}
get allExpressions() {
return [this.expression]
}
setExpressionAt(_: number, expr: IExpression) {
this.expression = expr
}
isStatic() {
return this.expression.isStatic()
}
get isReturn() {
return this.expression.isReturn
}
get isBreak() {
return this.expression.isBreak
}
get isContinue() {
return this.expression.isContinue
}
eval() {
return this.expression.eval()
}
toString() {
return `${this.brackets[0]}${this.expression.toString()}${
this.brackets[1]
}`
}
}

View File

@@ -0,0 +1,19 @@
export { ArrayAccessExpression } from './arrayAccess'
export { BooleanExpression } from './boolean'
export { BreakExpression } from './break'
export { ContinueExpression } from './continue'
export { ForEachExpression } from './forEach'
export { FunctionExpression } from './function'
export { GenericOperatorExpression } from './genericOperator'
export { GroupExpression } from './group'
export { LoopExpression } from './loop'
export { NameExpression } from './name'
export { NumberExpression } from './number'
export { PostfixExpression } from './postfix'
export { PrefixExpression } from './prefix'
export { ReturnExpression } from './return'
export { StatementExpression } from './statement'
export { StaticExpression } from './static'
export { StringExpression } from './string'
export { TernaryExpression } from './ternary'
export { VoidExpression } from './void'

View File

@@ -0,0 +1,56 @@
import { Expression, IExpression } from '../expression'
export class LoopExpression extends Expression {
type = 'LoopExpression'
constructor(
protected count: IExpression,
protected expression: IExpression
) {
super()
}
get allExpressions() {
return [this.count, this.expression]
}
setExpressionAt(index: number, expr: IExpression) {
if (index === 0) this.count = expr
else if (index === 1) this.expression = expr
}
get isReturn() {
return this.expression.isReturn
}
isStatic() {
return this.count.isStatic() && this.expression.isStatic()
}
eval() {
const repeatCount = Number(this.count.eval())
if (Number.isNaN(repeatCount))
throw new Error(
`First loop() argument must be of type number, received "${typeof this.count.eval()}"`
)
if (repeatCount > 1024)
throw new Error(
`Cannot loop more than 1024x times, received "${repeatCount}"`
)
let i = 0
while (i < repeatCount) {
i++
const res = this.expression.eval()
if (this.expression.isBreak) break
else if (this.expression.isContinue) continue
else if (this.expression.isReturn) return res
}
return 0
}
toString() {
return `loop(${this.count.toString()},${this.expression.toString()})`
}
}

View File

@@ -0,0 +1,47 @@
import { ExecutionEnvironment } from '../../env/env'
import { Expression } from '../expression'
export class NameExpression extends Expression {
type = 'NameExpression'
constructor(
public executionEnv: ExecutionEnvironment,
protected name: string,
protected isFunctionCall = false
) {
super()
}
get allExpressions() {
return []
}
setExpressionAt() {}
isStatic() {
return false
}
setPointer(value: unknown) {
this.executionEnv.setAt(this.name, value)
}
setFunctionCall(value = true) {
this.isFunctionCall = value
}
setName(name: string) {
this.name = name
}
setExecutionEnv(executionEnv: ExecutionEnvironment) {
this.executionEnv = executionEnv
}
eval() {
const value = this.executionEnv.getFrom(this.name)
if (!this.isFunctionCall && typeof value === 'function') return value()
return value
}
toString() {
return this.name
}
}

View File

@@ -0,0 +1,25 @@
import { Expression } from '../expression'
export class NumberExpression extends Expression {
type = 'NumberExpression'
constructor(protected value: number) {
super()
}
get allExpressions() {
return []
}
setExpressionAt() {}
isStatic() {
return true
}
eval() {
return this.value
}
toString() {
return '' + this.value
}
}

View File

@@ -0,0 +1,32 @@
import { TTokenType } from '../../tokenizer/token'
import { Expression, IExpression } from '../expression'
export class PostfixExpression extends Expression {
type = 'PostfixExpression'
constructor(
protected expression: IExpression,
protected tokenType: TTokenType
) {
super()
}
get allExpressions() {
return [this.expression]
}
setExpressionAt(_: number, expr: IExpression) {
this.expression = expr
}
isStatic() {
return this.expression.isStatic()
}
eval() {
switch (this.tokenType) {
case 'X': {
// DO SOMETHING
}
}
}
}

View File

@@ -0,0 +1,57 @@
import { TTokenType } from '../../tokenizer/token'
import { Expression, IExpression } from '../expression'
export class PrefixExpression extends Expression {
type = 'PrefixExpression'
constructor(
protected tokenType: TTokenType,
protected expression: IExpression
) {
super()
}
get allExpressions() {
return [this.expression]
}
setExpressionAt(_: number, expr: IExpression) {
this.expression = expr
}
isStatic() {
return this.expression.isStatic()
}
eval() {
const value = this.expression.eval()
if (typeof value !== 'number')
throw new Error(
`Cannot use "${
this.tokenType
}" operator in front of ${typeof value} "${value}"`
)
switch (this.tokenType) {
case 'MINUS': {
return -value
}
case 'BANG': {
return !value
}
}
}
toString() {
switch (this.tokenType) {
case 'MINUS': {
return `-${this.expression.toString()}`
}
case 'BANG': {
return `!${this.expression.toString()}`
}
}
throw new Error(`Unknown prefix operator: "${this.tokenType}"`)
}
}

View File

@@ -0,0 +1,29 @@
import { Expression, IExpression } from '../expression'
export class ReturnExpression extends Expression {
type = 'ReturnExpression'
isReturn = true
constructor(protected expression: IExpression) {
super()
}
get allExpressions() {
return [this.expression]
}
setExpressionAt(_: number, expr: IExpression) {
this.expression = expr
}
isStatic() {
return false
}
eval() {
return this.expression.eval()
}
toString() {
return `return ${this.expression.toString()}`
}
}

View File

@@ -0,0 +1,102 @@
import { Expression, IExpression } from '../expression'
import { StaticExpression } from './static'
import { VoidExpression } from './void'
export class StatementExpression extends Expression {
type = 'StatementExpression'
protected didReturn?: boolean = undefined
protected wasLoopBroken = false
protected wasLoopContinued = false
constructor(protected expressions: IExpression[]) {
super()
}
get allExpressions() {
return this.expressions
}
setExpressionAt(index: number, expr: IExpression) {
this.expressions[index] = expr
}
get isReturn() {
if (this.didReturn !== undefined) return this.didReturn
// This breaks scope vs. statement parsing for some reason
let i = 0
while (i < this.expressions.length) {
const expr = this.expressions[i]
if (expr.isBreak) return false
if (expr.isContinue) return false
if (expr.isReturn) {
this.didReturn = true
return true
}
i++
}
this.didReturn = false
return false
}
get isBreak() {
if (this.wasLoopBroken) {
this.wasLoopBroken = false
return true
}
return false
}
get isContinue() {
if (this.wasLoopContinued) {
this.wasLoopContinued = false
return true
}
return false
}
isStatic() {
let i = 0
while (i < this.expressions.length) {
if (!this.expressions[i].isStatic()) return false
i++
}
return true
}
eval() {
this.didReturn = false
this.wasLoopBroken = false
this.wasLoopContinued = false
let i = 0
while (i < this.expressions.length) {
let res = this.expressions[i].eval()
if (this.expressions[i].isReturn) {
this.didReturn = true
return res
} else if (this.expressions[i].isContinue) {
this.wasLoopContinued = true
return
} else if (this.expressions[i].isBreak) {
this.wasLoopBroken = true
return
}
i++
}
return 0
}
toString() {
let str = ''
for (const expr of this.expressions) {
if (
expr instanceof VoidExpression ||
(expr instanceof StaticExpression && !expr.isReturn)
)
continue
str += `${expr.toString()};`
}
return str
}
}

View File

@@ -0,0 +1,28 @@
import { Expression } from '../expression'
export class StaticExpression extends Expression {
type = 'StaticExpression'
constructor(protected value: unknown, public readonly isReturn = false) {
super()
}
get allExpressions() {
return []
}
setExpressionAt() {}
isStatic() {
return true
}
eval() {
return this.value
}
toString() {
let val = this.value
if (typeof val === 'string') val = `'${val}'`
if (this.isReturn) return `return ${val}`
return '' + val
}
}

View File

@@ -0,0 +1,26 @@
import { Expression } from '../expression'
export class StringExpression extends Expression {
type = 'StringExpression'
constructor(protected name: string) {
super()
}
get allExpressions() {
return []
}
setExpressionAt() {}
isStatic() {
return true
}
eval() {
return this.name.substring(1, this.name.length - 1)
}
toString() {
return this.name
}
}

View File

@@ -0,0 +1,79 @@
import { Expression, IExpression } from '../expression'
import { VoidExpression } from './void'
export class TernaryExpression extends Expression {
type = 'TernaryExpression'
protected leftResult: unknown
constructor(
protected leftExpression: IExpression,
protected thenExpression: IExpression,
protected elseExpression: IExpression
) {
super()
}
get allExpressions() {
if (this.leftExpression.isStatic())
return [
this.leftExpression,
this.leftExpression.eval()
? this.thenExpression
: this.elseExpression,
]
return [this.leftExpression, this.thenExpression, this.elseExpression]
}
setExpressionAt(index: number, expr: IExpression) {
if (index === 0) this.leftExpression = expr
else if (index === 1) this.thenExpression = expr
else if (index === 2) this.elseExpression = expr
}
get isReturn() {
if (this.leftResult === undefined)
return this.thenExpression.isReturn && this.elseExpression.isReturn
return this.leftResult
? this.thenExpression.isReturn
: this.elseExpression.isReturn
}
get hasReturn() {
return this.thenExpression.isReturn || this.elseExpression.isReturn
}
get isContinue() {
if (this.leftResult === undefined)
return (
this.thenExpression.isContinue && this.elseExpression.isContinue
)
return this.leftResult
? this.thenExpression.isContinue
: this.elseExpression.isContinue
}
get isBreak() {
if (this.leftResult === undefined)
return this.thenExpression.isBreak && this.elseExpression.isBreak
return this.leftResult
? this.thenExpression.isBreak
: this.elseExpression.isBreak
}
isStatic() {
return (
this.leftExpression.isStatic() &&
this.thenExpression.isStatic() &&
this.elseExpression.isStatic()
)
}
eval() {
this.leftResult = this.leftExpression.eval()
return this.leftResult
? this.thenExpression.eval()
: this.elseExpression.eval()
}
toString() {
if (this.elseExpression instanceof VoidExpression)
return `${this.leftExpression.toString()}?${this.thenExpression.toString()}`
return `${this.leftExpression.toString()}?${this.thenExpression.toString()}:${this.elseExpression.toString()}`
}
}

View File

@@ -0,0 +1,21 @@
import { Expression } from '../expression'
export class VoidExpression extends Expression {
type = 'VoidExpression'
get allExpressions() {
return []
}
setExpressionAt() {}
isStatic() {
return true
}
eval() {
return 0
}
toString() {
return ''
}
}