Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/juancarlospaco/nim-contra
Lightweight Self-Documenting Design by Contract Programming and Security Hardened mode.
https://github.com/juancarlospaco/nim-contra
assertion-library compiletime contracts contracts-programming design-by-contract hardened hardening immutable immutable-objects javascript nim nim-lang nimscript security-hardening self-test testing-framework testing-tools
Last synced: 6 days ago
JSON representation
Lightweight Self-Documenting Design by Contract Programming and Security Hardened mode.
- Host: GitHub
- URL: https://github.com/juancarlospaco/nim-contra
- Owner: juancarlospaco
- License: mit
- Created: 2019-06-28T04:54:08.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2022-01-26T21:47:47.000Z (almost 3 years ago)
- Last Synced: 2024-10-15T00:53:16.461Z (20 days ago)
- Topics: assertion-library, compiletime, contracts, contracts-programming, design-by-contract, hardened, hardening, immutable, immutable-objects, javascript, nim, nim-lang, nimscript, security-hardening, self-test, testing-framework, testing-tools
- Language: Nim
- Homepage: https://dev.to/juancarlospaco/design-by-contract-immutability-side-effects-and-gulag-44fk
- Size: 327 KB
- Stars: 54
- Watchers: 5
- Forks: 3
- Open Issues: 3
-
Metadata Files:
- Readme: README.rst
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-nim - contra - Lightweight and fast self-documenting design by contract programming. (Language Features / Contracts)
README
Contra
======- Lightweight and fast Self-Documenting Design by Contract Programming, Design by Contract, zero cost at runtime.
- `Security Hardened `_ mode (based from `Debian Hardened `_ & `Gentoo Hardened `_, checked with `hardening-check `_).
- Change `Immutable variables `_, into Immutable variables, Immutable programming... image:: https://raw.githubusercontent.com/juancarlospaco/nim-contra/master/contra.jpg
:align: center- Produces no code at all when build for release, `KISS `_
- Works on NimScript, JavaScript, ``{.compiletime.}`` and ``static:``.Use
===Design By Contract
------------------.. image:: https://raw.githubusercontent.com/juancarlospaco/nim-contra/master/selfdocumentdbc.png
:align: center.. code-block:: nim
import contra
func funcWithContract(mustBePositive: int): int =
preconditions mustBePositive > 0, mustBePositive > -1 ## Require (Preconditions)
postconditions result > 0, result < int32.high ## Ensure (Postconditions)result = mustBePositive - 1 ## Mimic some logic, notice theres no "body" block
discard funcWithContract(2)
# discard funcWithContract(0) # Uncomment to see it fail as expected.Hardened mode
-------------.. code-block:: nim
import contra
hardenedBuild() # Security Hardened mode enabled, compile with: -d:hardened
echo "Hello World"Changing Immutable Variables
----------------------------.. code-block:: nim
import contra
type Person = object # Changing Immutable Variables,into Immutable Variables.
name: string
age: Naturallet
bob = Person(name: "Bob", age: 42) # Immutable Variable, original.
olderBob = bob.deepCopy: # Immutable Variable, but changed.
this.age = 45
this.name = this.name[0..^2]echo bob # (name: "Bob", age: 42) Original Immutable
echo olderBob # (name: "Bo", age: 45) Changed ImmutableIts inspired by Scala:
.. code-block:: scala
val immutableButChanged = immutable.copy(attribute = 9)
Assercho
--------``assert(conditionBool, errorString)`` + ``echo(Nim_Code)`` + ``printf(C_Code)`` Combined *"3-in-1"*.
.. image:: https://raw.githubusercontent.com/juancarlospaco/nim-contra/master/assercho.png
:align: center- Is different than ``echo`` that you dont have to manually remove it from the code for Release builds.
- Is different than ``assert`` that it prints to terminal when ``conditionBool`` is ``true``.It only works ``when not defined(release) and not defined(danger)`` for Debugging purposes.
C Source code debug is similar to JS Source Maps, shows C code corresponding to the same Nim code.
The assertion is a vanilla ``assert``, when fails produces an ``AssertionError``.
Produces No code at all when build for Release, zero runtime performance cost. ``assercho`` is a Macro.
``assercho`` when ``assert`` is ``true`` (Ok)
.. code-block:: nim
import contra
let foo = 42
let bar = 9
assercho(foo > bar, "Assercho for all the Brochachos!") # 42 > 9.. code-block:: bash
Nim foo > bar = true --> /home/juan/code/example.nim(3, 8)
C (bar_1kqJRPNteJWdjlPR09aibuA < foo_FvFJfeatIb8qi8DN7lzoAQ) = 1 --> /tmp/example.nim.c(9)``assercho`` when ``assert`` is ``false`` (Error)
.. code-block:: nim
import contra
let foo = 42
let bar = 9
assercho(foo < bar, "Assercho for all the Brochachos!") # 42 < 9.. code-block:: bash
Nim foo < bar = false --> /home/juan/code/example.nim(3, 8)
C (bar_1kqJRPNteJWdjlPR09aibuA < foo_FvFJfeatIb8qi8DN7lzoAQ) = 0 --> /tmp/example.nim.c(9)
Error: unhandled exception: `foo < bar` Assercho for all the Brochachos! [AssertionError]Replaces approx the following code:
.. code-block:: nim
when not defined(danger) and not defined(release):
debugEcho "foo = ", foo
debugEcho "bar = ", bar
debugEcho foo > bar
assert foo > bar, "Error message"
# Custom Macro to get the Nim Source Code line info of origin (macros.NimNode.LineInfo)
# Custom Macro/Template to get the C Source Code line info of originCompile-Time Term-Rewriting Template Optimizations
--------------------------------------------------- ``echo()`` and ``debugEcho()`` gets Rewritten to use ``fwrite()`` from ``stdio.h`` directly. Logging is not touched.
- ``fwrite()`` is faster than ``puts()``, ``puts()`` is faster than ``printf()``. ``fwrite() > puts() > printf()``.
- Optimizations are optional, only get enabled ``when defined(release) and defined(danger)``... code-block:: nim
echo "a", "b", "c"
Gets optimized to:
.. code-block:: c
fwrite("abc\012", ((unsigned int) 1), ((unsigned int) 4), stdout);
- Float Division is slower than multiplication. `1` CPU Cycle for Multiplication, `100` CPU Cycles or more for Division.
- Float Division gets Rewritten to multiplication with the inverse.
- Example ``x / 3.0`` --> ``x * static(1.0 / 3.0)``.
- Optimizations are optional, only get enabled ``when defined(release) and defined(danger)``... code-block:: nim
var x, y = 2.0
echo x / 2.0Gets optimized to:
.. code-block:: c
NF x_9b3J8iZeIHRoRKYxMY9a9bzzQ;
x_9b3J8iZeIHRoRKYxMY9a9bzzQ = 2.0000000000000000e+00;
( (NF)(x_9b3J8iZeIHRoRKYxMY9a9bzzQ) * (NF)(5.0000000000000000e-01) );- `For more info check this commit. `_
Description
-----------**Contract Preconditions:**
- ``preconditions`` takes preconditions separated by commas, asserts on arguments or local variables.
**Contract Postconditions:**
- ``postconditions`` takes postconditions separated by commas, must assert on ``result``, can assert on local variables.
**Contracts Preconditions and Postconditions:**
- ``postconditions`` must be AFTER ``preconditions``.
- ``postconditions`` must NOT be repeated.
- ``-d:contracts`` Force enable Contracts, can be used independently of ``-d:release``.**Security Hardened Mode:**
- ``-d:hardened`` Force enable Security Hardened mode, can be used independently of ``-d:release``.
- ``-d:hardened`` requires ``-d:contracts``.
- Security Hardened mode only works for default target backend.
- Produces no code at all if ``-d:hardened`` is not defined.
- ``hardenedBuild()`` is 1 Template, takes no arguments, returns nothing.
- ``hardenedBuild()`` must be called on the root top of your main module.
- Hardened build is ideal companion for a Contracts module, still optional anyway.**Changing Immutable Variables:**
- ``deepCopy`` Lets you change Immutable Variables, into Immutable Variables, using Mutated copies. It mimic Scala's ``val immutableButChanged = immutable.copy(attribute = 9)``. Immutable programming.
**Assercho:**
- ``assert`` + ``echo`` = ``assercho``. Assertive programming util.
Install
-------- ``nimble install contra``
FAQ
---- Why not just use `Contracts `_ ?
.. code-block::
$ cat example.nim
import contracts
from math import sqrt, floor
proc isqrt[T: SomeInteger](x: T): T {.contractual.} =
require:
x >= 0
ensure:
result * result <= x
(result+1) * (result+1) > x
body:
(T)(x.toBiggestFloat().sqrt().floor().toBiggestInt())
echo isqrt(18)
echo isqrt(-8)$ nim js -r example.nim
Error: undeclared identifier: 'deepCopy'$ nim e example.nim
Error: undeclared identifier: 'deepCopy'$ cat example2compiletime.nim
import contracts
from math import sqrt, floor
proc isqrt[T: SomeInteger](x: T): T {.contractual, compiletime.} =
require:
x >= 0
ensure:
result * result <= x
(result+1) * (result+1) > x
body:
(T)(x.toBiggestFloat().sqrt().floor().toBiggestInt())
echo isqrt(18)
echo isqrt(-8)$ nim c -r example2compiletime.nim
Error: request to generate code for .compileTime proc: isqrt$ cloc ~/.nimble/pkgs/contracts-0.1.0/
Language files blank comment code
----------------------------------------------------------------
Nim 21 119 515 640- Whats Contract Programming, Design by Contract?.
https://www.youtube.com/watch?v=DRVoh5XiAZo
https://en.wikipedia.org/wiki/Defensive_programming#Other_techniques
http://stackoverflow.com/questions/787643/benefits-of-assertive-programming
https://en.wikipedia.org/wiki/Hoare_logic#Hoare_triple
- What about No Side Effects?.
https://nim-lang.org/docs/manual.html#procedures-func
https://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma
- What about Types?.
https://nim-lang.org/docs/manual_experimental.html#concepts
- How to use this at Compile Time?.
Add ``{.compiletime.}`` or ``static:``.
- What about ``assume`` blocks?.
Assume blocks produce no code at all and are only meant for human reading only,
you can do that using ``discard`` or similar contruct on Nim. KISS.- What about ``body`` blocks?.
This library does NOT uses nor needs ``body`` blocks.
- What about ``invariant`` blocks?.
You can pass Invariants on the ``postconditions`` block.
- What about ``forall`` and ``forsome`` blocks?.
Use ``sequtils.filterIt``, ``sequtils.mapIt``, ``sequtils.keepItIf``, ``sequtils.allIt``, ``sequtils.anyIt``, etc.
- What about ``ghost`` block?.
Use ``when defined(release):`` or ``when defined(contracts):``
- Whats the performance and speed cost of using Contra?.
Zero cost at runtime, since it produces no code at all when build for Release.
- I prefer the naming ``require`` and ``ensure`` ?.
.. code-block:: nim
from contra import preconditions as require
from contra import postconditions as ensure- I prefer the naming ``pre`` and ``post`` ?.
.. code-block:: nim
from contra import preconditions as pre
from contra import postconditions as post- If I add this to my project I am forced to use it everywhere?.
No.
The code will just work on blocks without Contract.
You only need to add 2 lines to your existing code (1 for Preconditions, 1 for Postconditions).
Is recommended to at least use it con "core" functionality.- Whats Hardened mode ?.
https://en.wikipedia.org/wiki/Hardening_%28computing%29#Binary_hardening
- More Documentation?.
``nim doc contra.nim``
*" TDD is Poor-Man's Contracts "*