janet_sandbox 0.1.0 → 0.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37d030736271dabc94ac931ad2cd3af8c5cfc1ccf8570b47cba5ab1c5c9c3907
4
- data.tar.gz: fbd1407297ff9a11c2c561e262c368d404363c6097e936e76940d6b5dff6ec4f
3
+ metadata.gz: 5adec66ce21c7b331ee3909c772e72d400344d3290a3476dbcf227244a5d0ba1
4
+ data.tar.gz: c7b59916442200fe158320af167ec5eca20bc5e00b8eed23d9fd48520178ecad
5
5
  SHA512:
6
- metadata.gz: 4fcf025e8c2f3ae4b40db1e459d15ffa6e07188a758eb0c98c9254ad043aef99aa406b4e7539b521afd2cd221550fd5cfbba85d14eceee2226a2de67be7424fc
7
- data.tar.gz: c2b3a8624af767fbeff5bf457008337b999b6a1c0a542d22f3a85ceac8bdc3a6cec762de22360ca4cc66026652a3a59241289e7d6fa9050c55d73cf0f3220ac9
6
+ metadata.gz: 760a3cdf9d5c816f97b9eb1b537863a2ddbf0efe3d53c57ae285afadc15f1b50616d17fab7e022f70eccc3645a478f688eac8acfcace1f51e5f3897c6174cefa
7
+ data.tar.gz: 957146cdd157de22c3f5dda4f4ac7bfdda7782efe52962dcb32d07bdacecfe95b6d49643eaedd154b6c1b70f81735b4672c038b2e7331151294ffaa54090b36a
@@ -0,0 +1,283 @@
1
+ # Code Inspector DSL
2
+ #
3
+ # Provides functions for parsing and inspecting Janet code structure
4
+ # without executing it. Useful for building visual logic editors,
5
+ # syntax validators, and code analysis tools.
6
+ # This also provides a way to take that tree and turn it back into source text.
7
+ #
8
+ # Register in your application:
9
+ #
10
+ # JanetSandbox.configure do |config|
11
+ # config.register_dsl(:code_inspector, JanetSandbox::BuiltinDsls::CODE_INSPECTOR)
12
+ # config.enable_dsl(:code_inspector)
13
+ # end
14
+
15
+ (defn- node-type [node]
16
+ "Determine the type string for a Janet value"
17
+ (cond
18
+ (tuple? node) (if (= (tuple/type node) :brackets) "array" "call")
19
+ (array? node) "array"
20
+ (struct? node) "struct"
21
+ (table? node) "table"
22
+ (symbol? node) "symbol"
23
+ (keyword? node) "keyword"
24
+ (number? node) "number"
25
+ (string? node) "string"
26
+ (boolean? node) "boolean"
27
+ (nil? node) "nil"
28
+ (fiber? node) "fiber"
29
+ (function? node) "function"
30
+ (cfunction? node) "cfunction"
31
+ (buffer? node) "buffer"
32
+ "unknown"))
33
+
34
+ (defn to-tree [node]
35
+ "Convert a Janet value into a structured tree representation.
36
+
37
+ Returns a table with :type and type-specific fields:
38
+ - call: {:type \"call\" :op <string> :args [<children>]}
39
+ - array: {:type \"array\" :items [<children>]}
40
+ - struct/table: {:type \"struct\"|\"table\" :pairs [{:key <tree> :value <tree>} ...]}
41
+ - symbol: {:type \"symbol\" :name <string>}
42
+ - keyword: {:type \"keyword\" :name <string>}
43
+ - number: {:type \"number\" :value <number>}
44
+ - string: {:type \"string\" :value <string>}
45
+ - boolean: {:type \"boolean\" :value <boolean>}
46
+ - nil: {:type \"nil\"}
47
+
48
+ Example:
49
+ (to-tree (parse \"(if (> x 3) (foo) (bar))\"))
50
+ # => {:type \"call\" :op \"if\" :args [...]}"
51
+ (def t (node-type node))
52
+ (case t
53
+ "call"
54
+ {:type "call"
55
+ :op (string (first node))
56
+ :args (map to-tree (tuple/slice node 1))}
57
+
58
+ "array"
59
+ {:type "array"
60
+ :items (map to-tree node)}
61
+
62
+ "struct"
63
+ {:type "struct"
64
+ :pairs (seq [[k v] :pairs node]
65
+ {:key (to-tree k) :value (to-tree v)})}
66
+
67
+ "table"
68
+ {:type "table"
69
+ :pairs (seq [[k v] :pairs node]
70
+ {:key (to-tree k) :value (to-tree v)})}
71
+
72
+ "symbol"
73
+ {:type "symbol" :name (string node)}
74
+
75
+ "keyword"
76
+ {:type "keyword" :name (string node)}
77
+
78
+ "number"
79
+ {:type "number" :value node}
80
+
81
+ "string"
82
+ {:type "string" :value node}
83
+
84
+ "boolean"
85
+ {:type "boolean" :value node}
86
+
87
+ "nil"
88
+ {:type "nil"}
89
+
90
+ # fallback
91
+ {:type t :value (string node)}))
92
+
93
+ (defn parse-to-tree [code-string]
94
+ "Parse a Janet code string and return its tree representation.
95
+
96
+ Example:
97
+ (parse-to-tree \"(if (> x 3) (some-function) (other-function))\")
98
+ # => {:type \"call\" :op \"if\" :args [
99
+ # {:type \"call\" :op \">\" :args [
100
+ # {:type \"symbol\" :name \"x\"}
101
+ # {:type \"number\" :value 3}]}
102
+ # {:type \"call\" :op \"some-function\" :args []}
103
+ # {:type \"call\" :op \"other-function\" :args []}]}"
104
+ (to-tree (parse code-string)))
105
+
106
+ (defn parse-all-to-tree [code-string]
107
+ "Parse a Janet code string containing multiple expressions
108
+ and return an array of tree representations.
109
+
110
+ Example:
111
+ (parse-all-to-tree \"(def x 1) (+ x 2)\")
112
+ # => [{:type \"call\" :op \"def\" ...} {:type \"call\" :op \"+\" ...}]"
113
+ (def p (parser/new))
114
+ (parser/consume p code-string)
115
+ (def results @[])
116
+ (while (parser/has-more p)
117
+ (array/push results (to-tree (parser/produce p))))
118
+ results)
119
+
120
+ (defn extract-symbols [node]
121
+ "Extract all symbol names referenced in a parsed expression.
122
+ Returns an array of unique symbol name strings.
123
+
124
+ Example:
125
+ (extract-symbols (parse \"(if (> x y) (foo x) (bar y))\"))
126
+ # => [\"if\" \">\" \"x\" \"y\" \"foo\" \"bar\"]"
127
+ (def seen @{})
128
+ (def results @[])
129
+
130
+ (defn walk [n]
131
+ (cond
132
+ (symbol? n)
133
+ (let [name (string n)]
134
+ (unless (seen name)
135
+ (put seen name true)
136
+ (array/push results name)))
137
+
138
+ (tuple? n)
139
+ (each child n (walk child))
140
+
141
+ (array? n)
142
+ (each child n (walk child))
143
+
144
+ (or (struct? n) (table? n))
145
+ (eachp [k v] n
146
+ (walk k)
147
+ (walk v))))
148
+
149
+ (walk node)
150
+ results)
151
+
152
+ (defn extract-calls [node]
153
+ "Extract all function/macro call names from a parsed expression.
154
+ Returns an array of unique call names (first element of each parentheses tuple).
155
+ Does not include bracket tuples (e.g., parameter lists like [x y]).
156
+
157
+ Example:
158
+ (extract-calls (parse \"(if (> x 3) (foo) (bar))\"))
159
+ # => [\"if\" \">\" \"foo\" \"bar\"]"
160
+ (def seen @{})
161
+ (def results @[])
162
+
163
+ (defn walk [n]
164
+ (when (tuple? n)
165
+ # Only process parentheses tuples (actual calls), not bracket tuples
166
+ (when (= (tuple/type n) :parens)
167
+ (when (and (> (length n) 0) (symbol? (first n)))
168
+ (let [name (string (first n))]
169
+ (unless (seen name)
170
+ (put seen name true)
171
+ (array/push results name)))))
172
+ (each child n (walk child))))
173
+
174
+ (walk node)
175
+ results)
176
+
177
+ (defn- escape-string [s]
178
+ "Escape a string for Janet source output"
179
+ (def buf @"")
180
+ (each byte s
181
+ (case byte
182
+ (chr "\"") (buffer/push buf "\\\"")
183
+ (chr "\\") (buffer/push buf "\\\\")
184
+ (chr "\n") (buffer/push buf "\\n")
185
+ (chr "\r") (buffer/push buf "\\r")
186
+ (chr "\t") (buffer/push buf "\\t")
187
+ (buffer/push buf byte)))
188
+ (string buf))
189
+
190
+ (defn tree-to-source [tree]
191
+ "Convert a tree representation back to Janet source code.
192
+ This is the inverse of parse-to-tree.
193
+
194
+ Example:
195
+ (tree-to-source {:type \"call\" :op \"+\" :args [
196
+ {:type \"number\" :value 1}
197
+ {:type \"number\" :value 2}]})
198
+ # => \"(+ 1 2)\"
199
+
200
+ (def tree (parse-to-tree \"(if (> x 3) (foo) (bar))\"))
201
+ (tree-to-source tree)
202
+ # => \"(if (> x 3) (foo) (bar))\""
203
+ (case (tree :type)
204
+ "call"
205
+ (let [op (tree :op)
206
+ args (tree :args)]
207
+ (if (empty? args)
208
+ (string "(" op ")")
209
+ (string "(" op " " (string/join (map tree-to-source args) " ") ")")))
210
+
211
+ "array"
212
+ (let [items (tree :items)]
213
+ (if (empty? items)
214
+ "[]"
215
+ (string "[" (string/join (map tree-to-source items) " ") "]")))
216
+
217
+ "struct"
218
+ (let [pairs (tree :pairs)]
219
+ (if (empty? pairs)
220
+ "{}"
221
+ (string "{"
222
+ (string/join
223
+ (map (fn [p] (string (tree-to-source (p :key)) " " (tree-to-source (p :value))))
224
+ pairs)
225
+ " ")
226
+ "}")))
227
+
228
+ "table"
229
+ (let [pairs (tree :pairs)]
230
+ (if (empty? pairs)
231
+ "@{}"
232
+ (string "@{"
233
+ (string/join
234
+ (map (fn [p] (string (tree-to-source (p :key)) " " (tree-to-source (p :value))))
235
+ pairs)
236
+ " ")
237
+ "}")))
238
+
239
+ "symbol"
240
+ (tree :name)
241
+
242
+ "keyword"
243
+ (string ":" (tree :name))
244
+
245
+ "number"
246
+ (string (tree :value))
247
+
248
+ "string"
249
+ (string "\"" (escape-string (tree :value)) "\"")
250
+
251
+ "boolean"
252
+ (if (tree :value) "true" "false")
253
+
254
+ "nil"
255
+ "nil"
256
+
257
+ # fallback for unknown types
258
+ (string "<unknown:" (tree :type) ">")))
259
+
260
+ (defn validate-syntax [code-string]
261
+ "Check if a code string is syntactically valid Janet.
262
+ Returns {:valid true} on success, or {:valid false :error <message>} on failure.
263
+
264
+ Example:
265
+ (validate-syntax \"(+ 1 2)\") # => {:valid true}
266
+ (validate-syntax \"(+ 1 2\") # => {:valid false :error \"...\"}"
267
+ (def p (parser/new))
268
+ (def [ok err] (protect (parser/consume p code-string)))
269
+ (cond
270
+ # Parse error during consume
271
+ (not ok)
272
+ {:valid false :error (string err)}
273
+
274
+ # Check for parser errors
275
+ (parser/error p)
276
+ {:valid false :error (parser/error p)}
277
+
278
+ # Check for incomplete input (unclosed parens, strings, etc.)
279
+ (not= (parser/status p) :root)
280
+ {:valid false :error (string "unexpected end of input, parser status: " (parser/status p))}
281
+
282
+ # All good
283
+ {:valid true}))
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JanetSandbox
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: janet_sandbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Harris
@@ -91,6 +91,7 @@ files:
91
91
  - lib/janet_sandbox.rb
92
92
  - lib/janet_sandbox/builtin_dsls.rb
93
93
  - lib/janet_sandbox/configuration.rb
94
+ - lib/janet_sandbox/dsls/code_inspector.janet
94
95
  - lib/janet_sandbox/engine.rb
95
96
  - lib/janet_sandbox/errors.rb
96
97
  - lib/janet_sandbox/rails/railtie.rb