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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c7c0fed477b8bf5d13cef8946c4353f8471bed74f64411807276e5a33e8732d
4
- data.tar.gz: 31d87e2d4881eed6a31d49a3052a9a5b1596522fb3efad0b8747e47a694fa2fe
3
+ metadata.gz: 356131522cd6dc5260a05d28d7f9777c99ddc9de820905a2efef5e47100f6bfe
4
+ data.tar.gz: 6c89731d470c30587d9fd46532d8ab45d43b764e33114df65d3a78adebefe1f2
5
5
  SHA512:
6
- metadata.gz: 2055e5a74b6f446ec0aa4014d1db932518c87a9627b071f3ba114054c7d3adf34e2396a64293bbe557a47707437dd0e9821c0c38e3b6696b5c892c3fde572a2d
7
- data.tar.gz: 2c3a80ab4b3274ca13d4f33d0e1fdeea50f5afc4e23c4fd57e47b6d171ed66b50808b60a4e8a9b9a1acd47c09fb2c46a1ac2a1d9b63995935cf1e1cd53493b37
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
- - [ ] **Powers, roots and logarithms** (`^`, `^2`, `sqrt`, `log`, `log2`, `log10`, `ln`)
46
- - [ ] **Trigonometric functions** (`sin`, `cos`, `tan`, etc.)
47
- - [ ] **Thread macros** (`->`, `->>`)
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
 
@@ -20,24 +20,88 @@ module Lispcalc
20
20
  arithmetic_op(:/, args)
21
21
  end
22
22
 
23
- def do(*args)
24
- args.last unless args.empty?
23
+ def ^(base, index)
24
+ resolve_symbol(base).to_d ** resolve_symbol(index).to_d
25
25
  end
26
26
 
27
- private
27
+ def sq(x)
28
+ resolve_symbol(x).to_d ** 2.to_d
29
+ end
28
30
 
29
- def arithmetic_op(op, args)
30
- raise ArgumentError, "expecting at least 2 arguments, receiving #{args.size}" unless args.size >= 2
31
- raise ArgumentError, 'expecting all arguments to be BigDecimal' unless args.all?(BigDecimal)
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
- args.inject(op)
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 set_var(name, value)
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
@@ -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, eval_args(args))
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.send(:set_var, Regexp.last_match(1).to_sym, *args)
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
@@ -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[+\-*\/>a-zA-Z][+\-*\/>a-zA-Z0-9]*/.freeze # TODO: review this regexp
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
@@ -1,3 +1,3 @@
1
1
  module Lispcalc
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.2.1'.freeze
3
3
  end
data/lib/lispcalc.rb CHANGED
@@ -12,7 +12,7 @@ module Lispcalc
12
12
  def eval(input, ctx = Context.new)
13
13
  tokens = Lexer.tokenize(input)
14
14
  forms = Parser.new(tokens).parse
15
- Interpreter.new(ctx).eval(forms)
15
+ Interpreter.new(ctx).eval_list(forms)
16
16
  end
17
17
  end
18
18
  end
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.1.1
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-04-23 00:00:00.000000000 Z
11
+ date: 2025-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal