mirror of
https://github.com/ershisan99/www.git
synced 2025-12-18 21:09:23 +00:00
update lua table parser
This commit is contained in:
241
src/app/(home)/log-parser/lua-parser.ts
Normal file
241
src/app/(home)/log-parser/lua-parser.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
// @ts-nocheck
|
||||
'use client'
|
||||
|
||||
type LuaValue = string | number | boolean | null | LuaTable | LuaValue[]
|
||||
type LuaTable = { [key: string]: LuaValue }
|
||||
|
||||
export class LuaParser {
|
||||
private pos = 0
|
||||
private input = ''
|
||||
private currentChar: string | null | undefined = null
|
||||
|
||||
constructor(input: string) {
|
||||
this.input = input.trim()
|
||||
this.currentChar = this.input[0] ?? null
|
||||
}
|
||||
|
||||
private advance(): void {
|
||||
this.pos++
|
||||
this.currentChar =
|
||||
this.pos < this.input.length ? this.input[this.pos] : null
|
||||
}
|
||||
|
||||
private skipWhitespace(): void {
|
||||
while (
|
||||
this.currentChar?.match(/\s/) !== null &&
|
||||
this.currentChar !== null
|
||||
) {
|
||||
this.advance()
|
||||
}
|
||||
}
|
||||
|
||||
private parseString(): string {
|
||||
let result = ''
|
||||
const quote = this.currentChar
|
||||
this.advance() // Skip opening quote
|
||||
|
||||
while (this.currentChar !== null && this.currentChar !== quote) {
|
||||
if (this.currentChar === '\\') {
|
||||
this.advance()
|
||||
switch (this.currentChar) {
|
||||
case 'n':
|
||||
result += '\n'
|
||||
break
|
||||
case 't':
|
||||
result += '\t'
|
||||
break
|
||||
case 'r':
|
||||
result += '\r'
|
||||
break
|
||||
case 'b':
|
||||
result += '\b'
|
||||
break
|
||||
case 'f':
|
||||
result += '\f'
|
||||
break
|
||||
case '"':
|
||||
case "'":
|
||||
case '\\':
|
||||
result += this.currentChar
|
||||
break
|
||||
default:
|
||||
throw new Error(`Invalid escape sequence: \\${this.currentChar}`)
|
||||
}
|
||||
} else {
|
||||
result += this.currentChar
|
||||
}
|
||||
this.advance()
|
||||
}
|
||||
|
||||
if (this.currentChar === null) {
|
||||
throw new Error('Unterminated string')
|
||||
}
|
||||
this.advance() // Skip closing quote
|
||||
return result
|
||||
}
|
||||
|
||||
private parseNumber(): number {
|
||||
let result = ''
|
||||
|
||||
if (this.currentChar === '-') {
|
||||
result += this.currentChar
|
||||
this.advance()
|
||||
}
|
||||
|
||||
while (
|
||||
this.currentChar?.match(/[\d.]/) !== null &&
|
||||
this.currentChar !== null
|
||||
) {
|
||||
result += this.currentChar
|
||||
this.advance()
|
||||
}
|
||||
|
||||
const num = Number.parseFloat(result)
|
||||
if (isNaN(num)) {
|
||||
throw new Error(`Invalid number: ${result}`)
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
private parseIdentifier(): string {
|
||||
let result = ''
|
||||
|
||||
while (
|
||||
this.currentChar?.match(/[a-zA-Z0-9_]/) !== null &&
|
||||
this.currentChar !== null
|
||||
) {
|
||||
result += this.currentChar
|
||||
this.advance()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private parseValue(): LuaValue {
|
||||
this.skipWhitespace()
|
||||
|
||||
if (this.currentChar === null) {
|
||||
throw new Error('Unexpected end of input')
|
||||
}
|
||||
|
||||
switch (this.currentChar) {
|
||||
case '{':
|
||||
return this.parseTable()
|
||||
case '"':
|
||||
case "'":
|
||||
return this.parseString()
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
return this.parseNumber()
|
||||
default: {
|
||||
const identifier = this.parseIdentifier()
|
||||
switch (identifier.toLowerCase()) {
|
||||
case 'true':
|
||||
return true
|
||||
case 'false':
|
||||
return false
|
||||
case 'nil':
|
||||
return null
|
||||
default:
|
||||
throw new Error(`Unexpected identifier: ${identifier}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private parseTable(): LuaTable | LuaValue[] {
|
||||
this.advance() // Skip '{'
|
||||
this.skipWhitespace()
|
||||
|
||||
const result: LuaTable = {}
|
||||
const array: LuaValue[] = []
|
||||
let isArray = true
|
||||
let index = 0
|
||||
|
||||
while (this.currentChar !== null && this.currentChar !== '}') {
|
||||
this.skipWhitespace()
|
||||
|
||||
if (this.currentChar === '[') {
|
||||
isArray = false
|
||||
this.advance() // Skip '['
|
||||
const key = this.parseValue()
|
||||
if (typeof key !== 'string' && typeof key !== 'number') {
|
||||
throw new Error('Table key must be string or number')
|
||||
}
|
||||
this.skipWhitespace()
|
||||
|
||||
if (this.currentChar !== ']') {
|
||||
throw new Error("Expected ']'")
|
||||
}
|
||||
this.advance() // Skip ']'
|
||||
this.skipWhitespace()
|
||||
|
||||
if (this.currentChar !== '=') {
|
||||
throw new Error("Expected '='")
|
||||
}
|
||||
this.advance() // Skip '='
|
||||
|
||||
const value = this.parseValue()
|
||||
result[String(key)] = value
|
||||
} else {
|
||||
const value = this.parseValue()
|
||||
if (isArray) {
|
||||
array.push(value)
|
||||
index++
|
||||
} else {
|
||||
result[String(index)] = value
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
this.skipWhitespace()
|
||||
if (this.currentChar === ',') {
|
||||
this.advance()
|
||||
} else if (this.currentChar !== '}') {
|
||||
throw new Error("Expected ',' or '}'")
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentChar === null) {
|
||||
throw new Error('Unterminated table')
|
||||
}
|
||||
this.advance() // Skip '}'
|
||||
|
||||
return isArray ? array : result
|
||||
}
|
||||
|
||||
public parse(): LuaValue {
|
||||
const result = this.parseValue()
|
||||
this.skipWhitespace()
|
||||
|
||||
if (this.currentChar !== null) {
|
||||
throw new Error('Unexpected characters after end of input')
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export class LuaToJsonConverter {
|
||||
private static readonly logger = console
|
||||
|
||||
public static async convert(luaString: string): Promise<string> {
|
||||
try {
|
||||
const parser = new LuaParser(luaString)
|
||||
const parsed = parser.parse()
|
||||
return JSON.stringify(parsed, null, 2)
|
||||
} catch (error) {
|
||||
this.logger.error('Error converting Lua to JSON:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { LuaToJsonConverter } from '@/app/(home)/log-parser/lua-parser'
|
||||
import { OptimizedImage } from '@/components/optimized-image'
|
||||
import {
|
||||
Card,
|
||||
@@ -37,7 +38,6 @@ import { jokers } from '@/shared/jokers'
|
||||
import { useFormatter } from 'next-intl'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { type PvpBlind, PvpBlindsCard } from './_components/pvp-blinds'
|
||||
|
||||
// Define the structure for individual log events within a game
|
||||
type LogEvent = {
|
||||
timestamp: Date
|
||||
@@ -1418,31 +1418,9 @@ type JsonValue =
|
||||
| JsonValue[]
|
||||
| { [key: string]: JsonValue }
|
||||
|
||||
function luaTableToJson(luaString: string): string {
|
||||
let str = luaString.replace(/^return\s*/, '')
|
||||
|
||||
// Handle nested tables closing properly
|
||||
str = str.replace(/,(\s*})/g, '$1')
|
||||
|
||||
// Handle array-style table entries [1] = value
|
||||
str = str.replace(/\[(\d+)\]\s*=/g, '"$1":')
|
||||
|
||||
// Handle string keys ["key"] = value
|
||||
str = str.replace(/\[(["'][^"']+["'])\]\s*=/g, '$1:')
|
||||
|
||||
// Handle regular key = value
|
||||
str = str.replace(/(\w+)\s*=/g, '"$1":')
|
||||
|
||||
// Replace single quotes with double quotes
|
||||
str = str.replace(/'/g, '"')
|
||||
|
||||
// Clean up empty tables
|
||||
str = str.replace(/{}/g, '{}')
|
||||
|
||||
// Remove trailing commas inside objects
|
||||
str = str.replace(/,(\s*[}\]])/g, '$1')
|
||||
|
||||
return str
|
||||
async function luaTableToJson(luaString: string): string {
|
||||
const str = luaString.replace(/^return\s*/, '')
|
||||
return LuaToJsonConverter.convert(str)
|
||||
}
|
||||
|
||||
async function decodePackedString(encodedString: string): Promise<JsonValue> {
|
||||
@@ -1466,7 +1444,8 @@ async function decodePackedString(encodedString: string): Promise<JsonValue> {
|
||||
}
|
||||
|
||||
// Convert Lua table to JSON
|
||||
const jsonString = luaTableToJson(decompressedString)
|
||||
const jsonString = await luaTableToJson(decompressedString)
|
||||
console.log(jsonString)
|
||||
const result = JSON.parse(jsonString) as JsonValue
|
||||
return result
|
||||
} catch (error) {
|
||||
@@ -1479,6 +1458,7 @@ async function decodePackedString(encodedString: string): Promise<JsonValue> {
|
||||
async function parseJokersFromString(str: string) {
|
||||
// Check if the string starts with 'H4' indicating a packed string
|
||||
// This is a common prefix for base64 encoded gzip strings
|
||||
try {
|
||||
if (str.startsWith('H4')) {
|
||||
const decoded = await decodePackedString(str)
|
||||
if (decoded && typeof decoded === 'object' && 'cards' in decoded) {
|
||||
@@ -1487,5 +1467,9 @@ async function parseJokersFromString(str: string) {
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse jokers from string:', str, e)
|
||||
return []
|
||||
}
|
||||
return str.split(';').filter(Boolean) // Remove empty strings if any
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user