You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
5.8 KiB
185 lines
5.8 KiB
|
6 years ago
|
import { MetaMethods, Table, getn } from './Table'
|
||
|
|
import { coerceToNumber, coerceToString, LuaType, coerceToBoolean } from './utils'
|
||
|
|
import { LuaError } from './LuaError'
|
||
|
|
|
||
|
|
const binaryArithmetic = <R extends boolean | number>(
|
||
|
|
left: LuaType,
|
||
|
|
right: LuaType,
|
||
|
|
metaMethodName: MetaMethods,
|
||
|
|
callback: (l: number, r: number) => R
|
||
|
|
): R => {
|
||
|
|
const mm =
|
||
|
|
(left instanceof Table && left.getMetaMethod(metaMethodName)) ||
|
||
|
|
(right instanceof Table && right.getMetaMethod(metaMethodName))
|
||
|
|
if (mm) return mm(left, right)[0]
|
||
|
|
|
||
|
|
const L = coerceToNumber(left, 'attempt to perform arithmetic on a %type value')
|
||
|
|
const R = coerceToNumber(right, 'attempt to perform arithmetic on a %type value')
|
||
|
|
return callback(L, R)
|
||
|
|
}
|
||
|
|
|
||
|
|
const binaryBooleanArithmetic = (
|
||
|
|
left: LuaType,
|
||
|
|
right: LuaType,
|
||
|
|
metaMethodName: MetaMethods,
|
||
|
|
callback: (l: LuaType, r: LuaType) => boolean
|
||
|
|
): boolean => {
|
||
|
|
if (
|
||
|
|
(typeof left === 'string' && typeof right === 'string') ||
|
||
|
|
(typeof left === 'number' && typeof right === 'number')
|
||
|
|
) {
|
||
|
|
return callback(left, right)
|
||
|
|
}
|
||
|
|
return binaryArithmetic<boolean>(left, right, metaMethodName, callback)
|
||
|
|
}
|
||
|
|
|
||
|
|
// extra
|
||
|
|
const bool = (value: LuaType): boolean => coerceToBoolean(value)
|
||
|
|
|
||
|
|
// unary
|
||
|
|
const not = (value: LuaType): boolean => !bool(value)
|
||
|
|
|
||
|
|
const unm = (value: LuaType): number => {
|
||
|
|
const mm = value instanceof Table && value.getMetaMethod('__unm')
|
||
|
|
if (mm) return mm(value)[0]
|
||
|
|
|
||
|
|
return -1 * coerceToNumber(value, 'attempt to perform arithmetic on a %type value')
|
||
|
|
}
|
||
|
|
|
||
|
|
const bnot = (value: LuaType): number => {
|
||
|
|
const mm = value instanceof Table && value.getMetaMethod('__bnot')
|
||
|
|
if (mm) return mm(value)[0]
|
||
|
|
|
||
|
|
return ~coerceToNumber(value, 'attempt to perform arithmetic on a %type value')
|
||
|
|
}
|
||
|
|
|
||
|
|
const len = (value: LuaType): number => {
|
||
|
|
if (value instanceof Table) {
|
||
|
|
const mm = value.getMetaMethod('__len')
|
||
|
|
if (mm) return mm(value)[0]
|
||
|
|
|
||
|
|
return getn(value)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (typeof value === 'string') return value.length
|
||
|
|
|
||
|
|
throw new LuaError('attempt to get length of an unsupported value')
|
||
|
|
|
||
|
|
// if (typeof value === 'object') {
|
||
|
|
// let length = 0
|
||
|
|
// for (const key in value) {
|
||
|
|
// if (hasOwnProperty(value, key)) {
|
||
|
|
// length += 1
|
||
|
|
// }
|
||
|
|
// }
|
||
|
|
// return length
|
||
|
|
// }
|
||
|
|
}
|
||
|
|
|
||
|
|
// binary
|
||
|
|
const add = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__add', (l, r) => l + r)
|
||
|
|
|
||
|
|
const sub = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__sub', (l, r) => l - r)
|
||
|
|
|
||
|
|
const mul = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__mul', (l, r) => l * r)
|
||
|
|
|
||
|
|
const mod = (left: LuaType, right: LuaType): number =>
|
||
|
|
binaryArithmetic(left, right, '__mod', (l, r) => {
|
||
|
|
if (r === 0 || r === -Infinity || r === Infinity || isNaN(l) || isNaN(r)) return NaN
|
||
|
|
|
||
|
|
const absR = Math.abs(r)
|
||
|
|
let result = Math.abs(l) % absR
|
||
|
|
|
||
|
|
if (l * r < 0) result = absR - result
|
||
|
|
if (r < 0) result *= -1
|
||
|
|
|
||
|
|
return result
|
||
|
|
})
|
||
|
|
|
||
|
|
const pow = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__pow', Math.pow)
|
||
|
|
|
||
|
|
const div = (left: LuaType, right: LuaType): number =>
|
||
|
|
binaryArithmetic(left, right, '__div', (l, r) => {
|
||
|
|
if (r === undefined) throw new LuaError('attempt to perform arithmetic on a nil value')
|
||
|
|
return l / r
|
||
|
|
})
|
||
|
|
|
||
|
|
const idiv = (left: LuaType, right: LuaType): number =>
|
||
|
|
binaryArithmetic(left, right, '__idiv', (l, r) => {
|
||
|
|
if (r === undefined) throw new LuaError('attempt to perform arithmetic on a nil value')
|
||
|
|
return Math.floor(l / r)
|
||
|
|
})
|
||
|
|
|
||
|
|
const band = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__band', (l, r) => l & r)
|
||
|
|
|
||
|
|
const bor = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__bor', (l, r) => l | r)
|
||
|
|
|
||
|
|
const bxor = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__bxor', (l, r) => l ^ r)
|
||
|
|
|
||
|
|
const shl = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__shl', (l, r) => l << r)
|
||
|
|
|
||
|
|
const shr = (left: LuaType, right: LuaType): number => binaryArithmetic(left, right, '__shr', (l, r) => l >> r)
|
||
|
|
|
||
|
|
const concat = (left: LuaType, right: LuaType): string => {
|
||
|
|
const mm =
|
||
|
|
(left instanceof Table && left.getMetaMethod('__concat')) ||
|
||
|
|
(right instanceof Table && right.getMetaMethod('__concat'))
|
||
|
|
if (mm) return mm(left, right)[0]
|
||
|
|
|
||
|
|
const L = coerceToString(left, 'attempt to concatenate a %type value')
|
||
|
|
const R = coerceToString(right, 'attempt to concatenate a %type value')
|
||
|
|
return `${L}${R}`
|
||
|
|
}
|
||
|
|
|
||
|
|
const neq = (left: LuaType, right: LuaType): boolean => !eq(left, right)
|
||
|
|
|
||
|
|
const eq = (left: LuaType, right: LuaType): boolean => {
|
||
|
|
const mm =
|
||
|
|
right !== left &&
|
||
|
|
left instanceof Table &&
|
||
|
|
right instanceof Table &&
|
||
|
|
left.metatable === right.metatable &&
|
||
|
|
left.getMetaMethod('__eq')
|
||
|
|
|
||
|
|
if (mm) return !!mm(left, right)[0]
|
||
|
|
|
||
|
|
return left === right
|
||
|
|
}
|
||
|
|
|
||
|
|
const lt = (left: LuaType, right: LuaType): boolean => binaryBooleanArithmetic(left, right, '__lt', (l, r) => l < r)
|
||
|
|
|
||
|
|
const le = (left: LuaType, right: LuaType): boolean => binaryBooleanArithmetic(left, right, '__le', (l, r) => l <= r)
|
||
|
|
|
||
|
|
const gt = (left: LuaType, right: LuaType): boolean => !le(left, right)
|
||
|
|
|
||
|
|
const ge = (left: LuaType, right: LuaType): boolean => !lt(left, right)
|
||
|
|
|
||
|
|
const operators = {
|
||
|
|
bool,
|
||
|
|
not,
|
||
|
|
unm,
|
||
|
|
bnot,
|
||
|
|
len,
|
||
|
|
add,
|
||
|
|
sub,
|
||
|
|
mul,
|
||
|
|
mod,
|
||
|
|
pow,
|
||
|
|
div,
|
||
|
|
idiv,
|
||
|
|
band,
|
||
|
|
bor,
|
||
|
|
bxor,
|
||
|
|
shl,
|
||
|
|
shr,
|
||
|
|
concat,
|
||
|
|
neq,
|
||
|
|
eq,
|
||
|
|
lt,
|
||
|
|
le,
|
||
|
|
gt,
|
||
|
|
ge
|
||
|
|
}
|
||
|
|
|
||
|
|
export { operators }
|