Skip to content
Closed
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
6 changes: 5 additions & 1 deletion backend/experiments/CanvasHack/Main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ let seedCanvasV2 (canvasName : string) =
do! C.saveTLIDs canvasID oplists

print
$"Success saved canvas - endpoints available at {host} (bwdserver) and {experimentalHost} (bwd-danger-server)"
$"Success saved canvas - endpoints available
at {host} (bwdserver)
and {experimentalHost} (bwd-danger-server)"
}

[<EntryPoint>]
Expand All @@ -112,6 +114,8 @@ let main (args : string []) =
`canvas-hack {CommandNames.export}' to save dark-editor to disk"

| [| CommandNames.import; canvasName |] ->
print $"Loading canvas {canvasName} from disk"

let config =
parseYamlExn<CanvasHackConfig.JustVersion>
$"{baseDir}/{canvasName}/config.yml"
Expand Down
121 changes: 121 additions & 0 deletions backend/experiments/StdLibExperimental/Libs/DarkTypes.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
module StdLibExperimental.Libs.DarkTypes

open System.Threading.Tasks
open FSharp.Control.Tasks

open Prelude
open Tablecloth

module Errors = LibExecution.Errors

open LibExecution.RuntimeTypes
module PT = LibExecution.ProgramTypes

open LibExecution.StdLib.Shortcuts

let fn = FQFnName.stdlibFnName

let incorrectArgs = Errors.incorrectArgs


// maybe these types/fns are only available to DarkEditor - giving access to users at this point
// is probably more confusing than it's worth

// TODO: separately, write a fuzzer that will randomly generate these types

// TODO: a bunch of stuff is missing
let types : List<BuiltInType> =
[ { name = typ "ProgramTypes" "TypeReference" 0
typeParams = []
definition =
CustomType.Enum(
{ name = "TInt"; fields = [] },
[ { name = "TString"; fields = [] }
{ name = "TCustomType"; fields = [] } ]
)
description = "Represents a PT.TypeReference"
deprecated = NotDeprecated }


{ name = typ "ProgramTypes" "Expr" 0
typeParams = []
definition =
CustomType.Enum(
{ name = "EUnit"; fields = [] },
[ { name = "EBool"; fields = [ { typ = TBool; label = None } ] }

{ name = "EString"
// TODO: support interpolation
fields = [ { typ = TString; label = None } ] }

{ name = "EInt"; fields = [ { typ = TInt; label = None } ] }

{ name = "ELet"
fields =
[ { typ = TString; label = Some "binding" }
{ typ = TVariable "TODO: ANY"; label = Some "value" }
{ typ = TVariable "TODO: ANY"; label = Some "body" } ] }

{ name = "EVariable"; fields = [ { typ = TString; label = None } ] } ]
)
description = "Represents a PT.Expr"
deprecated = NotDeprecated }


(*
module UserFunction =
type Parameter =
{ id : id
name : string
typ : TypeReference
description : string }

type T =
{ tlid : tlid
name : string
typeParams : List<string>
parameters : List<Parameter>
returnType : TypeReference
description : string
infix : bool
body : Expr }
*)


// { name = tp "ProgramTypes" "UserFunctionParameter" 0
// typeParams = [ ]
// definition =
// PT.CustomType.Record(
// { id = gid(); name = "name"; typ : TypeReference }
// // name: string
// { id = 1UL; name = "name"; typ = PT.TString; label = None },
// // typ : TypeReference
// )
// description = "Represents a PT.UserFunction.Parameter" }

// { name = tp "ProgramTypes" "UserFunction" 0
// typeParams = [ ]
// definition =
// PT.CustomType.Enum() }
]




// // Dark fn that goes from SynTree -> PT.Expr
// // later: Dark fn that goes from SynTree -> PT.TypeReference
// // later: Dark fn that goes from SynTree -> PT.Function

// ... I'm not sure how we could

// //type StdlibTypeName = { module_ : string; typ : string; version : int }

// module Parser =

// // lol what if this were a Dark fn?
// let toExpr (node : SynTree.Node) : PT.Expr =
// let (typ, text, children) = Node node

// match typ, text, children with
// | ("Int", i, []) -> ProgramTypes.Expr(typeName, "EConstructor", [int i])
// | ("String", s, []) -> ProgramTypes.Expr.EString(s)
49 changes: 49 additions & 0 deletions backend/experiments/StdLibExperimental/Libs/TreeSitter.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module StdLibExperimental.Libs.TreeSitter

open System.Threading.Tasks
open FSharp.Control.Tasks

open Prelude
open Tablecloth
open LibExecution.RuntimeTypes

module Errors = LibExecution.Errors

let fn = FQFnName.stdlibFnName

let incorrectArgs = Errors.incorrectArgs
module PT = LibExecution.ProgramTypes



// Ideally, all of this would be in Dark code
module SyntaxTree =
// This is what's sent to us from the frontend
type TreeSitterNode =
{ typ : string
text : string
fieldName : Option<string>
children : List<TreeSitterNode> }

type TreeSitterTreeTpl = Node of (string * string * List<TreeSitterTreeTpl>)

let rec toTuple (node : TreeSitterNode) : TreeSitterTreeTpl =
Node(node.typ, node.text, (node.children |> List.map toTuple))

// Ideally this would return the _Dark_ PT.Expr, not the F# one
let toExpr (node : TreeSitterNode) : PT.Expr =
let t = toTuple node

match t with
| Node ("string", s, []) -> PT.Expr.EString(gid (), [ PT.StringText s ])
| Node ("int", s, []) -> PT.Expr.EInt(gid (), int s)
//| Node ("float", s, []) -> PT.Expr.EFloat (gid(), float s)
//| Node ("bool", s, []) -> PT.Expr.EBool (gid(), bool s)
| _ -> Exception.raiseInternal $"Couldn't parse expression" [ "node", node ]


//type toFunction (node: TreeSitterNode) : PT.UserFunction.T



let fns : List<BuiltInFn> = []
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
</PropertyGroup>
<ItemGroup>
<None Include="paket.references" />
<Compile Include="Libs/DarkTypes.fs" />
<Compile Include="Libs/Experiments.fs" />
<Compile Include="Libs/TreeSitter.fs" />
<Compile Include="StdLib.fs" />
</ItemGroup>
<ItemGroup>
Expand Down
23 changes: 14 additions & 9 deletions backend/src/StdLibExecution/Libs/Json.fs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ let parse
match Map.tryFind "Just" objFields with
| Some v -> DOption(Some(convert oType v))
| None -> DOption None
| TOption oType, v -> convert oType j |> Some |> DOption

| TResult (okType, errType), JsonValueKind.Object ->
let objFields =
Expand Down Expand Up @@ -410,16 +411,20 @@ let parse

| CustomType.Record (firstField, additionalFields) ->
let fieldDefs = firstField :: additionalFields
let enumerated = j.EnumerateObject() |> Seq.toList

let dvalMap =
j.EnumerateObject()
|> Seq.map (fun jp ->
let correspondingType =
fieldDefs
// TODO: handle case where field isn't found
|> List.find (fun def -> def.name = jp.Name)
|> fun def -> def.typ
let converted = convert correspondingType jp.Value
(jp.Name, converted))
fieldDefs
|> List.map (fun def ->
let correspondingValue =
enumerated
// TODO: handle case where value isn't found for
// and maybe, if it's an Option<>al thing, don't complain
|> List.find (fun v -> v.Name = def.name)
|> fun field -> field.Value

let converted = convert def.typ correspondingValue
(def.name, converted))
|> Map.ofSeq

// TYPESCLEANUP add typename
Expand Down
43 changes: 43 additions & 0 deletions backend/src/Wasm/LibWASM.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// Standard libraries for Files, Directories, and other OS/file system stuff
module Wasm.LibWASM

open System
open System.Threading.Tasks
open FSharp.Control.Tasks

open Prelude
open Tablecloth

open LibExecution.RuntimeTypes

open LibExecution.StdLib.Shortcuts

module PT = LibExecution.ProgramTypes
module Exe = LibExecution.Execution

let types : List<BuiltInType> = []

let fns : List<BuiltInFn> =
[ { name = fn "WASM" "callJSFunction" 0
typeParams = []
parameters =
[ Param.make "functionName" TString ""
Param.make "serializedArgs" TString "" ]
returnType = TResult(TUnit, TString)
description = "TODO"
fn =
(function
| _, _, [ DString functionName; DString args ] ->
// TODO: I'm not really sure what to do about `args`. Maybe it should be an object to serialize?
// or have an additiol function like callJSFunctionTyped<T>(functionName: string, args: T)
uply {
try
WasmHelpers.postMessage functionName args
return DResult(Ok DUnit)
with
| e -> return DResult(Error(DString($"Error: {e.Message}")))
}
| _ -> incorrectArgs ())
sqlSpec = NotQueryable
previewable = Impure
deprecated = NotDeprecated } ]
1 change: 1 addition & 0 deletions backend/src/Wasm/Wasm.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="WasmHelpers.fs" />
<Compile Include="LibWASM.fs" />
<Compile Include="Init.fs" />
<Compile Include="DarkEditor.fs" />
<Compile Include="Program.fs" />
Expand Down
2 changes: 2 additions & 0 deletions backend/static/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This directory contains static files that are served by a Dark backend for a webpage.
The `dark_wasm` and `tree-sitter` directories should ideally be hosted in a CDN, long-term.
7 changes: 7 additions & 0 deletions backend/static/tree-sitter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Tree-Sitter WebAssembly/JS Bindings

These files support parsing Darklang code in the browser.

The `tree-sitter.js` and `tree-sitter.wasm` files were pulled from the releases of `tree-sitter`. The .js file is a wrapper around the .wasm file, and the .wasm file is the actual parser which calls upon language-specific parsers (like `tree-sitter-darklang.wasm`) after loading them.

The actual parsing of Darklang code is done by the `tree-sitter-darklang.wasm` file, which is a compiled version of the `grammar.js` file in the `parser/tree-sitter-darklang` directory. See the README.md in that directory for more information.
Binary file not shown.
1 change: 1 addition & 0 deletions backend/static/tree-sitter/tree-sitter.js

Large diffs are not rendered by default.

Binary file added backend/static/tree-sitter/tree-sitter.wasm
Binary file not shown.
5 changes: 5 additions & 0 deletions backend/testfiles/execution/json.tests
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ Json.serialize<Option<Int>> Nothing = Ok "{\"Nothing\":null}"
Json.parse<Option<Int>> "{\"Nothing\":null}" = Ok Nothing
Json.serialize<Option<Int>> (Just 1) = Ok "{\"Just\":1}"
Json.parse<Option<Int>> "{\"Just\":1}" = Ok (Just 1)
Json.parse<Option<Int>> "null" = Ok Nothing
Json.parse<Option<Int>> "1" = Ok (Just 1)
// TODO: more...


Expand Down Expand Up @@ -304,6 +306,9 @@ Json.parse<Person> """{ "Name": "Bob", "Age": 42 }""" = Ok (Person { Name = "Bob
Json.serialize<People> (People { GroupName = "Two Georges"; People = [Person { Name = "George A"; Age = 27 }; Person { Name = "George B"; Age = 42 }] }) = Ok """{"GroupName":"Two Georges","People":[{"Age":27,"Name":"George A"},{"Age":42,"Name":"George B"}]}"""
Json.parse<People> """{"GroupName":"Two Georges","People":[{"Age":27,"Name":"George A"},{"Age":42,"Name":"George B"}]}""" = Ok (People { GroupName = "Two Georges"; People = [Person { Name = "George A"; Age = 27 }; Person { Name = "George B"; Age = 42 }] })

// can parse even if the JSON has _extra_ fields
Json.parse<Person> """{ "Name": "Bob", "Age": 42, "Height": "6 ft" }""" = Ok (Person { Name = "Bob"; Age = 42 })

(let personMaybe = Json.parse<Person> """{ "Name": "Bob", "Age": 42 }"""
match personMaybe with
| Ok person -> person.Age
Expand Down
2 changes: 2 additions & 0 deletions canvases/dark-tree-sitter-demo/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
version: 2
main: main
Loading