hytescript.js
Version:
A package for programming anything you want with ease.
270 lines (226 loc) • 9.84 kB
JavaScript
const { unescape, replaceLast, clone, HscLog } = require("../utils/BaseUtils")
module.exports = class Compiler {
static compile(code, trim = true, line = 1) {
const compiler = {
type: 'text',
source: code,
text: [],
writing: '',
write(str) {
this.writing += str
},
line: line,
function: {
name: '',
source: code,
parameters: [],
param: 0,
index: 0,
closed: false
},
functions: [],
opens: 0,
resetFunction() {
this.functions.push(this.function)
this.function = {
name: '',
source: code,
parameters: [],
param: 0,
index: 0,
closed: false
}
},
resetWriting() {
this.text.push(this.writing)
this.writing = ''
},
async parse(d, returnResults = false) {
return await Compiler.parse(d, this, returnResults)
}
}
const types = {
text(c) {
if (c === '#') {
compiler.type = 'tag'
} else {
compiler.write(c)
}
},
tag(c) {
if (c === '(') {
compiler.type = 'function_name'
compiler.resetWriting()
compiler.function.index = compiler.text.length + compiler.functions.length
compiler.opens++
compiler.function.line = compiler.line
} else {
compiler.type = 'text'
compiler.write('#')
types.text(c)
}
},
function_name(c) {
if ([' ', '\n'].includes(c)) {
if (c == '\n') compiler.function.line++
compiler.type = 'function_parameters'
} else if (c === ')') {
compiler.type = 'text'
compiler.function.closed = true
compiler.resetFunction()
compiler.opens = 0
} else {
compiler.function.name += c
}
},
function_parameters(c) {
if (c === '|' && compiler.opens <= 1) return compiler.function.param++
if (c === '(') compiler.opens++
if (c === ')') compiler.opens--
if (compiler.opens <= 0) {
compiler.type = 'text'
compiler.function.closed = true
compiler.resetFunction()
} else {
if (compiler.function.parameters[compiler.function.param] == undefined) compiler.function.parameters[compiler.function.param] = ''
compiler.function.parameters[compiler.function.param] += c
}
}
}
code = code.split('\n').map(x => trim ? x.trim() : x).join('\n')
let chars = [...code]
for (const char of chars) {
let read = types[compiler.type]
read(char)
if (char === '\n') compiler.line++
}
if (compiler.writing !== '') {
compiler.resetWriting()
}
if (compiler.type === 'tag') {
compiler.type = 'text'
compiler.write('#')
compiler.resetWriting()
}
if (compiler.type.startsWith('function_')) {
compiler.type = 'text'
compiler.resetFunction()
}
let newFunctions = []
for (const func of compiler.functions) {
let newParameters = []
let lastLine = 0
for (const parameter of func.parameters) {
if (['', undefined].includes(parameter)) newParameters.push(undefined)
else {
let compiledParameter = Compiler.compile(removeSpaces(parameter), false, func.line + lastLine)
compiledParameter.source = func.source
compiledParameter.functions = compiledParameter.functions.map(x => {
x.parent = func.name
x.source = func.source
lastLine += x.line - func.line
return x
})
newParameters.push(compiledParameter)
}
}
func.parameters = newParameters
delete func.param
newFunctions.push(func)
}
compiler.functions = newFunctions
let obj = {
text: compiler.text.map(x => x.replaceAll('\n', '').replace(/%BR%/ig, '\n')),
source: compiler.source,
functions: compiler.functions,
parse: compiler.parse,
nosource() {
let comp = clone(this)
function removeSource(comp) {
if (comp == undefined) return comp
delete comp.source
comp.functions = comp.functions.map(x => {
delete x.source
x.parameters = x.parameters.map(x => removeSource(x))
return x
})
return comp
}
return removeSource(comp)
}
}
return obj
}
static async parse(d, compiledCode, returnResults = false) {
let compiled = clone(compiledCode)
if (d.clientOptions.debug === true && d.sourceCode == undefined) HscLog.debug(`parsing command: "${typeof d.command.name === 'string' ? d.command.name : 'unknown'}".\nCompiled code: ${require('util').inspect(compiled.nosource(), {showHidden: false, compact: true, depth: null, colors: true}, )}`)
if (d.sourceCode == undefined) d.sourceCode = compiled.source
for (const placeholder of d.data.placeholders) {
compiled.text = compiled.text.map(text => text.replace(eval(`/${placeholder.name}/ig`), placeholder.value))
}
let results = []
for (const func of compiled.functions) {
let funcData = {
name: func.name,
line: func.line,
parameters: [],
parent: func.parent
}
d.function = funcData
let loadedFunc = d.functions.get(func.name.toLowerCase())
if (!loadedFunc) {
new d.error("custom", d, `#(${func.name}) is not a function`)
return {error: true};
}
if (!func.closed) {
new d.error("custom", d, `#(${func.name}) is not closed`)
return {error: true};
}
{
let idx = 0
for (const parameter of func.parameters) {
if (parameter == undefined) funcData.parameters.push(undefined)
else {
if (!loadedFunc.dontParse.includes(idx)) {
d.function = undefined
let parsed = await this.parse(d, parameter)
if (parsed.error) return {error: true}
if (parsed.result.toLowerCase() === '%blank%') funcData.parameters.push('')
else funcData.parameters.push(!loadedFunc.dontUnescape.includes(idx) ? unescape(parsed.result) : parsed.result)
} else {
funcData.parameters.push(parameter)
}
}
idx++
}
}
d.function = funcData
let result = await loadedFunc.run(d, ...d.function.parameters).catch(e => {
if (d.data.logJSErrors) console.error(e)
HscLog.error(`\x1b[31m#(${d.function.name}): (internal) ${e.message}`)
})
if (d.err) return {error: true}
results.push(result)
if (result == undefined) result = ''
else if (typeof result !== 'string') result = JSON.stringify(result)
for (const placeholder of d.data.placeholders) {
compiled.text = compiled.text.map(text => text.replace(eval(`/${placeholder.name}/ig`), placeholder.value))
}
if (d.err) return {error: true}
let before = compiled.text.slice(0, func.index)
let after = compiled.text.slice(func.index, compiled.text.length)
compiled.text = [...before, result, ...after]
}
d.data.message.content = compiled.text.join('').trim().replaceAll('\n', '') === '' ? undefined : unescape(compiled.text.join(''))
return {
result: returnResults ? results : compiled.text.join(''),
message: d.data.message,
error: d.err
};
}
}
function removeSpaces(str) {
if (str.startsWith(' ') && str !== ' ') str = str.replace(' ', '')
if (str.endsWith(' ') && str !== ' ') str = replaceLast(str, ' ', '')
return str
}