-
-
Save fdcastel/5a88332d61572947deb3 to your computer and use it in GitHub Desktop.
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 | |
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:
| "+" | "-" | "*" | "/" | "%"
| "<<<" | ">>>" | "&&&" | "|||" | "^^^"
| "~~~" | "~-" | "&&" | "||" ->
let expr = applyOp com info info.args info.methodName
match maskFor info.returnType with
| Some mask ->
sprintf "$0 & %s" mask
|> Fable.Replacements.Util.emit info <| expr
|> Some
| None -> expr
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.
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.
First try. The key idea is to look at ApplyInfo.returnType and apply the & mask.