Skip to content

Project which started out as a calculator and evolved from there

License

Notifications You must be signed in to change notification settings

C0deH4cker/SuperCalc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SuperCalc

A mathematical expression parser and evaluator, plus more.

Currently, SuperCalc supports the following binary operators:

  • Addition (a + b)
  • Subtraction (a - b)
  • Multiplication (a * b)
  • Division (a / b)
  • Modulus (a % b)
  • Power (a ^ b)

SuperCalc also supports the following unary operators:

  • Negation (-x)
  • Factorial (x!)

Additionally, the following constants are defined:

  • pi
  • e
  • phi (golden ratio)

As well as the following mathematical functions:

  • sqrt(x)
  • abs(x) -> |x|
  • exp(x) -> ex
  • log(x) -> log10(x)
  • log2(x) -> log2(x)
  • ln(x) -> loge(x)
  • logbase(x, b) -> logb(x)
  • sin(x)
  • cos(x)
  • tan(x)
  • sec(x)
  • csc(x)
  • cot(x)
  • asin(x)
  • acos(x)
  • atan(x)
  • atan2(y, x) -> tan-1(y/x)
  • asec(x)
  • acsc(x)
  • acot(x)
  • sinh(x)
  • cosh(x)
  • tanh(x)
  • sech(x)
  • csch(x)
  • coth(x)
  • asinh(x)
  • acosh(x)
  • atanh(x)
  • asech(x)
  • acsch(x)
  • acoth(x)

SuperCalc likes to be as precise as it knows how, so floating point values are avoided as much as possible. Even for division and negative powers, SuperCalc will attempt to use fractions as a value type instead of floating point values.

Example of using fractions:

sc> (2 / 7) ^ 2
4/49 (0.0816326530612245)
sc> -(3 + 4!/7)^3
-91125/343 (-265.67055393586)

Variables are supported:

sc> a = 5
5
sc> a * 3
15
sc> b = 2 * a + 4
14
sc> ans + 4
18

Variable names may only contain alphanumeric characters and '_', but the first character may not be a number.

Shorthand notations for variable modification work as well using any of the binary operators:

sc> x = 4
4
sc> x += 3
7
sc> x *= 2
14
sc> x /= 3
14/3 (4.66666666666667)
sc> x *= 6
28
sc> x %= 2
0
sc> x += 8
8
sc> x ^= 2
64

Functions are also supported:

sc> f(x) = 3x + 4
sc> f(7)
25

Even with multiple arguments:

sc> f(x, y) = x + y
sc> f(3, 5)
8
sc> g(x, y, z) = f(x, y) * z
sc> g(1, 2, 3)
9

Function arguments do not affect variables:

sc> x = 7
7
sc> f(x) = 4x
sc> f(6)
24
sc> x
7

Functions can use global variables:

sc> myFunc(arg) = 3 * arg + glb
sc> glb = 7
7
sc> myFunc(4)
19
sc> glb += 3
10
sc> myFunc(4)
22
sc> other(x) = x + ans
sc> 8
8
sc> other(4)
12
sc> other(4)
16

Functions are variables:

sc> f(x, y) = x^2 - y^2
sc> g = f
sc> g(4, 3)
7
sc> h(x) = x^2
sc> getFunc() = h
sc> func = getFunc()
sc> func(3)
9

Vectors are supported as well:

sc> a = <1, 2, 3>
<1, 2, 3>
sc> b = <6, 5, 3>
<6, 5, 3>
sc> a - b
<-5, -3, 0>
sc> 6 * a - b
<0, 7, 15>

Vector specific builtins:

  • dot(vector1, vector2)
  • cross(vector1, vector2)
  • map(function, vector)
  • elem(vector, index) -> vector[index]
  • mag(vector) -> |vector|
  • norm(vector) -> Normalize the vector (vector / |vector|)

Vector functions:

sc> a = <1, 2, 3>
<1, 2, 3>
sc> b = <6, 5, 3>
<6, 5, 3>
sc> cross(a, b)
<-9, 15, -7>
sc> dot(a, b)
25

Function calls and vector subscripting is recursive:

sc> getVec() = <1, 2, 3, <4, 5>>
sc> getVec()[3][1]
5
sc> getFunc() = getVec
sc> getFunc()()[3][0]
4

Examples using map:

sc> a = <3, 4, 5>
<3, 4, 5>
sc> f(x) = 2x + 1
sc> map(f, a)
<7, 9, 11>
sc> angles = <0, pi/2, pi, 3pi/2, 2pi>
<0, 1.5707963267949, 3.14159265358979, 4.71238898038469, 6.28318530717959>
sc> map(sin, angles)
<0, 1, 0, -1, 0>
sc> add1(x) = 1 + x
sc> map(add1, map(sqrt, <1, 4, 9, 16, 20, 16/9>))
<2, 3, 4, 5, 5.47213595499958, 7/3>

