File fibonacci.aec
:
syntax GAS ;We are, of course, targeting GNU Assembler here, rather than FlatAssembler, to be compatible with GCC. verboseMode on ;Tells ArithmeticExpressionCompiler to output more comments into the assembly code it produces (fibonacci.s). AsmStart .global fibonacci #We need to tell the linker that "fibonacci" is the name of a function, and not some random label. fibonacci: AsmEnd If not(mod(n,1)=0) ;If 'n' is not a integer, round it to the nearest integer. n := n + ( mod(n,1) > 1/2 ? 1-mod(n,1) : (-mod(n,1))) EndIf If n<2 ;The 1st Fibonacci number is 1, and the 0th one is 0. returnValue := n > -1 ? n : 0/0 ;0/0 is NaN (indicating error), because negative Fibonacci numbers don't exist AsmStart .intel_syntax noprefix ret #Far return (to the other section, that is, to the C++ program). The way to do a same-section return depends on whether we are in a 32-bit Assembler or a 64-bit Assembler, while the far return is the same (at least in the "intel_syntax mode"). .att_syntax AsmEnd ElseIf not(memoisation[n]=0) ;Has that Fibonacci number already been calculated? returnValue:=memoisation[n] AsmStart .intel_syntax noprefix ret .att_syntax AsmEnd EndIf ;And now comes the part where we are tricking ArithmeticExpressionCompiler into supporting recursion... topOfTheStackWithLocalVariables := topOfTheStackWithLocalVariables + 2 ;Allocate space on the stack for 2 local variables ('n', the argument passed to the function, and the temporary result). temporaryResult := 0 ;The sum of fib(n-1) and fib(n-2) will be stored here, first 0 then fib(n-1) then fib(n-1)+fib(n-2). stackWithLocalVariables[topOfTheStackWithLocalVariables - 1] := temporaryResult ;Save the local variables onto the stack, for the recursive calls will corrupt them (as they are actually global variables, because ArithmeticExpressionCompiler doesn't support local ones). stackWithLocalVariables[topOfTheStackWithLocalVariables] := n n:=n-1 AsmStart .intel_syntax noprefix call fibonacci .att_syntax AsmEnd temporaryResult := stackWithLocalVariables[topOfTheStackWithLocalVariables - 1] temporaryResult := temporaryResult + returnValue ;"returnValue" is supposed to contain fib(n-1). ;And we repeat what we did the last time, now with n-2 instead of n-1... stackWithLocalVariables[topOfTheStackWithLocalVariables - 1] := temporaryResult n := stackWithLocalVariables[topOfTheStackWithLocalVariables] n := n - 2 AsmStart .intel_syntax noprefix call fibonacci .att_syntax AsmEnd temporaryResult := stackWithLocalVariables[topOfTheStackWithLocalVariables - 1] temporaryResult := temporaryResult + returnValue stackWithLocalVariables[topOfTheStackWithLocalVariables - 1] := temporaryResult n := stackWithLocalVariables [topOfTheStackWithLocalVariables] returnValue := temporaryResult memoisation[n] := returnValue topOfTheStackWithLocalVariables := topOfTheStackWithLocalVariables - 2 AsmStart .intel_syntax noprefix ret .att_syntax AsmEnd
File let_gcc_setup_gas.cpp
:
/*The C++ wrapper around "fibonacci.aec". Compile this as: node aec fibonacci.aec #Assuming you've downloaded aec.js from the releases. g++ -o fibonacci let_gcc_setup_gas.cpp fibonacci.s */ #include <algorithm> //The "fill" function. #include <cmath> //The "isnan" function. #include <iostream> #ifdef _WIN32 #include <cstdlib> //system("PAUSE"); #endif extern "C" { // To the GNU Linker (which comes with Linux and is used by GCC), // AEC language is a dialect of C, and AEC is a C compiler. float n, stackWithLocalVariables[1024], memoisation[1024], topOfTheStackWithLocalVariables, temporaryResult, returnValue, result; // When using GCC, there is no need to declare variables in the same // file as you will be using them, or even in the same language. So, // no need to look up the hard-to-find information about how to // declare variables in GNU Assembler while targeting 64-bit Linux. // GCC and GNU Linker will take care of that. void fibonacci(); // The ".global fibonacci" from inline assembly in // "fibonacci.aec" (you need to declare it, so that the C++ // compiler doesn't complain: C++ isn't like JavaScript or AEC // in that regard, C++ tries to catch errors such as a // mistyped function or variable name in compile-time). } int main() { std::cout << "Enter n:" << std::endl; std::cin >> n; topOfTheStackWithLocalVariables = -1; if (n >= 2) std::fill(&memoisation[0], &memoisation[int(n)], 0); // This is way more easily done in C++ than in AEC here, // because the AEC subprogram doesn't know if it's being // called by C++ or recursively by itself. fibonacci(); if (std::isnan(returnValue)) { std::cerr << "The AEC program returned an invalid decimal number." << std::endl; return 1; } std::cout << "The " << n << ((int(n) % 10 == 3) ? ("rd") : (int(n) % 10 == 2) ? ("nd") : (int(n) % 10 == 1) ? ("st") : "th") << " Fibonacci number is " << returnValue << "." << std::endl; #ifdef _WIN32 std::system("PAUSE"); #endif return 0; }
The executable files for Windows and Linux are available here, and the assembly code that my compiler for AEC generates is available here.
So, what do you think about it?