kapusta 0.1.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 (77) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +10 -0
  4. data/README.md +58 -0
  5. data/Rakefile +10 -0
  6. data/bin/console +8 -0
  7. data/bin/setup +4 -0
  8. data/examples/accumulator.kap +16 -0
  9. data/examples/ackermann.kap +7 -0
  10. data/examples/anagram.kap +13 -0
  11. data/examples/binary-search.kap +16 -0
  12. data/examples/block-sort.kap +3 -0
  13. data/examples/calc.kap +10 -0
  14. data/examples/counter.kap +19 -0
  15. data/examples/describe.kap +9 -0
  16. data/examples/destructure.kap +4 -0
  17. data/examples/doto.kap +2 -0
  18. data/examples/egg-count.kap +10 -0
  19. data/examples/even-squares.kap +7 -0
  20. data/examples/exceptions.kap +14 -0
  21. data/examples/factorial.kap +8 -0
  22. data/examples/fib.kap +4 -0
  23. data/examples/fizzbuzz.kap +7 -0
  24. data/examples/gcd.kap +5 -0
  25. data/examples/greet.kap +2 -0
  26. data/examples/hashfn.kap +4 -0
  27. data/examples/kwargs.kap +1 -0
  28. data/examples/leap-year.kap +5 -0
  29. data/examples/match.kap +9 -0
  30. data/examples/min-max.kap +11 -0
  31. data/examples/module-header.kap +6 -0
  32. data/examples/palindrome.kap +8 -0
  33. data/examples/pangram.kap +9 -0
  34. data/examples/pcall.kap +9 -0
  35. data/examples/pipeline.kap +6 -0
  36. data/examples/points.kap +9 -0
  37. data/examples/primes.kap +8 -0
  38. data/examples/raindrops.kap +13 -0
  39. data/examples/record.kap +6 -0
  40. data/examples/regex.kap +9 -0
  41. data/examples/ruby-eval.kap +1 -0
  42. data/examples/safe-lookup.kap +6 -0
  43. data/examples/scopes.kap +18 -0
  44. data/examples/shapes.kap +9 -0
  45. data/examples/squares.kap +3 -0
  46. data/examples/stack.kap +19 -0
  47. data/examples/sum.kap +3 -0
  48. data/examples/tset.kap +4 -0
  49. data/examples/two-sum.kap +17 -0
  50. data/exe/kapfmt +6 -0
  51. data/exe/kapusta +6 -0
  52. data/kapfmt +4 -0
  53. data/kapusta.gemspec +25 -0
  54. data/lib/kapusta/ast.rb +76 -0
  55. data/lib/kapusta/cli.rb +61 -0
  56. data/lib/kapusta/compiler/emitter/bindings.rb +178 -0
  57. data/lib/kapusta/compiler/emitter/collections.rb +245 -0
  58. data/lib/kapusta/compiler/emitter/control_flow.rb +168 -0
  59. data/lib/kapusta/compiler/emitter/expressions.rb +107 -0
  60. data/lib/kapusta/compiler/emitter/interop.rb +277 -0
  61. data/lib/kapusta/compiler/emitter/patterns.rb +105 -0
  62. data/lib/kapusta/compiler/emitter/support.rb +169 -0
  63. data/lib/kapusta/compiler/emitter.rb +45 -0
  64. data/lib/kapusta/compiler/normalizer.rb +122 -0
  65. data/lib/kapusta/compiler/runtime.rb +583 -0
  66. data/lib/kapusta/compiler.rb +47 -0
  67. data/lib/kapusta/env.rb +42 -0
  68. data/lib/kapusta/formatter.rb +685 -0
  69. data/lib/kapusta/reader.rb +215 -0
  70. data/lib/kapusta/support.rb +7 -0
  71. data/lib/kapusta/version.rb +5 -0
  72. data/lib/kapusta.rb +30 -0
  73. data/spec/cli_spec.rb +77 -0
  74. data/spec/examples_spec.rb +258 -0
  75. data/spec/formatter_spec.rb +176 -0
  76. data/spec/spec_helper.rb +12 -0
  77. metadata +119 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'emitter/support'