Anonymous functions (syntax is similar to Rust's lambdas):

sc> map(|x| x + 1, <1, 2, 3>)
<2, 3, 4>

Vectors can have any dimension greater than or equal to one:

sc> a = <7, 2, 5.5, 7.6>
<7, 2, 5.5, 7.6>
sc> dot(a, <1, 2, 3, 4>)
57.9
sc> c = <1, 2>
<1, 2>
sc> sillyVec = <0>
<0>

Multiplying or dividing two vectors uses their components:

sc> <1, 2, 3> * <4, 7, 2>
<4, 14, 6>
sc> <1, 4, 5> / <4, 6, 2>
<1/4, 2/3, 5/2>

Vectors even support scalar operations:

sc> a = <4, 7, -3>
<4, 7, -3>
sc> a + 2
<4.92998110995055, 8.62746694241347, -3.69748583246292>
sc> ans / 3
<1.64332703665018, 2.87582231413782, -1.23249527748764>
sc> ans ^ 2
<2.70052374938548, 8.27035398249302, 1.51904460902933>
sc> 2 / ans
<0.740597078790777, 0.241827617564335, 1.31661702896138>

Variables can be deleted using ~:

sc> a = 4
4
sc> ~a
sc> a
Name Error: No variable named 'a' found.
sc> f(x) = 2x + 5
sc> ~f
sc> f(3)
Name Error: No variable named 'f' found.

And the interpreter can be reset using ~~~:

sc> a = 4
4
sc> b = 7
7
sc> f(x) = 4x - 3!
sc> ~~~
sc> a
Name Error: No variable named 'a' found.
sc> f(3)
Name Error: No variable named 'f' found.

Error messages attempt to be clear:

sc> 3 / (1 - 1)
Math Error: Division by zero.
sc> sqrt()
Type Error: Builtin 'sqrt' expects 1 argument, not 0.
sc> sqrt(4, 3)
Type Error: Builtin 'sqrt' expects 1 argument, not 2.
sc> sqrt(-1)
Math Error: Builtin function 'sqrt' returned an invalid value.
sc> a
Name Error: No variable named 'a' found.
sc> 17 $ 8
Syntax Error: Unexpected character: '$'.
sc> 8 +
Syntax Error: Premature end of input.
sc> pi(1, 2)
Type Error: Builtin 'pi' is not a function.

For curious users, there is a verbose printing feature. Starting the input with a ? will enable verbose printing of some form. Directly following the ? will be character codes to specify which types of verbose printing to use. A single ? by itself is the same as ?r. There must be at least one space after all character codes in the ? group before the expression to calculate starts. Multiple printing codes can be mixed in a single grouping.

  • r - Reprint output. Prints the expression out in normal mathematical form, using as few parentheses as possible.
  • p - Pretty reprint output. Same as r but will use certain Unicode characters instead of function names, such as for sqrt or π for pi.
  • w - Wrapped reprint output. Same as reprint, but wraps every binary operation in parentheses to clarify order of operations.
  • t - Tree output. Outputs the expression tree as parsed and stored internally.
  • x - XML output. Outputs the expression tree in XML format. More info coming soon.

Examples of verbose printing:

sc> ?w 3 + 4 - 2
(3 + 4) - 2

5
sc> ?rwt 8 - 9(6^2 + 3/7)^3
- (
  [a] 8
  [b] * (
    [a] 9
    [b] ^ (
      [a] + (
        [a] ^ (
          [a] 6
          [b] 2
        )
        [b] / (
          [a] 3
          [b] 7
        )
      )
      [b] 3
    )
  )
)

8 - (9 * (((6 ^ 2) + (3 / 7)) ^ 3))

8 - 9 * (6 ^ 2 + 3 / 7) ^ 3

-149229631/343 (-435071.810495627)

Another usage of SuperCalc's verbose output is with functions. For verbosity >= 1, SuperCalc will print a parenthesized version of the function declaration, showing the function's name, argument names, and body. For verbosity >= 2, SuperCalc will also print the function's name, argument names, and the parse tree of its body.

Examples of printing functions verbosely:

sc> f(x) = 3x
sc> ? f
f(x) = 3 * x
sc> ?p sqrt(4)
√(4)
2
sc> g(x, y) = x^2 - 2x*y + 1
sc> ?rt g
g(x, y) {
  + (
    [a] - (
      [a] ^ (
        [a] x
        [b] 2
      )
      [b] * (
        [a] * (
          [a] 2
          [b] x
        )
        [b] y
      )
    )
    [b] 1
  )
}

g(x, y) = x ^ 2 - 2 * x * y + 1
sc> ?x g
<vardata name="g">
  <func>
    <argnames>
      <arg name="x"/>
      <arg name="y"/>
    </argnames>
    <expr>
      <add>
        <sub>
          <pow>
            <var name="x"/>
            <int>2</int>
          </pow>
          <mul>
            <mul>
              <int>2</int>
              <var name="x"/>
            </mul>
            <var name="y"/>
          </mul>
        </sub>
        <int>1</int>
      </add>
    </expr>
  </func>
</vardata>

Turing Completeness?

It turns out that SuperCalc is accidentally Turing Complete, or at least I think so. For some examples of using SuperCalc like a functional programming language, refer to std/turing.scs.

Building

To compile SuperCalc, make sure you have installed GNU make and Clang. Then, clone the repo and run make:

git clone --recursive https://github.com/C0deH4cker/SuperCalc.git
cd SuperCalc
make
./sc

It compiles a lot faster if you run make in parallel like make -j8.

Tests

SuperCalc now has a unit testing suite, using the utest.h unit testing framework for C. Run the tests with make check.

Clang Static Analyzer

SuperCalc by default runs the Clang Static Analyzer during compilation. Annotations have been added to the source code to inform the analyzer about nullability as well as object ownership and lifetimes.