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:
57
packages/molang/src/parser/expression.ts
Normal file
57
packages/molang/src/parser/expression.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Interface that describes an AST Expression
|
||||
*/
|
||||
export interface IExpression {
|
||||
readonly type: string
|
||||
readonly isReturn?: boolean
|
||||
readonly isBreak?: boolean
|
||||
readonly isContinue?: boolean
|
||||
readonly allExpressions: IExpression[]
|
||||
|
||||
setFunctionCall?: (value: boolean) => void
|
||||
setPointer?: (value: unknown) => void
|
||||
setExpressionAt(index: number, expr: IExpression): void
|
||||
eval(): unknown
|
||||
isStatic(): boolean
|
||||
walk(cb: TIterateCallback): IExpression
|
||||
iterate(cb: TIterateCallback, visited: Set<IExpression>): void
|
||||
}
|
||||
|
||||
export abstract class Expression implements IExpression {
|
||||
public abstract readonly type: string
|
||||
|
||||
abstract eval(): unknown
|
||||
abstract isStatic(): boolean
|
||||
|
||||
toString() {
|
||||
return `${this.eval()}`
|
||||
}
|
||||
|
||||
abstract allExpressions: IExpression[]
|
||||
abstract setExpressionAt(index: number, expr: IExpression): void
|
||||
|
||||
walk(cb: TIterateCallback, visited = new Set<IExpression>()): IExpression {
|
||||
let expr = cb(this) ?? this
|
||||
|
||||
expr.iterate(cb, visited)
|
||||
|
||||
return expr
|
||||
}
|
||||
iterate(cb: TIterateCallback, visited: Set<IExpression>) {
|
||||
for (let i = 0; i < this.allExpressions.length; i++) {
|
||||
const originalExpr = this.allExpressions[i]
|
||||
if (visited.has(originalExpr)) continue
|
||||
else visited.add(originalExpr)
|
||||
|
||||
const expr = cb(originalExpr) ?? originalExpr
|
||||
|
||||
if (expr !== originalExpr && visited.has(expr)) continue
|
||||
else visited.add(expr)
|
||||
|
||||
this.setExpressionAt(i, expr)
|
||||
expr.iterate(cb, visited)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type TIterateCallback = (expr: IExpression) => IExpression | undefined
|
||||
32
packages/molang/src/parser/expressions/arrayAccess.ts
Normal file
32
packages/molang/src/parser/expressions/arrayAccess.ts
Normal 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()}]`
|
||||
}
|
||||
}
|
||||
21
packages/molang/src/parser/expressions/boolean.ts
Normal file
21
packages/molang/src/parser/expressions/boolean.ts
Normal 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
|
||||
}
|
||||
}
|
||||
26
packages/molang/src/parser/expressions/break.ts
Normal file
26
packages/molang/src/parser/expressions/break.ts
Normal 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'
|
||||
}
|
||||
}
|
||||
48
packages/molang/src/parser/expressions/contextSwitch.ts
Normal file
48
packages/molang/src/parser/expressions/contextSwitch.ts
Normal 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()}`
|
||||
}
|
||||
}
|
||||
26
packages/molang/src/parser/expressions/continue.ts
Normal file
26
packages/molang/src/parser/expressions/continue.ts
Normal 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'
|
||||
}
|
||||
}
|
||||
63
packages/molang/src/parser/expressions/forEach.ts
Normal file
63
packages/molang/src/parser/expressions/forEach.ts
Normal 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()})`
|
||||
}
|
||||
}
|
||||
46
packages/molang/src/parser/expressions/function.ts
Normal file
46
packages/molang/src/parser/expressions/function.ts
Normal 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})`
|
||||
}
|
||||
}
|
||||
37
packages/molang/src/parser/expressions/genericOperator.ts
Normal file
37
packages/molang/src/parser/expressions/genericOperator.ts
Normal 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()}`
|
||||
}
|
||||
}
|
||||
38
packages/molang/src/parser/expressions/group.ts
Normal file
38
packages/molang/src/parser/expressions/group.ts
Normal 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]
|
||||
}`
|
||||
}
|
||||
}
|
||||
19
packages/molang/src/parser/expressions/index.ts
Normal file
19
packages/molang/src/parser/expressions/index.ts
Normal 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'
|
||||
56
packages/molang/src/parser/expressions/loop.ts
Normal file
56
packages/molang/src/parser/expressions/loop.ts
Normal 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()})`
|
||||
}
|
||||
}
|
||||
47
packages/molang/src/parser/expressions/name.ts
Normal file
47
packages/molang/src/parser/expressions/name.ts
Normal 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
|
||||
}
|
||||
}
|
||||
25
packages/molang/src/parser/expressions/number.ts
Normal file
25
packages/molang/src/parser/expressions/number.ts
Normal 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
|
||||
}
|
||||
}
|
||||
32
packages/molang/src/parser/expressions/postfix.ts
Normal file
32
packages/molang/src/parser/expressions/postfix.ts
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
packages/molang/src/parser/expressions/prefix.ts
Normal file
57
packages/molang/src/parser/expressions/prefix.ts
Normal 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}"`)
|
||||
}
|
||||
}
|
||||
29
packages/molang/src/parser/expressions/return.ts
Normal file
29
packages/molang/src/parser/expressions/return.ts
Normal 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()}`
|
||||
}
|
||||
}
|
||||
102
packages/molang/src/parser/expressions/statement.ts
Normal file
102
packages/molang/src/parser/expressions/statement.ts
Normal 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
|
||||
}
|
||||
}
|
||||
28
packages/molang/src/parser/expressions/static.ts
Normal file
28
packages/molang/src/parser/expressions/static.ts
Normal 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
|
||||
}
|
||||
}
|
||||
26
packages/molang/src/parser/expressions/string.ts
Normal file
26
packages/molang/src/parser/expressions/string.ts
Normal 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
|
||||
}
|
||||
}
|
||||
79
packages/molang/src/parser/expressions/ternary.ts
Normal file
79
packages/molang/src/parser/expressions/ternary.ts
Normal 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()}`
|
||||
}
|
||||
}
|
||||
21
packages/molang/src/parser/expressions/void.ts
Normal file
21
packages/molang/src/parser/expressions/void.ts
Normal 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 ''
|
||||
}
|
||||
}
|
||||
88
packages/molang/src/parser/molang.ts
Normal file
88
packages/molang/src/parser/molang.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { Parser } from './parse'
|
||||
import { BinaryOperator } from './parselets/binaryOperator'
|
||||
import { EPrecedence } from './precedence'
|
||||
import { PrefixOperator } from './parselets/prefix'
|
||||
import { NumberParselet } from './parselets/number'
|
||||
import { NameParselet } from './parselets/name'
|
||||
import { GroupParselet } from './parselets/groupParselet'
|
||||
import { ReturnParselet } from './parselets/return'
|
||||
import { StatementParselet } from './parselets/statement'
|
||||
import { StringParselet } from './parselets/string'
|
||||
import { FunctionParselet } from './parselets/function'
|
||||
import { ArrayAccessParselet } from './parselets/arrayAccess'
|
||||
import { ScopeParselet } from './parselets/scope'
|
||||
import { LoopParselet } from './parselets/loop'
|
||||
import { ForEachParselet } from './parselets/forEach'
|
||||
import { ContinueParselet } from './parselets/continue'
|
||||
import { BreakParselet } from './parselets/break'
|
||||
import { BooleanParselet } from './parselets/boolean'
|
||||
import { IParserConfig } from '../main'
|
||||
import { EqualsOperator } from './parselets/equals'
|
||||
import { NotEqualsOperator } from './parselets/notEquals'
|
||||
import { AndOperator } from './parselets/andOperator'
|
||||
import { OrOperator } from './parselets/orOperator'
|
||||
import { SmallerOperator } from './parselets/smallerOperator'
|
||||
import { GreaterOperator } from './parselets/greaterOperator'
|
||||
import { QuestionOperator } from './parselets/questionOperator'
|
||||
|
||||
export class MoLangParser extends Parser {
|
||||
constructor(config: Partial<IParserConfig>) {
|
||||
super(config)
|
||||
|
||||
//Special parselets
|
||||
this.registerPrefix('NAME', new NameParselet())
|
||||
this.registerPrefix('STRING', new StringParselet())
|
||||
this.registerPrefix('NUMBER', new NumberParselet())
|
||||
this.registerPrefix('TRUE', new BooleanParselet(EPrecedence.PREFIX))
|
||||
this.registerPrefix('FALSE', new BooleanParselet(EPrecedence.PREFIX))
|
||||
this.registerPrefix('RETURN', new ReturnParselet())
|
||||
this.registerPrefix('CONTINUE', new ContinueParselet())
|
||||
this.registerPrefix('BREAK', new BreakParselet())
|
||||
this.registerPrefix('LOOP', new LoopParselet())
|
||||
this.registerPrefix('FOR_EACH', new ForEachParselet())
|
||||
this.registerInfix(
|
||||
'QUESTION',
|
||||
new QuestionOperator(EPrecedence.CONDITIONAL)
|
||||
)
|
||||
this.registerPrefix('LEFT_PARENT', new GroupParselet())
|
||||
this.registerInfix(
|
||||
'LEFT_PARENT',
|
||||
new FunctionParselet(EPrecedence.FUNCTION)
|
||||
)
|
||||
this.registerInfix(
|
||||
'ARRAY_LEFT',
|
||||
new ArrayAccessParselet(EPrecedence.ARRAY_ACCESS)
|
||||
)
|
||||
this.registerPrefix('CURLY_LEFT', new ScopeParselet(EPrecedence.SCOPE))
|
||||
this.registerInfix(
|
||||
'SEMICOLON',
|
||||
new StatementParselet(EPrecedence.STATEMENT)
|
||||
)
|
||||
|
||||
//Prefix parselets
|
||||
this.registerPrefix('MINUS', new PrefixOperator(EPrecedence.PREFIX))
|
||||
this.registerPrefix('BANG', new PrefixOperator(EPrecedence.PREFIX))
|
||||
|
||||
//Postfix parselets
|
||||
//Nothing here yet
|
||||
|
||||
//Infix parselets
|
||||
this.registerInfix('PLUS', new BinaryOperator(EPrecedence.SUM))
|
||||
this.registerInfix('MINUS', new BinaryOperator(EPrecedence.SUM))
|
||||
this.registerInfix('ASTERISK', new BinaryOperator(EPrecedence.PRODUCT))
|
||||
this.registerInfix('SLASH', new BinaryOperator(EPrecedence.PRODUCT))
|
||||
this.registerInfix(
|
||||
'EQUALS',
|
||||
new EqualsOperator(EPrecedence.EQUALS_COMPARE)
|
||||
)
|
||||
this.registerInfix(
|
||||
'BANG',
|
||||
new NotEqualsOperator(EPrecedence.EQUALS_COMPARE)
|
||||
)
|
||||
this.registerInfix('GREATER', new GreaterOperator(EPrecedence.COMPARE))
|
||||
this.registerInfix('SMALLER', new SmallerOperator(EPrecedence.COMPARE))
|
||||
this.registerInfix('AND', new AndOperator(EPrecedence.AND))
|
||||
this.registerInfix('OR', new OrOperator(EPrecedence.OR))
|
||||
this.registerInfix('ASSIGN', new BinaryOperator(EPrecedence.ASSIGNMENT))
|
||||
}
|
||||
}
|
||||
118
packages/molang/src/parser/parse.ts
Normal file
118
packages/molang/src/parser/parse.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { Tokenizer } from '../tokenizer/Tokenizer'
|
||||
import { TTokenType, Token } from '../tokenizer/token'
|
||||
import { IPrefixParselet } from './parselets/prefix'
|
||||
import { IInfixParselet } from './parselets/infix'
|
||||
import { IExpression } from './expression'
|
||||
import { ExecutionEnvironment } from '../env/env'
|
||||
import { IParserConfig } from '../main'
|
||||
import { VoidExpression } from './expressions/void'
|
||||
|
||||
export class Parser {
|
||||
protected prefixParselets = new Map<TTokenType, IPrefixParselet>()
|
||||
protected infixParselets = new Map<TTokenType, IInfixParselet>()
|
||||
protected readTokens: Token[] = []
|
||||
protected tokenIterator = new Tokenizer()
|
||||
executionEnv!: ExecutionEnvironment
|
||||
|
||||
constructor(public config: Partial<IParserConfig>) {}
|
||||
|
||||
updateConfig(config: Partial<IParserConfig>) {
|
||||
this.config = config
|
||||
}
|
||||
|
||||
init(expression: string) {
|
||||
this.tokenIterator.init(expression)
|
||||
this.readTokens = []
|
||||
}
|
||||
setTokenizer(tokenizer: Tokenizer) {
|
||||
this.tokenIterator = tokenizer
|
||||
}
|
||||
setExecutionEnvironment(executionEnv: ExecutionEnvironment) {
|
||||
this.executionEnv = executionEnv
|
||||
}
|
||||
|
||||
parseExpression(precedence = 0): IExpression {
|
||||
let token = this.consume()
|
||||
if (token.getType() === 'EOF') return new VoidExpression()
|
||||
|
||||
const prefix = this.prefixParselets.get(token.getType())
|
||||
if (!prefix) {
|
||||
throw new Error(
|
||||
`Cannot parse ${token.getType()} expression "${token.getType()}"`
|
||||
)
|
||||
}
|
||||
|
||||
let expressionLeft = prefix.parse(this, token)
|
||||
return this.parseInfixExpression(expressionLeft, precedence)
|
||||
}
|
||||
|
||||
parseInfixExpression(expressionLeft: IExpression, precedence = 0) {
|
||||
let token
|
||||
|
||||
while (this.getPrecedence() > precedence) {
|
||||
token = this.consume()
|
||||
let tokenType = token.getType()
|
||||
if (tokenType === 'EQUALS' && !this.match('EQUALS')) {
|
||||
tokenType = 'ASSIGN'
|
||||
}
|
||||
|
||||
const infix = <IInfixParselet>this.infixParselets.get(tokenType)
|
||||
if (!infix)
|
||||
throw new Error(`Unknown infix parselet: "${tokenType}"`)
|
||||
expressionLeft = infix.parse(this, expressionLeft, token)
|
||||
}
|
||||
|
||||
return expressionLeft
|
||||
}
|
||||
|
||||
getPrecedence() {
|
||||
const parselet = this.infixParselets.get(this.lookAhead(0).getType())
|
||||
return parselet?.precedence ?? 0
|
||||
}
|
||||
|
||||
consume(expected?: TTokenType) {
|
||||
//Sets the lastLineNumber & startColumn before consuming next token
|
||||
//Used for getting the exact location an error occurs
|
||||
// this.tokenIterator.step()
|
||||
|
||||
const token = this.lookAhead(0)
|
||||
|
||||
if (expected && token.getType() !== expected) {
|
||||
throw new Error(
|
||||
`Expected token "${expected}" and found "${token.getType()}"`
|
||||
)
|
||||
}
|
||||
|
||||
this.readTokens.shift()!
|
||||
return token
|
||||
}
|
||||
|
||||
match(expected: TTokenType, consume = true) {
|
||||
const token = this.lookAhead(0)
|
||||
if (token.getType() !== expected) return false
|
||||
|
||||
if (consume) this.consume()
|
||||
return true
|
||||
}
|
||||
|
||||
lookAhead(distance: number) {
|
||||
while (distance >= this.readTokens.length)
|
||||
this.readTokens.push(this.tokenIterator.next())
|
||||
|
||||
return this.readTokens[distance]
|
||||
}
|
||||
|
||||
registerInfix(tokenType: TTokenType, infixParselet: IInfixParselet) {
|
||||
this.infixParselets.set(tokenType, infixParselet)
|
||||
}
|
||||
registerPrefix(tokenType: TTokenType, prefixParselet: IPrefixParselet) {
|
||||
this.prefixParselets.set(tokenType, prefixParselet)
|
||||
}
|
||||
|
||||
getInfix(tokenType: TTokenType) {
|
||||
return this.infixParselets.get(tokenType)
|
||||
}
|
||||
getPrefix(tokenType: TTokenType) {
|
||||
return this.prefixParselets.get(tokenType)
|
||||
}
|
||||
}
|
||||
21
packages/molang/src/parser/parselets/andOperator.ts
Normal file
21
packages/molang/src/parser/parselets/andOperator.ts
Normal 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 "&"`)
|
||||
}
|
||||
}
|
||||
22
packages/molang/src/parser/parselets/arrayAccess.ts
Normal file
22
packages/molang/src/parser/parselets/arrayAccess.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
134
packages/molang/src/parser/parselets/binaryOperator.ts
Normal file
134
packages/molang/src/parser/parselets/binaryOperator.ts
Normal 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`)
|
||||
}
|
||||
}
|
||||
}
|
||||
12
packages/molang/src/parser/parselets/boolean.ts
Normal file
12
packages/molang/src/parser/parselets/boolean.ts
Normal 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')
|
||||
}
|
||||
}
|
||||
12
packages/molang/src/parser/parselets/break.ts
Normal file
12
packages/molang/src/parser/parselets/break.ts
Normal 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()
|
||||
}
|
||||
}
|
||||
12
packages/molang/src/parser/parselets/continue.ts
Normal file
12
packages/molang/src/parser/parselets/continue.ts
Normal 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()
|
||||
}
|
||||
}
|
||||
19
packages/molang/src/parser/parselets/equals.ts
Normal file
19
packages/molang/src/parser/parselets/equals.ts
Normal 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
29
packages/molang/src/parser/parselets/forEach.ts
Normal file
29
packages/molang/src/parser/parselets/forEach.ts
Normal 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])
|
||||
}
|
||||
}
|
||||
26
packages/molang/src/parser/parselets/function.ts
Normal file
26
packages/molang/src/parser/parselets/function.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
31
packages/molang/src/parser/parselets/greaterOperator.ts
Normal file
31
packages/molang/src/parser/parselets/greaterOperator.ts
Normal 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
18
packages/molang/src/parser/parselets/groupParselet.ts
Normal file
18
packages/molang/src/parser/parselets/groupParselet.ts
Normal 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
|
||||
}
|
||||
}
|
||||
8
packages/molang/src/parser/parselets/infix.ts
Normal file
8
packages/molang/src/parser/parselets/infix.ts
Normal 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
|
||||
}
|
||||
29
packages/molang/src/parser/parselets/loop.ts
Normal file
29
packages/molang/src/parser/parselets/loop.ts
Normal 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])
|
||||
}
|
||||
}
|
||||
43
packages/molang/src/parser/parselets/name.ts
Normal file
43
packages/molang/src/parser/parselets/name.ts
Normal 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
|
||||
}
|
||||
}
|
||||
23
packages/molang/src/parser/parselets/notEquals.ts
Normal file
23
packages/molang/src/parser/parselets/notEquals.ts
Normal 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`)
|
||||
}
|
||||
}
|
||||
}
|
||||
12
packages/molang/src/parser/parselets/number.ts
Normal file
12
packages/molang/src/parser/parselets/number.ts
Normal 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()))
|
||||
}
|
||||
}
|
||||
21
packages/molang/src/parser/parselets/orOperator.ts
Normal file
21
packages/molang/src/parser/parselets/orOperator.ts
Normal 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 "|"`)
|
||||
}
|
||||
}
|
||||
3
packages/molang/src/parser/parselets/postfix.ts
Normal file
3
packages/molang/src/parser/parselets/postfix.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { IInfixParselet } from './infix'
|
||||
|
||||
export interface IPostfixParselet extends IInfixParselet {}
|
||||
20
packages/molang/src/parser/parselets/prefix.ts
Normal file
20
packages/molang/src/parser/parselets/prefix.ts
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
26
packages/molang/src/parser/parselets/questionOperator.ts
Normal file
26
packages/molang/src/parser/parselets/questionOperator.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
18
packages/molang/src/parser/parselets/return.ts
Normal file
18
packages/molang/src/parser/parselets/return.ts
Normal 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
22
packages/molang/src/parser/parselets/scope.ts
Normal file
22
packages/molang/src/parser/parselets/scope.ts
Normal 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
|
||||
}
|
||||
}
|
||||
31
packages/molang/src/parser/parselets/smallerOperator.ts
Normal file
31
packages/molang/src/parser/parselets/smallerOperator.ts
Normal 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
84
packages/molang/src/parser/parselets/statement.ts
Normal file
84
packages/molang/src/parser/parselets/statement.ts
Normal 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
|
||||
}
|
||||
}
|
||||
12
packages/molang/src/parser/parselets/string.ts
Normal file
12
packages/molang/src/parser/parselets/string.ts
Normal 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())
|
||||
}
|
||||
}
|
||||
28
packages/molang/src/parser/parselets/ternary.ts
Normal file
28
packages/molang/src/parser/parselets/ternary.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
25
packages/molang/src/parser/precedence.ts
Normal file
25
packages/molang/src/parser/precedence.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export enum EPrecedence {
|
||||
SCOPE = 1,
|
||||
STATEMENT,
|
||||
|
||||
ASSIGNMENT,
|
||||
CONDITIONAL,
|
||||
|
||||
ARRAY_ACCESS,
|
||||
|
||||
NULLISH_COALESCING,
|
||||
|
||||
AND,
|
||||
OR,
|
||||
|
||||
EQUALS_COMPARE,
|
||||
COMPARE,
|
||||
|
||||
SUM,
|
||||
PRODUCT,
|
||||
EXPONENT,
|
||||
|
||||
PREFIX,
|
||||
POSTFIX,
|
||||
FUNCTION,
|
||||
}
|
||||
Reference in New Issue
Block a user