lispcalc 0.1.1 → 0.2.1
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 +4 -4
- data/README.md +5 -3
- data/lib/lispcalc/functions.rb +73 -9
- data/lib/lispcalc/interpreter.rb +13 -23
- data/lib/lispcalc/lexer.rb +1 -1
- data/lib/lispcalc/version.rb +1 -1
- data/lib/lispcalc.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 356131522cd6dc5260a05d28d7f9777c99ddc9de820905a2efef5e47100f6bfe
|
4
|
+
data.tar.gz: 6c89731d470c30587d9fd46532d8ab45d43b764e33114df65d3a78adebefe1f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe40bc59263dd45e64c724b5fa7cd3c1254ce436c8dbdbfd1f2fa66f3b508e7a2d9baef0c04bf4862f92c075e1bfacde45c9c2c12877e05d7e4c66b7f1ae244d
|
7
|
+
data.tar.gz: 62763e99f31d08b1f7e61ffecf0b7ef89e8211ab1bcd890a25178ce8ff70f296080890913dfc90731f9d9762dc8edaa35be3440274b5dd1a3595355806f794dc
|
data/README.md
CHANGED
@@ -42,9 +42,11 @@ irb(main):012> (/ x y)))').to_s('F')
|
|
42
42
|
- [x] **Constants**: There are 2 available constants: `pi` and `e`, which hold the values of the corresponding mathematical constants. Unlike variables, you cannot change the value of a constant.
|
43
43
|
- [x] **Sequencing** (`(do expr1 expr2 ...)`): Evaluate each expression from left to right, returning the final value.
|
44
44
|
- [x] **Arithmetic operations** (`+`, `-`, `*`, `/`)
|
45
|
-
- [
|
46
|
-
- [
|
47
|
-
- [
|
45
|
+
- [x] **Powers** (`^`, `sq`)
|
46
|
+
- [x] **Roots** (`sqrt`, `cbrt`)
|
47
|
+
- [x] **Logarithms** (`log`, `log2`, `log10`, `ln`)
|
48
|
+
- [x] **Trigonometric functions** (`sin`, `cos`, `tan`, `asin`, `acos`, `atan`)
|
49
|
+
- [ ] **Clojure-style thread macros** (`->`, `->>`) ??
|
48
50
|
|
49
51
|
## Why?
|
50
52
|
|
data/lib/lispcalc/functions.rb
CHANGED
@@ -20,24 +20,88 @@ module Lispcalc
|
|
20
20
|
arithmetic_op(:/, args)
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
23
|
+
def ^(base, index)
|
24
|
+
resolve_symbol(base).to_d ** resolve_symbol(index).to_d
|
25
25
|
end
|
26
26
|
|
27
|
-
|
27
|
+
def sq(x)
|
28
|
+
resolve_symbol(x).to_d ** 2.to_d
|
29
|
+
end
|
28
30
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
31
|
+
def sqrt(x)
|
32
|
+
Math.sqrt(resolve_symbol(x)).to_d
|
33
|
+
end
|
34
|
+
|
35
|
+
def cbrt(x)
|
36
|
+
Math.cbrt(resolve_symbol(x)).to_d
|
37
|
+
end
|
38
|
+
|
39
|
+
def log(base, x)
|
40
|
+
Math.log(resolve_symbol(x), resolve_symbol(base)).to_d
|
41
|
+
end
|
42
|
+
|
43
|
+
def log10(x)
|
44
|
+
Math.log10(resolve_symbol(x)).to_d
|
45
|
+
end
|
46
|
+
|
47
|
+
def log2(x)
|
48
|
+
Math.log2(resolve_symbol(x)).to_d
|
49
|
+
end
|
50
|
+
|
51
|
+
def ln(x)
|
52
|
+
log(Math::E, x)
|
53
|
+
end
|
54
|
+
|
55
|
+
def sin(x)
|
56
|
+
Math.sin(resolve_symbol(x)).to_d
|
57
|
+
end
|
58
|
+
|
59
|
+
def cos(x)
|
60
|
+
Math.cos(resolve_symbol(x)).to_d
|
61
|
+
end
|
62
|
+
|
63
|
+
def tan(x)
|
64
|
+
Math.tan(resolve_symbol(x)).to_d
|
65
|
+
end
|
66
|
+
|
67
|
+
def asin(x)
|
68
|
+
Math.asin(resolve_symbol(x)).to_d
|
69
|
+
end
|
32
70
|
|
33
|
-
|
71
|
+
def acos(x)
|
72
|
+
Math.acos(resolve_symbol(x)).to_d
|
73
|
+
end
|
74
|
+
|
75
|
+
def atan(x)
|
76
|
+
Math.atan(resolve_symbol(x)).to_d
|
77
|
+
end
|
78
|
+
|
79
|
+
def do(*args)
|
80
|
+
resolve_symbol(args.last).to_d unless args.empty?
|
34
81
|
end
|
35
82
|
|
36
|
-
def
|
83
|
+
def set(name, value)
|
37
84
|
raise ContextError, "trying to change the value of the constant '#{name}'" if @ctx.const?(name)
|
38
85
|
raise ContextError, "undefined variable '#{name}'" unless @ctx.var?(name)
|
39
86
|
|
40
|
-
@ctx[name] = value
|
87
|
+
@ctx[name] = resolve_symbol(value).to_d
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# TODO: raise exception if symbol is not found in context
|
93
|
+
def resolve_symbol(x)
|
94
|
+
if x.instance_of?(Symbol)
|
95
|
+
@ctx[x]
|
96
|
+
else
|
97
|
+
x
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def arithmetic_op(op, args)
|
102
|
+
raise ArgumentError, "expecting at least 2 arguments, receiving #{args.size}" unless args.size >= 2
|
103
|
+
|
104
|
+
args.map { |x| resolve_symbol(x).to_d }.inject(op)
|
41
105
|
end
|
42
106
|
end
|
43
107
|
end
|
data/lib/lispcalc/interpreter.rb
CHANGED
@@ -8,6 +8,17 @@ module Lispcalc
|
|
8
8
|
@functions = Functions.new(ctx)
|
9
9
|
end
|
10
10
|
|
11
|
+
def eval(form)
|
12
|
+
case form
|
13
|
+
when Array
|
14
|
+
eval_list(form)
|
15
|
+
when Symbol, BigDecimal
|
16
|
+
form
|
17
|
+
else
|
18
|
+
raise UnknownFormError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
11
22
|
def eval_list(list)
|
12
23
|
raise ArgumentError, 'expecting an instance of Array' unless list.instance_of?(Array)
|
13
24
|
raise SyntaxError, 'empty list' if list.empty?
|
@@ -15,35 +26,14 @@ module Lispcalc
|
|
15
26
|
fn, *args = list
|
16
27
|
raise SyntaxError, 'the first element of a list must be a symbol' unless fn.instance_of?(Symbol)
|
17
28
|
|
18
|
-
call(fn,
|
19
|
-
end
|
20
|
-
|
21
|
-
alias eval eval_list
|
22
|
-
|
23
|
-
def eval_symbol(symbol)
|
24
|
-
@ctx[symbol] || symbol
|
29
|
+
call(fn, args.map { |arg| eval(arg) })
|
25
30
|
end
|
26
31
|
|
27
32
|
private
|
28
33
|
|
29
|
-
def eval_args(args)
|
30
|
-
args.map do |arg|
|
31
|
-
case arg
|
32
|
-
when Array
|
33
|
-
eval_list(arg)
|
34
|
-
when Symbol
|
35
|
-
eval_symbol(arg)
|
36
|
-
when BigDecimal
|
37
|
-
arg
|
38
|
-
else
|
39
|
-
raise UnknownFormError
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
34
|
def call(fn, args)
|
45
35
|
if fn =~ />(.+)/
|
46
|
-
@functions.
|
36
|
+
@functions.set(Regexp.last_match(1).to_sym, *args)
|
47
37
|
elsif Functions.public_method_defined?(fn, false)
|
48
38
|
@functions.send(fn, *args)
|
49
39
|
else
|
data/lib/lispcalc/lexer.rb
CHANGED
@@ -4,7 +4,7 @@ module Lispcalc
|
|
4
4
|
OPEN_PAREN = /\A\(/.freeze
|
5
5
|
CLOSE_PAREN = /\A\)/.freeze
|
6
6
|
NUMBER = /\A[+-]?([0-9]*[.])?[0-9]+/.freeze
|
7
|
-
SYMBOL = /\A[
|
7
|
+
SYMBOL = /\A[+\-*\/>^a-zA-Z][+\-*\/>a-zA-Z0-9]*/.freeze # TODO: review this regexp
|
8
8
|
|
9
9
|
def initialize(input)
|
10
10
|
@input = input
|
data/lib/lispcalc/version.rb
CHANGED
data/lib/lispcalc.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lispcalc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vicente Romero
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|