drakongen 1.0.0

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 (157) hide show
  1. package/.vscode/launch.json +18 -0
  2. package/LICENSE +24 -0
  3. package/README.md +80 -0
  4. package/browser/drakongen.js +1303 -0
  5. package/browsertest.html +84 -0
  6. package/buildbrowser +4 -0
  7. package/buildexamples.js +61 -0
  8. package/examples/00.Empty.drakon +1 -0
  9. package/examples/00.Empty.txt +5 -0
  10. package/examples/01. /320/221/320/265/320/273/321/213/320/270/314/206.drakon" +13 -0
  11. package/examples/01. /320/221/320/265/320/273/321/213/320/271.json" +16 -0
  12. package/examples/01. /320/221/320/265/320/273/321/213/320/271.txt" +4 -0
  13. package/examples/02. /320/247/320/265/314/210/321/200/320/275/321/213/320/270/314/206.drakon" +28 -0
  14. package/examples/02. /320/247/321/221/321/200/320/275/321/213/320/271.json" +31 -0
  15. package/examples/02. /320/247/321/221/321/200/320/275/321/213/320/271.txt" +10 -0
  16. package/examples/03. /320/241/320/265/321/200/321/213/320/270/314/206.drakon" +20 -0
  17. package/examples/03. /320/241/320/265/321/200/321/213/320/271.json" +23 -0
  18. package/examples/03. /320/241/320/265/321/200/321/213/320/271.txt" +6 -0
  19. package/examples/04. /320/221/321/203/321/200/321/213/320/270/314/206.drakon" +25 -0
  20. package/examples/04. /320/221/321/203/321/200/321/213/320/271.json" +29 -0
  21. package/examples/04. /320/221/321/203/321/200/321/213/320/271.txt" +7 -0
  22. package/examples/05. /320/226/320/265/314/210/320/273/321/202/321/213/320/270/314/206.drakon" +25 -0
  23. package/examples/05. /320/226/321/221/320/273/321/202/321/213/320/271.json" +29 -0
  24. package/examples/05. /320/226/321/221/320/273/321/202/321/213/320/271.txt" +7 -0
  25. package/examples/06. /320/221/320/260/320/263/321/200/320/276/320/262/321/213/320/270/314/206.drakon" +30 -0
  26. package/examples/06. /320/221/320/260/320/263/321/200/320/276/320/262/321/213/320/271.json" +35 -0
  27. package/examples/06. /320/221/320/260/320/263/321/200/320/276/320/262/321/213/320/271.txt" +10 -0
  28. package/examples/07. /320/244/320/270/320/276/320/273/320/265/321/202/320/276/320/262/321/213/320/270/314/206.drakon" +42 -0
  29. package/examples/07. /320/244/320/270/320/276/320/273/320/265/321/202/320/276/320/262/321/213/320/271.json" +49 -0
  30. package/examples/07. /320/244/320/270/320/276/320/273/320/265/321/202/320/276/320/262/321/213/320/271.txt" +14 -0
  31. package/examples/08. /320/221/320/270/321/200/321/216/320/267/320/276/320/262/321/213/320/270/314/206.drakon" +27 -0
  32. package/examples/08. /320/221/320/270/321/200/321/216/320/267/320/276/320/262/321/213/320/271.json" +27 -0
  33. package/examples/08. /320/221/320/270/321/200/321/216/320/267/320/276/320/262/321/213/320/271.txt" +6 -0
  34. package/examples/09. /320/236/321/200/320/260/320/275/320/266/320/265/320/262/321/213/320/270/314/206.drakon" +37 -0
  35. package/examples/09. /320/236/321/200/320/260/320/275/320/266/320/265/320/262/321/213/320/271.json" +39 -0
  36. package/examples/09. /320/236/321/200/320/260/320/275/320/266/320/265/320/262/321/213/320/271.txt" +10 -0
  37. package/examples/10. /320/240/320/276/320/267/320/276/320/262/321/213/320/270/314/206.drakon" +42 -0
  38. package/examples/10. /320/240/320/276/320/267/320/276/320/262/321/213/320/271.json" +54 -0
  39. package/examples/10. /320/240/320/276/320/267/320/276/320/262/321/213/320/271.txt" +16 -0
  40. package/examples/11. /320/227/320/260/321/211/320/270/321/202/320/275/321/213/320/270/314/206.drakon" +37 -0
  41. package/examples/11. /320/227/320/260/321/211/320/270/321/202/320/275/321/213/320/271.json" +39 -0
  42. package/examples/11. /320/227/320/260/321/211/320/270/321/202/320/275/321/213/320/271.txt" +10 -0
  43. package/examples/12. /320/221/320/276/320/273/320/276/321/202/320/275/321/213/320/270/314/206.drakon" +44 -0
  44. package/examples/12. /320/221/320/276/320/273/320/276/321/202/320/275/321/213/320/271.json" +43 -0
  45. package/examples/12. /320/221/320/276/320/273/320/276/321/202/320/275/321/213/320/271.txt" +10 -0
  46. package/examples/13. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/270/314/206.drakon" +54 -0
  47. package/examples/13. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.json" +63 -0
  48. package/examples/13. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.txt" +18 -0
  49. package/examples/14. /320/227/320/276/320/273/320/276/321/202/320/276/320/270/314/206.drakon" +64 -0
  50. package/examples/14. /320/227/320/276/320/273/320/276/321/202/320/276/320/271.json" +73 -0
  51. package/examples/14. /320/227/320/276/320/273/320/276/321/202/320/276/320/271.txt" +22 -0
  52. package/examples/15. /320/241/320/270/320/275/320/270/320/270/314/206.drakon" +54 -0
  53. package/examples/15. /320/241/320/270/320/275/320/270/320/271.json" +87 -0
  54. package/examples/15. /320/241/320/270/320/275/320/270/320/271.txt" +26 -0
  55. package/examples/16. /320/223/320/276/320/273/321/203/320/261/320/276/320/270/314/206.drakon" +37 -0
  56. package/examples/16. /320/223/320/276/320/273/321/203/320/261/320/276/320/271.json" +48 -0
  57. package/examples/16. /320/223/320/276/320/273/321/203/320/261/320/276/320/271.txt" +13 -0
  58. package/examples/17. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/270/314/206.drakon" +39 -0
  59. package/examples/17. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.json" +41 -0
  60. package/examples/17. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.txt" +10 -0
  61. package/examples/18. /320/241/321/202/320/260/320/273/321/214/320/275/320/276/320/270/314/206.drakon" +49 -0
  62. package/examples/18. /320/241/321/202/320/260/320/273/321/214/320/275/320/276/320/271.json" +67 -0
  63. package/examples/18. /320/241/321/202/320/260/320/273/321/214/320/275/320/276/320/271.txt" +19 -0
  64. package/examples/19. Lilla.drakon +39 -0
  65. package/examples/19. Lilla.json +45 -0
  66. package/examples/19. Lilla.txt +11 -0
  67. package/examples/Adaptive design.drakon +116 -0
  68. package/examples/Adaptive design.json +153 -0
  69. package/examples/Adaptive design.txt +47 -0
  70. package/examples/And test.drakon +46 -0
  71. package/examples/And test.json +46 -0
  72. package/examples/And test.txt +10 -0
  73. package/examples/Arrow - double exit.drakon +41 -0
  74. package/examples/Arrow - double exit.json +62 -0
  75. package/examples/Arrow - double exit.txt +14 -0
  76. package/examples/Complex arrow.drakon +93 -0
  77. package/examples/Complex arrow.json +162 -0
  78. package/examples/Complex arrow.txt +43 -0
  79. package/examples/DoubleArrow.drakon +53 -0
  80. package/examples/DoubleArrow.json +86 -0
  81. package/examples/DoubleArrow.txt +18 -0
  82. package/examples/Find pointing nodes.drakon +104 -0
  83. package/examples/Find pointing nodes.json +137 -0
  84. package/examples/Find pointing nodes.txt +29 -0
  85. package/examples/How to tune PID on a quadcopter.drakon +488 -0
  86. package/examples/How to tune PID on a quadcopter.json +734 -0
  87. package/examples/How to tune PID on a quadcopter.txt +169 -0
  88. package/examples/Or test.drakon +47 -0
  89. package/examples/Or test.json +46 -0
  90. package/examples/Or test.txt +10 -0
  91. package/examples/Silhouette test 1.drakon +47 -0
  92. package/examples/Silhouette test 1.json +82 -0
  93. package/examples/Silhouette test 1.txt +24 -0
  94. package/examples/Work out action-check.drakon +30 -0
  95. package/examples/Work out action-check.json +45 -0
  96. package/examples/Work out action-check.txt +9 -0
  97. package/examples/Workout foreach.drakon +63 -0
  98. package/examples/Workout foreach.json +62 -0
  99. package/examples/Workout foreach.txt +14 -0
  100. package/examples/ar01.drakon +25 -0
  101. package/examples/ar01.json +40 -0
  102. package/examples/ar01.txt +7 -0
  103. package/examples/ar02.drakon +30 -0
  104. package/examples/ar02.json +45 -0
  105. package/examples/ar02.txt +10 -0
  106. package/examples/ar03.drakon +32 -0
  107. package/examples/ar03.json +44 -0
  108. package/examples/ar03.txt +7 -0
  109. package/examples/ar04.drakon +37 -0
  110. package/examples/ar04.json +49 -0
  111. package/examples/ar04.txt +10 -0
  112. package/examples/ar05.drakon +37 -0
  113. package/examples/ar05.json +59 -0
  114. package/examples/ar05.txt +11 -0
  115. package/examples/ar06.drakon +40 -0
  116. package/examples/ar06.json +70 -0
  117. package/examples/ar06.txt +13 -0
  118. package/examples/ar07.drakon +40 -0
  119. package/examples/ar07.json +70 -0
  120. package/examples/ar07.txt +13 -0
  121. package/examples/ar08.drakon +47 -0
  122. package/examples/ar08.json +81 -0
  123. package/examples/ar08.txt +16 -0
  124. package/examples/ar09.drakon +52 -0
  125. package/examples/ar09.json +87 -0
  126. package/examples/ar09.txt +17 -0
  127. package/examples/ar10.drakon +52 -0
  128. package/examples/ar10.json +88 -0
  129. package/examples/ar10.txt +18 -0
  130. package/examples/ar11.drakon +47 -0
  131. package/examples/ar11.json +82 -0
  132. package/examples/ar11.txt +17 -0
  133. package/examples/ar12.drakon +37 -0
  134. package/examples/ar12.json +49 -0
  135. package/examples/ar12.txt +10 -0
  136. package/examples/getToken.drakon +76 -0
  137. package/examples/getToken.json +88 -0
  138. package/examples/getToken.txt +41 -0
  139. package/examples/tmp.drakon +49 -0
  140. package/examples/tmp.json +82 -0
  141. package/examples/tmp.txt +37 -0
  142. package/package.json +21 -0
  143. package/prompts/drakonToPrompt.txt +139 -0
  144. package/prompts/index.txt +17 -0
  145. package/prompts/printPseudo.txt +116 -0
  146. package/src/browserTools.js +39 -0
  147. package/src/drakonToPromptStruct.js +44 -0
  148. package/src/drakonToStruct.js +416 -0
  149. package/src/drakongen.js +16 -0
  150. package/src/index.js +17 -0
  151. package/src/main.js +157 -0
  152. package/src/nodeTools.js +38 -0
  153. package/src/printPseudo.js +167 -0
  154. package/src/structFlow.js +382 -0
  155. package/src/technicalTree.js +84 -0
  156. package/src/tools.js +36 -0
  157. package/src/translate.js +108 -0
