Roll ANGLE from ab187c35b601 to c5a3897631f8 (11 revisions)

https://chromium.googlesource.com/angle/angle.git/+log/ab187c35b601..c5a3897631f8

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/angle-android-autoroll
Please CC cnorthrop@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in ANGLE: https://bugs.chromium.org/p/angleproject/issues/entry

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Test: Presubmit checks will test this change.
Exempt-From-Owner-Approval: The autoroll bot does not require owner approval.
Change-Id: I91e99e3b40493c02d0b3dd111f52861f7acee5d4
diff --git a/DEPS b/DEPS
index dbdd821..59b8d17 100644
--- a/DEPS
+++ b/DEPS
@@ -34,7 +34,7 @@
   'checkout_android_native_support': 'checkout_android or checkout_chromeos',
 
   # Version of Chromium our Chromium-based DEPS are mirrored from.
-  'chromium_revision': 'f60c2130504ab3c123a845b2de40651eaa09ec99',
+  'chromium_revision': 'e1a053dddd92cd61254ed3702fe4cc1e1025c9a8',
   # We never want to checkout chromium,
   # but need a dummy DEPS entry for the autoroller
   'dummy_checkout_chromium': False,
@@ -84,7 +84,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling luci-go
   # and whatever else without interference from each other.
-  'luci_go': 'git_revision:7f42370cb3b75398bdb9ae0aabe215a70d40cd31',
+  'luci_go': 'git_revision:3e1f1f7a109ed8aefc7feba94fa737f0b5b4847e',
 
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_build-tools_version
@@ -119,7 +119,7 @@
 deps = {
 
   'build': {
-    'url': '{chromium_git}/chromium/src/build.git@5c325311f3b3840adf17809b5c54fb4124e79ab1',
+    'url': '{chromium_git}/chromium/src/build.git@e2b29e04ac858c5a7d81ab683d6f1aaac34966ea',
     'condition': 'not build_with_chromium',
   },
 
@@ -161,7 +161,7 @@
   },
 
   'buildtools/third_party/libc++abi/trunk': {
-    'url': '{chromium_git}/external/github.com/llvm/llvm-project/libcxxabi.git@7de86cbf37e0cf76ffe786ab6a21a728b1027bc2',
+    'url': '{chromium_git}/external/github.com/llvm/llvm-project/libcxxabi.git@fc376196a1b4365b542db5ef2a07dcd1b557aa35',
     'condition': 'not build_with_chromium',
   },
 
@@ -177,7 +177,7 @@
   },
 
   'testing': {
-    'url': '{chromium_git}/chromium/src/testing@f7a79060020b8d924432e531f4985e3ee220541e',
+    'url': '{chromium_git}/chromium/src/testing@eeafbf2b441e138e02fd0f512abaf57f966eb101',
     'condition': 'not build_with_chromium',
   },
 
@@ -327,7 +327,7 @@
   },
 
   'third_party/depot_tools': {
-    'url': '{chromium_git}/chromium/tools/depot_tools.git@a6bd3ae4469de807793e4940ddb21567e2f352c4',
+    'url': '{chromium_git}/chromium/tools/depot_tools.git@d579cbcbc257d93f16334163c9816e4e581b7be2',
     'condition': 'not build_with_chromium',
   },
 
@@ -491,7 +491,7 @@
   },
 
   'third_party/SwiftShader': {
-    'url': '{swiftshader_git}/SwiftShader@bac3c5559b2bcfea445b26956ea91f40dce930b9',
+    'url': '{swiftshader_git}/SwiftShader@aed9a7633481fa60ccd63a5cc37733f53b15d9dc',
     'condition': 'not build_with_chromium',
   },
 
@@ -531,7 +531,7 @@
   },
 
   'tools/clang': {
-    'url': '{chromium_git}/chromium/src/tools/clang.git@403247ef95e2b17530ab2a46980dac16245b7852',
+    'url': '{chromium_git}/chromium/src/tools/clang.git@1277083d6fef317ff83fceee3bc0364d9ba1009f',
     'condition': 'not build_with_chromium',
   },
 
@@ -566,7 +566,7 @@
   },
 
   'tools/mb': {
-    'url': '{chromium_git}/chromium/src/tools/mb@8723bc48f3a4e2d0c1883129a2140840df9a6c74',
+    'url': '{chromium_git}/chromium/src/tools/mb@56d74d41aa78d62f0d13e077180c5fec1f68c8fb',
     'condition': 'not build_with_chromium',
   },
 
@@ -581,7 +581,7 @@
   },
 
   'tools/perf': {
-    'url': '{chromium_git}/chromium/src/tools/perf@ddfcfd93c11d1dd1dde09836c80201bee7e6e557',
+    'url': '{chromium_git}/chromium/src/tools/perf@df796b313d9a0ceb35086ab4587e7a0b9a9aad32',
     'condition': 'not build_with_chromium',
   },
 
diff --git a/infra/README.md b/infra/README.md
index b66919b..f5b26c8 100644
--- a/infra/README.md
+++ b/infra/README.md
@@ -24,12 +24,12 @@
 
 Looking at an example build shows how tests are split up between machines. See for example:
 
