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,21 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IExpression } from '../expression'
import { GenericOperatorExpression } from '../expressions/genericOperator'
import { IInfixParselet } from './infix'
export class AndOperator implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
if (parser.match('AND'))
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'&&',
(leftExpression: IExpression, rightExpression: IExpression) =>
leftExpression.eval() && rightExpression.eval()
)
else throw new Error(`"&" not followed by another "&"`)
}
}

View File

@@ -0,0 +1,22 @@
import { Token } from '../../tokenizer/token'
import { Parser } from '../parse'
import { IInfixParselet } from './infix'
import { IExpression } from '../expression'
import { ArrayAccessExpression } from '../expressions/arrayAccess'
export class ArrayAccessParselet implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, left: IExpression, token: Token) {
const expr = parser.parseExpression(this.precedence - 1)
if (!left.setPointer) throw new Error(`"${left.type}" is not an array`)
if (!parser.match('ARRAY_RIGHT'))
throw new Error(
`No closing bracket for opening bracket "[${expr.eval()}"`
)
return new ArrayAccessExpression(left, expr)
}
}

View File

@@ -0,0 +1,134 @@
import { IInfixParselet } from './infix'
import { Parser } from '../parse'
import { IExpression } from '../expression'
import { Token } from '../../tokenizer/token'
import { GenericOperatorExpression } from '../expressions/genericOperator'
const plusHelper = (
leftExpression: IExpression,
rightExpression: IExpression
) => {
const leftValue = leftExpression.eval()
const rightValue = rightExpression.eval()
if (
!(typeof leftValue === 'number' || typeof leftValue === 'boolean') ||
!(typeof rightValue === 'number' || typeof rightValue === 'boolean')
)
throw new Error(
`Cannot use numeric operators for expression "${leftValue} + ${rightValue}"`
)
//@ts-ignore
return leftValue + rightValue
}
const minusHelper = (
leftExpression: IExpression,
rightExpression: IExpression
) => {
const leftValue = leftExpression.eval()
const rightValue = rightExpression.eval()
if (
!(typeof leftValue === 'number' || typeof leftValue === 'boolean') ||
!(typeof rightValue === 'number' || typeof rightValue === 'boolean')
)
throw new Error(
`Cannot use numeric operators for expression "${leftValue} - ${rightValue}"`
)
//@ts-ignore
return leftValue - rightValue
}
const divideHelper = (
leftExpression: IExpression,
rightExpression: IExpression
) => {
const leftValue = leftExpression.eval()
const rightValue = rightExpression.eval()
if (
!(typeof leftValue === 'number' || typeof leftValue === 'boolean') ||
!(typeof rightValue === 'number' || typeof rightValue === 'boolean')
)
throw new Error(
`Cannot use numeric operators for expression "${leftValue} / ${rightValue}"`
)
//@ts-ignore
return leftValue / rightValue
}
const multiplyHelper = (
leftExpression: IExpression,
rightExpression: IExpression
) => {
const leftValue = leftExpression.eval()
const rightValue = rightExpression.eval()
if (
!(typeof leftValue === 'number' || typeof leftValue === 'boolean') ||
!(typeof rightValue === 'number' || typeof rightValue === 'boolean')
)
throw new Error(
`Cannot use numeric operators for expression "${leftValue} * ${rightValue}"`
)
//@ts-ignore
return leftValue * rightValue
}
const assignHelper = (
leftExpression: IExpression,
rightExpression: IExpression
) => {
if (leftExpression.setPointer) {
leftExpression.setPointer(rightExpression.eval())
return 0
} else {
throw Error(`Cannot assign to ${leftExpression.type}`)
}
}
export class BinaryOperator implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
const rightExpression = parser.parseExpression(this.precedence)
// return new AdditionExpression(leftExpression, rightExpression)
const tokenText = token.getText()
switch (tokenText) {
case '+':
return new GenericOperatorExpression(
leftExpression,
rightExpression,
tokenText,
plusHelper
)
case '-':
return new GenericOperatorExpression(
leftExpression,
rightExpression,
tokenText,
minusHelper
)
case '*':
return new GenericOperatorExpression(
leftExpression,
rightExpression,
tokenText,
multiplyHelper
)
case '/':
return new GenericOperatorExpression(
leftExpression,
rightExpression,
tokenText,
divideHelper
)
case '=': {
return new GenericOperatorExpression(
leftExpression,
rightExpression,
'=',
assignHelper
)
}
default:
throw new Error(`Operator not implemented`)
}
}
}

