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 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 does support
17
- functions, variable assignment, and arithmetic.
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
- IF, OR, NOT, AND, RAND, SYSTEM, ISLOGICAL, ISNONTEXT, ISNUMBER, ISTEXT, ABS,
66
- DEGREES, PRODUCT, RADIANS, ROUND, SUM, TRUNC, LN, ACOS, ACOSH, ASIN, ASINH,
67
- ATAN, ATANH, CBRT, COS, COSH, ERF, ERFC, EXP, GAMMA, LGAMMA, LOG, LOG2,
68
- LOG10, SIN, SINH, SQRT, TAN, TANH, UPPER, LOWER, CHOMP, CHOP, CHR, CLEAR,
69
- COUNT, DOWNCASE, HEX, INSPECT, INTERN, TO_SYM, LENGTH, SIZE, LSTRIP, SUCC,
70
- NEXT, OCT, ORD, REVERSE, RSTRIP, STRIP, SWAPCASE, TO_C, TO_F, TO_I, TO_R,
71
- UPCASE, P, PP, PUTS, PLUS_ONE, MINUS_ONE, SQUARE, CUBE, FIB, FACTORIAL,
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) { PUTS(a) IF(a == 1, 1, SAMPLE_LOOP(a - 1)) }
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
- '=' and '==' are both equality operators. Use ':=' for assignment.
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Kalc
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -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.7.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-27 00:00:00.000000000 Z
12
+ date: 2012-08-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake