kapusta 0.8.0 → 0.10.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -2
  3. data/bin/check-all +17 -0
  4. data/bin/compile-examples +70 -0
  5. data/bin/fennel-parity +4 -37
  6. data/examples/account-lockout.kap +11 -0
  7. data/examples/circle.kap +16 -0
  8. data/examples/convert-temperature.kap +14 -0
  9. data/examples/count-effects.kap +13 -0
  10. data/examples/falling-drops.kap +12 -0
  11. data/examples/fennel-parity-examples.txt +40 -0
  12. data/examples/hit-counter.kap +17 -0
  13. data/examples/max-achievable.kap +8 -0
  14. data/examples/mruby-runtime-examples.txt +89 -0
  15. data/examples/number-of-1-bits.kap +13 -0
  16. data/examples/number-of-steps.kap +15 -0
  17. data/examples/parking-system.kap +18 -0
  18. data/examples/thread-styles.kap +41 -0
  19. data/examples/two-sum-hash.kap +11 -14
  20. data/examples/underscore-patterns.kap +1 -1
  21. data/lib/kapusta/ast.rb +1 -1
  22. data/lib/kapusta/cli.rb +11 -6
  23. data/lib/kapusta/compiler/emitter/bindings.rb +27 -2
  24. data/lib/kapusta/compiler/emitter/control_flow.rb +97 -14
  25. data/lib/kapusta/compiler/emitter/interop.rb +2 -0
  26. data/lib/kapusta/compiler/emitter/patterns.rb +125 -0
  27. data/lib/kapusta/compiler/emitter/support.rb +9 -2
  28. data/lib/kapusta/compiler/emitter.rb +2 -1
  29. data/lib/kapusta/compiler/normalizer.rb +22 -12
  30. data/lib/kapusta/compiler.rb +13 -4
  31. data/lib/kapusta/errors.rb +3 -0
  32. data/lib/kapusta/formatter.rb +9 -2
  33. data/lib/kapusta/lsp/scope_walker.rb +55 -5
  34. data/lib/kapusta/reader.rb +28 -0
  35. data/lib/kapusta/version.rb +1 -1
  36. data/lib/kapusta.rb +2 -2
  37. data/spec/cli_spec.rb +35 -0
  38. data/spec/examples_spec.rb +128 -0
  39. data/spec/lsp_spec.rb +86 -0
  40. data/spec/spec_helper.rb +9 -0
  41. metadata +14 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f64991d78295bfc4a633136af86607f2d9536f3e113ae9ea819a0c5708837427
4
- data.tar.gz: 0d656fcf3afefbb2c89acad7a0fa4de8a57f8a484c2ce887e5bc70e7493c71ea
3
+ metadata.gz: 3cafef3668504ca08a40ba087c4c2b754bfda02e6f61161f19bcf8d56a254e00
4
+ data.tar.gz: 501b99f6ec3bfa950865b63b26ec6fd7f5b64977bb86f41d4eff51957602cc84
5
5
  SHA512:
6
- metadata.gz: a27e51a26db8069c998d7e0b586beb4fda1505c2f13f572b82bbbda92bc79dbe6e3d4e308547877a0e455cd56b5c9f1c226830ca2645664dae65b47b552dc3a1
7
- data.tar.gz: e6e675b9be4492d7bf75e341915bfd5e022a733240a2b7c4139c2904a99577207a7b4d182208ad1706c31bcded3ebaf3a50e03d18f27eafc68373b96141ae8b0
6
+ metadata.gz: b774d8f0bbd772e223743a2ec844bec4949a6f1a5a545767c3b608c8ec0c2da055fe9537c68a3cf57573ac80ed75d11d85884245b4fcbd401af16646aaab5283
7
+ data.tar.gz: 10b6d3f30fdfc760569c40c70e22f2b63dcf124ea3de3cffca2f061642523bc7057c878b81460181950356193c5786a35df28d4fd2b4859c1c38bc2285392f40
data/README.md CHANGED
@@ -4,7 +4,7 @@ Kapusta is a Lisp for the Ruby runtime.
4
4
 