View File

@@ -0,0 +1,12 @@
import { IPrefixParselet } from './prefix'
import { Token } from '../../tokenizer/token'
import { Parser } from '../parse'
import { BooleanExpression } from '../expressions/boolean'
export class BooleanParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
return new BooleanExpression(token.getText() === 'true')
}
}

View File

@@ -0,0 +1,12 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IPrefixParselet } from './prefix'
import { BreakExpression } from '../expressions/break'
export class BreakParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
return new BreakExpression()
}
}

View File

@@ -0,0 +1,12 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IPrefixParselet } from './prefix'
import { ContinueExpression } from '../expressions/continue'
export class ContinueParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
return new ContinueExpression()
}
}

View File

@@ -0,0 +1,19 @@
import { Parser } from '../../parser/parse'
import { Token } from '../../tokenizer/token'
import { IExpression } from '../expression'
import { GenericOperatorExpression } from '../expressions/genericOperator'
import { IInfixParselet } from './infix'
export class EqualsOperator implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'==',
(leftExpression: IExpression, rightExpression: IExpression) =>
leftExpression.eval() === rightExpression.eval()
)
}
}

View File

@@ -0,0 +1,29 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IPrefixParselet } from './prefix'
import { IExpression } from '../expression'
import { ForEachExpression } from '../expressions/forEach'
export class ForEachParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
parser.consume('LEFT_PARENT')
const args: IExpression[] = []
if (parser.match('RIGHT_PARENT'))
throw new Error(`for_each() called without arguments`)
do {
args.push(parser.parseExpression())
} while (parser.match('COMMA'))
parser.consume('RIGHT_PARENT')
if (args.length !== 3)
throw new Error(
`There must be exactly three for_each() arguments; found ${args.length}`
)
return new ForEachExpression(args[0], args[1], args[2])
}
}

View File

@@ -0,0 +1,26 @@
import { Token } from '../../tokenizer/token'
import { Parser } from '../parse'
import { IInfixParselet } from './infix'
import { IExpression } from '../expression'
import { FunctionExpression } from '../expressions/function'
export class FunctionParselet implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, left: IExpression, token: Token) {
const args: IExpression[] = []
if (!left.setFunctionCall)
throw new Error(`${left.type} is not callable!`)
left.setFunctionCall(true)
if (!parser.match('RIGHT_PARENT')) {
do {
args.push(parser.parseExpression())
} while (parser.match('COMMA'))
parser.consume('RIGHT_PARENT')
}
return new FunctionExpression(left, args)
}
}

View File

@@ -0,0 +1,31 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IExpression } from '../expression'
import { GenericOperatorExpression } from '../expressions/genericOperator'
import { IInfixParselet } from './infix'
export class GreaterOperator implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
if (parser.match('EQUALS'))
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'>=',
(leftExpression: IExpression, rightExpression: IExpression) =>
// @ts-ignore
leftExpression.eval() >= rightExpression.eval()
)
else {
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'>',
(leftExpression: IExpression, rightExpression: IExpression) =>
// @ts-ignore
leftExpression.eval() > rightExpression.eval()
)
}
}
}

View File

@@ -0,0 +1,18 @@
import { IPrefixParselet } from './prefix'
import { Token } from '../../tokenizer/token'
import { Parser } from '../parse'
import { GroupExpression } from '../expressions/group'
export class GroupParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
const expression = parser.parseExpression(this.precedence)
parser.consume('RIGHT_PARENT')
if (parser.config.keepGroups)
return new GroupExpression(expression, '()')
return expression
}
}

View File

@@ -0,0 +1,8 @@
import { Parser } from '../parse'
import { IExpression } from '../expression'
import { Token } from '../../tokenizer/token'
export interface IInfixParselet {
readonly precedence: number
parse: (parser: Parser, left: IExpression, token: Token) => IExpression
}

View File

@@ -0,0 +1,29 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IPrefixParselet } from './prefix'
import { IExpression } from '../expression'
import { LoopExpression } from '../expressions/loop'
export class LoopParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
parser.consume('LEFT_PARENT')
const args: IExpression[] = []
if (parser.match('RIGHT_PARENT'))
throw new Error(`loop() called without arguments`)
do {
args.push(parser.parseExpression())
} while (parser.match('COMMA'))
parser.consume('RIGHT_PARENT')
if (args.length !== 2)
throw new Error(
`There must be exactly two loop() arguments; found ${args.length}`
)
return new LoopExpression(args[0], args[1])
}
}

