Jump to content

EasyCodeIt - cross-platform AutoIt implementation


Recommended Posts

Link to comment
Share on other sites

22 hours ago, Nine said:

Then use my slow script :

  Reveal hidden contents
Func _BitAND64($iValue1, $iValue2)
  If VarGetType($iValue1) <> "Int64" And VarGetType($iValue2) <> "Int64" Then Return BitAND($iValue1, $iValue2)
  $iValue1 = __DecToBin64($iValue1)
  $iValue2 = __DecToBin64($iValue2)
  Local $aValueANDed[64]
  For $i = 0 To 63
    $aValueANDed[$i] = ($iValue1[$i] And $iValue2[$i]) ? 1 : 0
  Next
  Return __BinToDec64($aValueANDed)
EndFunc   ;==>_BitAND64

Func _BitOR64($iValue1, $iValue2)
  If VarGetType($iValue1) <> "Int64" And VarGetType($iValue2) <> "Int64" Then Return BitOR($iValue1, $iValue2)
  $iValue1 = __DecToBin64($iValue1)
  $iValue2 = __DecToBin64($iValue2)
  Local $aValueORed[64]
  For $i = 0 To 63
    $aValueORed[$i] = ($iValue1[$i] Or $iValue2[$i]) ? 1 : 0
  Next
  Return __BinToDec64($aValueORed)
EndFunc   ;==>_BitOR64

Func _BitXOR64($iValue1, $iValue2)
  If VarGetType($iValue1) <> "Int64" And VarGetType($iValue2) <> "Int64" Then Return BitXOR($iValue1, $iValue2)
  $iValue1 = __DecToBin64($iValue1)
  $iValue2 = __DecToBin64($iValue2)
  Local $aValueXORed[64]
  For $i = 0 To 63
    $aValueXORed[$i] = (($iValue1[$i] And (Not $iValue2[$i])) Or ((Not $iValue1[$i]) And $iValue2)) ? 1 : 0
  Next
  Return __BinToDec64($aValueXORed)
EndFunc   ;==>_BitXOR64

Func _BitNOT64($iValue)
  If Not VarGetType($iValue) = "Int64" Then Return BitNOT($iValue)
  $iValue = __DecToBin64($iValue)
  For $i = 0 To 63
    $iValue[$i] = Not $iValue[$i]
  Next
  Return __BinToDec64($iValue)
EndFunc   ;==>_BitNOT64

Func _BitRotate64($iValue, $iShift)
  If VarGetType($iValue) <> "Int64" Then Return BitRotate($iValue, $iShift, "D")
  $iValue = __DecToBin64($iValue)
  Local $iTmp
  If $iShift < 0 Then ; rotate right
    For $i = 1 To Abs($iShift)
      $iTmp = $iValue[0]
      For $j = 1 To 63
        $iValue[$j - 1] = $iValue[$j]
      Next
      $iValue[63] = $iTmp
    Next
  Else
    For $i = 1 To $iShift
      $iTmp = $iValue[63]
      For $j = 63 To 1 Step -1
        $iValue[$j] = $iValue[$j - 1]
      Next
      $iValue[0] = $iTmp
    Next
  EndIf
  Return __BinToDec64($iValue)
EndFunc   ;==>_BitRotate64

Func _BitShift64($iValue, $iShift, $bSigned = True)
  If $iShift <= 0 Then $bSigned = True
  If VarGetType($iValue) <> "Int64" Then
    If $bSigned Then Return BitShift($iValue, $iShift)
    If $iShift > 0 Then
      $iValue = BitAND(BitShift($iValue, 1), 0x7FFFFFFF)
      Return BitShift($iValue, $iShift-1)
    EndIf
  EndIf
  $iValue = __DecToBin64($iValue)
  Local $iTmp
  If $iShift > 0 Then ; shift right
    $iTmp = $bSigned ? $iValue[63] : 0
    For $i = 1 To Abs($iShift)
      For $j = 1 To 63
        $iValue[$j - 1] = $iValue[$j]
      Next
      $iValue[63] = $iTmp
    Next
  Else
    For $i = 1 To Abs($iShift)
      For $j = 63 To 1 Step -1
        $iValue[$j] = $iValue[$j - 1]
      Next
      $iValue[0] = 0
    Next
  EndIf
  Return __BinToDec64($iValue)
EndFunc   ;==>_BitShift64