5
5
  It is inspired by [Fennel](https://fennel-lang.org). It is not intended to be production-ready like Clojure: that would be a lot of work, and Ruby is already a rich, elegant language.
6
6
 
7
- Instead, Kapusta aims to bring some of the simplicity and joy of Lisp to Ruby. Where Lua is intentionally minimal, and Fennel follows that design for good reason, Kapusta exists mostly for fun. You can use it for small apps, LeetCode, DragonRuby, or maybe even Rails.
7
+ Instead, Kapusta aims to bring some of the simplicity and joy of Lisp to Ruby. Where Fennel uses Lua's stdlib and runtime, Kapusta uses Ruby's. You can use it for small apps, LeetCode, DragonRuby, or maybe even Rails.
8
8
 
9
9
  For more information about Kapusta, see the official Fennel documentation and tutorials.
10
10
 
@@ -45,6 +45,12 @@ exe/kapusta --compile examples/fizzbuzz.kap > examples/fizzbuzz.rb
45
45
  ruby examples/fizzbuzz.rb
46
46
  ```
47
47
 
48
+ For mruby-compatible output, such as DragonRuby, use:
49
+
50
+ ```
51
+ exe/kapusta --compile --target=mruby examples/match.kap > examples/match-mruby.rb
52
+ ```
53
+
48
54
  ## Use from Ruby
49
55
 
50
56
  Ruby can require a `.kap` file and use it directly.
@@ -104,7 +110,7 @@ Kapusta keeps most core Fennel forms. The main differences come from Ruby's runt
104
110
  Kapusta-specific additions:
105
111
 
106
112
  - `module` and `class` for Ruby host structure, including file-header forms
107
- - `ivar` (`@var`) / `cvar` (`@@var`) / `gvar` (`$var`) escape hatches
113
+ - `ivar` or `@var` / `cvar` or `@@var` / `gvar` or `$var`
108
114
  - `try` / `catch` / `finally` plus `raise` for exceptions
109
115
  - `(ruby "...")` raw host escape hatch
110
116
  - a trailing symbol-keyed hash is emitted as Ruby keyword arguments
data/bin/check-all CHANGED
@@ -16,4 +16,21 @@ bin/fennel-parity
16
16
  echo "== compile examples =="
17
17
  bin/compile-examples
18
18
 
19
+ echo "== run mruby-compatible examples on mruby =="
20
+ MRUBY_RUNTIME_EXAMPLES=()
21
+ while IFS= read -r example || [[ -n "$example" ]]; do
22
+ [[ -n "$example" ]] && MRUBY_RUNTIME_EXAMPLES+=("$example")
23
+ done < examples/mruby-runtime-examples.txt
24
+
25
+ for example in "${MRUBY_RUNTIME_EXAMPLES[@]}"; do
26
+ ruby_file="examples-compiled/$example.rb"
27
+ file="examples-compiled/$example-mruby.rb"
28
+ if [[ ! -f "$file" ]]; then
29
+ file="$ruby_file"
30
+ fi
31
+
32
+ ruby "$ruby_file" >/dev/null
33
+ mruby "$file" >/dev/null
34
+ done
35
+
19
36
  echo "== Success! =="
data/bin/compile-examples CHANGED
@@ -4,6 +4,61 @@ set -euo pipefail
4
4
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
5
  SOURCE_DIR="$ROOT_DIR/examples"
6
6
  TARGET_DIR="$ROOT_DIR/examples-compiled"
7
+ tmp_files=()
8
+ MRUBY_RUNTIME_EXAMPLES=()
9
+
10
+ while IFS= read -r example || [[ -n "$example" ]]; do
11
+ [[ -n "$example" ]] && MRUBY_RUNTIME_EXAMPLES+=("$example")
12
+ done < "$SOURCE_DIR/mruby-runtime-examples.txt"
13
+
14
+ cleanup() {
15
+ rm -f "${tmp_files[@]}"
16
+ }
17
+ trap cleanup EXIT
18
+
19
+ is_mruby_runtime_example() {
20
+ local name="$1"
21
+
22
+ for example in "${MRUBY_RUNTIME_EXAMPLES[@]}"; do
23
+ [[ "$name" == "$example" ]] && return 0
24
+ done
25
+
26
+ return 1
27
+ }
28
+
29
+ needs_mruby_artifact() {
30
+ local name="$1"
31
+ local ruby_file="$2"
32
+ local ruby_stdout
33
+ local ruby_stderr
34
+ local mruby_stdout
35
+ local mruby_stderr
36
+
37
+ is_mruby_runtime_example "$name" || return 1
38
+
39
+ ruby_stdout="$(mktemp)"
40
+ ruby_stderr="$(mktemp)"
41
+ mruby_stdout="$(mktemp)"
42
+ mruby_stderr="$(mktemp)"
43
+ tmp_files+=("$ruby_stdout" "$ruby_stderr" "$mruby_stdout" "$mruby_stderr")
44
+
45
+ if ! ruby "$ruby_file" > "$ruby_stdout" 2> "$ruby_stderr"; then
46
+ printf 'Compiled example failed under ruby: %s\n' "$ruby_file" >&2
47
+ cat "$ruby_stderr" >&2
48
+ exit 1
49
+ fi
50
+
51
+ if ! mruby "$ruby_file" > "$mruby_stdout" 2> "$mruby_stderr"; then
52
+ return 0
53
+ fi
54
+
55
+ ! cmp -s "$ruby_stdout" "$mruby_stdout"
56
+ }
57
+
58
+ if (($# > 0)); then
59
+ printf 'usage: bin/compile-examples\n' >&2
60
+ exit 1
61
+ fi
7
62
 
8
63
  mkdir -p "$TARGET_DIR"
9
64
 
@@ -18,7 +73,22 @@ fi
18
73
  for kap_file in "${kap_files[@]}"; do
19
74
  name="$(basename "$kap_file" .kap)"
20
75
  ruby_file="$TARGET_DIR/$name.rb"
76
+ mruby_file="$TARGET_DIR/$name-mruby.rb"
21
77
 
22
78
  "$ROOT_DIR/exe/kapusta" --compile "$kap_file" > "$ruby_file"
23
79
  printf 'Compiled %s -> %s\n' "$kap_file" "$ruby_file"
80
+
81
+ if needs_mruby_artifact "$name" "$ruby_file"; then
82
+ tmp_mruby="$(mktemp)"
83
+ tmp_files+=("$tmp_mruby")
84
+ "$ROOT_DIR/exe/kapusta" --compile --target=mruby "$kap_file" > "$tmp_mruby"
85
+ if cmp -s "$ruby_file" "$tmp_mruby"; then
86
+ rm -f "$mruby_file"
87
+ else
88
+ mv "$tmp_mruby" "$mruby_file"
89
+ printf 'Compiled %s -> %s\n' "$kap_file" "$mruby_file"
90
+ fi
91
+ else
92
+ rm -f "$mruby_file"
93
+ fi
24
94
  done
data/bin/fennel-parity CHANGED
@@ -9,43 +9,10 @@ EXAMPLES_ERRORS = File.join(ROOT, 'examples-errors')
9
9
  KAPUSTA = File.join(ROOT, 'exe', 'kapusta')
10
10
  KAPFMT = File.join(ROOT, 'exe', 'kapfmt')
11
11
 
12
- COMPATIBLE = %w[
13
- ackermann.kap
14
- anonymous-greeter.kap
15
- classify-wallet.kap
16
- climbing-stairs.kap
17
- describe.kap
18
- destructure.kap
19
- even-squares.kap
20
- factorial.kap
21
- fib.kap
22
- fizzbuzz.kap
23
- gcd.kap
24
- hashfn.kap
25
- leap-year.kap
26
- macros-dbg.kap
27
- macros-import-helpers.kap
28
- macros-import-whole.kap
29
- macros-import.kap
30
- macros-multi.kap
31
- macros-swap.kap
32
- macros-thrice-if.kap
33
- macros-unless.kap
34
- macros-when-let.kap
35
- match.kap
36
- min-max.kap
37
- or-patterns.kap
38
- power-of-three.kap
39
- packet-router.kap
40
- points.kap
41
- primes.kap
42
- safe-lookup.kap
43
- shapes.kap
44
- squares.kap
45
- sum.kap
46
- tic-tac-toe.kap
47
- underscore-patterns.kap
48
- ].freeze
12
+ COMPATIBLE = File.readlines(File.join(EXAMPLES, 'fennel-parity-examples.txt'), chomp: true)
13
+ .reject(&:empty?)
14
+ .map { |name| "#{name}.kap" }
15
+ .freeze
49
16
 
50
17
  def run(cmd, file, chdir: EXAMPLES, env: {})
51
18
  out, err, status = Open3.capture3(env, cmd, file, chdir:)
@@ -0,0 +1,11 @@
1
+ (local max-missed 3)
2
+
3
+ (fn classify [guesses]
4
+ (var missed 0)
5
+ (each [_ g (ipairs guesses)]
6
+ (when (= g 1) (set missed (+ missed 1))))
7
+ (if (< missed max-missed) :ok :locked))
8
+
9
+ (print (classify [0 1 0 1]))
10
+ (print (classify [1 1 1]))
11
+ (print (classify [1 1 1 0]))
@@ -0,0 +1,16 @@
1
+ (class Circle)
2
+
3
+ (local pi 3.14159)
4
+
5
+ (fn initialize [radius]
6
+ (set @radius radius))
7
+
8
+ (fn area []
9
+ (* pi @radius @radius))
10
+
11
+ (fn circumference []
12
+ (* 2 pi @radius))
13
+
14
+ (let [c (Circle.new 5)]
15
+ (print (c.area))
16
+ (print (c.circumference)))
@@ -0,0 +1,14 @@
1
+ (local KELVIN-OFFSET 273.15)
2
+ (local FAHRENHEIT-SCALE 1.8)
3
+ (local FAHRENHEIT-OFFSET 32.0)
4
+
5
+ (fn convert-temperature [celsius]
6
+ (let [k (+ celsius KELVIN-OFFSET)
7
+ f (+ (* celsius FAHRENHEIT-SCALE) FAHRENHEIT-OFFSET)]
8
+ [k f]))
9
+
10
+ (let [[k f] (convert-temperature 36.5)]
11
+ (print k f))
12
+
13
+ (let [[k f] (convert-temperature 122.11)]
14
+ (print k f))
@@ -0,0 +1,13 @@
1
+ (fn count-by-kind [effects]
2
+ (var quits 0)
3
+ (var moves 0)
4
+ (each [_ effect (ipairs effects)]
5
+ (case (. effect :kind)
6
+ :quit (set quits (+ quits 1))
7
+ :move (set moves (+ moves 1))
8
+ _ nil))
9
+ [quits moves])
10
+
11
+ (let [effects [{:kind :move} {:kind :quit} {:kind :move} {:kind :other}]
12
+ [q m] (count-by-kind effects)]
13
+ (print q m))
@@ -0,0 +1,12 @@
1
+ (fn step [drops]
2
+ (each [_ drop (ipairs drops)]
3
+ (let [falling {:kind (. drop :kind)
4
+ :x (. drop :x)
5
+ :y (+ (. drop :y) (. drop :speed))
6
+ :w (. drop :w)
7
+ :h (. drop :h)
8
+ :speed (. drop :speed)}]
9
+ (print (. falling :kind) (. falling :x) (. falling :y)))))
10
+
11
+ (step [{:kind "rain" :x 0 :y 0 :w 1 :h 1 :speed 2}
12
+ {:kind "snow" :x 5 :y 1 :w 1 :h 1 :speed 1}])
@@ -0,0 +1,40 @@
1
+ ackermann
2
+ anonymous-greeter
3
+ classify-wallet
4
+ climbing-stairs
5
+ convert-temperature
6
+ count-effects
7
+ describe
8
+ destructure
9
+ even-squares
10
+ factorial
11
+ falling-drops
12
+ fib
13
+ fizzbuzz
14
+ gcd
15
+ hashfn
16
+ leap-year
17
+ macros-dbg
18
+ macros-import-helpers
19
+ macros-import-whole
20
+ macros-import
21
+ macros-multi
22
+ macros-swap
23
+ macros-thrice-if
24
+ macros-unless
25
+ macros-when-let
26
+ match
27
+ max-achievable
28
+ min-max
29
+ or-patterns
30
+ power-of-three
31
+ packet-router
32
+ points
33
+ primes
34
+ safe-lookup
35
+ shapes
36
+ squares
37
+ sum
38
+ thread-styles
39
+ tic-tac-toe
40
+ underscore-patterns
@@ -0,0 +1,17 @@
1
+ (class HitCounter)
2
+
3
+ (set @@total 0)
4
+
5
+ (fn initialize [name] (set @name name))
6
+
7
+ (fn hit []
8
+ (set @@total (+ @@total 1))
9
+ (set $last-hitter @name)
10
+ @@total)
11
+
12
+ (let [a (HitCounter.new "alice")
13
+ b (HitCounter.new "bob")]
14
+ (print (a.hit))
15
+ (print (b.hit))
16
+ (print (a.hit))
17
+ (print $last-hitter))
@@ -0,0 +1,8 @@
1
+ (local steps 2)
2
+
3
+ (fn max-achievable [num t]
4
+ (+ num (* steps t)))
5
+
6
+ (print (max-achievable 4 1))
7
+ (print (max-achievable 3 2))
8
+ (print (max-achievable 0 5))
@@ -0,0 +1,89 @@
1
+ account-lockout
2
+ accumulator
3
+ ackermann
4
+ anagram
5
+ anonymous-greeter
6
+ bank-account
7
+ baseball-game
8
+ best-time-to-buy-sell-stock
9
+ binary-search
10
+ binary-to-decimal
11
+ block-sort
12
+ calc
13
+ circle
14
+ classify-wallet
15
+ climbing-stairs
16
+ contains-duplicate
17
+ convert-temperature
18
+ count-effects
19
+ counter
20
+ describe
21
+ destructure
22
+ doto-hygiene
23
+ doto
24
+ egg-count
25
+ even-squares
26
+ exceptions
27
+ factorial
28
+ falling-drops
29
+ fib
30
+ fizzbuzz
31
+ gcd
32
+ greet
33
+ happy-number
34
+ hashfn
35
+ hit-counter
36
+ kwargs
37
+ leap-year
38
+ length-of-last-word
39
+ macros-dbg
40
+ macros-import-helpers
41
+ macros-import-whole
42
+ macros-import
43
+ macros-multi
44
+ macros-swap
45
+ macros-thrice-if
46
+ macros-unless
47
+ macros-when-let
48
+ majority-element
49
+ manhattan-distance
50
+ match
51
+ max-achievable
52
+ maximum-subarray
53
+ min-max
54
+ module-header
55
+ move-zeroes
56
+ number-of-1-bits
57
+ number-of-steps
58
+ or-patterns
59
+ packet-router
60
+ parking-system
61
+ pipeline
62
+ pivot-index
63
+ plus-one
64
+ points
65
+ power-of-three
66
+ primes
67
+ raindrops
68
+ record
69
+ reverse-integer
70
+ roman-to-integer
71
+ ruby-eval
72
+ safe-lookup
73
+ scopes
74
+ shapes
75
+ single-number
76
+ squares
77
+ stack
78
+ subtract-product-sum
79
+ sum
80
+ thread-styles
81
+ threading
82
+ tic-tac-toe
83
+ tset
84
+ two-sum-hash
85
+ two-sum
86
+ ugly-number
87
+ underscore-patterns
88
+ valid-parentheses-1
89
+ zoo-animal-1
@@ -0,0 +1,13 @@
1
+ (local BIT-WIDTH 32)
2
+
3
+ (fn hamming-weight [n]
4
+ (var x n)
5
+ (var count 0)
6
+ (for [_ 1 BIT-WIDTH]
7
+ (set count (+ count (% x 2)))
8
+ (set x (: (/ x 2) :floor)))
9
+ count)
10
+
11
+ (print (hamming-weight 11))
12
+ (print (hamming-weight 128))
13
+ (print (hamming-weight 4294967293))
@@ -0,0 +1,15 @@
1
+ (local target 0)
2
+
3
+ (fn steps-to-zero [n]
4
+ (var x n)
5
+ (var steps 0)
6
+ (while (not= x target)
7
+ (if (= 0 (% x 2))
8
+ (set x (: (/ x 2) :floor))
9
+ (set x (- x 1)))
10
+ (set steps (+ steps 1)))
11
+ steps)
12
+
13
+ (print (steps-to-zero 14))
14
+ (print (steps-to-zero 8))
15
+ (print (steps-to-zero 123))
@@ -0,0 +1,18 @@
1
+ (class ParkingSystem)
2
+
3
+ (fn initialize [big medium small]
4
+ (set @big big)
5
+ (set @medium medium)
6
+ (set @small small))
7
+
8
+ (fn add-car [car-type]
9
+ (if (and (= car-type 1) (> @big 0)) (do (set @big (- @big 1)) true)
10
+ (and (= car-type 2) (> @medium 0)) (do (set @medium (- @medium 1)) true)
11
+ (and (= car-type 3) (> @small 0)) (do (set @small (- @small 1)) true)
12
+ false))
13
+
14
+ (let [parking (ParkingSystem.new 1 1 0)]
15
+ (print (parking.add-car 1))
16
+ (print (parking.add-car 2))
17
+ (print (parking.add-car 3))
18
+ (print (parking.add-car 1)))
@@ -0,0 +1,41 @@
1
+ (fn positive? [n] (> n 0))
2
+ (fn square [n] (* n n))
3
+ (fn add [x y] (+ x y))
4
+ (fn mul [x y] (* x y))
5
+ (fn nonzero [n] (if (= n 0) nil n))
6
+ (fn non-empty [s] (if (= s "") nil s))
7
+ (fn wrap [s] (.. ">>" s "<<"))
8
+ (fn shout [s] (.. s "!"))
9
+
10
+ (fn keep [pred xs]
11
+ (icollect [_ x (ipairs xs)]
12
+ (when (pred x) x)))
13
+
14
+ (fn map [f xs]
15
+ (icollect [_ x (ipairs xs)]
16
+ (f x)))
17
+
18
+ (fn join [sep xs]
19
+ (var s "")
20
+ (each [_ x (ipairs xs)]
21
+ (if (= s "")
22
+ (set s (.. x))
23
+ (set s (.. s sep x))))
24
+ s)
25
+
26
+ (let [scores [-2 3 -1 4 0 5]
27
+ report (->> scores
28
+ (keep positive?)
29
+ (map square)
30
+ (join ", "))
31
+ adjusted (-> 7 (add 3) (mul 2) (square))
32
+ ok (-?> "hello" (non-empty) (wrap) (shout))
33
+ bad (-?> "" (non-empty) (wrap) (shout))
34
+ live (-?>> 5 (nonzero) (mul 3) (add 1))
35
+ dead (-?>> 0 (nonzero) (mul 3) (add 1))]
36
+ (print report)
37
+ (print adjusted)
38
+ (print ok)
39
+ (print bad)
40
+ (print live)
41
+ (print dead))
@@ -1,17 +1,14 @@
1
- (let [
2
- two-sum-hash
3
- (fn [nums target seen]
4
- (var i 0)
5
- (var answer nil)
6
- (while (and (< i (length nums)) (= answer nil))
7
- (let [n (. nums i)
8
- complement (- target n)]
9
- (if (seen.key? complement)
10
- (set answer [(. seen complement) i])
11
- (tset seen n i)))
12
- (set i (+ i 1)))
13
- answer)
14
- ]
1
+ (let [two-sum-hash (fn [nums target seen]
2
+ (var i 0)
3
+ (var answer nil)
4
+ (while (and (< i (length nums)) (= answer nil))
5
+ (let [n (. nums i)
6
+ complement (- target n)]
7
+ (if (seen.key? complement)
8
+ (set answer [(. seen complement) i])
9
+ (tset seen n i)))
10
+ (set i (+ i 1)))
11
+ answer)]
15
12
  (print (two-sum-hash [2 7 11 15] 9 {}))
16
13
  (print (two-sum-hash [3 2 4] 6 {}))
17
14
  (print (two-sum-hash [1 2 3] 10 {})))
@@ -1,7 +1,7 @@
1
1
  (fn loose [v]
2
2
  (case v
3
3
  _x _x
4
- _ "fallback"))
4
+ _ "not-reachable"))
5
5
 
6
6
  (fn strict [v]
7
7
  (case v
data/lib/kapusta/ast.rb CHANGED
@@ -117,7 +117,7 @@ module Kapusta
117
117
 
118
118
  class List
119
119
  attr_reader :items
120
- attr_accessor :multiline_source, :line, :column
120
+ attr_accessor :multiline_source, :line, :column, :sigil
121
121
 
122
122
  def initialize(items)
123
123
  @items = items
data/lib/kapusta/cli.rb CHANGED
@@ -5,7 +5,7 @@ require 'optparse'
5
5
 
6
6
  module Kapusta
7
7
  class CLI
8
- Options = Struct.new(:compile, :help, :version, keyword_init: true)
8
+ Options = Struct.new(:compile, :target, :help, :version, keyword_init: true)
9
9
 
10
10
  def self.start(argv = ARGV)
11
11
  args = argv.dup
@@ -21,8 +21,10 @@ module Kapusta
21
21
  return
22
22
  end
23
23
 
24
+ raise Kapusta::Error, Kapusta::Errors.format(:target_requires_compile) if options.target && !options.compile
25
+
24
26
  if options.compile
25
- compile_file(args)
27
+ compile_file(args, target: options.target)
26
28
  else
27
29
  run_file(args)
28
30
  end
@@ -32,11 +34,14 @@ module Kapusta
32
34
  end
33
35
 
34
36
  def self.parse_options(args)
35
- options = Options.new(compile: false, help: false, version: false)
37
+ options = Options.new(compile: false, target: nil, help: false, version: false)
36
38
 
37
39
  OptionParser.new do |parser|
38
40
  parser.banner = usage
39
41
  parser.on('-c', '--compile', 'Compile .kap to Ruby') { options.compile = true }
42
+ parser.on('--target=TARGET', 'Compile for mruby') do |target|
43
+ options.target = Kapusta::Compiler.normalize_target(target)
44
+ end
40
45
  parser.on('-h', '--help', 'Show this help') { options.help = true }
41
46
  parser.on('-v', '--version', 'Show version') { options.version = true }
42
47
  end.order!(args)
@@ -44,12 +49,12 @@ module Kapusta
44
49
  options
45
50
  end
46
51
 
47
- def self.compile_file(args)
52
+ def self.compile_file(args, target:)
48
53
  path = args.shift
49
54
  abort usage unless path
50
55
  abort usage unless args.empty?
51
56
 
52
- $stdout.write(Kapusta.compile(File.read(path), path:))
57
+ $stdout.write(Kapusta.compile(File.read(path), path:, target:))
53
58
  end
54
59
 
55
60
  def self.run_file(args)
@@ -64,7 +69,7 @@ module Kapusta
64
69
  end
65
70
 
66
71
  def self.usage
67
- 'usage: kapusta [--compile|-c] <file.kap> | kapusta <file.kap> [args...]'
72
+ 'usage: kapusta [--compile|-c] [--target=mruby] <file.kap> | kapusta <file.kap> [args...]'
68
73
  end
69
74
  end
70
75
  end