kalc 0.7.0 → 0.8.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.
- data/README.md +64 -11
- data/lib/kalc/grammar.rb +10 -1
- data/lib/kalc/interpreter.rb +111 -9
- data/lib/kalc/version.rb +1 -1
- data/spec/interpreter_spec.rb +5 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -13,8 +13,9 @@ typing in `ikalc`
|
|
13
13
|
Syntax
|
14
14
|
------
|
15
15
|
|
16
|
-
kalc is a tiny language, and it has very little syntax. It
|
17
|
-
functions, variable assignment, and
|
16
|
+
kalc is a tiny language, and it has very little syntax. It supports
|
17
|
+
functions, variable assignment, arithmetic, and some string parsing
|
18
|
+
functionality.
|
18
19
|
|
19
20
|
Numbers
|
20
21
|
-------
|
@@ -39,8 +40,32 @@ Logical operations
|
|
39
40
|
Variable assignment
|
40
41
|
-------------------
|
41
42
|
|
43
|
+
The assignment operator `:=` is borrowed from Pascal. This decision was
|
44
|
+
made for practical reason. Since comparison operators are both `=` and
|
45
|
+
`==` and the language is expression-based, `=` could not be chosen.
|
46
|
+
|
47
|
+
Variables come in a lot of different flavors.
|
48
|
+
|
49
|
+
You have standard variables:
|
50
|
+
|
42
51
|
a := 1 b := 2 d := a + b
|
43
52
|
|
53
|
+
d := 100.0
|
54
|
+
|
55
|
+
and you have quoted variables:
|
56
|
+
|
57
|
+
'a' := 1 'b' := 2 'd' := 'a' + 'b'
|
58
|
+
|
59
|
+
Quoted variables can contain pretty much any character that you can
|
60
|
+
think of:
|
61
|
+
|
62
|
+
'Hello world' := 1
|
63
|
+
'Hello 2 the world' := 1
|
64
|
+
'This \' is a \' [string]' := 1
|
65
|
+
'!@#$%^& !@#$%^& *&^%$%^&*' := 1
|
66
|
+
|
67
|
+
The only real rule is that you need to escape a standard single quote.
|
68
|
+
|
44
69
|
Functions
|
45
70
|
---------
|
46
71
|
|
@@ -62,13 +87,38 @@ on the Excel formula functions, so you should see some overlap.
|
|
62
87
|
|
63
88
|
Some of them are:
|
64
89
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
90
|
+
# Conditional
|
91
|
+
IF, OR, NOT, AND
|
92
|
+
|
93
|
+
# Random number generation
|
94
|
+
RAND
|
95
|
+
|
96
|
+
# System level integration
|
97
|
+
SYSTEM
|
98
|
+
|
99
|
+
# Boolean functions
|
100
|
+
ISLOGICAL, ISNONTEXT, ISNUMBER, ISTEXT
|
101
|
+
|
102
|
+
# Math functions
|
103
|
+
ABS, DEGREES, PRODUCT, RADIANS, ROUND, SUM, TRUNC, LN, ACOS,
|
104
|
+
ACOSH, ASIN, ASINH, ATAN, ATANH, CBRT, COS, COSH, ERF, ERFC, EXP, GAMMA,
|
105
|
+
LGAMMA, LOG, LOG2, LOG10, SIN, SINH, SQRT, TAN, TANH
|
106
|
+
|
107
|
+
# String functions
|
108
|
+
CHOMP, CHOP, CHR, CLEAR, COUNT, DOWNCASE, HEX, INSPECT, INTERN, TO_SYM, LENGTH, SIZE,
|
109
|
+
LSTRIP, SUCC, NEXT, OCT, ORD, REVERSE, RSTRIP, STRIP, SWAPCASE, TO_C,
|
110
|
+
TO_F, TO_I, TO_R, UPCASE, CHAR, CLEAN, CODE, CONCATENATE, DOLLAR, EXACT,
|
111
|
+
FIND, FIXED, LEFT, LEN, LOWER, MID, PROPER, REPLACE, REPT, RIGHT,
|
112
|
+
SEARCH, SUBSTITUTE, TRIM, UPPER, VALUE
|
113
|
+
|
114
|
+
# Regular expression functions
|
115
|
+
REGEXP_MATCH, REGEXP_REPLACE
|
116
|
+
|
117
|
+
# Debugging
|
118
|
+
P, PP, PUTS,
|
119
|
+
|
120
|
+
# Other
|
121
|
+
PLUS_ONE, MINUS_ONE, SQUARE, CUBE, FIB, FACTORIAL,
|
72
122
|
TOWERS_OF_HANOI
|
73
123
|
|
74
124
|
Loops
|
@@ -77,7 +127,10 @@ Loops
|
|
77
127
|
There are no looping mechanisms to speak of, but recursion works (pretty) well.
|
78
128
|
**Note:** *go too deep and you might blow the stack!*
|
79
129
|
|
80
|
-
DEFINE SAMPLE_LOOP(a) {
|
130
|
+
DEFINE SAMPLE_LOOP(a) {
|
131
|
+
PUTS(a)
|
132
|
+
IF(a == 1, 1, SAMPLE_LOOP(a - 1))
|
133
|
+
}
|
81
134
|
|
82
135
|
There are a few examples of loops via recursion in `lib/stdlib.kalc`
|
83
136
|
|
@@ -97,7 +150,7 @@ For example, here is how you compare 2 variables:
|
|
97
150
|
|
98
151
|
(a == a) && (b = b) \> true
|
99
152
|
|
100
|
-
|
153
|
+
`=` and `==` are both equality operators. Use `:=` for assignment.
|
101
154
|
|
102
155
|
More inside
|
103
156
|
-----------
|
data/lib/kalc/grammar.rb
CHANGED
@@ -126,13 +126,22 @@ class Kalc::Grammar < Parslet::Parser
|
|
126
126
|
(alpha >> (alpha | digit).repeat) >> spaces?
|
127
127
|
}
|
128
128
|
|
129
|
+
rule(:quoted_identifier) {
|
130
|
+
str("'") >>
|
131
|
+
(
|
132
|
+
str('\\') >> any | str("'").absnt? >> any
|
133
|
+
).repeat(1) >>
|
134
|
+
str("'") >> spaces?
|
135
|
+
}
|
136
|
+
|
129
137
|
rule(:argument) {
|
130
138
|
identifier.as(:argument)
|
131
139
|
}
|
132
140
|
|
133
141
|
# Should look like 'Name'
|
134
142
|
rule(:variable) {
|
135
|
-
identifier | (str("'") >> spaces? >> identifier.repeat >> str("'")) >> spaces?
|
143
|
+
#identifier | (str("'") >> spaces? >> identifier.repeat >> str("'")) >> spaces?
|
144
|
+
identifier | quoted_identifier
|
136
145
|
}
|
137
146
|
|
138
147
|
# Does not self-evaluate
|
data/lib/kalc/interpreter.rb
CHANGED
@@ -115,15 +115,6 @@ module Kalc
|
|
115
115
|
})
|
116
116
|
end
|
117
117
|
|
118
|
-
# Change case of text
|
119
|
-
env.add_function(:UPPER, lambda { |cxt, val|
|
120
|
-
val.eval(cxt).upcase
|
121
|
-
})
|
122
|
-
|
123
|
-
env.add_function(:LOWER, lambda { |cxt, val|
|
124
|
-
val.eval(cxt).downcase
|
125
|
-
})
|
126
|
-
|
127
118
|
# Strings
|
128
119
|
string_funs =
|
129
120
|
[
|
@@ -145,6 +136,117 @@ module Kalc
|
|
145
136
|
})
|
146
137
|
end
|
147
138
|
|
139
|
+
env.add_function(:CHAR, lambda { |cxt, val|
|
140
|
+
Integer(val.eval(cxt)).chr
|
141
|
+
})
|
142
|
+
|
143
|
+
env.add_function(:CLEAN, lambda { |cxt, val|
|
144
|
+
val.eval(cxt).gsub(/\P{ASCII}/, '')
|
145
|
+
})
|
146
|
+
|
147
|
+
env.add_function(:CODE, lambda { |cxt, val|
|
148
|
+
val.eval(cxt).ord
|
149
|
+
})
|
150
|
+
|
151
|
+
env.add_function(:CONCATENATE, lambda { |cxt, *args|
|
152
|
+
args.map { |a| a.eval(cxt) }.join
|
153
|
+
})
|
154
|
+
|
155
|
+
env.add_function(:DOLLAR, lambda { |cxt, val, decimal_places|
|
156
|
+
"%.#{Integer(decimal_places.eval(cxt))}f" % Float(val.eval(cxt))
|
157
|
+
})
|
158
|
+
|
159
|
+
env.add_function(:EXACT, lambda { |cxt, string1, string2|
|
160
|
+
string1.eval(cxt) == string2.eval(cxt)
|
161
|
+
})
|
162
|
+
|
163
|
+
env.add_function(:FIND, lambda { |cxt, string1, string2, starting_pos|
|
164
|
+
start = Integer(starting_pos.eval(cxt)) - 1
|
165
|
+
string1.eval(cxt)[start..-1].index(string2.eval(cxt)) + 1
|
166
|
+
})
|
167
|
+
|
168
|
+
env.add_function(:FIXED, lambda { |cxt, val, decimal_places, no_commas|
|
169
|
+
output = "%.#{Integer(decimal_places.eval(cxt))}f" % Float(val.eval(cxt))
|
170
|
+
output = output.to_s.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse if no_commas.eval(cxt) == false
|
171
|
+
output
|
172
|
+
})
|
173
|
+
|
174
|
+
env.add_function(:LEFT, lambda { |cxt, val, number_of_characters|
|
175
|
+
num = Integer(number_of_characters.eval(cxt)) - 1
|
176
|
+
val.eval(cxt)[0..num]
|
177
|
+
})
|
178
|
+
|
179
|
+
env.add_function(:LEN, lambda { |cxt, val|
|
180
|
+
val.eval(cxt).length
|
181
|
+
})
|
182
|
+
|
183
|
+
env.add_function(:LOWER, lambda { |cxt, val|
|
184
|
+
val.eval(cxt).downcase
|
185
|
+
})
|
186
|
+
|
187
|
+
env.add_function(:MID, lambda { |cxt, val, start_position, number_of_characters|
|
188
|
+
|
189
|
+
start = Integer(start_position.eval(cxt)) - 1
|
190
|
+
chars = Integer(number_of_characters.eval(cxt)) - 1
|
191
|
+
|
192
|
+
val.eval(cxt)[start..chars + start]
|
193
|
+
})
|
194
|
+
|
195
|
+
env.add_function(:PROPER, lambda { |cxt, val|
|
196
|
+
val.eval(cxt).split(" ").map { |c| c.capitalize }.join(" ")
|
197
|
+
})
|
198
|
+
|
199
|
+
env.add_function(:REPLACE, lambda { |cxt, val, start_position, number_of_chars, new_text|
|
200
|
+
start = Integer(start_position.eval(cxt)) - 1
|
201
|
+
chars = Integer(number_of_chars.eval(cxt)) - 1
|
202
|
+
output = val.eval(cxt)
|
203
|
+
output[start..chars + start] = new_text.eval(cxt)
|
204
|
+
output
|
205
|
+
})
|
206
|
+
|
207
|
+
env.add_function(:REPT, lambda { |cxt, val, number_of_times|
|
208
|
+
val.eval(cxt) * Integer(number_of_times.eval(cxt))
|
209
|
+
})
|
210
|
+
|
211
|
+
env.add_function(:RIGHT, lambda { |cxt, val, number_of_characters|
|
212
|
+
num = -Integer(number_of_characters.eval(cxt))
|
213
|
+
val.eval(cxt)[num..-1]
|
214
|
+
})
|
215
|
+
|
216
|
+
env.add_function(:SEARCH, lambda { |cxt, string1, string2, start_position|
|
217
|
+
start = Integer(start_position.eval(cxt)) - 1
|
218
|
+
string2.eval(cxt).downcase[start..-1].index(string1.eval(cxt).downcase) + 1
|
219
|
+
})
|
220
|
+
|
221
|
+
env.add_function(:SUBSTITUTE, lambda { |cxt, val, old_text, new_text|
|
222
|
+
val.eval(cxt).gsub(old_text.eval(cxt), new_text.eval(cxt))
|
223
|
+
})
|
224
|
+
|
225
|
+
env.add_function(:TRIM, lambda { |cxt, val|
|
226
|
+
val.eval(cxt).strip
|
227
|
+
})
|
228
|
+
|
229
|
+
env.add_function(:UPPER, lambda { |cxt, val|
|
230
|
+
val.eval(cxt).upcase
|
231
|
+
})
|
232
|
+
|
233
|
+
env.add_function(:VALUE, lambda { |cxt, val|
|
234
|
+
val.eval(cxt).to_f
|
235
|
+
})
|
236
|
+
|
237
|
+
# Regular expressions
|
238
|
+
|
239
|
+
env.add_function(:REGEXP_MATCH, lambda { |cxt, val, regex|
|
240
|
+
r = regex.eval(cxt)
|
241
|
+
/#{r}/.match(val.eval(cxt))
|
242
|
+
})
|
243
|
+
|
244
|
+
env.add_function(:REGEXP_REPLACE, lambda { |cxt, val, regex, to_replace|
|
245
|
+
r = regex.eval(cxt)
|
246
|
+
|
247
|
+
val.eval(cxt).gsub(/#{r}/, to_replace.eval(cxt))
|
248
|
+
})
|
249
|
+
|
148
250
|
# Debug
|
149
251
|
env.add_function(:P, lambda { |cxt, *output|
|
150
252
|
p output
|
data/lib/kalc/version.rb
CHANGED
data/spec/interpreter_spec.rb
CHANGED
@@ -32,6 +32,11 @@ describe Kalc::Interpreter do
|
|
32
32
|
evaluate("a := 1; b := 2; 1 + b").should == 3
|
33
33
|
end
|
34
34
|
|
35
|
+
it "should be able to load quoted variables" do
|
36
|
+
evaluate("'Item (a)' := 1; 1 + 'Item (a)'").should == 2
|
37
|
+
evaluate("'a' := 1; 'b[a]' := 2 + 'a'; 1 + 'b[a]'").should == 4
|
38
|
+
end
|
39
|
+
|
35
40
|
it "should be able to load single quoted variables" do
|
36
41
|
evaluate("'a' := 1; 1 + 'a'").should == 2
|
37
42
|
evaluate("'a' := 1; 'b' := 2; 'b' + 'a'").should == 3
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kalc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|