-[`https://ci.chromium.org/ui/p/chromium/builders/try/mac-angle-try/454/overview`](https://ci.chromium.org/ui/p/chromium/builders/try/mac-angle-try/454/overview)
+[`https://ci.chromium.org/ui/p/angle/builders/ci/mac-rel/8123/overview`](https://ci.chromium.org/ui/p/angle/builders/ci/mac-rel/8123/overview)
 
-This build ran 81 test steps across 3 GPU families. In some cases (e.g.
+This build ran 68 test steps across 3 GPU families. In some cases (e.g.
 `angle_deqp_gles3_metal_tests`) the test is split up between multiple machines to
-run faster (in this case 4 different machines at once). This build took 15
-minutes to complete 1 hour of real automated testing.
+run faster (in this case 2 different machines at once). This build took 23
+minutes to complete 72 minutes of real automated testing.
 
 For more details on running and working with our test sets see the docs in [Contributing Code][Contrib].
 
diff --git a/src/common/string_utils.cpp b/src/common/string_utils.cpp
index 9b75462..6956c23 100644
--- a/src/common/string_utils.cpp
+++ b/src/common/string_utils.cpp
@@ -260,6 +260,18 @@
     return true;
 }
 
+int ReplaceAllSubstrings(std::string *str,
+                         const std::string &substring,
+                         const std::string &replacement)
+{
+    int count = 0;
+    while (ReplaceSubstring(str, substring, replacement))
+    {
+        count++;
+    }
+    return count;
+}
+
 std::vector<std::string> GetStringsFromEnvironmentVarOrAndroidProperty(const char *varName,
                                                                        const char *propertyName,
                                                                        const char *separator)
diff --git a/src/common/string_utils.h b/src/common/string_utils.h
index 245a9c6..fadfe17 100644
--- a/src/common/string_utils.h
+++ b/src/common/string_utils.h
@@ -98,6 +98,11 @@
                       const std::string &substring,
                       const std::string &replacement);
 
+// Replaces all substrings 'substring' in 'str' with 'replacement'. Returns count of replacements.
+int ReplaceAllSubstrings(std::string *str,
+                         const std::string &substring,
+                         const std::string &replacement);
+
 // Split up a string parsed from an environment variable.
 std::vector<std::string> GetStringsFromEnvironmentVarOrAndroidProperty(const char *varName,
                                                                        const char *propertyName,
diff --git a/src/compiler/translator/tree_ops/SeparateDeclarations.cpp b/src/compiler/translator/tree_ops/SeparateDeclarations.cpp
index 917b9cd..23e21c9 100644
--- a/src/compiler/translator/tree_ops/SeparateDeclarations.cpp
+++ b/src/compiler/translator/tree_ops/SeparateDeclarations.cpp
@@ -119,9 +119,11 @@
     {
         if (*replacementStructure == nullptr)
         {
-            structure = new TStructure(mSymbolTable, kEmptyImmutableString, &structure->fields(),
-                                       SymbolType::AngleInternal);
-            *replacementStructure = structure;
+            TStructure *newStructure =
+                new TStructure(mSymbolTable, kEmptyImmutableString, &structure->fields(),
+                               SymbolType::AngleInternal);
+            newStructure->setAtGlobalScope(structure->atGlobalScope());
+            *replacementStructure = structure = newStructure;
 
             TType *namedType = new TType(structure, true);
             namedType->setQualifier(EvqGlobal);
diff --git a/src/libANGLE/capture/FrameCapture.cpp b/src/libANGLE/capture/FrameCapture.cpp
index 84d3db6..82249d8 100644
--- a/src/libANGLE/capture/FrameCapture.cpp
+++ b/src/libANGLE/capture/FrameCapture.cpp
@@ -42,6 +42,7 @@
 #include "libANGLE/entry_points_utils.h"
 #include "libANGLE/queryconversions.h"
 #include "libANGLE/queryutils.h"
+#include "third_party/ceval/ceval.h"
 
 #define USE_SYSTEM_ZLIB
 #include "compression_utils_portable.h"
@@ -64,6 +65,7 @@
 constexpr char kCompressionVarName[]    = "ANGLE_CAPTURE_COMPRESSION";
 constexpr char kSerializeStateVarName[] = "ANGLE_CAPTURE_SERIALIZE_STATE";
 constexpr char kValidationVarName[]     = "ANGLE_CAPTURE_VALIDATION";
+constexpr char kValidationExprVarName[] = "ANGLE_CAPTURE_VALIDATION_EXPR";
 
 constexpr size_t kBinaryAlignment   = 16;
 constexpr size_t kFunctionSizeLimit = 5000;
@@ -80,6 +82,7 @@
 constexpr char kAndroidCaptureLabel[]   = "debug.angle.capture.label";
 constexpr char kAndroidCompression[]    = "debug.angle.capture.compression";
 constexpr char kAndroidValidation[]     = "debug.angle.capture.validation";
+constexpr char kAndroidValidationExpr[] = "debug.angle.capture.validation_expr";
 
 struct FramebufferCaptureFuncs
 {
@@ -4267,6 +4270,14 @@
         mValidateSerializedState = true;
     }
 
+    mValidationExpression =
+        GetEnvironmentVarOrUnCachedAndroidProperty(kValidationExprVarName, kAndroidValidationExpr);
+
+    if (!mValidationExpression.empty())
+    {
+        INFO() << "Validation expression is " << kValidationExprVarName;
+    }
+
     if (mFrameIndex == mCaptureStartFrame)
     {
         // Capture is starting from the first frame, so set the capture active to ensure all GLES
@@ -5295,6 +5306,26 @@
             mFrameCalls.emplace_back(std::move(call));
             maybeCapturePostCallUpdates(context);
         }
+
+        // Evaluate the validation expression to determine if we insert a validation checkpoint.
+        // This lets the user pick a subset of calls to check instead of checking every call.
+        if (mValidateSerializedState && !mValidationExpression.empty())
+        {
+            // Example substitution for frame #2, call #110:
+            // Before: (call == 2) && (frame >= 100) && (frame <= 120) && ((frame % 10) == 0)
+            // After:  (2 == 2) && (110 >= 100) && (110 <= 120) && ((110 % 10) == 0)
+            // Evaluates to 1.0.
+            std::string expression = mValidationExpression;
+
+            angle::ReplaceAllSubstrings(&expression, "frame", std::to_string(mFrameIndex));
+            angle::ReplaceAllSubstrings(&expression, "call", std::to_string(mFrameCalls.size()));
+
+            double result = ceval_result(expression);
+            if (result > 0)
+            {
+                CaptureValidateSerializedState(context, &mFrameCalls);
+            }
+        }
     }
     else
     {
diff --git a/src/libANGLE/capture/FrameCapture.h b/src/libANGLE/capture/FrameCapture.h
index 4dd79bb..8d5a664 100644
--- a/src/libANGLE/capture/FrameCapture.h
+++ b/src/libANGLE/capture/FrameCapture.h
@@ -548,6 +548,7 @@
     HasResourceTypeMap mHasResourceType;
     BufferDataMap mBufferDataMap;
     bool mValidateSerializedState = false;
+    std::string mValidationExpression;
     PackedEnumMap<ResourceIDType, uint32_t> mMaxAccessedResourceIDs;
 
     ResourceTracker mResourceTracker;
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index 99deae2..e7cb95a 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -2139,12 +2139,6 @@
         maxVersion = LimitVersionTo(maxVersion, {2, 0});
     }
 
-    // If the command buffer doesn't support queries, we can't support ES3.
-    if (!vk::CommandBuffer::SupportsQueries(mPhysicalDeviceFeatures))
-    {
-        maxVersion = LimitVersionTo(maxVersion, {2, 0});
-    }
-
     // If independentBlend is not supported, we can't have a mix of has-alpha and emulated-alpha
     // render targets in a framebuffer.  We also cannot perform masked clears of multiple render
     // targets.
diff --git a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
index 949f86a..28e1c77 100644
--- a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
@@ -409,20 +409,27 @@
 
     mNativeExtensions.vertexAttribType1010102OES = true;
 
-    // We use secondary command buffers almost everywhere and they require a feature to be
-    // able to execute in the presence of queries.  As a result, we won't support queries
-    // unless that feature is available.
-    mNativeExtensions.occlusionQueryBoolean =
-        vk::CommandBuffer::SupportsQueries(mPhysicalDeviceFeatures);
+    // Occlusion queries are natively supported in Vulkan.  ANGLE only issues this query inside a
+    // render pass, so there is no dependency to `inheritedQueries`.
+    mNativeExtensions.occlusionQueryBoolean = true;
 
     // From the Vulkan specs:
     // > The number of valid bits in a timestamp value is determined by the
     // > VkQueueFamilyProperties::timestampValidBits property of the queue on which the timestamp is
     // > written. Timestamps are supported on any queue which reports a non-zero value for
     // > timestampValidBits via vkGetPhysicalDeviceQueueFamilyProperties.
-    mNativeExtensions.disjointTimerQuery          = queueFamilyProperties.timestampValidBits > 0;
-    mNativeExtensions.queryCounterBitsTimeElapsed = queueFamilyProperties.timestampValidBits;
-    mNativeExtensions.queryCounterBitsTimestamp   = queueFamilyProperties.timestampValidBits;
+    //
+    // This query is applicable to render passes, but the `inheritedQueries` feature may not be
+    // present.  The extension is not exposed in that case.
+    // We use secondary command buffers almost everywhere and they require a feature to be
+    // able to execute in the presence of queries.  As a result, we won't support queries
+    // unless that feature is available.
+    if (vk::CommandBuffer::SupportsQueries(mPhysicalDeviceFeatures))
+    {
+        mNativeExtensions.disjointTimerQuery = queueFamilyProperties.timestampValidBits > 0;
+        mNativeExtensions.queryCounterBitsTimeElapsed = queueFamilyProperties.timestampValidBits;
+        mNativeExtensions.queryCounterBitsTimestamp   = queueFamilyProperties.timestampValidBits;
+    }
 
     mNativeExtensions.textureFilterAnisotropic =
         mPhysicalDeviceFeatures.samplerAnisotropy && limitsVk.maxSamplerAnisotropy > 1.0f;
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index b868eff..2867a5f 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -4030,6 +4030,12 @@
             {
                 return kVertexBufferBoundForTransformFeedback;
             }
+
+            // Validate that we are rendering with a linked program.
+            if (!program->isLinked())
+            {
+                return kProgramNotLinked;
+            }
         }
     }
 
diff --git a/src/libGLESv2.gni b/src/libGLESv2.gni
index 23cab68..e6a668e 100644
--- a/src/libGLESv2.gni
+++ b/src/libGLESv2.gni
@@ -552,6 +552,7 @@
   "src/libANGLE/capture/frame_capture_utils_autogen.cpp",
   "src/libANGLE/capture/gl_enum_utils.cpp",
   "src/libANGLE/capture/gl_enum_utils_autogen.cpp",
+  "src/third_party/ceval/ceval.h",
 ]
 
 libgl_sources = [
diff --git a/src/tests/angle_end2end_tests_expectations.txt b/src/tests/angle_end2end_tests_expectations.txt
index add33be..e70f64a 100644
--- a/src/tests/angle_end2end_tests_expectations.txt
+++ b/src/tests/angle_end2end_tests_expectations.txt
@@ -225,3 +225,7 @@
 6251 : MultiThreadingTest* = TIMEOUT
 
 6336 VULKAN : GetImageTest.InconsistentTexture2D/* = SKIP
+
+6358 D3D11 : GLSLTest_ES3.UnsuccessfulRelinkWithBindAttribLocation/* = SKIP
+6358 SWIFTSHADER : GLSLTest_ES3.UnsuccessfulRelinkWithBindAttribLocation/* = SKIP
+6358 METAL : GLSLTest_ES3.UnsuccessfulRelinkWithBindAttribLocation/* = SKIP
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index a286b0e..fc877a0 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -13732,6 +13732,54 @@
     ASSERT_GL_NO_ERROR();
     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
 }
+
+// Tests an unsuccessful re-link using glBindAttribLocation.
+TEST_P(GLSLTest_ES3, UnsuccessfulRelinkWithBindAttribLocation)
+{
+    // Make a simple program.
+    ANGLE_GL_PROGRAM(testProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
+
+    // Install the executable.
+    glUseProgram(testProgram);
+
+    // Re-link with a bad XFB varying and a bound attrib location.
+    const char *tfVaryings = "gl_FragColor";
+    glTransformFeedbackVaryings(testProgram, 1, &tfVaryings, GL_SEPARATE_ATTRIBS);
+    glBindAttribLocation(testProgram, 8, essl1_shaders::PositionAttrib());
+    glLinkProgram(testProgram);
+    GLint linkStatus = 999;
+    glGetProgramiv(testProgram, GL_LINK_STATUS, &linkStatus);
+    ASSERT_GL_NO_ERROR();
+    ASSERT_EQ(linkStatus, GL_FALSE);
+
+    // Under normal GL this is not an error.
+    glDrawArrays(GL_TRIANGLES, 79, 16);
+    EXPECT_GL_NO_ERROR();
+}
+
+// Tests an unsuccessful re-link using glBindAttribLocation under WebGL.
+TEST_P(WebGL2GLSLTest, UnsuccessfulRelinkWithBindAttribLocation)
+{
+    // Make a simple program.
+    ANGLE_GL_PROGRAM(testProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
+
+    // Install the executable.
+    glUseProgram(testProgram);
+
+    // Re-link with a bad XFB varying and a bound attrib location.
+    const char *tfVaryings = "gl_FragColor";
+    glTransformFeedbackVaryings(testProgram, 1, &tfVaryings, GL_SEPARATE_ATTRIBS);
+    glBindAttribLocation(testProgram, 8, essl1_shaders::PositionAttrib());
+    glLinkProgram(testProgram);
+    GLint linkStatus = 999;
+    glGetProgramiv(testProgram, GL_LINK_STATUS, &linkStatus);
+    ASSERT_GL_NO_ERROR();
+    ASSERT_EQ(linkStatus, GL_FALSE);
+
+    // Under WebGL this is an error.
+    glDrawArrays(GL_TRIANGLES, 79, 16);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+}
 }  // anonymous namespace
 
 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(GLSLTest, WithDirectSPIRVGeneration(ES2_VULKAN()));
diff --git a/src/tests/restricted_traces/retrace_restricted_traces.py b/src/tests/restricted_traces/retrace_restricted_traces.py
index 6905b13..1e93384 100755
--- a/src/tests/restricted_traces/retrace_restricted_traces.py
+++ b/src/tests/restricted_traces/retrace_restricted_traces.py
@@ -118,6 +118,9 @@
     parser.add_argument(
         '--validation', help='Enable state serialization validation calls.', action='store_true')
     parser.add_argument(
+        '--validation-expr',
+        help='Validation expression, used to add more validation checkpoints.')
+    parser.add_argument(
         '--limit',
         '--frame-limit',
         type=int,
@@ -170,6 +173,8 @@
             # Also turn on shader output init to ensure we have no undefined values.
             # This feature is also enabled in replay when using --validation.
             additional_env['ANGLE_FEATURE_OVERRIDES_ENABLED'] = 'forceInitShaderOutputVariables'
+        if args.validation_expr:
+            additional_env['ANGLE_CAPTURE_VALIDATION_EXPR'] = args.validation_expr
 
         env = {**os.environ.copy(), **additional_env}
 
diff --git a/src/tests/test_utils/runner/TestSuite_unittest.cpp b/src/tests/test_utils/runner/TestSuite_unittest.cpp
index f131053..db9885f 100644
--- a/src/tests/test_utils/runner/TestSuite_unittest.cpp
+++ b/src/tests/test_utils/runner/TestSuite_unittest.cpp
@@ -109,11 +109,8 @@
 {
     std::vector<std::string> extraArgs = {"--gtest_filter=MockTestSuiteTest.DISABLED_*"};
 
-    // TODO(crbug.com/1234124): Clang's profile runtime currently emits warnings to stderr, so we
-    // can't validate the stderr output in those builds. Remove this when that is fixed.
-    bool validateStderr = false;
     TestResults actual;
-    ASSERT_TRUE(runTestSuite(extraArgs, &actual, validateStderr));
+    ASSERT_TRUE(runTestSuite(extraArgs, &actual, true));
 
     std::map<TestIdentifier, TestResult> expectedResults = {
         {{"MockTestSuiteTest", "DISABLED_Pass"}, {TestResultType::Pass, 0.0}},
diff --git a/src/third_party/ceval/.clang-format b/src/third_party/ceval/.clang-format
new file mode 100644
index 0000000..9d15924
--- /dev/null
+++ b/src/third_party/ceval/.clang-format
@@ -0,0 +1,2 @@
+DisableFormat: true
+SortIncludes: false
diff --git a/src/third_party/ceval/LICENSE b/src/third_party/ceval/LICENSE
new file mode 100644
index 0000000..eda5f70
--- /dev/null
+++ b/src/third_party/ceval/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 e_t
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/third_party/ceval/README.chromium b/src/third_party/ceval/README.chromium
new file mode 100644
index 0000000..97158a6
--- /dev/null
+++ b/src/third_party/ceval/README.chromium
@@ -0,0 +1,8 @@
+Name: ceval
+URL: https://github.com/erstan/ceval-single-header
+Version: 1ed80ae78ba7bef1744b1ccdffc6c6802eeec8fa
+License: MIT
+License File: LICENSE
+
+Description:
+A C/C++ header for parsing and evaluation of arithmetic expressions.
diff --git a/src/third_party/ceval/README.md b/src/third_party/ceval/README.md
new file mode 100644
index 0000000..2d3befe
--- /dev/null
+++ b/src/third_party/ceval/README.md
@@ -0,0 +1,263 @@
+# ceval
+A C/C++ header for parsing and evaluation of arithmetic expressions.
+
+[README file is almost identical to that of the <a href="https://github.com/erstan/ceval#readme">ceval</a> library]
+
+## Functions accessibe from main()
+<table>
+<thead><th>Function</th><th>Argument(s)</th><th>Return Value</th></thead>
+<tbody>
+ <tr>
+  <td><code>ceval_result()</code></td>
+  <td>A mathematical expression in the form of a character array or a CPP string</td>
+  <td>The result of the expression as a floating point number</td>
+</tr>
+<tr>
+  <td><code>ceval_tree()</code></td>
+  <td>A mathematical expression in the form of a character array or a CPP string</td>
+  <td>The function prints the parse tree with each node properly indented depending on it's location in the tree structure</td>
+</tr>
+</tbody>
+</table>
+
+## Supported expressions
+Any valid combination of the following operators and functions, with floating point numbers as operands can be parsed by <b>ceval</b>. Parentheses can be used to override the default operator precedences. 
+
+* Arithematic operators
+
+`+` (addition), `-` (subtraction), `*` (multiplication), `/` (division), `%` (modulo), `**` (exponentiation), `//` (quotient)
+* Relational operators
+
+`==` (equal), `!=` (not equal), `<` (strictly less), `>` (strictly greater), `<=` (less or equal), `>=` (greater or equal) to compare the results of two expressions
+
+* Single-argument functions
+
+`exp()`, `sqrt()`, `cbrt()`, `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `sinh()`, `cosh()`, `tanh()`, `abs()`, `ceil()`, `floor()`, `log10()`, `ln()`, `deg2rad()`, `rad2deg()`, `signum()`, `int()`, `frac()`, `fact()`
+
+* Two-argument functions
+
+`pow()`, `atan2()`, `gcd()`, `hcf()`, `lcm()`, `log()` (generalized log(b, x) to any base `b`)
+
+* Pre-defined math constants
+
+`_pi`, `_e`
+
+...pre-defined constants are prefixed with an underscore
+
+* Logical operators
+
+`&&`, `||` and `!`
+
+* Bitwise operators 
+
+`&`, `|`, `^`, `<<`, `>>`, `~`
+
+* Other operators
+
+  * `,` (Comma operator)
+  Comma operator returns the result of it's rightmost operand
+  Ex: `2,3` would give `3`; `4,3,0` would be equal to `0`; and `cos(_pi/2,_pi/3,_pi)` would return `cos(_pi)` i.e, `-1`
+  * `e` (e-operator for scientific notation)
+  Using the binary `e` operator, we can use scientific notation in our arithmetic expressions
+  Ex: `0.0314` could be written as `3.14e-2`; `1230000` could be subsituted by `1.23e6`
+
+## Usage
+Include the ceval library using the `#include "PATH_TO_CEVAL.H"` directive your C/C++ project. 
+
+The code snippet given below is a console based interpreter that interactively takes in math expressions from stdin, and prints out their parse trees and results. 
+
+```
+//lang=c
+#include<stdio.h>
+#include<stdlib.h>
+
+#include "ceval.h"
+
+int main(int argc, char ** argv) {
+  char expr[100];
+  while (1) {
+    printf("In = ");
+    fgets(expr, 100, stdin);
+    if (!strcmp(expr, "exit\n")) {
+      break;
+    } else if (!strcmp(expr, "clear\n")) {
+      system("clear");
+      continue;
+    } else {
+      ceval_tree(expr);
+      printf("\nOut = %f\n\n", ceval_result(expr));
+    }
+  }
+  return 0;
+}
+```
+
+## Test Run
+```
+In = 3*7**2
+                2
+        **
+                7
+*
+        3
+
+Out = 147.000000
+
+
+In = (3.2+2.8)/2
+        2
+/
+                2.80
+        +
+                3.20
+
+Out = 3.000000
+
+
+In = _e**_pi>_pi**_e
+                2.72
+        **
+                3.14
+>
+                3.14
+        **
+                2.72
+
+Out = 1.000000
+
+
+In = 5.4%2
+        2
+%
+        5.40
+
+Out = 1.400000
+
+
+In = 5.4//2
+        2
+//
+        5.40
+
+Out = 2.000000
+
+
+In = 2*2.0+1.4
+        1.40
++
+                2
+        *
+                2
+
+Out = 5.400000
+
+
+In = (5/4+3*-5)+(sin(_pi))**2+(cos(_pi))**2
+                2
+        **
+                        3.14
+                cos
++
+                        2
+                **
+                                3.14
+                        sin
+        +
+                                        5
+                                -
+                        *
+                                3
+                +
+                                4
+                        /
+                                5
+
+Out = -12.750000
+
+
+In = 3,4,5,6
+        6
+,
+                5
+        ,
+                        4
+                ,
+                        3
+
+Out = 6.000000
+
+
+In = tanh(2/3)==(sinh(2/3)/cosh(2/3))
+                                3
+                        /
+                                2
+                cosh
+        /
+                                3
+                        /
+                                2
+                sinh
+==
+                        3
+                /
+                        2
+        tanh
+
+Out = 1.000000
+
+
+In = (2+3/3+(3+9.7))
+                9.70
+        +
+                3
++
+                        3
+                /
+                        3
+        +
+                2
+
+Out = 15.700000
+
+
+In = sin(_pi/2)+cos(_pi/2)+tan(_pi/2)
+                        2
+                /
+                        3.14
+        tan
++
+                                2
+                        /
+                                3.14
+                cos
+        +
+                                2
+                        /
+                                3.14
+                sin
+
+[ceval]: tan() is not defined for odd-integral multiples of _pi/2
+
+Out = nan
+
+
+In = asin(2)
+        2
+asin
+
+[ceval]: Numerical argument out of domain
+
+Out = nan
+
+
+In = exit
+... Program finished with exit code 0
+
+```
+## Note
+When the `ceval.h` file is included in a C-program, you might require the `-lm` flag to link `math.h`
+
+```shell
+gcc file.c -lm 
+```
+
diff --git a/src/third_party/ceval/ceval.h b/src/third_party/ceval/ceval.h
new file mode 100644
index 0000000..e8825ae
--- /dev/null
+++ b/src/third_party/ceval/ceval.h
@@ -0,0 +1,1072 @@
+#ifndef CEVAL
+#define CEVAL
+//functions accessible from main() 
+// - double ceval_result(char * inp) returns the result of valid math expression stored as a char array `inp`
+// - void ceval_tree(char * inp) prints the parse tree for the input expression `inp`
+
+#include<stdio.h>
+#include<string.h>
+#include<math.h>
+#include<ctype.h>
+#include<stdarg.h>
+/****************************************** TOKENS ***********************************************/
+typedef enum ceval_node_id {
+    CEVAL_WHITESPACE, CEVAL_OPENPAR, CEVAL_CLOSEPAR, CEVAL_COMMA, 
+    CEVAL_OR, CEVAL_AND, CEVAL_BIT_OR, CEVAL_BIT_XOR,
+    CEVAL_BIT_AND, CEVAL_EQUAL, CEVAL_NOTEQUAL,CEVAL_LESSER,
+    CEVAL_GREATER, CEVAL_LESSER_S, CEVAL_GREATER_S, CEVAL_BIT_LSHIFT, 
+    CEVAL_BIT_RSHIFT, CEVAL_PLUS, CEVAL_MINUS, CEVAL_TIMES, 
+    CEVAL_DIVIDE, CEVAL_MODULO, CEVAL_QUOTIENT, CEVAL_POW,
+    CEVAL_GCD, CEVAL_HCF, CEVAL_LCM, CEVAL_LOG,
+    CEVAL_ATAN2, CEVAL_SCI2DEC, CEVAL_POWFUN, 
+
+    CEVAL_ABS, CEVAL_EXP, CEVAL_SQRT,CEVAL_CBRT, 
+    CEVAL_LN, CEVAL_LOG10, CEVAL_CEIL, CEVAL_FLOOR, 
+    CEVAL_SIGNUM, CEVAL_FACTORIAL, CEVAL_INT, CEVAL_FRAC, 
+    CEVAL_DEG2RAD, CEVAL_RAD2DEG, CEVAL_SIN, CEVAL_COS, 
+    CEVAL_TAN, CEVAL_ASIN, CEVAL_ACOS, CEVAL_ATAN, 
+    CEVAL_SINH, CEVAL_COSH, CEVAL_TANH,CEVAL_NOT, 
+    CEVAL_BIT_NOT,CEVAL_POSSIGN, CEVAL_NEGSIGN, 
+    
+    CEVAL_NUMBER, CEVAL_CONST_PI, CEVAL_CONST_E
+} ceval_node_id;
+typedef enum ceval_token_prec_specifiers {
+// precedences :: <https://en.cppreference.com/w/cpp/language/operator_precedence>
+// these precision specifiers are ordered in the ascending order of their precedences
+// here, the higher precedence operators are evaluated first and end up at the bottom of the parse trees
+    CEVAL_PREC_IGNORE, 
+    // {' ', '\t', '\n', '\b', '\r'}
+    CEVAL_PREC_PARANTHESES,
+    // {'(', ')'}
+    CEVAL_PREC_COMMA_OPR,
+    // {','}
+    CEVAL_PREC_LOGICAL_OR_OPR,
+    // {'||'}
+    CEVAL_PREC_LOGICAL_AND_OPR,
+    // {'&&'}
+    CEVAL_PREC_BIT_OR_OPR,
+    // {'|'}
+    CEVAL_PREC_BIT_XOR_OPR,
+    // {'^'}
+    CEVAL_PREC_BIT_AND_OPR,
+    // {'&'}
+    CEVAL_PREC_EQUALITY_OPRS,
+    // {'==', '!='}
+    CEVAL_PREC_RELATIONAL_OPRS,
+    // {'<', '>', '<=', '>='}
+    CEVAL_PREC_BIT_SHIFT_OPRS,
+    // {'<<', '>>'}
+    CEVAL_PREC_ADDITIVE_OPRS,
+    // {'+', '-'}
+    CEVAL_PREC_SIGN_OPRS,
+    // {'+', '-'}
+    CEVAL_PREC_MULTIPLICATIVE_OPRS,
+    // {'*', '/', '%', '//'}
+    CEVAL_PREC_EXPONENTIATION_OPR,
+    // {'**'}
+    CEVAL_PREC_FUNCTIONS,
+    // {
+    //     'exp()', 'sqrt()', 'cbrt()', 'sin()',
+    //     'cos()', 'tan()', 'asin()', 'acos()', 
+    //     'atan()', 'sinh()', 'cosh()', 'tanh()', 
+    //     'abs()', 'ceil()', 'floor()', 'log10()', 
+    //     'ln()', 'deg2rad()', 'rad2deg()', 'signum()',
+    //     'int()', 'frac()', 'fact()', `pow()`, 
+    //     `atan2()`, `gcd()`, `hcf()`, `lcm()`,
+    //     `log()`
+    // }
+    CEVAL_PREC_NOT_OPRS,
+    // {'!', '~'}}
+    CEVAL_PREC_SCI2DEC_OPR,
+    // {'e'},
+    CEVAL_PREC_NUMERIC
+    // {'_pi', '_e', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
+} ceval_token_prec_specifiers;
+typedef enum ceval_token_type {
+    CEVAL_UNARY_OPERATOR,
+    CEVAL_BINARY_OPERATOR,
+    CEVAL_UNARY_FUNCTION,
+    CEVAL_BINARY_FUNCTION,
+    CEVAL_OTHER
+} ceval_token_type;
+typedef struct ceval_token_info_ {
+    ceval_node_id id;
+    const char * symbol; 
+    double prec;
+    ceval_token_type token_type;
+} ceval_token_info_; 
+ceval_token_info_ ceval_token_info[] = {
+    { CEVAL_WHITESPACE, " ", CEVAL_PREC_IGNORE, CEVAL_OTHER },
+    { CEVAL_WHITESPACE, "\n", CEVAL_PREC_IGNORE, CEVAL_OTHER },
+    { CEVAL_WHITESPACE, "\t", CEVAL_PREC_IGNORE, CEVAL_OTHER },
+    { CEVAL_WHITESPACE, "\r", CEVAL_PREC_IGNORE, CEVAL_OTHER },
+    { CEVAL_WHITESPACE, "\b", CEVAL_PREC_IGNORE, CEVAL_OTHER },
+
+    { CEVAL_DEG2RAD, "deg2rad", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_RAD2DEG, "rad2deg", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+
+    { CEVAL_SIGNUM, "signum", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+
+    { CEVAL_ATAN2, "atan2", CEVAL_PREC_FUNCTIONS, CEVAL_BINARY_FUNCTION }, 
+    { CEVAL_LOG10, "log10", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_FLOOR, "floor", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+
+    { CEVAL_SQRT, "sqrt", CEVAL_PREC_FUNCTIONS , CEVAL_UNARY_FUNCTION },
+    { CEVAL_CBRT, "cbrt", CEVAL_PREC_FUNCTIONS , CEVAL_UNARY_FUNCTION },
+    { CEVAL_CEIL, "ceil", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_FRAC, "frac", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_FACTORIAL, "fact", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION }, 
+    { CEVAL_SINH, "sinh", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION }, 
+    { CEVAL_COSH, "cosh", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_TANH, "tanh", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_ASIN, "asin", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION }, 
+    { CEVAL_ACOS, "acos", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_ATAN, "atan", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    
+    { CEVAL_POWFUN, "pow", CEVAL_PREC_FUNCTIONS, CEVAL_BINARY_FUNCTION }, 
+    { CEVAL_GCD, "gcd", CEVAL_PREC_FUNCTIONS, CEVAL_BINARY_FUNCTION }, 
+    { CEVAL_HCF, "hcf", CEVAL_PREC_FUNCTIONS, CEVAL_BINARY_FUNCTION }, 
+    { CEVAL_LCM, "lcm", CEVAL_PREC_FUNCTIONS, CEVAL_BINARY_FUNCTION }, 
+    { CEVAL_LOG, "log", CEVAL_PREC_FUNCTIONS, CEVAL_BINARY_FUNCTION }, 
+    { CEVAL_INT, "int", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_SIN, "sin", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_COS, "cos", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_TAN, "tan", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_ABS, "abs", CEVAL_PREC_FUNCTIONS , CEVAL_UNARY_FUNCTION },
+    { CEVAL_EXP, "exp", CEVAL_PREC_FUNCTIONS , CEVAL_UNARY_FUNCTION },
+    { CEVAL_CONST_PI, "_pi", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+
+    { CEVAL_CONST_E, "_e", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_LN, "ln", CEVAL_PREC_FUNCTIONS, CEVAL_UNARY_FUNCTION },
+    { CEVAL_OR, "||", CEVAL_PREC_LOGICAL_OR_OPR, CEVAL_BINARY_OPERATOR },
+    { CEVAL_AND, "&&", CEVAL_PREC_LOGICAL_AND_OPR, CEVAL_BINARY_OPERATOR },
+    { CEVAL_EQUAL, "==", CEVAL_PREC_EQUALITY_OPRS, CEVAL_BINARY_OPERATOR },
+    { CEVAL_NOTEQUAL, "!=", CEVAL_PREC_EQUALITY_OPRS, CEVAL_BINARY_OPERATOR },
+    { CEVAL_LESSER, "<=", CEVAL_PREC_RELATIONAL_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_GREATER, ">=", CEVAL_PREC_RELATIONAL_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_BIT_LSHIFT, "<<", CEVAL_PREC_BIT_SHIFT_OPRS, CEVAL_BINARY_OPERATOR},
+    { CEVAL_BIT_RSHIFT, ">>", CEVAL_PREC_BIT_SHIFT_OPRS, CEVAL_BINARY_OPERATOR},
+    { CEVAL_QUOTIENT, "//", CEVAL_PREC_MULTIPLICATIVE_OPRS , CEVAL_BINARY_OPERATOR }, 
+    { CEVAL_POW, "**", CEVAL_PREC_EXPONENTIATION_OPR , CEVAL_BINARY_OPERATOR },
+
+    { CEVAL_OPENPAR, "(", CEVAL_PREC_PARANTHESES, CEVAL_OTHER },
+    { CEVAL_CLOSEPAR, ")", CEVAL_PREC_PARANTHESES, CEVAL_OTHER },
+    { CEVAL_COMMA, ",", CEVAL_PREC_COMMA_OPR , CEVAL_BINARY_OPERATOR },
+    { CEVAL_BIT_OR, "|", CEVAL_PREC_BIT_OR_OPR, CEVAL_BINARY_OPERATOR},
+    { CEVAL_BIT_XOR, "^", CEVAL_PREC_BIT_XOR_OPR, CEVAL_BINARY_OPERATOR},
+    { CEVAL_BIT_AND, "&", CEVAL_PREC_BIT_AND_OPR, CEVAL_BINARY_OPERATOR},
+    { CEVAL_LESSER_S, "<", CEVAL_PREC_RELATIONAL_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_GREATER_S, ">", CEVAL_PREC_RELATIONAL_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_PLUS, "+", CEVAL_PREC_ADDITIVE_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_MINUS, "-", CEVAL_PREC_ADDITIVE_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_POSSIGN, "+", CEVAL_PREC_SIGN_OPRS, CEVAL_UNARY_OPERATOR }, 
+    { CEVAL_NEGSIGN, "-", CEVAL_PREC_SIGN_OPRS, CEVAL_UNARY_OPERATOR }, 
+    { CEVAL_TIMES, "*", CEVAL_PREC_MULTIPLICATIVE_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_DIVIDE, "/", CEVAL_PREC_MULTIPLICATIVE_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_MODULO, "%", CEVAL_PREC_MULTIPLICATIVE_OPRS , CEVAL_BINARY_OPERATOR },
+    { CEVAL_NOT, "!", CEVAL_PREC_NOT_OPRS, CEVAL_UNARY_FUNCTION},
+    { CEVAL_BIT_NOT, "~", CEVAL_PREC_NOT_OPRS, CEVAL_UNARY_OPERATOR},
+
+    { CEVAL_SCI2DEC, "e", CEVAL_PREC_SCI2DEC_OPR , CEVAL_BINARY_OPERATOR },
+    { CEVAL_NUMBER, "0", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "1", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "2", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "3", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "4", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "5", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "6", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "7", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "8", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+    { CEVAL_NUMBER, "9", CEVAL_PREC_NUMERIC, CEVAL_OTHER },
+}; 
+#ifndef CEVAL_TOKEN_TABLE_SIZE
+#define CEVAL_TOKEN_TABLE_SIZE sizeof(ceval_token_info) / sizeof(ceval_token_info[0])
+#endif
+int ceval_is_binary_opr(ceval_node_id id) {
+    for(unsigned int i = 0; i < CEVAL_TOKEN_TABLE_SIZE; i++) {
+        if (ceval_token_info[i].id == id && ceval_token_info[i].token_type == CEVAL_BINARY_OPERATOR) {
+            return 1;
+        }
+    }
+    return 0;
+}
+int ceval_is_binary_fun(ceval_node_id id) {
+    for(unsigned int i = 0; i < CEVAL_TOKEN_TABLE_SIZE; i++) {
+        if (ceval_token_info[i].id == id && ceval_token_info[i].token_type == CEVAL_BINARY_FUNCTION) {
+            return 1;
+        }
+    }
+    return 0;
+}
+const char * ceval_token_symbol(ceval_node_id id) {
+    for(unsigned int i = 0; i < CEVAL_TOKEN_TABLE_SIZE; i++) {
+        if (id == ceval_token_info[i].id) {
+            return ceval_token_info[i].symbol;
+        }
+    }
+return "";
+}
+ceval_node_id ceval_token_id(char * symbol) {
+    for(unsigned int i = 0; i < CEVAL_TOKEN_TABLE_SIZE; i++) {
+        if (!strcmp(ceval_token_info[i].symbol, symbol)) {
+            return ceval_token_info[i].id;
+        }
+    }
+return CEVAL_WHITESPACE;
+}
+double ceval_token_prec(ceval_node_id id) {
+    for(unsigned int i = 0; i < CEVAL_TOKEN_TABLE_SIZE; i++) {
+        if (id == ceval_token_info[i].id) {
+            return ceval_token_info[i].prec;
+        }
+    }
+return 0;
+}
+typedef struct ceval_node {
+    enum ceval_node_id id;
+    double pre;
+    double number;
+    struct ceval_node * left, * right, * parent;
+}
+ceval_node;
+#ifdef __cplusplus
+  #define CEVAL_CXX
+  #include<iostream>
+  #include<string>
+#endif
+/***************************************** !TOKENS *******************************************/
+
+/****************************************** FUNCTIONS ******************************************/
+//constant definitions
+#ifdef M_PI
+#define CEVAL_PI M_PI
+#else
+#define CEVAL_PI 3.14159265358979323846
+#endif
+#ifdef M_E
+#define CEVAL_E M_E
+#else
+#define CEVAL_E 2.71828182845904523536
+#endif
+
+#ifndef CEVAL_EPSILON
+#define CEVAL_EPSILON 1e-2
+#endif
+#ifndef CEVAL_DELTA
+#define CEVAL_DELTA 1e-6
+#endif
+#ifndef CEVAL_MAX_DIGITS
+#define CEVAL_MAX_DIGITS 15
+#endif
+//these can be defined by the user before the include directive depending the desired level of precision
+
+//helper function prototypes
+void ceval_error(const char * , ...);
+double ceval_gcd_binary(int, int);
+char * ceval_shrink(char * );
+
+//single argument funtion prototypes
+double ceval_signum(double);
+double ceval_asin(double);
+double ceval_acos(double);
+double ceval_atan(double);
+double ceval_sin(double);
+double ceval_cos(double);
+double ceval_tan(double);
+double ceval_sinh(double);
+double ceval_cosh(double);
+double ceval_tanh(double);
+double ceval_rad2deg(double);
+double ceval_deg2rad(double);
+double ceval_int_part(double);
+double ceval_frac_part(double);
+double ceval_log10(double);
+double ceval_ln(double);
+double ceval_exp(double);
+double ceval_factorial(double);
+double ceval_positive_sign(double);
+double ceval_negative_sign(double);
+double ceval_abs(double);
+double ceval_sqrt(double);
+double ceval_sqrt(double);
+double ceval_cbrt(double);
+double ceval_ceil(double);
+double ceval_floor(double);
+double ceval_not(double);
+double ceval_bit_not(double);
+
+//double argument function prototypes
+double ceval_sum(double, double, int);
+double ceval_diff(double, double, int);
+double ceval_prod(double, double, int);
+double ceval_div(double, double, int);
+double ceval_quotient(double, double, int);
+double ceval_modulus(double, double, int);
+double ceval_gcd(double, double, int);
+double ceval_hcf(double, double, int);
+double ceval_lcm(double, double, int);
+double ceval_log(double, double, int);
+double ceval_are_equal(double, double, int);
+double ceval_not_equal(double, double, int);
+double ceval_lesser(double, double, int);
+double ceval_greater(double, double, int);
+double ceval_lesser_s(double, double, int);
+double ceval_greater_s(double, double, int);
+double ceval_comma(double, double, int);
+double ceval_power(double, double, int);
+double ceval_atan2(double, double, int);
+double ceval_sci2dec(double, double, int);
+double ceval_and(double, double, int);
+double ceval_or(double, double, int);
+double ceval_bit_and(double, double, int);
+double ceval_bit_xor(double, double, int);
+double ceval_bit_or(double, double, int);
+double ceval_bit_lshift(double, double, int);
+double ceval_bit_rshift(double, double, int);
+
+//helper function definitions
+void ceval_error(const char* error_format_string, ...) {
+    #ifndef CEVAL_STOICAL
+        // start whining
+        printf("\n[ceval]: ");
+        va_list args;
+        va_start(args, error_format_string);
+        vprintf(error_format_string, args);
+        va_end(args);
+        printf("\n");
+    #endif
+}
+double ceval_gcd_binary(int a, int b) {
+    if (a == 0 && b == 0)
+        return 0;
+    while (b) {
+        a %= b;
+        b ^= a;
+        a ^= b;
+        b ^= a;
+    }
+    return a;
+}
+char * ceval_shrink(char * x) {
+    char * y = x;
+    unsigned int len = 0;
+    for (unsigned int i = 0; i < strlen(x); i++) {
+        if(x[i] == ' ' || x[i] == '\n' || x[i] == '\t' || x[i] == '\r') {
+            continue;
+        } else {
+            if(x[i]=='(' && x[i+1]==')') {
+                // empty pairs of parantheses are ignored
+                // simlar to c lang where {} are ignored as empty blocks of code
+                i++;
+                continue;
+            }
+            *(y + len) = (char)tolower(x[i]);
+            len++;
+        }
+    }
+    y[len] = '\0';
+    return y;
+}
+//single argument function definitions
+double( * single_arg_fun[])(double) = {
+    // double_arg_fun (first three tokens are whitespace and parantheses)
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, 
+    NULL, NULL, NULL, NULL, 
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, 
+    NULL, NULL, NULL,
+    // single_arg_fun
+    ceval_abs, ceval_exp, ceval_sqrt, ceval_cbrt,
+    ceval_ln, ceval_log10, ceval_ceil, ceval_floor,
+    ceval_signum, ceval_factorial, ceval_int_part, ceval_frac_part,
+    ceval_deg2rad, ceval_rad2deg, ceval_sin, ceval_cos,
+    ceval_tan, ceval_asin, ceval_acos, ceval_atan,
+    ceval_sinh, ceval_cosh, ceval_tanh, ceval_not,
+    ceval_bit_not, ceval_positive_sign, ceval_negative_sign,
+    // number and constant tokens
+    NULL, NULL, NULL
+};
+double ceval_signum(double x) {
+    return (x == 0) ? 0 :
+        (x > 0) ? 1 :
+        -1;
+}
+double ceval_asin(double x) {
+    if (x > 1 || x < -1) {
+        ceval_error("Numerical argument out of domain");
+        return NAN;
+    }
+    return asin(x);
+}
+double ceval_acos(double x) {
+    if (x > 1 || x < -1) {
+        ceval_error("Numerical argument out of domain");
+        return NAN;
+    }
+    return acos(x);
+}
+double ceval_atan(double x) {
+    return atan(x);
+}
+double ceval_sin(double x) {
+    double sin_val = sin(x);
+    //sin(pi) == 0.000000, but sin(pi-CEVAL_EPSILON) == -0.00000* and sin(pi+CEVAL_EPSILON) == +0.00000*
+    //since the precision of pi (approx) is limited, it often leads to -0.0000 printed out as a result
+    //thus, we assumse 0.0000 value for all |sin(x)|<=CEVAL_EPSILON
+    return (fabs(sin_val) <= CEVAL_EPSILON) ? 0 : sin_val;
+}
+double ceval_cos(double x) {
+    double cos_val = cos(x);
+    return (fabs(cos_val) <= CEVAL_EPSILON) ? 0 : cos_val;
+}
+double ceval_tan(double x) {
+    double tan_val = tan(x);
+    if (fabs(ceval_modulus(x - CEVAL_PI / 2, CEVAL_PI, 0)) <= CEVAL_DELTA) {
+        ceval_error("tan() is not defined for odd-integral multiples of pi/2");
+        return NAN;
+    }
+    return (fabs(tan_val) <= CEVAL_EPSILON) ? 0 : tan_val;
+}
+double ceval_rad2deg(double x) {
+    return x / CEVAL_PI * 180;
+}
+double ceval_deg2rad(double x) {
+    return x / 180 * CEVAL_PI;
+}
+double ceval_int_part(double x) {
+    double x_i;
+    modf(x, & x_i);
+    return x_i;
+}
+double ceval_frac_part(double x) {
+    double x_i, x_f;
+    x_f = modf(x, & x_i);
+    return x_f;
+}
+double ceval_log10(double x) {
+    return ceval_log(10, x, 0);
+}
+double ceval_ln(double x) {
+    return ceval_log(CEVAL_E, x, 0);
+}
+double ceval_exp(double x) {
+    return ceval_power(CEVAL_E, x, 0);
+}
+double ceval_factorial(double x) {
+    if (x < 0) {
+        ceval_error("Numerical argument out of domain");
+        return NAN;
+    }
+    return tgamma(x + 1);
+}
+double ceval_positive_sign(double x) {
+    return x;
+}
+double ceval_negative_sign(double x) {
+    return -x;
+}
+double ceval_abs(double x) {
+    return fabs(x);
+}
+double ceval_sqrt(double x) {
+    if (x >= 0) return sqrt(x);
+    ceval_error("sqrt(): can't operate on negative numbers");
+    return NAN;
+}
+double ceval_cbrt(double x) {
+    return cbrt(x);
+}
+double ceval_ceil(double x) {
+    return ceil(x);
+}
+double ceval_floor(double x) {
+    return floor(x);
+}
+double ceval_sinh(double x) {
+    return sinh(x);
+}
+double ceval_cosh(double x) {
+    return cosh(x);
+}
+double ceval_tanh(double x) {
+    return tanh(x);
+}
+double ceval_not(double x) {
+    return (double) ! (int)x;
+}
+double ceval_bit_not(double x) {
+    if(ceval_frac_part(x) == 0) {
+        return ~(int)x;
+    } else {
+        ceval_error("bit_not(): operand must be of integral type");
+        return NAN;
+    }
+}
+//double argument function definitions
+double( * double_arg_fun[])(double, double, int) = {
+    // double_arg_fun (first three tokens are whitespace and parantheses)
+    NULL, NULL, NULL, ceval_comma,
+    ceval_or, ceval_and, ceval_bit_or, ceval_bit_xor,
+    ceval_bit_and, ceval_are_equal, ceval_not_equal, ceval_lesser,
+    ceval_greater, ceval_lesser_s, ceval_greater_s, ceval_bit_lshift,
+    ceval_bit_rshift, ceval_sum, ceval_diff, ceval_prod,
+    ceval_div, ceval_modulus, ceval_quotient, ceval_power, 
+    ceval_gcd, ceval_hcf, ceval_lcm, ceval_log,
+    ceval_atan2, ceval_sci2dec, ceval_power,
+    // single_arg_fun
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL,
+    // number and constant tokens
+    NULL, NULL, NULL
+};
+double ceval_sum(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("sum(): function takes two arguments");
+        return NAN;
+    }
+    return a + b;
+}
+double ceval_diff(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("diff(): function takes two arguments");
+        return NAN;
+    }
+    return a - b;
+}
+double ceval_prod(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("prod(): function takes two arguments");
+        return NAN;
+    }
+    return a * b;
+}
+double ceval_div(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("div(): function takes two arguments");
+        return NAN;
+    }
+    if (b == 0 && a == 0) {
+        ceval_error("0/0 is indeterminate...");
+        ceval_error("Continuing evaluation with the assumption 0/0 = 1");
+        return 1;
+    } else if (b == 0) {
+        ceval_error("Division by 0 is not defined...");
+        ceval_error("Continuing evaluation with the assumption 1/0 = inf");
+        return a * INFINITY;
+    }
+    return a / b;
+}
+double ceval_modulus(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("modulo(): function takes two arguments");
+        return NAN;
+    }
+    if (b == 0) {
+        ceval_error("Division by 0 is not defined...");
+        ceval_error("Continuing evaluation with the assumption 1%0 = 0");
+        return 0;
+    }
+    return fmod(a, b);
+}
+double ceval_quotient(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("quotient(): function takes two arguments");
+        return NAN;
+    }
+    //a = b*q + r
+    //q = (a - r)/b
+    if (b == 0 && a == 0) {
+        ceval_error("0/0 is indeterminate...");
+        ceval_error("Continuing evaluation with the assumption 0/0 = 1");
+        return 1;
+
+    } else if (b == 0) {
+        ceval_error("Division by 0 is not defined...");
+        ceval_error("Continuing evaluation with the assumption 1/0 = inf");
+        return a * INFINITY;
+    }
+    return (a - ceval_modulus(a, b, 0)) / b;
+}
+double ceval_gcd(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("gcd(): function takes two arguments");
+        return NAN;
+    }
+    double a_f = ceval_frac_part(a),
+        b_f = ceval_frac_part(b);
+    int a_i = (int)ceval_int_part(a),
+        b_i = (int)ceval_int_part(b);
+    if (a_f == 0 && b_f == 0) {
+        return (double) ceval_gcd_binary(a_i, b_i);
+    } else {
+        ceval_error("gcd() takes only integral parameters");
+        return NAN;
+    }
+}
+double ceval_hcf(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("hcf(): function takes two arguments");
+        return NAN;
+    }
+    return ceval_gcd(a, b, 0);
+}
+double ceval_lcm(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("lcm(): function takes two arguments");
+        return NAN;
+    }
+    return a * b / ceval_gcd(a, b, 0);
+}
+double ceval_log(double b, double x, int arg_check) {
+    if (arg_check) {
+        ceval_error("log(): function takes two arguments");
+        return NAN;
+    }
+    if (b == 0) {
+        if (x == 0) {
+            ceval_error("log(0,0) is indeterminate");
+            return NAN;
+        } else {
+            return 0;
+        }
+    }
+    return log(x) / log(b);
+}
+double ceval_are_equal(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("==: function takes two arguments");
+        return NAN;
+    }
+    if (fabs(a - b) <= CEVAL_EPSILON) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+double ceval_not_equal(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("!=: function takes two arguments");
+        return NAN;
+    }
+    return (double)!(int)ceval_are_equal(a, b, 0);
+}
+double ceval_lesser(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("<=: function takes two arguments");
+        return NAN;
+    }
+    return (double)!(int)ceval_greater_s(a, b, 0);
+}
+double ceval_greater(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error(">=: function takes two arguments");
+        return NAN;
+    }
+    return (double)!(int)ceval_lesser_s(a, b, 0);
+}
+double ceval_lesser_s(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error("<: function takes two arguments");
+        return NAN;
+    }
+    return (b - a) >= CEVAL_EPSILON;
+}
+double ceval_greater_s(double a, double b, int arg_check) {
+    if (arg_check) {
+        ceval_error(">: function takes two arguments");
+        return NAN;
+    }
+    return (a - b) >= CEVAL_EPSILON;
+}
+double ceval_comma(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error(",: function takes two arguments");
+        return NAN;
+    }
+    return y;
+}
+double ceval_power(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("pow(): function takes two arguments");
+        return NAN;
+    }
+    if(x<0 && ceval_frac_part(y)!=0) {
+        ceval_error("pow(): negative numbers can only be raised to integral powers");
+        return NAN;
+    }
+    return pow(x, y);
+}
+double ceval_atan2(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("atan2(): function takes two arguments");
+        return NAN;
+    }
+    return atan2(x, y);
+}
+double ceval_sci2dec(double m, double e, int arg_check) {
+    if (arg_check) {
+        ceval_error("sci2dec(): function takes two arguments");
+        return NAN;
+    }
+    return (double) m * ceval_power(10, e, 0);
+}
+double ceval_and(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("and(): function takes two arguments");
+        return NAN;
+    }
+    return (double) ((int)x && (int)y);
+}
+double ceval_or(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("or(): function takes two arguments");
+        return NAN;
+    }
+    return (double) ((int)x || (int)y);
+}
+double ceval_bit_and(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("bit_and(): function takes two arguments");
+        return NAN;
+    }
+    if(ceval_frac_part(x) == 0 && ceval_frac_part(y) == 0) {
+        return (int)x & (int)y;
+    } else {
+        ceval_error("bit_and(): operands must be of integral type");
+        return NAN;
+    }
+}
+double ceval_bit_xor(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("bit_xor(): function takes two arguments");
+        return NAN;
+    }
+    if(ceval_frac_part(x) == 0 && ceval_frac_part(y) == 0) {
+        return (int)x ^ (int)y;
+    } else {
+        ceval_error("bit_xor(): operands must be of integral type");
+        return NAN;
+    }
+}
+double ceval_bit_or(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("bit_or(): function takes two arguments");
+        return NAN;
+    }
+    if(ceval_frac_part(x) == 0 && ceval_frac_part(y) == 0) {
+        return (int)x | (int)y;
+    } else {
+        ceval_error("bit_or(): operands must be of integral type");
+        return NAN;
+    }
+}
+double ceval_bit_lshift(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("bit_lshift(): function takes two arguments");
+        return NAN;
+    }
+    if(ceval_frac_part(x) == 0 && ceval_frac_part(y) == 0) {
+        return (int)x << (int)y;
+    } else {
+        ceval_error("bit_lshift(): operands must be of integral type");
+        return NAN;
+    }
+
+}
+double ceval_bit_rshift(double x, double y, int arg_check) {
+    if (arg_check) {
+        ceval_error("bit_rshift(): function takes two arguments");
+        return NAN;
+    }
+    if(ceval_frac_part(x) == 0 && ceval_frac_part(y) == 0) {
+        return (int)x >> (int)y;
+    } else {
+        ceval_error("bit_rshift(): operands must be of integral type");
+        return NAN;
+    }
+}
+/**************************************** !FUNCTIONS ********************************************/
+
+/***************************************** PARSE_TREE_CONSTRUCTION *******************************************/
+void * ceval_make_tree(char * );
+ceval_node * ceval_insert_node(ceval_node * , ceval_node, int);
+void ceval_print_tree(const void * );
+void ceval_print_node(const ceval_node * , int);
+void ceval_delete_node(ceval_node * );
+void ceval_delete_tree(void * );
+
+void ceval_delete_node(ceval_node * node) {
+    if (!node) return;
+    ceval_delete_node(node -> left);
+    ceval_delete_node(node -> right);
+    free(node);
+}
+void ceval_delete_tree(void * tree) {
+    ceval_delete_node((ceval_node * ) tree);
+}
+ceval_node * ceval_insert_node(ceval_node * current, ceval_node item, int isRightAssoc) {
+    if (item.id != CEVAL_OPENPAR &&
+        item.id != CEVAL_NEGSIGN &&
+        item.id != CEVAL_POSSIGN) {
+        if (isRightAssoc) {
+            while (current -> pre > item.pre) {
+                current = current -> parent;
+            }
+        } else {
+            while (current -> pre >= item.pre) {
+                current = current -> parent;
+            }
+        }
+    }
+    if (item.id == CEVAL_CLOSEPAR) {
+        ceval_node * parent_of_openpar = current -> parent;
+        parent_of_openpar -> right = current -> right;
+        if (current -> right) current -> right -> parent = parent_of_openpar;
+        free(current);
+        current = parent_of_openpar;
+
+        if (current -> right -> id == CEVAL_COMMA &&
+            ceval_is_binary_fun(current -> id)) {
+            ceval_node * address_of_comma = current -> right;
+            parent_of_openpar -> left = address_of_comma -> left;
+            address_of_comma -> left -> parent = parent_of_openpar;
+            parent_of_openpar -> right = address_of_comma -> right;
+            address_of_comma -> right -> parent = parent_of_openpar;
+            free(address_of_comma);
+        }
+        return current;
+    }
+    ceval_node * newnode = (ceval_node * ) malloc(sizeof(ceval_node));
+    * newnode = item;
+    newnode -> right = NULL;
+
+    newnode -> left = current -> right;
+    if (current -> right) current -> right -> parent = newnode;
+    current -> right = newnode;
+    newnode -> parent = current;
+    current = newnode;
+    return current;
+}
+
+void * ceval_make_tree(char * expression) {
+    if (expression == NULL) return NULL;
+    strcpy(expression, ceval_shrink(expression));
+    ceval_node root = {
+        CEVAL_OPENPAR,
+        ceval_token_prec(CEVAL_OPENPAR),
+        0,
+        NULL,
+        NULL,
+        NULL
+    };
+    ceval_node_id previous_id = CEVAL_OPENPAR;
+    ceval_node * current = & root;
+    int isRightAssoc = 0;
+    while (1) {
+        ceval_node node;
+        char c = * expression++;
+        if (c == '\0') break;
+        int token_found = -1;
+        char token[50];
+        unsigned int len = 0;
+        for(unsigned int i = 0; i < CEVAL_TOKEN_TABLE_SIZE; i++) {
+            strcpy(token, ceval_token_info[i].symbol);
+            len = (unsigned int) strlen(token);
+            if (!memcmp(expression - 1, token, len)) {
+                token_found = ceval_token_info[i].id;
+                isRightAssoc = (token_found == CEVAL_POW || token_found == CEVAL_CLOSEPAR ) ? 1 : 0;
+                break;
+            }
+        }
+        // if token is found
+        if (token_found > -1) {
+            // check if the token is a binary operator
+            if (ceval_is_binary_opr((ceval_node_id)token_found)) {
+                // a binary operator must be preceded by a number, a numerical constant, a clospar, or a factorial
+                if (previous_id == CEVAL_NUMBER ||
+                    previous_id == CEVAL_CONST_PI ||
+                    previous_id == CEVAL_CONST_E ||
+                    previous_id == CEVAL_CLOSEPAR) {
+                    // other tokens (other than CEVAL_NUMBER, CEVAL_CLOSEPAR) are allowed only before '+'s or '-'s
+                    expression = expression + (len - 1);
+                    node.id = (ceval_node_id)token_found;
+                    node.pre = ceval_token_prec(node.id);
+                } else {
+                    // if the operator is not preceded by a number, a numerical constant, a closepar, or a factorial, then check if the 
+                    // character is a sign ('+' or '-')
+                    if (c == '+') {
+                        node.id = CEVAL_POSSIGN;
+                        node.pre = ceval_token_prec(node.id);
+                    } else if (c == '-') {
+                        node.id = CEVAL_NEGSIGN;
+                        node.pre = ceval_token_prec(node.id);
+                    } else {
+                        // if it is not a sign, then it must be a misplaced character
+                        ceval_error("Misplaced '%c'.", c);
+                        ceval_delete_tree(root.right);
+                        root.right = NULL;
+                        return NULL;
+                    }
+                }
+            } else if (token_found == CEVAL_NUMBER){
+                // if the token is a number, then store it in an array
+                node.pre = ceval_token_prec(CEVAL_NUMBER);
+                unsigned int i;
+                char number[CEVAL_MAX_DIGITS];
+                for (i = 0; i + 1 < sizeof(number);) {
+                    number[i++] = c;
+                    c = * expression;
+                    if (('0' <= c && c <= '9') || 
+                                          c == '.')
+                        expression++;
+                    else 
+                        break;
+                }
+                number[i] = '\0';
+                //copy the contents of the number array at &node.number
+                sscanf(number, "%lf", & node.number);
+                node.id = CEVAL_NUMBER;
+                goto END;
+            } else if (token_found == CEVAL_WHITESPACE) {
+                // skip whitespace
+                continue;
+            } else {
+                // for any other token
+                expression = expression + (len - 1);
+                node.id = (ceval_node_id)token_found;
+                node.pre = ceval_token_prec(node.id);
+                if (node.id == CEVAL_CONST_PI || node.id == CEVAL_CONST_E) {
+                    node.number = (node.id == CEVAL_CONST_PI) ? CEVAL_PI : CEVAL_E;
+                }
+            }
+        } else {
+            // if the token is not found in the token table
+            ceval_error("Unknown token '%c'.", c);
+            ceval_delete_tree(root.right);
+            root.right = NULL;
+            return NULL;
+        }
+        END: ;
+        previous_id = node.id;
+        current = ceval_insert_node(current, node, isRightAssoc);
+    }
+    if (root.right) root.right -> parent = NULL;
+    return root.right;
+}
+void ceval_print_node(const ceval_node * node, int indent) {
+    int i;
+    char number[CEVAL_MAX_DIGITS];
+    const char * str;
+    if (!node) return;
+    ceval_print_node(node -> right, indent + 4);
+    if (node -> id == CEVAL_NUMBER) {
+        if ((long) node -> number == node -> number) //for integers, skip the trailing zeroes
+            sprintf(number, "%.0f", node -> number);
+        else sprintf(number, "%.2f", node -> number);
+        str = number;
+    } else {
+        str = ceval_token_symbol(node -> id);
+    }
+    for (i = 0; i < indent; i++) {
+        putchar(' ');
+        putchar(' ');
+    }
+    printf("%s\n", str);
+    ceval_print_node(node -> left, indent + 4);
+}
+void ceval_print_tree(const void * tree) {
+    ceval_print_node((const ceval_node * ) tree, 0);
+}
+/***************************************** !PARSE_TREE_CONSTRUCTION *******************************************/
+
+/***************************************** EVALUATION *******************************************/
+double ceval_evaluate_tree_(const ceval_node * );
+double ceval_evaluate_tree(const void * );
+
+double ceval_evaluate_tree_(const ceval_node * node) {
+    if (!node) 
+        return 0;
+    
+    double left, right;
+    left = ceval_evaluate_tree_(node -> left);
+    right = ceval_evaluate_tree_(node -> right);
+    switch (node -> id) {
+
+        //unary-right operators/functions (operate on the expression to their right)
+        case CEVAL_ABS: case CEVAL_EXP: case CEVAL_SQRT: case CEVAL_CBRT: 
+        case CEVAL_LN: case CEVAL_LOG10: case CEVAL_CEIL: case CEVAL_FLOOR: 
+        case CEVAL_SIGNUM: case CEVAL_FACTORIAL: case CEVAL_INT: case CEVAL_FRAC: 
+        case CEVAL_DEG2RAD: case CEVAL_RAD2DEG: case CEVAL_SIN: case CEVAL_COS: 
+        case CEVAL_TAN: case CEVAL_ASIN: case CEVAL_ACOS: case CEVAL_ATAN: 
+        case CEVAL_SINH: case CEVAL_COSH: case CEVAL_TANH: case CEVAL_NOT: 
+        case CEVAL_BIT_NOT: case CEVAL_POSSIGN: case CEVAL_NEGSIGN: 
+            if (node -> right != NULL) {
+                //operate on right operand
+                return ( * single_arg_fun[node -> id])(right);
+            } else {
+                ceval_error("Missing operand(s)");
+                return NAN;
+            }
+        //binary operators/functions
+        case CEVAL_COMMA:  
+        case CEVAL_OR:  case CEVAL_AND:  case CEVAL_BIT_OR:  case CEVAL_BIT_XOR: 
+        case CEVAL_BIT_AND:  case CEVAL_EQUAL:  case CEVAL_NOTEQUAL: case CEVAL_LESSER: 
+        case CEVAL_GREATER:  case CEVAL_LESSER_S:  case CEVAL_GREATER_S:  case CEVAL_BIT_LSHIFT:  
+        case CEVAL_BIT_RSHIFT:  case CEVAL_PLUS:  case CEVAL_MINUS:  case CEVAL_TIMES:  
+        case CEVAL_DIVIDE:  case CEVAL_MODULO:  case CEVAL_QUOTIENT:  case CEVAL_POW: 
+        case CEVAL_GCD:  case CEVAL_HCF:  case CEVAL_LCM:  case CEVAL_LOG: 
+        case CEVAL_ATAN2:  case CEVAL_SCI2DEC: case CEVAL_POWFUN:
+            if (node -> left == NULL) {
+                return ( * double_arg_fun[node -> id])(left, right, -1);
+            } else if (node -> right == NULL) {
+                return ( * double_arg_fun[node -> id])(left, right, 1);
+            } else {
+                return ( * double_arg_fun[node -> id])(left, right, 0);
+            }
+            default:
+                return node -> number;
+    }
+}
+double ceval_evaluate_tree(const void * node) {
+    return (node == NULL)? NAN :
+            ceval_evaluate_tree_((ceval_node * ) node);
+}
+/***************************************** !EVALUATION *******************************************/
+
+/***************************************** MAIN FUNCTIONS *******************************************/
+// functions accessible from main() 
+// - double ceval_result(char * inp) returns the result of valid math expression stored as a char array `inp`
+// - void ceval_tree(char * inp) prints the parse tree for the input expression `inp`
+// - define CEVAL_EPSILON (default value : 1e-2), CEVAL_DELTA (default value : 1e-6) and CEVAL_MAX_DIGITS (default value : 15) manually before the include directive
+// - define CEVAL_STOICAL before the #include directive to use the parser/evaluator in stoical (non-complaining) mode. It suppresses all the error messages from [ceval]. 
+
+double ceval_result(char * expr) {
+    void * tree = ceval_make_tree(expr);
+    double result = ceval_evaluate_tree(tree);
+    ceval_delete_tree(tree);
+    return result;
+}
+void ceval_tree(char * expr) {
+    void * tree = ceval_make_tree(expr);
+    ceval_print_tree(tree);
+    ceval_delete_tree(tree);
+}
+
+#ifdef CEVAL_CXX
+    double ceval_result(std::string expr) {
+        return ceval_result((char * ) expr.c_str());
+    }
+    void ceval_tree(std::string expr) {
+        ceval_tree((char * ) expr.c_str());
+    }
+#endif
+/***************************************** !MAIN FUNCTIONS *******************************************/
+#endif
diff --git a/src/third_party/ceval/package.json b/src/third_party/ceval/package.json
new file mode 100644
index 0000000..beb64b9
--- /dev/null
+++ b/src/third_party/ceval/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "ceval",
+  "version": "1.0.0",
+  "description": "A single-header C/C++ library for parsing and evaluation of arithmetic expressions",
+  "main": "ceval.h",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/erstan/ceval-single-header.git"
+  },
+  "keywords": [
+    "c",
+    "calculator",
+    "math",
+    "interpreter",
+    "parser",
+    "eval",
+    "single-header"
+  ],
+  "author": "Ershad Tantry",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/erstan/ceval-single-header/issues"
+  },
+  "homepage": "https://github.com/erstan/ceval-single-header#readme"
+}