Func __DecToBin64($iDec)
  Local $tI64 = DllStructCreate("int64 num"), $aBin[64], $iVal
  Local $tI32 = DllStructCreate("align 4;uint low;uint high", DllStructGetPtr($tI64))
  $tI64.num = $iDec
  For $i = 0 To 31
    $iVal = 2 ^ $i
    $aBin[$i] = BitAND($tI32.low, $iVal) ? 1 : 0
    $aBin[$i + 32] = BitAND($tI32.high, $iVal) ? 1 : 0
  Next
  Return $aBin
EndFunc   ;==>__DecToBin64

Func __BinToDec64($aBin)
  Local $tI32 = DllStructCreate("align 4;uint low;uint high"), $iVal
  Local $tI64 = DllStructCreate("UINT64 num", DllStructGetPtr($tI32))
  For $i = 0 To 31
    $iVal = 2 ^ $i
    $tI32.low += $aBin[$i] ? $iVal : 0
    $tI32.high += $aBin[$i + 32] ? $iVal : 0
  Next
  Return $tI64.num
EndFunc   ;==>__BinToDec64

 

 

THANK YOU!!!! I got code working for variable length bits (specifically for BitNOT) up to 48 bits but this will work!

My UDFs are generally for me. If they aren't updated for a while, it means I'm not using them myself. As soon as I start using them again, they'll get updated.

My Projects

WhyNotWin11
Cisco FinesseGithubIRC UDFWindowEx UDF

 

Link to comment
Share on other sites

17 hours ago, rcmaehl said:

THANK YOU!!!! 

lol, glad you like it  :)

Should work up to 64 bits, doesn't it ?

ps. corrected a small bug on XOR, code has been updated

Edited by Nine
Link to comment
Share on other sites

To prevent this topic from turning into a discussion on bitwise operations, I will post an update on the progress :D

In the last updated I showed off the parser I wrote... unfortunately it turns out it isn't capable of handling pretty much anything other than infix binary operators :(. So I had to go back and start from scratch, I read many articles again to see how they handled expressions, the problem with these articles is that they use object-oriented programming which is hard to follow... so it took me a long time to finally grasp the concept driving all the recursive decent parsers I had been reading about.

So I wrote my own parser in JavaScript as a proof of concept, I chose JS because I am familiar with it and it is easier to write high-level code in it, not to mention that it runs in any computer with a browser. Here is the code:

/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

const TokenType = {
    CONSTANT: Symbol("Constant"),
    OPERATOR: Symbol("Operator"),
    BRACKET: Symbol("Bracket"),
};

const Operation = {
    INVERT: Symbol("Invert"),
    NEGATE: Symbol("Negate"),
    ADD: Symbol("Add"),
    SUBTRACT: Symbol("Subtract"),
    MULTIPLY: Symbol("Multiply"),
    DIVIDE: Symbol("Divide"),
    EXPONENTIATE: Symbol("Exponentiate"),
    ASSIGN: Symbol("Assign"),
    SINE: Symbol("Sin"),
    LOG: Symbol("Log"),
};

const Precedence = [
    [Operation.INVERT],
    [Operation.NEGATE],
    [
    Operation.SINE,
    Operation.LOG
    ],
    [Operation.EXPONENTIATE],
    [
    Operation.MULTIPLY, 
    Operation.DIVIDE,
    ],
    [
    Operation.ADD,
    Operation.SUBTRACT,
    ],
    [Operation.ASSIGN],
];
Precedence.reverse(); // IMPORTANT

Precedence.next = op => Precedence.find(a => a.includes(op)) + 1;

const OP_MAP = new Map([
    ['!', Operation.NEGATE],
    ['+', Operation.ADD],
    ['-', Operation.SUBTRACT],
    ['*', Operation.MULTIPLY],
    ['/', Operation.DIVIDE],
    ['^', Operation.EXPONENTIATE],
    ['=', Operation.ASSIGN],
    ['s', Operation.SINE],
    ['l', Operation.LOG],
]);

if (globalThis.process) main(process.argv);

function main(args) {
    const input = args[2];
    
    if (!input) {
        console.error("No arguments supplied!");
        return;
    }
    
    var tokens = tokenize(input);
    var expression = parse(tokens);
    expression.raw = input;
    
    var output = JSON.stringify(expression, (key, value) => {
        if (typeof value == 'symbol') return value.description;
        return value;
    }, '\t');
    console.log(output);
    
    return output;
}