4
+ require_relative 'emitter/expressions'
5
+ require_relative 'emitter/bindings'
6
+ require_relative 'emitter/control_flow'
7
+ require_relative 'emitter/collections'
8
+ require_relative 'emitter/interop'
9
+ require_relative 'emitter/patterns'
10
+
11
+ module Kapusta
12
+ module Compiler
13
+ class Emitter
14
+ RUBY_KEYWORDS = %w[
15
+ BEGIN END alias and begin break case class def defined? do else elsif end ensure false for if in
16
+ module next nil not or redo rescue retry return self super then true undef unless until when while yield
17
+ ].freeze
18
+
19
+ include EmitterModules::Support
20
+ include EmitterModules::Expressions
21
+ include EmitterModules::Bindings
22
+ include EmitterModules::ControlFlow
23
+ include EmitterModules::Collections
24
+ include EmitterModules::Interop
25
+ include EmitterModules::Patterns
26
+
27
+ def initialize(path:)
28
+ @path = path
29
+ @temp_index = 0
30
+ @runtime_helpers = []
31
+ end
32
+
33
+ def emit_file(forms)
34
+ env = Env.new
35
+ body = emit_forms_with_headers(forms, env, :toplevel)
36
+ helpers = Runtime.helper_source(@runtime_helpers)
37
+ [
38
+ '# frozen_string_literal: true',
39
+ helpers,
40
+ body
41
+ ].reject(&:empty?).join("\n\n") << "\n"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kapusta
4
+ module Compiler
5
+ class Normalizer
6
+ def normalize_all(forms)
7
+ forms.map { |form| normalize(form) }
8
+ end
9
+
10
+ def normalize(form)
11
+ case form
12
+ when List then normalize_list(form)
13
+ when Vec then Vec.new(form.items.map { |item| normalize(item) })
14
+ when HashLit
15
+ HashLit.new(form.pairs.map { |key, value| [normalize_hash_key(key), normalize(value)] })
16
+ else
17
+ form
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def normalize_hash_key(key)
24
+ case key
25
+ when List, Vec, HashLit then normalize(key)
26
+ else key
27
+ end
28
+ end
29
+
30
+ def normalize_list(list)
31
+ return list if list.empty?
32
+
33
+ head = list.head
34
+ items = list.items.map { |item| normalize(item) }
35
+ return List.new(items) unless head.is_a?(Sym)
36
+
37
+ case head.name
38
+ when 'when'
39
+ cond = items[1]
40
+ body = wrap_do(items[2..])
41
+ List.new([Sym.new('if'), cond, body, nil])
42
+ when 'unless'
43
+ cond = items[1]
44
+ body = wrap_do(items[2..])
45
+ List.new([Sym.new('if'), cond, nil, body])
46
+ when 'tset'
47
+ List.new([Sym.new('set'), List.new([Sym.new('.'), items[1], items[2]]), items[3]])
48
+ when 'pcall'
49
+ fn = items[1]
50
+ args = items[2..]
51
+ List.new([
52
+ Sym.new('try'),
53
+ List.new([Sym.new('values'), true, List.new([fn, *args])]),
54
+ List.new([Sym.new('catch'), Sym.new('StandardError'), Sym.new('e'),
55
+ List.new([Sym.new('values'), false, Sym.new('e')])])
56
+ ])
57
+ when 'xpcall'
58
+ fn = items[1]
59
+ handler = items[2]
60
+ args = items[3..]
61
+ List.new([
62
+ Sym.new('try'),
63
+ List.new([Sym.new('values'), true, List.new([fn, *args])]),
64
+ List.new([Sym.new('catch'), Sym.new('StandardError'), Sym.new('e'),
65
+ List.new([Sym.new('values'), false, List.new([handler, Sym.new('e')])])])
66
+ ])
67
+ when '->', '->>', '-?>', '-?>>'
68
+ normalize(thread(items[1..], head.name))
69
+ when 'doto'
70
+ normalize(doto(items[1..]))
71
+ else
72
+ List.new(items)
73
+ end
74
+ end
75
+
76
+ def wrap_do(forms)
77
+ return nil if forms.empty?
78
+ return forms.first if forms.length == 1
79
+
80
+ List.new([Sym.new('do'), *forms])
81
+ end
82
+
83
+ def thread(forms, kind)
84
+ value = forms.first
85
+ short = %w[-?> -?>>].include?(kind)
86
+ position = %w[-> -?>].include?(kind) ? :first : :last
87
+
88
+ forms[1..].reduce(value) do |memo, form|
89
+ threaded =
90
+ if form.is_a?(List)
91
+ if position == :first
92
+ List.new([form.items[0], memo, *form.items[1..]])
93
+ else
94
+ List.new([*form.items, memo])
95
+ end
96
+ else
97
+ List.new([form, memo])
98
+ end
99
+
100
+ if short
101
+ List.new([Sym.new('if'), List.new([Sym.new('='), memo, nil]), nil, threaded])
102
+ else
103
+ threaded
104
+ end
105
+ end
106
+ end
107
+
108
+ def doto(forms)
109
+ value = forms.first
110
+ temp = Sym.new('__doto__')
111
+ body = forms[1..].map do |form|
112
+ if form.is_a?(List)
113
+ List.new([form.items[0], temp, *form.items[1..]])
114
+ else
115
+ List.new([form, temp])
116
+ end
117
+ end
118
+ List.new([Sym.new('let'), Vec.new([temp, value]), *body, temp])
119
+ end
120
+ end
121
+ end
122
+ end