View File

@@ -0,0 +1,43 @@
import { IPrefixParselet } from './prefix'
import { Token } from '../../tokenizer/token'
import { Parser } from '../parse'
import { NameExpression } from '../expressions/name'
import { ContextSwitchExpression } from '../expressions/contextSwitch'
export class NameParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
const nameExpr = new NameExpression(
parser.executionEnv,
token.getText()
)
const nextTokens = <const>[parser.lookAhead(0), parser.lookAhead(1)]
// Context switching operator "->"
if (
nextTokens[0].getType() === 'MINUS' &&
nextTokens[1].getType() === 'GREATER'
) {
parser.consume('MINUS')
parser.consume('GREATER')
const nameToken = parser.lookAhead(0)
if (nameToken.getType() !== 'NAME')
throw new Error(
`Cannot use context switch operator "->" on ${parser.lookAhead(
0
)}`
)
parser.consume('NAME')
return new ContextSwitchExpression(
nameExpr,
new NameExpression(parser.executionEnv, nameToken.getText())
)
}
return nameExpr
}
}

View File

@@ -0,0 +1,23 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IExpression } from '../expression'
import { GenericOperatorExpression } from '../expressions/genericOperator'
import { IInfixParselet } from './infix'
export class NotEqualsOperator implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
if (parser.match('EQUALS')) {
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'!=',
(leftExpression: IExpression, rightExpression: IExpression) =>
leftExpression.eval() !== rightExpression.eval()
)
} else {
throw new Error(`! was used as a binary operator`)
}
}
}

View File

@@ -0,0 +1,12 @@
import { IPrefixParselet } from './prefix'
import { Token } from '../../tokenizer/token'
import { Parser } from '../parse'
import { NumberExpression } from '../expressions/number'
export class NumberParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
return new NumberExpression(Number(token.getText()))
}
}

View File

@@ -0,0 +1,21 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IExpression } from '../expression'
import { GenericOperatorExpression } from '../expressions/genericOperator'
import { IInfixParselet } from './infix'
export class OrOperator implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
if (parser.match('OR'))
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'||',
(leftExpression: IExpression, rightExpression: IExpression) =>
leftExpression.eval() || rightExpression.eval()
)
else throw new Error(`"|" not followed by another "|"`)
}
}

View File

@@ -0,0 +1,3 @@
import { IInfixParselet } from './infix'
export interface IPostfixParselet extends IInfixParselet {}

View File

@@ -0,0 +1,20 @@
import { Parser } from '../parse'
import { IExpression } from '../expression'
import { Token } from '../../tokenizer/token'
import { PrefixExpression } from '../expressions/prefix'
export interface IPrefixParselet {
readonly precedence: number
parse: (parser: Parser, token: Token) => IExpression
}
export class PrefixOperator implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
return new PrefixExpression(
token.getType(),
parser.parseExpression(this.precedence)
)
}
}

View File

@@ -0,0 +1,26 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IExpression } from '../expression'
import { GenericOperatorExpression } from '../expressions/genericOperator'
import { IInfixParselet } from './infix'
import { TernaryParselet } from './ternary'
import { EPrecedence } from '../precedence'
const ternaryParselet = new TernaryParselet(EPrecedence.CONDITIONAL)
export class QuestionOperator implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
if (parser.match('QUESTION')) {
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'??',
(leftExpression: IExpression, rightExpression: IExpression) =>
leftExpression.eval() ?? rightExpression.eval()
)
} else {
return ternaryParselet.parse(parser, leftExpression, token)
}
}
}

View File

@@ -0,0 +1,18 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IPrefixParselet } from './prefix'
import { NumberExpression } from '../expressions/number'
import { ReturnExpression } from '../expressions/return'
import { EPrecedence } from '../precedence'
export class ReturnParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
const expr = parser.parseExpression(EPrecedence.STATEMENT + 1)
return new ReturnExpression(
parser.match('SEMICOLON', false) ? expr : new NumberExpression(0)
)
}
}

View File

@@ -0,0 +1,22 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IPrefixParselet } from './prefix'
import { GroupExpression } from '../expressions/group'
export class ScopeParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
let expr = parser.parseExpression(this.precedence)
if (
parser.config.useOptimizer &&
parser.config.earlyReturnsSkipTokenization &&
expr.isReturn
)
parser.match('CURLY_RIGHT')
else parser.consume('CURLY_RIGHT')
return parser.config.keepGroups ? new GroupExpression(expr, '{}') : expr
}
}

