kalc 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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