Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

* [Python/Beam] Add F# quotation support — construction, pattern matching, and evaluation via `LeafExpressionConverter.EvaluateQuotation` (by @dbrattli)
* [JS/TS] Add F# quotation support — construction, pattern matching, and evaluation (by @OnurGumus)
* [All] Add support for `Guid.CreateVersion7()` and `Guid.CreateVersion7(DateTimeOffset)` (by @OnurGumus)
* [All] Add missing `Array`, `List`, and `Seq` random choice/shuffle/sample members and tests (by @ncave)
* [Dart/Rust] Add missing `System.Random` implementations and tests (by @ncave)
Expand Down
1 change: 1 addition & 0 deletions src/Fable.Compiler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

* [Python/Beam] Add F# quotation support — construction, pattern matching, and evaluation via `LeafExpressionConverter.EvaluateQuotation` (by @dbrattli)
* [JS/TS] Add F# quotation support — construction, pattern matching, and evaluation (by @OnurGumus)
* [All] Add support for `Guid.CreateVersion7()` and `Guid.CreateVersion7(DateTimeOffset)` (by @OnurGumus)
* [All] Add missing `Array`, `List`, and `Seq` random choice/shuffle/sample members and tests (by @ncave)
* [Dart/Rust] Add missing `System.Random` implementations and tests (by @ncave)
Expand Down
4 changes: 4 additions & 0 deletions src/Fable.Core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

* Add `Fable.Core.Quotations` namespace with `QuotExpr` and `QuotVar` types for .NET-side deserialization of Fable quotation ASTs

## 5.0.0-rc.1 - 2026-02-26

### Added
Expand Down
36 changes: 36 additions & 0 deletions src/Fable.Core/Fable.Core.Quotations.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Fable.Core.Quotations

/// Serializable variable in a quotation expression.
/// Matches the JSON shape produced by Fable's quotation runtime.
type QuotVar =
{
Name: string
Type: string
IsMutable: bool
}

/// Serializable quotation expression tree.
/// Designed for cross-platform JSON deserialization (e.g. via Thoth.Json)
/// of quotations constructed on Fable JS/TS clients.
///
/// JSON format: ["Tag", ...fields] arrays, e.g. ["Value", 42, "int32"]
Comment thread
MangelMaxime marked this conversation as resolved.
Resolved
[<RequireQualifiedAccess>]
type QuotExpr =
| Value of value: obj * typeName: string
| Var of var: QuotVar
| Lambda of var: QuotVar * body: QuotExpr
| Application of func: QuotExpr * arg: QuotExpr
| Let of var: QuotVar * value: QuotExpr * body: QuotExpr
| IfThenElse of guard: QuotExpr * thenExpr: QuotExpr * elseExpr: QuotExpr
| Call of instance: QuotExpr option * methodName: string * args: QuotExpr array
| Sequential of first: QuotExpr * second: QuotExpr
| NewTuple of elements: QuotExpr array
| TupleGet of tuple: QuotExpr * index: int
| NewUnion of typeName: string * tag: int * fields: QuotExpr array
| UnionTag of expr: QuotExpr
| UnionField of expr: QuotExpr * index: int
| NewRecord of fieldNames: string array * values: QuotExpr array
| FieldGet of expr: QuotExpr * fieldName: string
| FieldSet of expr: QuotExpr * fieldName: string * value: QuotExpr
| VarSet of target: QuotExpr * value: QuotExpr
| NewList of head: QuotExpr * tail: QuotExpr
1 change: 1 addition & 0 deletions src/Fable.Core/Fable.Core.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<Compile Include="Fable.Core.RustInterop.fs" />
<Compile Include="Fable.Core.BeamInterop.fs" />
<Compile Include="Fable.Core.Extensions.fs" />
<Compile Include="Fable.Core.Quotations.fs" />
<Content Include="CHANGELOG.md" />
</ItemGroup>
<ItemGroup>
Expand Down
10 changes: 6 additions & 4 deletions src/Fable.Transforms/Fable2Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3142,7 +3142,9 @@ but thanks to the optimisation done below we get
| Fable.Throw _
| Fable.Debugger -> iife com ctx expr

| Fable.Quote _ -> addErrorAndReturnNull com None "Quotations are not yet supported for JS/TS target"
| Fable.Quote(quotedExpr, _isTyped, _r) ->
let emitted = QuotationEmitter.emitQuotedExpr com quotedExpr
transformAsExpr com ctx emitted

let rec transformAsStatements (com: IBabelCompiler) ctx returnStrategy (expr: Fable.Expr) : Statement array =
match expr with
Expand Down Expand Up @@ -3311,9 +3313,9 @@ but thanks to the optimisation done below we get
)
|]

| Fable.Quote _ ->
addError com [] None "Quotations are not yet supported for JS/TS target"
[||]
| Fable.Quote(quotedExpr, _isTyped, _r) ->
let emitted = QuotationEmitter.emitQuotedExpr com quotedExpr
[| transformAsExpr com ctx emitted |> resolveExpr Fable.Any returnStrategy |]

let transformFunction com ctx name (args: Fable.Ident list) (body: Fable.Expr) : Parameter array * BlockStatement =
let tailcallChance =
Expand Down
5 changes: 4 additions & 1 deletion src/Fable.Transforms/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,8 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
// Erased operators.
// KeyValuePair is already compiled as a tuple
| ("KeyValuePattern" | "Identity" | "Box" | "Unbox" | "ToEnum"), [ arg ] -> TypeCast(arg, t) |> Some
// Quotation splice: %expr inside a quotation. The arg is already a QuotExpr at runtime.
| ("SpliceExpression" | "SpliceUntypedExpression"), [ arg ] -> Some arg
// Cast to unit to make sure nothing is returned when wrapped in a lambda, see #1360
| "Ignore", _ -> TypeCast(args.Head, Unit) |> Some
// Number and String conversions
Expand Down Expand Up @@ -4344,7 +4346,8 @@ let tryCall (com: ICompiler) (ctx: Context) r t (info: CallInfo) (thisArg: Expr
getTypeName com ctx loc exprType |> StringConstant |> makeValue r |> Some
| c -> Helper.LibCall(com, "Reflection", "name", t, [ c ], ?loc = r) |> Some
| _ -> None
| _ -> None
// F# Quotations
| typeName -> Quotations.tryQuotationCall "quotation" com ctx r t info thisArg args typeName

let tryBaseConstructor com ctx (ent: EntityRef) (argTypes: Lazy<Type list>) genArgs args =
match ent.FullName with
Expand Down
1 change: 1 addition & 0 deletions src/fable-library-ts/Fable.Library.TypeScript.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
<TypeScriptCompile Include="Timer.ts" />
<TypeScriptCompile Include="TimeSpan.ts" />
<Content Include="tsconfig.json" />
<TypeScriptCompile Include="quotation.ts" />
<TypeScriptCompile Include="Types.ts" />
<TypeScriptCompile Include="Unicode.13.0.0.ts" />
<TypeScriptCompile Include="Uri.ts" />
Expand Down
Loading
Loading