function tokenize(input) {
    var tokens = [];
    
    var constant;
    for (let i = 0; i < input.length; ++i) {
        // Parse constants (numbers)
        constant = "";
        while (true) {
            if (isNaN(parseInt(input[i], 10))) {
                if (constant) --i;
                break;
            }
            constant += input[i++];
        }
        if (constant) {
            tokens.push({type: TokenType.CONSTANT, value: parseInt(constant, 10)});
            continue;
        }
        
        // Parse operators
        if (OP_MAP.has(input[i])) {
            tokens.push({type: TokenType.OPERATOR, value: OP_MAP.get(input[i])});
            continue;
        }
        
        // Parse brackets
        if (input[i] == '(' || input[i] == ')') {
            tokens.push({type: TokenType.BRACKET, value: {open: input[i] == '('}});
            continue;
        }
        
        // Skip whitespace
        if (input[i] == ' ') continue;
        
        // Unknown token, so break out of the loop
        break;
    }
    
    return tokens;
}

function parse(tokens, precedence = 0) {
    var expression = {op: undefined, args: []};
    
    // Unpack a sub-expression (remove the surrounding brackets)
    if (tokens[0].type == TokenType.BRACKET && tokens[tokens.length - 1].type == TokenType.BRACKET) tokens = tokens.slice(1, tokens.length - 1);
    
    switch (Precedence[precedence] ? Precedence[precedence][0] : undefined) {
        case Operation.INVERT:
            if (tokens[0].value == Operation.SUBTRACT) {
                expression.op = Operation.INVERT;
                push_sub_exp_as_arg(1);
            } else return next();
            break;
        case Operation.NEGATE:
            if (tokens[0].type == (expression.op = Operation.NEGATE)) {
                push_sub_exp_as_arg(1);
            } else return next();
            break;
        case Operation.SINE:
        case Operation.LOG:
            if (
                (tokens[tokens.length - 1].value  == (expression.op = Operation.SINE))
                ||
                (tokens[tokens.length - 1].value  == (expression.op = Operation.LOG))
            ) {
                push_sub_exp_as_arg(0, -1);
            } else return next();
            break;
        case Operation.EXPONENTIATE:
        case Operation.MULTIPLY:
        case Operation.DIVIDE:
        case Operation.ADD:
        case Operation.SUBTRACT:
        case Operation.ASSIGN:
            parse_infix(Precedence[precedence], Precedence[precedence][0] != Operation.ASSIGN);
            break;
        default:
            return tokens[0].value;
    }
    return expression;
    
    function parse_infix(ops, left_assoc = true) {
        var op_pos = find_token_index_by_types(ops, left_assoc ? -1 : +1);
        if (op_pos < 1) return expression = next();
        expression.op = tokens[op_pos].value;
        push_sub_exp_as_arg(0, op_pos);
        push_sub_exp_as_arg(op_pos + 1);
    }
    
    function find_token_index_by_types(types, direction = +1) {
        for (let i = (direction > 0 ? 0 : tokens.length - 1); direction > 0 ? i < tokens.length : i >= 0; i += direction) {
            // Skip sub-expressions (which are in brackets)
            if (tokens[i].type == TokenType.BRACKET) {
                var open = 1;
                do {
                    if (tokens[i += direction].type == TokenType.BRACKET) {
                        open += (direction > 0 ? tokens[i].value.open : !tokens[i].value.open) ? +1 : -1;
                        continue;
                    }
                } while (open > 0);
            }
            
            if (types.includes(tokens[i].value)) return i;
        }
        return -1;
    }
    
    function push_sub_exp_as_arg(start, end) {
        var sub_exp = parse(tokens.slice(start, end));
        expression.args.push(sub_exp);
    }
    
    function next() {
        return parse(tokens, precedence + 1);
    }
}

I know what you are thinking, it's not easy to follow my code, but the reason I wrote it because I wanted to learn the concept... it will be much harder to write code for the purpose of educating others, and I don't really have time for that at the moment. But I will write a brief explanation of the core concept:

This method's key to parsing expressions while keeping operator precedence is to use separate functions for each operation. By "function" I just mean a piece of code which can parse a single operation, this should be trivial because a single operation has a clearly defined structure (e.g the addition operation takes two arguments, which are separated by the + symbol).

The second key point is calling these functions in the order of desired precedence, you can have a single main function calling all the individual functions for parsing operations, basically you are blindly calling these functions to see if it work:
1. If it DOES work, then great! Mission successful!
2. If it DOES NOT work, then just simply move onto the next function and hope it works.

