Recently to do data processing, custom some of the structure, such as mat,vector,point, for subtraction, such as arithmetic to repeat the definition, the code is not very intuitive, JavaScript does not have operator overloading this kind of functions like C + +, C # is really unpleasant, So think "curve salvation", automatic translation code to implement operator overloading, the realization of the idea is actually very simple, is to write an interpreter, the code compiled. For example:
S = A + B
(B-c.fun ())/2 + D
Translated into
' S = replace(replace (A, ' + ', replace (replace (B, '‘,(
Replace(B, '-', C.fun ())))), '/', 2), ' + ', D) '
In
Replacefunction, we call the corresponding operator function of the object,
ReplaceThe function code is as follows:
/** * Conversion Method * @param A * @param op * @param b * @returns {*} * @private */export function __replace__ (a,op,b) {if (typeof (a)! = ' object ' && typeof (b)! = ' object ') {return new Function (' A ', ' B ', ' Return a ' + op + ' B ') (a)} if (! Object.getprototypeof (a). isPrototypeOf (b) && object.getprototypeof (b). isPrototypeOf (a)) {throw ' different types Object cannot use arithmetic '} let target = null if (Object.getprototypeof (a). isPrototypeOf (b)) {target = new Function (' R Eturn ' + b.__proto__.constructor.name) ()} if (object.getprototypeof (b). isPrototypeOf (a)) {target = new Fu Nction (' return ' + A.__proto__.constructor.name) ()} if (op = = ' + ') {if (target.__add__! = undefined) { Return target.__add__ (A, B)}else {throw target.tostring () + ' \ n undefined __add__ method '}}else I F (op = = '-') {if (target.__plus__! = undefined) {return target.__plus__ (A, b)}else { Throw Target.tostring () + ' \ n undefined __plus__ method '}}else if (op = = ' * ') {if (target.__multiply__! = undefined) {Retu RN Target.__multiply__ (A, B)}else {throw target.tostring () + ' \ n undefined __multiply__ method '}}} else if (op = = '/') {if (target.__divide__! = undefined) {return target.__divide__ (A, b)}else { Throw target.tostring () + ' \ n undefined __divide__ method '}} else if (op = = '% ') {if (target.__mod__! = und efined) {return target.__mod__ (A, B)}else {throw target.tostring () + ' \ n undefined __mod__ method ' }} else if (op = = '. * ') {if (target.__dot_multiply__! = undefined) {return target.__dot_multiply __ (A, B)}else {throw target.tostring () + ' \ n undefined __dot_multiply__ method '}} else if (op = = './') { if (target.__dot_divide__! = undefined) {return target.__dot_divide__ (A, b)}else { Throw target.tostring ()+ ' \ n undefined __dot_divide__ method '}} else if (op = = ' * ') {if (target.__power__! = undefined) {retur N Target.__power__ (A, B)}else {throw target.tostring () + ' \ n undefined __power__ method '}}else { Throw op + ' operator not recognized '}}
The replace implementation is very simple, does not do too much explanation, the important part is how to implement the code compilation. The realization of arithmetic when studying data structure in university is the basis of this translation, slightly different. Briefly describe the process:
1, split expression, extract variables and operators to get the meta-array A
2. Iterating through the array of tuples
- If the element is an operator subtraction, the previous element is popped from the stack, converted to replace(last, operator,
- If the element is ') ', the element is popped from the stack, stitched until it encounters ' (', and pressed into the stack. It is important to note that ' (' before the element is a function call or replace, if it is a function call or replace, you need to continue to eject the data forward and close the replace function.
- If it is a generic element, then see if the previous element is replace, if it is, you need to splice ') ' to make the replace function closed, or directly push the element into the stack.
3, the stack sequence obtained in 2 steps to get the compiled expression.
According to the above process, implementation code:
/** * Expression Conversion Tool method * @param code */export function Translate (code) {Let data = [] Let Tmp_code = Code.replace (/\s/g, ' ') Let TMP = [] Let Vari = Tmp_code.split (/["]+[^"]*["]+|["] +[^ ']*[']+|\*\*|\+|-|\*|\/|\ (|\) |\?| >[=]|<[=]|={2}|:| &{2}|\| {2}|\{|\}|=|%| \.\/|\.\*|,/g) Let Ops = Tmp_code.match (/["]+[^"]*["]+|["] +[^ ']*[']+|\*\*|\+|-|\*|\/|\ (|\) |\?| >[=]|<[=]|={2}|:| &{2}|\| {2}|\{|\}|=|%| \.\/|\.\*|,/g) for (let i = 0,len = Ops.length; i < Len; i++) {if (vari[i]! = ') {Tmp.push (vari [i])} if (Ops[i]! = ") {Tmp.push (Ops[i])}} tmp.push (Vari[ops.length]) for (l et i = 0; i < tmp.length; i++) {Let item = Tmp[i] if (/\*\*|\+|-|\*|\/|%| \.\/|\.\*/.test (Tmp[i])) {Let top = data.pop () Let trans = ' __replace__ (' + top + ', \ ' + tmp[i] + ' \ ', ' Data.push (trans)}else{if (') ' = = Tmp[i]) {Let TRANS0 = Tmp[i] Let top0 = Data.pop () while (top0! = ' (') {trans0 = top0 + trans0 Top0 = Data.pop ()} TRANS0 = top0 + trans0 Let pre = data[data.length-1] while (/[_\w]+[\.]? [_\w]+/.test (PRE) &&!/^__replace__\ (/.test (PRE) && pre! = undefined) { Pre = Data.pop () Trans0 = pre + TRANS0 pre = Data[data.length-1] } pre = data[data.length-1] while (pre! = undefined && /^__replace__\ (/.test (PRE)) {pre = Data.pop () Trans0 = pre + TRANS0 + ') ' Pre = Data[data.length-1]} data.push (TRANS0)}else {L ET pre = data[data.length-1] Let trans1 = Tmp[i] while (pre! = undefined &&/^__replace__\ (/.test (PRE) &&!/\*\*|\+|-|\*|\/|\ (|\?| >[=]|<[=]|={2}|:| &{2}|\| {2}|\{|=|\}|%| \.\/|\.\*/.test (item) &&!/^__replace__\ (/.test (item)) {if (tmp[i + 1] = = Undefi Ned) {pre = Data.pop () trans1 = pre + trans1 + ') ' BR Eak }else{pre = Data.pop () trans1 = pre + trans1 + ') ' P Re = Data[data.length-1]}} data.push (TRANS1)}} The Let result = ' Data.foreach ((value, key, own) = = {result + = value}) return result}
The method of expression compilation is written, and the next step is how to make the code written by our translation machine, which requires a container, two methods: one is the class constructor to redefine the method properties, and the other is to pass the code as a parameter to our custom method. Here's a look at the redefinition method in the Class Builder:
export default class OOkay { constructor () { let protos = Object.getOwnPropertyNames(Object.getPrototypeOf(this)) protos.forEach((proto, key, own) => { if(proto != ‘constructor‘){ Object.defineProperty(this, proto, { value:new Function(translate_block(proto, this[proto].toString())).call(this) }) } }) }}
As can be seen above, we use Object.defineproperty to redefine in the constructor, Translate_block is the entire block of code to be translated, the code is as follows:
/** * Class code block conversion tool * @param name * @param block * @returns {string} */export function Translate_block (name, block {Let codes = block.split (' \ n ') let Reg = new RegExp (' ^ ' + name + ' $ ') console.log (reg.source) codes[0] = Co Des[0].replace (name, ' function ') for (let i = 1; i < codes.length; i++) {if (Codes[i].indexof ('//')! =-1) { Codes[i] = codes[i].substring (0,codes[i].indexof ('//))} if (/\*\*|\+|-|\*|\/|%| \.\/|\.\*/g.test (Codes[i])) {if (Codes[i].indexof (' return ')! =-1) {Let Ret_index = codes[i].i Ndexof (' return ') + 7 codes[i] = codes[i].substring (0,ret_index) + Translate (codes[i].substring (Ret_index)) }else {Let Eq_index = codes[i].indexof (' = ') + 1 Codes[i] = codes[i].substring (0 , Eq_index) + Translate (codes[i].substring (Eq_index))}}} return ' return ' + codes.join (' \ n ')}
For the new class, we can use operator overloading in this class as long as we inherit the Ookay class. For inheriting from non-ookay classes, we can use the injected method as follows:
/** * 非继承类的注入方法 * @param target */ static inject (target) { let protos = Object.getOwnPropertyNames(Object.getPrototypeOf(target)) protos.forEach((proto, key, own) => { if (proto != ‘constructor‘) { Object.defineProperty(target, proto, { value:new Function(translate_block(proto, target[proto].toString())).call(target) }) } }) }
For the code in the non-class, we need a container, and here I use two ways, one in Ookay script, like this
<script type= ' Text/ookayscript ' >
Let A = a+b//A, B is an object instance
</script>
There is also the code as a parameter passed into __$$__
the method, the method compiles the code and executes, as follows:
static __$__(fn) { if(!(fn instanceof Function)){ throw ‘参数错误‘ } (new Function(translate_block(‘function‘,fn.toString()))).call(window)() }
This implements the overloading of the operator, which can be used to refer to the Git
git address: https://gitee.com/djxfire/javascript_operator_overloading.git Please add a link description
JavaScript implements operator overloading