Last active
March 30, 2016 07:27
-
-
Save fdcastel/5a88332d61572947deb3 to your computer and use it in GitHub Desktop.
Fable Plugin to keep integer arithmetic in JavaScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Fable.Plugins | |
#r "../../build/fable/bin/Fable.exe" | |
open Fable.AST | |
open Fable.AST.Fable | |
open Fable.FSharp2Fable | |
type RandomPlugin() = | |
interface IReplacePlugin with | |
member x.TryReplace com (info: Fable.ApplyInfo) = | |
match info.ownerFullName with | |
| "Microsoft.FSharp.Core.ExtraTopLevelOperators" | |
| "Microsoft.FSharp.Core.Operators" -> | |
let patternFor t = | |
match t with | |
| PrimitiveType (Number kind) -> | |
match kind with | |
| Int8 -> Some "($0 + 0x80 & 0xFF) - 0x80" | |
| UInt8 -> Some "$0 & 0xFF" | |
| Int16 -> Some "($0 + 0x8000 & 0xFFFF) - 0x8000" | |
| UInt16 -> Some "$0 & 0xFFFF" | |
| Int32 -> Some "($0 + 0x80000000 >>> 0) - 0x80000000" | |
| UInt32 -> Some "$0 >>> 0" | |
| _ -> None | |
| _ -> None | |
let applyMask t args = | |
match patternFor t with | |
| Some pattern -> | |
pattern | |
|> Fable.Replacements.Util.emit info <| args | |
|> Some | |
| _ -> None | |
match info.methodName with | |
| "sbyte" | |
| "byte" | |
| "int8" | |
| "uint8" | |
| "int16" | |
| "uint16" | |
| "int" | |
| "int32" | |
| "uint" | |
| "uint32" | |
| "~~~" | |
| "~-" | |
| "~+" -> | |
if info.args.Length <> 1 then | |
failwithf "Unexpected arg count for '%s'" info.methodName | |
applyMask info.returnType info.args | |
| "+" | |
| "-" | |
| "*" | |
| "/" | |
| "%" | |
| "<<<" | |
| ">>>" | |
| "&&&" | |
| "|||" | |
| "^^^" -> | |
if info.args.Length <> 2 then | |
failwithf "Unexpected arg count for %s" info.methodName | |
match Fable.Replacements.Util.applyOp com info info.args info.methodName with | |
| Some expr -> applyMask info.returnType [expr] | |
| _ -> None | |
| _ -> None | |
| _ -> None | |
Great tips! Working on it.
Also, I did somehow manage to completely miss the part you said about Babel managing the precedence order of operators. I see now you clearly state it in docs/plugins.md.
I know... I know... "More sleep, less coffee!" :)
Second version. Now with Alfonso's suggestions and handling signed types.
Third version. Fix a problem with signed int32.
& 0xFFFFFFFF
preserves the signal (hence, does nothing :) ). Now uses >>> 0
to convert to unsigned.
Source: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
Fourth version. Adds support to Microsoft.FSharp.Core.ExtraTopLevelOperators.int8/uint8.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The plugin looks fine :) I agree it should be easier to apply the mask after the operations instead of creating arrays for single elements. If you want to restrict arithmetic operations to the numeric types in the list it should be fine, but if you want to still support custom operators for types or such, you can add a wrapper to
Fable.Replacements.Util.applyOp
. Something like this:Note that in this case
maskFor
would return None instead of throwing an exception. Also, you don't need braces in the template, because Babel will build a syntax tree from it preserving the operation order.