View File

@@ -0,0 +1,31 @@
import { Parser } from '../parse'
import { Token } from '../../tokenizer/token'
import { IExpression } from '../expression'
import { GenericOperatorExpression } from '../expressions/genericOperator'
import { IInfixParselet } from './infix'
export class SmallerOperator implements IInfixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
if (parser.match('EQUALS'))
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'<=',
(leftExpression: IExpression, rightExpression: IExpression) =>
// @ts-ignore
leftExpression.eval() <= rightExpression.eval()
)
else {
return new GenericOperatorExpression(
leftExpression,
parser.parseExpression(this.precedence),
'<',
(leftExpression: IExpression, rightExpression: IExpression) =>
// @ts-ignore
leftExpression.eval() < rightExpression.eval()
)
}
}
}

View File

@@ -0,0 +1,84 @@
import { Parser } from '../parse'
import { IExpression } from '../expression'
import { Token } from '../../tokenizer/token'
import { IInfixParselet } from './infix'
import { StatementExpression } from '../expressions/statement'
import { StaticExpression } from '../expressions/static'
export class StatementParselet implements IInfixParselet {
constructor(public precedence = 0) {}
findReEntryPoint(parser: Parser) {
let bracketCount = 1
let tokenType = parser.lookAhead(0).getType()
while (tokenType !== 'EOF') {
if (tokenType == 'CURLY_RIGHT') bracketCount--
else if (tokenType === 'CURLY_LEFT') bracketCount++
if (bracketCount === 0) break
parser.consume()
tokenType = parser.lookAhead(0).getType()
}
}
parse(parser: Parser, left: IExpression, token: Token) {
if (parser.config.useOptimizer) {
if (left.isStatic())
left = new StaticExpression(left.eval(), left.isReturn)
if (parser.config.earlyReturnsSkipParsing && left.isReturn) {
if (!parser.config.earlyReturnsSkipTokenization)
this.findReEntryPoint(parser)
return new StatementExpression([left])
}
}
const expressions: IExpression[] = [left]
if (!parser.match('CURLY_RIGHT', false)) {
do {
let expr = parser.parseExpression(this.precedence)
if (parser.config.useOptimizer) {
if (expr.isStatic()) {
if (
parser.config.useAgressiveStaticOptimizer &&
!expr.isReturn
)
continue
expr = new StaticExpression(expr.eval(), expr.isReturn)
}
if (
parser.config.earlyReturnsSkipParsing &&
(expr.isBreak || expr.isContinue || expr.isReturn)
) {
expressions.push(expr)
if (!parser.config.earlyReturnsSkipTokenization)
this.findReEntryPoint(parser)
return new StatementExpression(expressions)
}
}
expressions.push(expr)
} while (
parser.match('SEMICOLON') &&
!parser.match('EOF') &&
!parser.match('CURLY_RIGHT', false)
)
}
parser.match('SEMICOLON')
const statementExpr = new StatementExpression(expressions)
// if (parser.config.useOptimizer && statementExpr.isStatic()) {
// return new StaticExpression(
// statementExpr.eval(),
// statementExpr.isReturn
// )
// }
return statementExpr
}
}

View File

@@ -0,0 +1,12 @@
import { IPrefixParselet } from './prefix'
import { Token } from '../../tokenizer/token'
import { Parser } from '../parse'
import { StringExpression } from '../expressions/string'
export class StringParselet implements IPrefixParselet {
constructor(public precedence = 0) {}
parse(parser: Parser, token: Token) {
return new StringExpression(token.getText())
}
}

View File

@@ -0,0 +1,28 @@
import { IInfixParselet } from './infix'
import { Parser } from '../parse'
import { IExpression } from '../expression'
import { Token } from '../../tokenizer/token'
import { TernaryExpression } from '../expressions/ternary'
import { VoidExpression } from '../expressions/void'
export class TernaryParselet implements IInfixParselet {
exprName = 'Ternary'
constructor(public precedence = 0) {}
parse(parser: Parser, leftExpression: IExpression, token: Token) {
let thenExpr = parser.parseExpression(this.precedence - 1)
let elseExpr: IExpression
if (parser.match('COLON')) {
elseExpr = parser.parseExpression(this.precedence - 1)
} else {
elseExpr = new VoidExpression()
}
if (parser.config.useOptimizer && leftExpression.isStatic()) {
return leftExpression.eval() ? thenExpr : elseExpr
}
return new TernaryExpression(leftExpression, thenExpr, elseExpr)
}
}