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.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/Gemfile +10 -0
- data/README.md +58 -0
- data/Rakefile +10 -0
- data/bin/console +8 -0
- data/bin/setup +4 -0
- data/examples/accumulator.kap +16 -0
- data/examples/ackermann.kap +7 -0
- data/examples/anagram.kap +13 -0
- data/examples/binary-search.kap +16 -0
- data/examples/block-sort.kap +3 -0
- data/examples/calc.kap +10 -0
- data/examples/counter.kap +19 -0
- data/examples/describe.kap +9 -0
- data/examples/destructure.kap +4 -0
- data/examples/doto.kap +2 -0
- data/examples/egg-count.kap +10 -0
- data/examples/even-squares.kap +7 -0
- data/examples/exceptions.kap +14 -0
- data/examples/factorial.kap +8 -0
- data/examples/fib.kap +4 -0
- data/examples/fizzbuzz.kap +7 -0
- data/examples/gcd.kap +5 -0
- data/examples/greet.kap +2 -0
- data/examples/hashfn.kap +4 -0
- data/examples/kwargs.kap +1 -0
- data/examples/leap-year.kap +5 -0
- data/examples/match.kap +9 -0
- data/examples/min-max.kap +11 -0
- data/examples/module-header.kap +6 -0
- data/examples/palindrome.kap +8 -0
- data/examples/pangram.kap +9 -0
- data/examples/pcall.kap +9 -0
- data/examples/pipeline.kap +6 -0
- data/examples/points.kap +9 -0
- data/examples/primes.kap +8 -0
- data/examples/raindrops.kap +13 -0
- data/examples/record.kap +6 -0
- data/examples/regex.kap +9 -0
- data/examples/ruby-eval.kap +1 -0
- data/examples/safe-lookup.kap +6 -0
- data/examples/scopes.kap +18 -0
- data/examples/shapes.kap +9 -0
- data/examples/squares.kap +3 -0
- data/examples/stack.kap +19 -0
- data/examples/sum.kap +3 -0
- data/examples/tset.kap +4 -0
- data/examples/two-sum.kap +17 -0
- data/exe/kapfmt +6 -0
- data/exe/kapusta +6 -0
- data/kapfmt +4 -0
- data/kapusta.gemspec +25 -0
- data/lib/kapusta/ast.rb +76 -0
- data/lib/kapusta/cli.rb +61 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +178 -0
- data/lib/kapusta/compiler/emitter/collections.rb +245 -0
- data/lib/kapusta/compiler/emitter/control_flow.rb +168 -0
- data/lib/kapusta/compiler/emitter/expressions.rb +107 -0
- data/lib/kapusta/compiler/emitter/interop.rb +277 -0
- data/lib/kapusta/compiler/emitter/patterns.rb +105 -0
- data/lib/kapusta/compiler/emitter/support.rb +169 -0
- data/lib/kapusta/compiler/emitter.rb +45 -0
- data/lib/kapusta/compiler/normalizer.rb +122 -0
- data/lib/kapusta/compiler/runtime.rb +583 -0
- data/lib/kapusta/compiler.rb +47 -0
- data/lib/kapusta/env.rb +42 -0
- data/lib/kapusta/formatter.rb +685 -0
- data/lib/kapusta/reader.rb +215 -0
- data/lib/kapusta/support.rb +7 -0
- data/lib/kapusta/version.rb +5 -0
- data/lib/kapusta.rb +30 -0
- data/spec/cli_spec.rb +77 -0
- data/spec/examples_spec.rb +258 -0
- data/spec/formatter_spec.rb +176 -0
- data/spec/spec_helper.rb +12 -0
- 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
|