firefly-compiler 0.4.5 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/.hintrc +4 -4
  2. package/README.md +1 -1
  3. package/core/HttpClient.ff +1 -1
  4. package/lsp/LanguageServer.ff +32 -7
  5. package/lsp/stderr.txt +1 -0
  6. package/lsp/stdin.txt +11 -0
  7. package/lsp/stdout.txt +41 -0
  8. package/lux/Main.ff +56 -2
  9. package/meetup/AutoCompletion.ff +6 -0
  10. package/output/js/ff/compiler/Builder.mjs +444 -444
  11. package/output/js/ff/compiler/Compiler.mjs +416 -416
  12. package/output/js/ff/compiler/Dependencies.mjs +389 -389
  13. package/output/js/ff/compiler/Deriver.mjs +1170 -1170
  14. package/output/js/ff/compiler/Dictionaries.mjs +1309 -1309
  15. package/output/js/ff/compiler/Environment.mjs +1015 -1015
  16. package/output/js/ff/compiler/Inference.mjs +4268 -4268
  17. package/output/js/ff/compiler/JsEmitter.mjs +5391 -5391
  18. package/output/js/ff/compiler/JsImporter.mjs +266 -266
  19. package/output/js/ff/compiler/LspHook.mjs +793 -793
  20. package/output/js/ff/compiler/Main.mjs +1675 -1675
  21. package/output/js/ff/compiler/Parser.mjs +4008 -4008
  22. package/output/js/ff/compiler/Patterns.mjs +927 -927
  23. package/output/js/ff/compiler/Resolver.mjs +2307 -2307
  24. package/output/js/ff/compiler/Substitution.mjs +1150 -1150
  25. package/output/js/ff/compiler/Syntax.mjs +12434 -12434
  26. package/output/js/ff/compiler/Token.mjs +3096 -3096
  27. package/output/js/ff/compiler/Tokenizer.mjs +593 -593
  28. package/output/js/ff/compiler/Unification.mjs +1752 -1752
  29. package/output/js/ff/compiler/Wildcards.mjs +608 -608
  30. package/output/js/ff/compiler/Workspace.mjs +687 -687
  31. package/output/js/ff/core/Any.mjs +143 -143
  32. package/output/js/ff/core/Array.mjs +547 -547
  33. package/output/js/ff/core/AssetSystem.mjs +274 -274
  34. package/output/js/ff/core/Atomic.mjs +154 -154
  35. package/output/js/ff/core/Bool.mjs +152 -152
  36. package/output/js/ff/core/Box.mjs +112 -112
  37. package/output/js/ff/core/BrowserSystem.mjs +126 -126
  38. package/output/js/ff/core/Buffer.mjs +395 -395
  39. package/output/js/ff/core/BuildSystem.mjs +296 -296
  40. package/output/js/ff/core/Channel.mjs +189 -189
  41. package/output/js/ff/core/Char.mjs +149 -149
  42. package/output/js/ff/core/Core.mjs +300 -300
  43. package/output/js/ff/core/Duration.mjs +116 -116
  44. package/output/js/ff/core/Equal.mjs +179 -179
  45. package/output/js/ff/core/Error.mjs +142 -142
  46. package/output/js/ff/core/FileHandle.mjs +150 -150
  47. package/output/js/ff/core/Float.mjs +225 -225
  48. package/output/js/ff/core/HttpClient.mjs +190 -190
  49. package/output/js/ff/core/Instant.mjs +109 -109
  50. package/output/js/ff/core/Int.mjs +265 -265
  51. package/output/js/ff/core/IntMap.mjs +280 -280
  52. package/output/js/ff/core/JsSystem.mjs +238 -238
  53. package/output/js/ff/core/JsValue.mjs +708 -708
  54. package/output/js/ff/core/List.mjs +2334 -2334
  55. package/output/js/ff/core/Lock.mjs +229 -229
  56. package/output/js/ff/core/Log.mjs +163 -163
  57. package/output/js/ff/core/Map.mjs +362 -362
  58. package/output/js/ff/core/NodeSystem.mjs +294 -294
  59. package/output/js/ff/core/Nothing.mjs +104 -104
  60. package/output/js/ff/core/Option.mjs +1015 -1015
  61. package/output/js/ff/core/Ordering.mjs +730 -730
  62. package/output/js/ff/core/Pair.mjs +331 -331
  63. package/output/js/ff/core/Path.mjs +545 -545
  64. package/output/js/ff/core/RbMap.mjs +1940 -1940
  65. package/output/js/ff/core/Serializable.mjs +428 -428
  66. package/output/js/ff/core/Set.mjs +254 -254
  67. package/output/js/ff/core/Show.mjs +205 -205
  68. package/output/js/ff/core/SourceLocation.mjs +229 -229
  69. package/output/js/ff/core/Stack.mjs +529 -529
  70. package/output/js/ff/core/Stream.mjs +1304 -1304
  71. package/output/js/ff/core/String.mjs +365 -365
  72. package/output/js/ff/core/StringMap.mjs +280 -280
  73. package/output/js/ff/core/Task.mjs +320 -320
  74. package/output/js/ff/core/Try.mjs +507 -507
  75. package/output/js/ff/core/Unit.mjs +151 -151
  76. package/package.json +29 -29
  77. package/vscode/package-lock.json +5 -5
  78. package/vscode/package.json +1 -1