That's it really, that's the magic, that is what makes operator precedence tick... calling the operation functions in the desired order and doing it until one succeeds.

And to finish, what makes this approach "recursive" is that you will be calling the main parse function to parse the arguments for the operation, and this will create a cycle until everything is parsed :).

Here is an example that I parsed using this code:

TheDcoder@arch /v/t/tdop> node parse.js '-((1 + 2) / 3) * 4'
{
    "op": "Multiply",
    "args": [
        {
            "op": "Invert",
            "args": [
                {
                    "op": "Divide",
                    "args": [
                        {
                            "op": "Add",
                            "args": [
                                1,
                                2
                            ]
                        },
                        3
                    ]
                }
            ]
        },
        4
    ],
    "raw": "-((1 + 2) / 3) * 4"
}

The output is in JSON format and it is hard to visualize, so here is a tree diagram generated from the output:

7seAnjA.png

I used NodeJS to run the script, but you can try it in your browser directly! I made a quick and dirty web interface and put the code on GitHub since they offer a free hosting service. Have fun :)

--

Now I have to re-write the parser in C using this concept and hopefully it will be enough for the time being.

EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time)

DcodingTheWeb Forum - Follow for updates and Join for discussion

Link to comment
Share on other sites

  • 5 weeks later...
if (actual_count == count) goto skip_strip;


I’ve always felt that the “goto“ statement has gotten more criticism than it deserved.  It’s brutal simplicity has been its downfall...

Sure it can be successfully pressed into service in totally inappropriate ways, but so can a flat-head screwdriver, and we still have those around...

Code hard, but don’t hard code...

Link to comment
Share on other sites

Growing up without GoTo**, it confuses me. It seems to make the logic less linear and harder to follow... plus I always forget if the code just jumps to the target, or if it acts like a function and returns at some point. If I think about it long enough, I eventually realize that makes no sense, but it's confusing as heck. I'd rather use another function, a bool, or nested Ifs.

Funny... I opened a particularly stubborn jar of salsa with a flat head today :D

** Not entirely true, they're kind of used in VBA for error handling, but I hate them. I was taught to avoid them at all costs. Especially On Error GoTo 0, which "ignores" all errors

Disclaimer: I've never programmed in C

Edited by seadoggie01

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.

Spoiler

My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager

Link to comment
Share on other sites

11 hours ago, JockoDundee said:

I’ve always felt that the “goto“ statement has gotten more criticism than it deserved.

Agreed. It's easy to misuse and often tends to be by newer users, so a blanket "ban" was imposed on it in newer programming languages, so it has slipped into the taboo area now.

It is mostly because If statements can replace 99% of the use cases for GoTo, however there are still cases where GoTo is more elegant and sometimes even necessary to avoid redundant code.

9 hours ago, seadoggie01 said:

Growing up without GoTo**, it confuses me. It seems to make the logic less linear and harder to follow...

It does make the logic less linear, and you can certainly live without it :)

I got a good dose of GoTo when I started programming in batch script, which in many ways was my first proper venture into programming. So I am not afraid of it and know when to use it, i.e when it makes the code more elegant (e.x. by eliminating deeply nested if statements) or when there is a genuine need.

--

I might even consider adding back GoTo into ECI, it would be interesting to see the response :)

EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time)

DcodingTheWeb Forum - Follow for updates and Join for discussion

Link to comment
Share on other sites

Yep, there is a simplicity and complexity to GoTo, and I still use it now and then in VBA and the odd Batch file.

There has only ever been the odd rare moment when I have kind of missed it in AutoIt, but not really.

I have this recollection that GoTo may have existed in early AutoIt.

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Link to comment
Share on other sites

13 hours ago, TheSaint said:

Batch file

Wait, y'all still use them? Wasn't that what AutoIt replaced? :o 

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.

Spoiler

My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager

Link to comment
Share on other sites

@seadoggie01 Sometimes it is just more convenient to use a batch script due to the native integration with Windows :)

@Earthshine Bash for *nix is basically what Batch is for Windows. Also, I personally prefer Makefiles and CMake ;)

EasyCodeIt - A cross-platform AutoIt implementation - Fund the development! (GitHub will double your donations for a limited time)

DcodingTheWeb Forum - Follow for updates and Join for discussion

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...