@@ -0,0 +1,116 @@
1
+ Write a JavaScript function printPseudo(algorithm, translations) that prints an algorithm as pseudo code
2
+ and returns it as a string.
3
+
4
+ "algorithm" is an object these properties:
5
+ - name: string, the name of the algorithm
6
+ - params: string, some textual description of the algorithm in HTML
7
+ - body: array, the steps of the algorithm
8
+
9
+ Types of steps:
10
+ - question
11
+ - non-question
12
+
13
+ A non-question step contains properties:
14
+ - id: string
15
+ - content: string with HTML
16
+ - secondary: string with HTML
17
+ - message: string with plain text
18
+
19
+ A question step contains properties:
20
+ - id: string
21
+ - content: either string with HTML or a structured object
22
+ - yes: an array with child nodes
23
+ - no: an array with child nodes
24
+
25
+ Examples of structured objects in the "content" property.
26
+ "operator" property is mandatory.
27
+ "operand", "left", "right" properties contain either an HTML string or another structured object
28
+
29
+ {operator:"not",operand:"<p>Hello</p>"}
30
+ {
31
+ "operator": "and",
32
+ "left": {
33
+ "operator": "and",
34
+ "left": "<p>One</p>",
35
+ "right": {
36
+ "operator": "not",
37
+ "operand": "<p>Two</p>"
38
+ }
39
+ }
40
+ "right": "<p>Three</p>"
41
+ }
42
+
43
+ The algorithm of printPseudo:
44
+ - start with an empty output list of lines
45
+ - run printPseudoSteps(algorithm.body, 0, output)
46
+ - joint the output with newline as separator and return it
47
+
48
+ printPseudoSteps(body, depth, output)
49
+ for each node in body
50
+ print node with indentation
51
+
52
+
53
+ Indentation contains "depth" * 4 spaces. Every line in the printed node should have the indentation.
54
+
55
+ how to print a non-question node
56
+ if neither node.secondary or node.content have a value, do not print the node
57
+ if node.type === "branch" or node.type === "comment", do not print the node
58
+ Else
59
+ if node.type === "error"
60
+ print a string: translated "Error" + ":"
61
+ print node.message
62
+ print node.secondary if it has a value
63
+ print node.content if it has a value
64
+ print an empty line
65
+
66
+ how to print a question node
67
+ if both node.yes and node.no are empty, do not print the node
68
+ if node.yes is empty
69
+ content = {operator:"not", operand:node.content}
70
+ print the translated string "If
71
+ print the structured content
72
+ if node.yes is empty
73
+ print the node.no using printPseudoSteps and incremented depth
74
+ Else
75
+ print the node.yes using printPseudoSteps and incremented depth
76
+ if node.no is not empty
77
+ print the translated string "Else"
78
+ print the node.no using printPseudoSteps and incremented depth
79
+
80
+ How to translate strings:
81
+ If a string is in "translations", take it. Otherwise do not translate the string, leave it as it is.
82
+
83
+ How to print structured content:
84
+ If the content is a string
85
+ convert the HTML to text
86
+ Else
87
+ if content.operator === "not"
88
+ print content.operand as operand, but prepend the first line with this string: tranlated "not" + " "
89
+ else if content.operator === "and"
90
+ print content.left as operand
91
+ print the translated string "and"
92
+ print content.right as operand
93
+ else if content.operator === "or"
94
+ print content.left as operand
95
+ print the translated string "or"
96
+ print content.right as operand
97
+
98
+ How to print content as operand:
99
+ if the content is a string
100
+ convert the HTML to text and print line by line, prepend each line with indentation
101
+ else
102
+ convert the HTML to text and print line by line, prepend each line with indentation, but prepend the first line with "(" (after indentation), and append the last line with ")"
103
+
104
+ How to convert HTML to string
105
+ if the string is empty, return the translated string "empty".
106
+ if the string is not HTML, split it by line end
107
+ else
108
+ ignore <script> tags alltogether
109
+ for every <p> tag create a line from the text content of the tag
110
+ for every <li> tag in an unordred list, create a line from the text content of the tag, prepend the line with "- "
111
+ for every <li> tag in an ordred list, create a line from the text content of the tag, prepend the line with the ordinal number: "1. ", "2. ", "3. ", etc.
112
+ skip all other tags
113
+
114
+ The code with run in Node Js.
115
+ Do not use the ... operator.
116
+ Use node-html-parser for HTML parsing.
@@ -0,0 +1,39 @@
1
+
2
+ function htmlToString(html) {
3
+ if (!html) return '';
4
+ if (!html.startsWith('<') || !html.endsWith('>')) {
5
+ return html.split("\n").map(line => {return line.trim()})
6
+ }
7
+
8
+ const parser = new DOMParser();
9
+ const doc = parser.parseFromString(html, "text/html");
10
+
11
+ const root = doc.body
12
+ const output = [];
13
+
14
+ root.childNodes.forEach((node) => {
15
+ if (node.tagName === 'P') {
16
+ output.push(node.textContent.trim());
17
+ } else if (node.tagName === 'UL') {
18
+ output.push('');
19
+ node.childNodes.forEach((item) => {
20
+ if (item.tagName === 'LI') {
21
+ output.push(`- ${item.textContent.trim()}`);
22
+ }
23
+ });
24
+ output.push('');
25
+ } else if (node.tagName === 'OL') {
26
+ output.push('');
27
+ node.childNodes.forEach((item, index) => {
28
+ if (item.tagName === 'LI') {
29
+ output.push(`${index + 1}. ${item.textContent.trim()}`);
30
+ }
31
+ });
32
+ output.push('');
33
+ }
34
+ });
35
+
36
+ return output;
37
+ }
38
+
39
+ module.exports = {htmlToString}
@@ -0,0 +1,44 @@
1
+ const {drakonToStruct} = require("./drakonToStruct");
2
+ const {printPseudo} = require('./printPseudo');
3
+ const {addRange} = require("./tools")
4
+
5
+ function drakonToPseudocode(drakonJson, name, filename, htmlToString, translate) {
6
+ var diagram = drakonToStruct(drakonJson, name, filename, translate)
7
+ var lines = []
8
+ if (diagram.params) {
9
+ addRange(lines, htmlToString(diagram.params))
10
+ lines.push("")
11
+ }
12
+
13
+ lines.push(translate("Procedure") + " \"" + diagram.name + "\"")
14
+ lines.push("")
15
+ lines.push(translate("Algorithm") + ":")
16
+
17
+ if (diagram.branches.length === 0) {
18
+ lines.push(translate("Empty"))
19
+ } else if (diagram.branches.length === 1) {
20
+ var first = diagram.branches[0]
21
+ printPseudo(first, translate, lines, htmlToString)
22
+ } else {
23
+ var first = diagram.branches[0]
24
+ lines.push(translate("Call subroutine") + ": \"" + htmlToString(first.name) + "\"")
25
+ diagram.branches.forEach(branch => {
26
+ lines.push("")
27
+ lines.push(translate("Subroutine") + ": \"" + htmlToString(branch.name) + "\"")
28
+ printPseudo(branch, translate, lines, htmlToString)
29
+ lines.push(translate("End of subroutine"))
30
+ })
31
+ }
32
+ lines.push(translate("End of procedure"))
33
+ if (diagram.description) {
34
+ lines.push("")
35
+ addRange(lines, htmlToString(diagram.description))
36
+ lines.push("")
37
+ }
38
+ var text = lines.join("\n")
39
+
40
+ var str = JSON.stringify(diagram, null, 4)
41
+ return {text:text,json:str}
42
+ }
43
+
44
+ module.exports = { drakonToPseudocode };
@@ -0,0 +1,416 @@
1
+ const { structFlow, redirectNode } = require("./structFlow");
2
+ const { createError } = require("./tools");
3
+
4
+ var translate
5
+
6
+ function drakonToStruct(drakonJson, name, filename, translateFunction) {
7
+ translate = translateFunction
8
+ let drakonGraph;
9
+ try {
10
+ drakonJson = drakonJson || ""
11
+ drakonJson = drakonJson.trim()
12
+ drakonJson = drakonJson || "{}"
13
+ drakonGraph = JSON.parse(drakonJson);
14
+ } catch (error) {
15
+ var message = translate("Error parsing JSON") + ": " + error.message
16
+ throw createError(message, filename)
17
+ }
18
+
19
+ const nodes = drakonGraph.items || {};
20
+
21
+ var branches = []
22
+ var firstNodeId = findStartNode(nodes, filename, branches)
23
+
24
+ if (!firstNodeId) {
25
+ return {
26
+ name: name,
27
+ params: drakonGraph.params || "",
28
+ description: drakonGraph.description || "",
29
+ branches: []
30
+ }
31
+ }
32
+
33
+ buildTwoWayConnections(nodes, firstNodeId)
34
+
35
+ rewireSelectsMarkLoops(nodes, filename)
36
+ branches.forEach(branch => checkBranchIsReferenced(branch, firstNodeId, filename))
37
+ rewireShortcircuit(nodes, filename)
38
+ branches.forEach(branch => cutOffBranch(nodes, branch))
39
+
40
+ var branchTrees = structFlow(nodes, branches, filename, translate)
41
+
42
+ return {
43
+ name: name,
44
+ params: drakonGraph.params || "",
45
+ description: drakonGraph.description || "",
46
+ branches: branchTrees
47
+ }
48
+ }
49
+
50
+ function drakonToGraph(drakonJson, name, filename, translateFunction) {
51
+ translate = translateFunction
52
+ let drakonGraph;
53
+ try {
54
+ drakonGraph = JSON.parse(drakonJson);
55
+ } catch (error) {
56
+ var message = translate("Error parsing JSON") + ": " + error.message
57
+ throw createError(message, filename)
58
+ }
59
+
60
+ const nodes = drakonGraph.items || {};
61
+
62
+ var branches = []
63
+ var firstNodeId = findStartNode(nodes, filename, branches)
64
+
65
+ if (!firstNodeId) {
66
+ return undefined
67
+ }
68
+
69
+ buildTwoWayConnections(nodes, firstNodeId)
70
+
71
+ rewireSelectsMarkLoops(nodes, filename)
72
+ rewireShortcircuit(nodes, filename)
73
+ branches.forEach(branch => checkBranchIsReferenced(branch, firstNodeId, filename))
74
+ branches.forEach(branch => cutOffBranch(nodes, branch))
75
+
76
+ var branchTrees = structFlow(nodes, branches, filename, translate)
77
+
78
+ return {
79
+ name: name,
80
+ params: drakonGraph.params || "",
81
+ description: drakonGraph.description || "",
82
+ branches: branchTrees
83
+ }
84
+ }
85
+
86
+
87
+ function checkBranchIsReferenced(branch, firstNodeId, filename) {
88
+ if (branch.id === firstNodeId) {
89
+ return
90
+ }
91
+ if (branch.prev.length === 0) {
92
+ throw createError(translate("A silhouette branch is not referenced"), filename, branch.id)
93
+ }
94
+ }
95
+
96
+ function cutOffBranch(nodes, branch) {
97
+ var end = {
98
+ type: "end",
99
+ id: branch.id + "-end",
100
+ prev: []
101
+ }
102
+ nodes[end.id] = end
103
+ branch.next = branch.one
104
+ var addresses = []
105
+ traverseToHitBranch(nodes, branch.id, {}, (prev, node) => addFakeEnd(nodes, prev, node, end, addresses))
106
+ }
107
+
108
+ function traverseToHitBranch(nodes, nodeId, visited, action) {
109
+ if (!nodeId) {return}
110
+ if (nodeId in visited) {return}
111
+ visited[nodeId] = true
112
+ var node = nodes[nodeId]
113
+ if (!node) {return}
114
+ if (node.one) {
115
+ var one = nodes[node.one]
116
+ if (one.type === "branch") {
117
+ action(node, one)
118
+ } else {
119
+ traverseToHitBranch(nodes, node.one, visited, action)
120
+ }
121
+ }
122
+ if (node.two) {
123
+ var two = nodes[node.two]
124
+ if (two.type === "branch") {
125
+ action(node, two)
126
+ } else {
127
+ traverseToHitBranch(nodes, node.two, visited, action)
128
+ }
129
+ }
130
+ }
131
+
132
+ var idCounter = 1000
133
+ function addFakeEnd(nodes, prev, node, end, addresses) {
134
+ var lastAddress = undefined
135
+ if (addresses.length > 0) {
136
+ lastAddress = addresses[addresses.length - 1]
137
+ }
138
+ var address
139
+ if (lastAddress && lastAddress.branch === node.id) {
140
+ address = lastAddress
141
+ } else {
142
+ address = {
143
+ type: "address",
144
+ content: node.content,
145
+ id: "ad-" + idCounter,
146
+ branch: node.id,
147
+ one: end.id,
148
+ prev: []
149
+ }
150
+ idCounter++
151
+ nodes[address.id] = address
152
+ end.prev.push(address.id)
153
+ addresses.push(address)
154
+ }
155
+ redirectNode(nodes, prev, node.id, address.id)
156
+ }
157
+
158
+ function buildTwoWayConnections(nodes, firstNodeId) {
159
+ for (var id in nodes) {
160
+ var node = nodes[id]
161
+ node.id = id
162
+ node.prev = []
163
+ }
164
+
165
+ traverse(nodes, firstNodeId, {}, connectBack)
166
+ }
167
+
168
+ function findStartNode(nodes, filename, branches) {
169
+ var firstNodeId = undefined
170
+ var minBranchId = 10000
171
+ for (var id in nodes) {
172
+ var node = nodes[id]
173
+ if (node.type === "branch") {
174
+ if (node.branchId < minBranchId) {
175
+ firstNodeId = id
176
+ minBranchId = node.branchId
177
+ }
178
+ branches.push(node)
179
+ } else if (node.type === "select") {
180
+ if (!node.content) {
181
+ throw createError(translate("A Select icon must have content"), filename, id)
182
+ }
183
+ node.cases = [];
184
+ } else if (node.type === "loopbegin") {
185
+ if (!node.content) {
186
+ throw createError(translate("A Loop begin icon must have content"), filename, id)
187
+ }
188
+ } else if (node.type === "question") {
189
+ if (!node.content) {
190
+ throw createError(translate("A Question icon must have content"), filename, id)
191
+ }
192
+ }
193
+ }
194
+
195
+ return firstNodeId
196
+ }
197
+
198
+ function rewireSelectsMarkLoops(nodes, filename) {
199
+ for (var id of Object.keys(nodes)) {
200
+ var node = nodes[id]
201
+ if (!node) { continue }
202
+ if (node.type === "select") {
203
+ rewireSelect(nodes, node, filename)
204
+ } else if (node.type === "loopbegin") {
205
+ markLoopBody(nodes, node, filename)
206
+ }
207
+ }
208
+ }
209
+
210
+ function rewireSelect(nodes, selectNode, filename) {
211
+ var caseNodeId = selectNode.one
212
+ while (caseNodeId) {
213
+ var caseNode = nodes[caseNodeId]
214
+ caseNodeId = caseNode.two
215
+ if (caseNode.content) {
216
+ caseNode.type = "question"
217
+ caseNode.flag1 = 1
218
+ caseNode.content = {operator: "equal", left:selectNode.content, right:caseNode.content}
219
+ if (!caseNode.two) {
220
+ var errorId = caseNode.id + "-unexpected"
221
+ var errorAction = insertIcon(nodes, "error", errorId, selectNode.content)
222
+ errorAction.message = "Unexpected case value"
223
+
224
+ caseNode.two = errorId
225
+ errorAction.prev.push(caseNode.id)
226
+ errorAction.one = caseNode.one
227
+
228
+ var next = nodes[caseNode.one]
229
+ next.prev.push(errorId)
230
+ }
231
+ } else {
232
+ if (caseNode.two) {
233
+ throw createError(translate("Only the rightmost Case icon can be empty"), filename, caseNode.id)
234
+ }
235
+ removeNodeOne(nodes, caseNode.id)
236
+ }
237
+ }
238
+ removeNodeOne(nodes, selectNode.id)
239
+ }
240
+
241
+ function insertIcon(nodes, type, id, content) {
242
+ var node = {
243
+ type: type,
244
+ id: id,
245
+ content: content,
246
+ prev: []
247
+ }
248
+ nodes[id] = node
249
+ return node
250
+ }
251
+
252
+ function removeNodeOne(nodes, nodeId) {
253
+ var node = nodes[nodeId]
254
+ redirectPrev(nodes, node, node.one)
255
+ redirectNext(nodes, node, node.one)
256
+ delete nodes[nodeId]
257
+ }
258
+
259
+ function removeFromNext(node, next) {
260
+ next.prev = next.prev.filter(prevId => prevId !== node.id)
261
+ }
262
+
263
+ function redirectPrev(nodes, node, newTarget) {
264
+ for (var prevId of node.prev) {
265
+ var prev = nodes[prevId]
266
+ if (prev.one === node.id) {
267
+ prev.one = newTarget
268
+ }
269
+ if (prev.two === node.id) {
270
+ prev.two = newTarget
271
+ }
272
+ }
273
+ }
274
+
275
+ function redirectNext(nodes, node, newTarget) {
276
+ var target = nodes[newTarget]
277
+ removeFromNext(node, target)
278
+ for (var prevId of node.prev) {
279
+ target.prev.push(prevId)
280
+ }
281
+ }
282
+
283
+ function rewireShortcircuit(nodes) {
284
+ while (findShortcusts(nodes)) {
285
+
286
+ }
287
+ }
288
+
289
+ function findShortcusts(nodes) {
290
+ for (var id in nodes) {
291
+ var node = nodes[id]
292
+ if (node.type === "question") {
293
+ var andOperand = findAndOperand(nodes, node)
294
+ if (andOperand) {
295
+ writeAndShortcut(nodes, node, andOperand)
296
+ return true
297
+ }
298
+ var orOperand = findOrOperand(nodes, node)
299
+ if (orOperand) {
300
+ writeOrShortcut(nodes, node, orOperand)
301
+ return true
302
+ }
303
+ }
304
+ }
305
+ return false
306
+ }
307
+
308
+ function findAndOperand(nodes, node) {
309
+ var below = nodes[node.one]
310
+ if (below.type === "question") {
311
+ if (below.prev.length === 1 && below.two === node.two) {
312
+ return below
313
+ }
314
+ }
315
+ return undefined
316
+ }
317
+
318
+ function findOrOperand(nodes, node) {
319
+ var right = nodes[node.two]
320
+ if (right.type === "question") {
321
+ if (right.prev.length === 1 && right.one === node.one) {
322
+ return right
323
+ }
324
+ }
325
+ return undefined
326
+ }
327
+
328
+ function writeAndShortcut(nodes, node, andOperand) {
329
+ var right = nodes[node.two]
330
+ var down = nodes[andOperand.one]
331
+ removeFromNext(andOperand, right)
332
+ removeFromNext(andOperand, down)
333
+ node.content = {
334
+ operator: "and",
335
+ left: normalizeContent(node),
336
+ right: normalizeContent(andOperand)
337
+ }
338
+ node.one = down.id
339
+ node.flag1 = 1
340
+ down.prev.push(node.id)
341
+ delete nodes[andOperand.id]
342
+ }
343
+
344
+ function writeOrShortcut(nodes, node, orOperand) {
345
+ var right = nodes[orOperand.two]
346
+ var down = nodes[orOperand.one]
347
+ removeFromNext(orOperand, right)
348
+ removeFromNext(orOperand, down)
349
+ node.content = {
350
+ operator: "or",
351
+ left: normalizeContent(node),
352
+ right: normalizeContent(orOperand)
353
+ }
354
+ node.two = right.id
355
+ node.flag1 = 1
356
+ right.prev.push(node.id)
357
+ delete nodes[orOperand.id]
358
+ }
359
+
360
+ function normalizeContent(question) {
361
+ if (question.flag1 === 1) {
362
+ return question.content
363
+ }
364
+
365
+ return {
366
+ operator: "not",
367
+ operand: question.content
368
+ }
369
+ }
370
+
371
+
372
+ function traverse(nodes, nodeId, visited, action) {
373
+ if (!nodeId) {
374
+ return
375
+ }
376
+
377
+ if (nodeId in visited) {
378
+ return
379
+ }
380
+ visited[nodeId] = true
381
+ var node = nodes[nodeId]
382
+ action(nodes, node)
383
+ traverse(nodes, node.one, visited, action)
384
+ traverse(nodes, node.two, visited, action)
385
+ }
386
+
387
+ function connectBack(nodes, node) {
388
+ if (node.one) {
389
+ var one = nodes[node.one]
390
+ one.prev.push(node.id)
391
+ }
392
+ if (node.two) {
393
+ var two = nodes[node.two]
394
+ two.prev.push(node.id)
395
+ }
396
+ }
397
+
398
+ function markLoopBody(nodes, start, filename) {
399
+ var nextNodeId = start.one
400
+ while (nextNodeId) {
401
+ var current = nodes[nextNodeId]
402
+ nextNodeId = current.one
403
+ current.parentLoopId = start.id
404
+ if (current.type === "loopbegin") {
405
+ nextNodeId = markLoopBody(nodes, current, filename)
406
+ } else if (current.type === "loopend") {
407
+ start.end = current.id
408
+ start.next = current.one
409
+ current.start = start.id
410
+ return nextNodeId
411
+ }
412
+ }
413
+ throw createError(translate("Loop end expected here"), filename, start.one)
414
+ }
415
+
416
+ module.exports = { drakonToStruct, drakonToGraph };
@@ -0,0 +1,16 @@
1
+ const { drakonToPseudocode } = require('./drakonToPromptStruct');
2
+ const {htmlToString} = require("./browserTools")
3
+ const { setUpLanguage, translate } = require("./translate")
4
+ const {drakonToStruct} = require("./drakonToStruct");
5
+
6
+
7
+ window.toPseudocode = function(drakonJson, name, filename, language) {
8
+ setUpLanguage(language)
9
+ return drakonToPseudocode(drakonJson, name, filename, htmlToString, translate).text
10
+ }
11
+
12
+ window.toTree = function(drakonJson, name, filename, language) {
13
+ setUpLanguage(language)
14
+ var result = drakonToStruct(drakonJson, name, filename, translate)
15
+ return JSON.stringify(result, null, 4)
16
+ }
package/src/index.js ADDED
@@ -0,0 +1,17 @@
1
+ const { drakonToPseudocode } = require('./drakonToPromptStruct');
2
+ const { htmlToString } = require("./nodeTools")
3
+ const { setUpLanguage, translate } = require("./translate")
4
+ const { drakonToStruct } = require("./drakonToStruct");
5
+
6
+ function toPseudocode(drakonJson, name, filename, language) {
7
+ setUpLanguage(language)
8
+ var result = drakonToPseudocode(drakonJson, name, filename, htmlToString, translate)
9
+ return result.text
10
+ }
11
+
12
+ function toTree(drakonJson, name, filename, language) {
13
+ setUpLanguage(language)
14
+ var result = drakonToStruct(drakonJson, name, filename, translate)
15
+ return JSON.stringify(result, null, 4)
16
+ }
17
+ module.exports = { toPseudocode, toTree }