package/.hintrc CHANGED
@@ -1,5 +1,5 @@
1
- {
2
- "extends": [
3
- "development"
4
- ]
1
+ {
2
+ "extends": [
3
+ "development"
4
+ ]
5
5
  }
package/README.md CHANGED
@@ -13,7 +13,7 @@ A straightforward language for full-stack development.
13
13
 
14
14
  ## Writing your first webapp
15
15
 
16
- Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang) (requires the upcoming [VSCode 1.82](https://code.visualstudio.com/insiders/)).
16
+ Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang).
17
17
 
18
18
  Create a `WebApp.ff` file.
19
19
 
@@ -27,7 +27,7 @@ extend self: HttpClient {
27
27
  try {
28
28
  const options = {headers: {}, signal: $task.controller.signal}
29
29
  options.method = method_
30
- ff_core_List.List_each(headers_, pair => {options.headers[pair.key_] = pair.value_})
30
+ ff_core_List.List_each(headers_, pair => {options.headers[pair.first_] = pair.second_})
31
31
  if(body_.value_) options.body = body_.value_
32
32
  if(redirect_.RedirectError) options.redirect = "error"
33
33
  else if(redirect_.RedirectManual) options.redirect = "manual"
@@ -17,17 +17,26 @@ capability RequestMessage(
17
17
  parameters: Map[String, JsValue]
18
18
  )
19
19
 
20
+ logMessages = False
20
21
 
21
22
  main(system: NodeSystem) {
23
+ let logDirectory = if(logMessages) {
24
+ let directory = system.path(".").slash("messages")
25
+ if(directory.isDirectory()) {directory.delete()}
26
+ directory.createDirectory()
27
+ directory
28
+ }
29
+
22
30
  let fireflyPath = system.path(".")
23
31
  let handler = Handler(fireflyPath, None, Map.empty(), [].toSet(), Map.empty(), Map.empty())
24
32
  try {
25
33
  mutable input = system.readStream()
26
- let responseChannel: Channel[JsValue] = system.mainTask().channel()
34
+ let responseChannel: Channel[Pair[JsValue, String]] = system.mainTask().channel()
27
35
  system.mainTask().spawn {_ =>
28
36
  while {True} {
29
37
  let body = responseChannel.read()
30
- let json = body.toJson(Some(" ")).toBuffer()
38
+ let json = body.first.toJson(Some(" ")).toBuffer()
39
+ logDirectory.each {logMessage(_, "response", body.first, body.second)}
31
40
  system.writeText("Content-Length: " + json.size() + "\r\n\r\n")
32
41
  system.writeBuffer(json)
33
42
  }
@@ -38,13 +47,14 @@ main(system: NodeSystem) {
38
47
  input = requestPair.second
39
48
  let request = requestPair.first
40
49
  let message = parseRequestMessage(request.object)
50
+ logDirectory.each {logMessage(_, "request", request.object, message.method)}
41
51
  spawn(message.id) {_ =>
42
52
  if(message.method == "$/cancelRequest") {
43
53
  abort(Some(parseMessageId(message.parameters.grab("id"))))
44
54
  }
45
55
  try {
46
56
  handleRequestMessage(system, handler, message).each {body =>
47
- responseChannel.write(body)
57
+ responseChannel.write(Pair(body, message.method))
48
58
  }
49
59
  } catchAny {error =>
50
60
  let problem = if(error.name() == "AbortError") {
@@ -55,7 +65,8 @@ main(system: NodeSystem) {
55
65
  Error(-32603, "Internal error")
56
66
  }
57
67
  message.id.each {id =>
58
- responseChannel.write(makeResponseMessage(system.js(), id, problem))
68
+ let body = makeResponseMessage(system.js(), id, problem)
69
+ responseChannel.write(Pair(body, message.method))
59
70
  }
60
71
  } grab()
61
72
  }
@@ -74,17 +85,17 @@ taskGroup[K: Order, R](
74
85
  mutable tasks = [].toMap()
75
86
  function spawn(key: K, body: Task => Unit): Task {
76
87
  let body2 = {task =>
77
- lock.do {tasks = tasks.add(key, task)}
88
+ lock.do(reentrant = True) {tasks = tasks.add(key, task)}
78
89
  try {
79
90
  body(task)
80
91
  } finally {
81
- lock.do {tasks = tasks.remove(key)}
92
+ lock.do(reentrant = True) {tasks = tasks.remove(key)}
82
93
  } grab()
83
94
  }
84
95
  parentTask.spawn(body2)
85
96
  }
86
97
  function abort(key: K): Unit {
87
- lock.do {tasks.get(key).each {_.abort()}}
98
+ lock.do(reentrant = True) {tasks.get(key).each {_.abort()}}
88
99
  }
89
100
  groupBody(spawn, abort)
90
101
  }
@@ -227,3 +238,17 @@ makeResponseMessage(js: JsSystem, id: MessageId, result: ResultOrError): JsValue
227
238
  }
228
239
  o
229
240
  }
241
+
242
+ logMessage(directory: Path, messageType: String, body: JsValue, method: String) {
243
+ let id = if(method == "$/cancelRequest") {
244
+ Some(body.get("params").get("id").grabInt())
245
+ } else {
246
+ body.getOwn("id").map {_.grabInt()}
247
+ }
248
+ let number = directory.entries().toList().size()
249
+ let idName = id.map {_ + ""}.else {"_"}
250
+ let methodName = method.replace("$", "notification").replace("/", "_")
251
+ let fileName = number + "-id-" + idName + "-" + messageType + "-" + methodName + ".json"
252
+ let path = directory.slash(fileName)
253
+ path.writeText(body.toJson(Some(" ")))
254
+ }
package/lsp/stderr.txt ADDED
@@ -0,0 +1 @@
1
+ End of input while parsing request headers
package/lsp/stdin.txt ADDED
@@ -0,0 +1,11 @@
1
+ Content-Length: 5836
2
+
3
+ {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":43727,"clientInfo":{"name":"Visual Studio Code","version":"1.76.2"},"locale":"en","rootPath":"/Users/ahnfelt/Downloads/firefly-boot","rootUri":"file:///Users/ahnfelt/Downloads/firefly-boot","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional","normalizesLineEndings":true,"changeAnnotationSupport":{"groupsOnLabel":true}},"configuration":true,"didChangeWatchedFiles":{"dynamicRegistration":true,"relativePatternSupport":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]},"resolveSupport":{"properties":["location.range"]}},"codeLens":{"refreshSupport":true},"executeCommand":{"dynamicRegistration":true},"didChangeConfiguration":{"dynamicRegistration":true},"workspaceFolders":true,"semanticTokens":{"refreshSupport":true},"fileOperations":{"dynamicRegistration":true,"didCreate":true,"didRename":true,"didDelete":true,"willCreate":true,"willRename":true,"willDelete":true},"inlineValue":{"refreshSupport":true},"inlayHint":{"refreshSupport":true},"diagnostics":{"refreshSupport":true}},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"codeDescriptionSupport":true,"dataSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true,"resolveSupport":{"properties":["documentation","detail","additionalTextEdits"]},"insertTextModeSupport":{"valueSet":[1,2]},"labelDetailsSupport":true},"insertTextMode":2,"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]},"completionList":{"itemDefaults":["commitCharacters","editRange","insertTextFormat","insertTextMode"]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true},"activeParameterSupport":true},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]},"labelSupport":true},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"disabledSupport":true,"dataSupport":true,"resolveSupport":{"properties":["edit"]},"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"honorsChangeAnnotations":false},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true,"prepareSupportDefaultBehavior":1,"honorsChangeAnnotations":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true,"foldingRangeKind":{"valueSet":["comment","imports","region"]},"foldingRange":{"collapsedText":false}},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"callHierarchy":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["namespace","type","class","enum","interface","struct","typeParameter","parameter","variable","property","enumMember","event","function","method","macro","keyword","modifier","comment","string","number","regexp","operator","decorator"],"tokenModifiers":["declaration","definition","readonly","static","deprecated","abstract","async","modification","documentation","defaultLibrary"],"formats":["relative"],"requests":{"range":true,"full":{"delta":true}},"multilineTokenSupport":false,"overlappingTokenSupport":false,"serverCancelSupport":true,"augmentsSyntaxTokens":true},"linkedEditingRange":{"dynamicRegistration":true},"typeHierarchy":{"dynamicRegistration":true},"inlineValue":{"dynamicRegistration":true},"inlayHint":{"dynamicRegistration":true,"resolveSupport":{"properties":["tooltip","textEdits","label.tooltip","label.location","label.command"]}},"diagnostic":{"dynamicRegistration":true,"relatedDocumentSupport":false}},"window":{"showMessage":{"messageActionItem":{"additionalPropertiesSupport":true}},"showDocument":{"support":true},"workDoneProgress":true},"general":{"staleRequestSupport":{"cancel":true,"retryOnContentModified":["textDocument/semanticTokens/full","textDocument/semanticTokens/range","textDocument/semanticTokens/full/delta"]},"regularExpressions":{"engine":"ECMAScript","version":"ES2020"},"markdown":{"parser":"marked","version":"1.1.0"},"positionEncodings":["utf-16"]},"notebookDocument":{"synchronization":{"dynamicRegistration":true,"executionSummarySupport":true}}},"trace":"off","workspaceFolders":[{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot","name":"firefly-boot"}]}}Content-Length: 52
4
+
5
+ {"jsonrpc":"2.0","method":"initialized","params":{}}Content-Length: 9421
6
+
7
+ {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot/lsp/LanguageServer.ff","languageId":"firefly","version":1,"text":"class LanguageServer(\n mutable openFiles: Map[String, String]\n)\n\ndata BadRequestException(reason: String)\n\ncapability Request(\n headers: Map[String, String]\n object: JsValue\n)\n\ncapability RequestMessage(\n id: Option[MessageId] // None when the request is a Notification Message\n method: String\n parameters: Map[String, JsValue]\n)\n\ndata MessageId {\n MessageIdInt(id: Int)\n MessageIdString(id: String)\n}\n\ncapability ResultOrError {\n Result(result: JsValue)\n Error(code: Int, message: String)\n}\n\n\nmain(system: NodeSystem) {\n try {\n mutable input = system.readStream()\n while {True} {\n let requestPair = parseRequest(system, input)\n input = requestPair.second\n let request = requestPair.first\n let message = parseRequestMessage(request.object)\n handleRequestMessage(system.js(), message).each {body =>\n system.writeText(makeResponse(body))\n }\n //system.writeText(makeResponse(makeDiagnoseNotification(system.js())))\n }\n } catch {| BadRequestException(reason), error =>\n system.writeErrorText(reason + \"\\n\")\n } grab()\n}\n \nparseRequest(system: NodeSystem, input: Stream[Buffer]): Pair[Request, Stream[Buffer]] {\n let headersPair = parseRequestHeaders(input)\n let headers = headersPair.first\n let contentLength = headers\n .get(\"content-length\").else {throw(BadRequestException(\"'content-length' header is missing\"))}\n .getInt().else {throw(BadRequestException(\"Value for 'content-length' is not an integer\"))}\n let bodyPair = parseRequestBody(system.js(), contentLength, headersPair.second)\n bodyPair.mapFirst {body => Request(headers, body)}\n}\n\nparseRequestHeaders(input: Stream[Buffer]): Pair[Map[String, String], Stream[Buffer]] {\n let buffers = Stack.make()\n mutable buffer = input.next().else {\n throw(BadRequestException(\"End of input while parsing request headers\"))\n }\n mutable offset = 0\n mutable droppingInitialNewlines = True\n mutable lastWasNewline = False\n mutable done = False\n while {!done} {\n if(offset == buffer.size()) {\n offset = 0\n buffers.push(buffer)\n buffer = input.next().else {\n throw(BadRequestException(\"End of input while parsing request headers\"))\n }\n }\n let byte = buffer.grabUint8(offset)\n if(droppingInitialNewlines && (byte == '\\n'.codeUnit || byte == '\\r'.codeUnit)) {\n // Skip\n } elseIf {byte == '\\n'.codeUnit} {\n if(lastWasNewline) {\n done = True\n } else {\n lastWasNewline = True\n }\n } elseIf {byte != '\\r'.codeUnit && lastWasNewline} {\n lastWasNewline = False\n } else {\n droppingInitialNewlines = False\n }\n offset += 1\n }\n buffers.push(buffer.view(0, offset))\n let headers = Buffer.fromBufferArray(buffers.drain()).toString()\n let map = headers.lines().pairs().filter {_.second.size() != 0}.map {| Pair(i, line) =>\n line.splitFirst(':').else {\n throw(BadRequestException(\"Invalid header at line \" + i + \" '\" + line + \"'\"))\n }.mapFirst {_.lower()} //.mapSecond {_.trim()}\n }.toMap()\n Pair(map, [buffer.view(offset, buffer.size())].toStream().addAll(input))\n}\n\nparseRequestBody(js: JsSystem, contentLength: Int, input: Stream[Buffer]): Pair[JsValue, Stream[Buffer]] {\n let bodyPair = try {\n input.readBytes(contentLength) // Should Stream.readBytes return an option?\n } catchAny {error =>\n throw(BadRequestException(\"End of input while parsing request body\"))\n } grab()\n let body = Buffer.fromBufferArray(bodyPair.first).toString()\n let json = try {\n js.parseJson(body)\n } catchAny {error =>\n throw(BadRequestException(\"Invalid JSON in request body: \" + body))\n } grab()\n Pair(json, bodyPair.second)\n}\n\nparseRequestMessage(object: JsValue): RequestMessage {\n let id = object.getOwn(\"id\").map {id =>\n if(id.isInt()) {\n MessageIdInt(id.grabInt())\n } elseIf {id.isString()} {\n MessageIdString(id.grabString())\n } else {\n throw(BadRequestException(\"Bad JSON-RPC id, int or string expected\"))\n }\n }\n\n let method = object.getOwn(\"method\").{\n | None => throw(BadRequestException(\"Bad JSON-RPC, missing method\"))\n | Some(m) {!m.isString()} => throw(BadRequestException(\"Bad JSON-RPC method, string expected\"))\n | Some(m) => m.grabString()\n }\n\n let parameters = object.getOwn(\"params\").{\n | None => [].toMap()\n | Some(o) {!o.isObject()} => throw(BadRequestException(\"Bad JSON-RPC params, object expected\"))\n | Some(o) => o.grabMap()\n }\n\n RequestMessage(id, method, parameters)\n}\n\nhandleRequestMessage(js: JsSystem, message: RequestMessage): Option[JsValue] {\n message.id.{\n // Notification Message\n | None =>\n message.method.{\n | \"initialized\" =>\n | _ =>\n }\n None\n // Request Message\n | Some(id) =>\n let result = message.method.{\n | \"initialize\" => handleInitialize(js, message.parameters)\n | \"textDocument/diagnostic\" => handleDiagnostic(js, message.parameters)\n | \"hover\" => handleHover(js, message.parameters)\n | _ => handleUnsupported(js)\n }\n Some(makeResponseMessage(js, id, result))\n }\n}\n\nmakeResponse(body: JsValue): String {\n let json = body.toJson(Some(\" \"))\n let length = json.size()\n \"Content-Length: \" + length + \"\\r\\n\" +\n \"\\r\\n\" +\n json\n}\n\nmakeResponseMessage(js: JsSystem, id: MessageId, result: ResultOrError): JsValue {\n let o = js.object()\n o.set(\"jsonrpc\", \"2.0\")\n id.{\n | MessageIdInt(id) => o.set(\"id\", id)\n | MessageIdString(id) => o.set(\"id\", id)\n }\n result.{\n | Result(result) =>\n o.set(\"result\", result)\n | Error(code, message) =>\n let e = js.object()\n e.set(\"code\", code)\n e.set(\"message\", message)\n o.set(\"error\", e)\n }\n o\n}\n\nhandleUnsupported(js: JsSystem): ResultOrError {\n Error(1234, \"Unsupported method\")\n}\n\nhandleInitialize(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let anyFireflyFile = js.object()\n .with(\"filters\", [\n js.object()\n .with(\"pattern\", js.object()\n .with(\"glob\", \"**/*.ff\")\n .with(\"matches\", \"file\")\n )\n ].toArray())\n\n let o = js.object()\n .with(\"capabilities\", js.object()\n .with(\"textDocumentSync\", js.object()\n .with(\"openClose\", True)\n .with(\"change\", 1 /* TextDocumentSyncKind.Full */)\n )\n .with(\"hoverProvider\", False)\n //.with(\"definitionProvider\", True)\n //.with(\"typeDefinitionProvider\", True)\n .with(\"diagnosticProvider\", js.object()\n .with(\"interFileDependencies\", True)\n .with(\"workspaceDiagnostics\", False)\n )\n /*.with(\"workspace\", js.object()\n .with(\"workspaceFolders\", js.object()\n .with(\"supported\", True)\n .with(\"changeNotifications\", True)\n )\n .with(\"fileOperations\", js.object()\n .with(\"didCreate\", anyFireflyFile)\n .with(\"didRename\", anyFireflyFile)\n .with(\"didDelete\", anyFireflyFile)\n )\n )*/\n )\n .with(\"serverInfo\", js.object()\n .with(\"name\", \"Firefly Language Server\")\n .with(\"version\", \"0.0.0\")\n )\n Result(o)\n}\n\nhandleDiagnostic(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let diagnostic = js.object()\n .with(\"range\", js.object()\n .with(\"start\", js.object()\n .with(\"line\", 0).with(\"character\", 6)\n )\n .with(\"end\", js.object()\n .with(\"line\", 0).with(\"character\", 20)\n )\n )\n .with(\"severity\", 1 /* Error */)\n .with(\"message\", \"Hurray!\")\n\n let o = js.object()\n .with(\"kind\", \"full\")\n .with(\"items\", [/*diagnostic*/].toArray())\n\n Result(o)\n}\n\nhandleHover(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let o = js.object().with(\"contents\", \"TODO\")\n Result(o)\n}\n\nmakeNotificationMessage(js: JsSystem, method: String, params: JsValue): JsValue {\n js.object()\n .with(\"jsonrpc\", \"2.0\")\n .with(\"method\", method)\n .with(\"params\", params)\n}\n"}}}Content-Length: 162
8
+
9
+ {"jsonrpc":"2.0","id":1,"method":"textDocument/diagnostic","params":{"textDocument":{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot/lsp/LanguageServer.ff"}}}Content-Length: 44
10
+
11
+ {"jsonrpc":"2.0","id":2,"method":"shutdown"}
package/lsp/stdout.txt ADDED
@@ -0,0 +1,41 @@
1
+ Content-Length: 500
2
+
3
+ {
4
+ "jsonrpc": "2.0",
5
+ "id": 0,
6
+ "result": {
7
+ "capabilities": {
8
+ "textDocumentSync": {
9
+ "openClose": true,
10
+ "change": 1
11
+ },
12
+ "hoverProvider": false,
13
+ "diagnosticProvider": {
14
+ "interFileDependencies": true,
15
+ "workspaceDiagnostics": false
16
+ }
17
+ },
18
+ "serverInfo": {
19
+ "name": "Firefly Language Server",
20
+ "version": "0.0.0"
21
+ }
22
+ }
23
+ }Content-Length: 104
24
+
25
+ {
26
+ "jsonrpc": "2.0",
27
+ "id": 1,
28
+ "result": {
29
+ "kind": "full",
30
+ "items": []
31
+ }
32
+ }Content-Length: 121
33
+
34
+ {
35
+ "jsonrpc": "2.0",
36
+ "id": 2,
37
+ "error": {
38
+ "code": 1234,
39
+ "message": "Unsupported method"
40
+ }
41
+ }
package/lux/Main.ff CHANGED
@@ -3,6 +3,10 @@ import HttpServer from ff:httpserver
3
3
  import Lux
4
4
  import LuxEvent
5
5
 
6
+ data ChatEntry(
7
+ question: String
8
+ answer: Option[String]
9
+ )
6
10
 
7
11
  mainComponent(lux: Lux, http: HttpClient) {
8
12
  lux.useState([]): chat, setChat =>
@@ -46,6 +50,7 @@ browserMain(system: BrowserSystem) {
46
50
  }
47
51
 
48
52
  nodeMain(system: NodeSystem) {
53
+ let openAiKey = system.arguments().first()
49
54
  HttpServer.listen(system, "localhost", 8080) {request, response =>
50
55
  if(request.path() == "/") {
51
56
  response.setHeader("Content-Type", ["text/html; charset=UTF-8"])
@@ -56,15 +61,64 @@ nodeMain(system: NodeSystem) {
56
61
  response.setHeader("Content-Type", ["text/javascript; charset=UTF-8"])
57
62
  response.writeText(system.assets().readText(request.path()))
58
63
  } elseIf {request.path() == "/chat"} {
59
- system.mainTask().sleep(Duration(request.readText().size().toFloat()))
64
+ let question = request.readText()
60
65
  response.setHeader("Content-Type", ["text/plain; charset=UTF-8"])
61
- response.writeText("Hello there!")
66
+ openAiKey.{
67
+ | None =>
68
+ system.mainTask().sleep(Duration(question.size().toFloat()))
69
+ response.writeText("Hi! You need to give an OpenAI secret key at the command line.")
70
+ | Some(key) =>
71
+ let chatJson = encodeChat(system.js(), question)
72
+ let answer = fetchAnswer(system.httpClient(), system.js(), key, chatJson)
73
+ response.writeText(answer)
74
+ }
62
75
  } else {
63
76
  response.writeStatus(404, Some("Not found"))
64
77
  }
65
78
  }
66
79
  }
67
80
 
81
+ fetchAnswer(httpClient: HttpClient, js: JsSystem, key: String, question: JsValue): String {
82
+ let json = httpClient.fetch(
83
+ url = "https://api.openai.com/v1/chat/completions"
84
+ method = "POST"
85
+ headers = [
86
+ Pair("Authorization", "Bearer " + key)
87
+ Pair("Content-Type", "application/json")
88
+ ]
89
+ body = Some(HttpClient.bodyText(question.toJson()))
90
+ ).readText()
91
+ js.parseJson(json).get("choices").get(0).get("message").get("content").grabString()
92
+ }
93
+
94
+ encodeChat(js: JsSystem, question: String): JsValue {
95
+ let systemJson = js.object()
96
+ .with("role", "system")
97
+ .with("content", "You are a helpful assistant.")
98
+ let questionJson = js.object()
99
+ .with("role", "user")
100
+ .with("content", question)
101
+ let messagesJson = js.array([systemJson, questionJson])
102
+ js.object()
103
+ .with("model", "gpt-3.5-turbo")
104
+ .with("messages", messagesJson)
105
+ }
106
+
107
+
108
+ /*
109
+ let
110
+ encodeMessage role content =
111
+ E.object [ ("role", E.string role), ("content", E.string content) ]
112
+ encodeEntry question maybeAnswer = case maybeAnswer of
113
+ Nothing -> [ encodeMessage "user" question ]
114
+ Just answer -> [ encodeMessage "user" question, encodeMessage "assistant" answer ]
115
+ encodedMessages = encodeMessage "system" "You are a helpful assistant."
116
+ :: List.concatMap (\entry -> encodeEntry entry.question entry.answer) chat
117
+ in E.object
118
+ [ ("model", E.string "gpt-3.5-turbo")
119
+ , ("messages", E.list (\x -> x) encodedMessages)
120
+ ]
121
+ */
68
122
  buildMain(system: BuildSystem) {
69
123
  let browser = system.compileForBrowser("Main.ff")
70
124
  let assets = AssetSystem.create().addAssets("/js", browser.assets())
@@ -0,0 +1,6 @@
1
+ main(system: NodeSystem) {
2
+
3
+ let list = ["Meetup", "MF#K"]
4
+ list.map {a => a.}
5
+
6
+ }